/* * Created on Oct 3, 2004 * * Copyright (c) 2005 Peter Johan Salomonsen (http://www.petersalomonsen.com) * * http://www.frinika.com * * This file is part of Frinika. * * Frinika is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frinika is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Frinika; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.frinika.tools; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import uk.org.toot.audio.core.AudioBuffer; import uk.org.toot.audio.core.AudioProcess; import uk.org.toot.audio.mixer.AudioMixer; import com.frinika.sequencer.FrinikaSequence; import com.frinika.sequencer.FrinikaSequencer; import com.frinika.sequencer.FrinikaTrackWrapper; /** * @author Peter Johan Salomonsen * */ public class MyMidiRenderer extends InputStream{ long tickPosition=0; double ticksPerSecond; AudioMixer mixer; double startTimeInMicros; // PJL FrinikaSequence sequence; FrinikaSequencer sequencer; double samplePos = 0; double tickSamples = 0; float sampleRate; int available; byte[] buffer; HashMap<FrinikaTrackWrapper,Integer> trackIndex = new HashMap<FrinikaTrackWrapper,Integer>(); int readpos = 0; byte[] byteBuffer = null; public MyMidiRenderer(AudioMixer mixer, FrinikaSequencer sequencer, long startTick, int ticks,float sampleRate) { this.mixer = mixer; this.sampleRate = sampleRate; this.sequence = (FrinikaSequence)sequencer.getSequence(); this.sequencer = sequencer; this.tickPosition = startTick; sequencer.setTickPosition(startTick); startTimeInMicros=sequencer.getMicrosecondPosition(); // TODO TEMPOLIST this.available =(int)(getNumberOfSamples(ticks) * 4); tickSamples = getNumberOfSamples(1); AudioProcess audioProcess = new AudioProcess() { public void close() { // TODO Auto-generated method stub } public void open() { // TODO Auto-generated method stub } public int processAudio(AudioBuffer buffer) { if(byteBuffer == null) byteBuffer = new byte[buffer.getSampleCount()*2*2]; /** * Convert floats to bytes - this is for export wav */ int i = 0; for(int n = 0;n<buffer.getSampleCount();n++) { //Left float floatSample = buffer.getChannel(0)[n]; short sample; if(floatSample>=1.0f) sample=0x7fff; else if(floatSample<=-1.0f) sample=-0x8000; else sample = (short)(floatSample*0x8000); byteBuffer[i++] = (byte)((sample & 0xff00) >> 8); byteBuffer[i++] = (byte)(sample & 0xff); //Right floatSample = buffer.getChannel(1)[n]; if(floatSample>=1.0f) sample=0x7fff; else if(floatSample<=-1.0f) sample=-0x8000; else sample = (short)(floatSample*0x8000); byteBuffer[i++] = (byte)((sample & 0xff00) >> 8); byteBuffer[i++] = (byte)(sample & 0xff); } return AUDIO_OK; } }; // Replace mixer output process mixer.getMainBus().setOutputProcess(audioProcess); } @Override public int available() throws IOException { return available; } /** * TODO fix for tempo changes * * @param ticks * @return */ double getNumberOfSamples(int ticks) { double ticksPerSecond = (sequence.getResolution() * sequencer.getTempoInBPM()) / 60.0; double seconds = ticks / ticksPerSecond; return(seconds * sampleRate); } void fillBuffer() { while (byteBuffer == null) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } mixer.setEnabled(true); // fix infinite loop problem PJL mixer.work(-1); // TODO what is the buffer size here } for(int n=0;n<buffer.length;n++) { if(readpos == byteBuffer.length) { mixer.work(-1); readpos = 0; } buffer[n] = byteBuffer[readpos++]; } } int bufferPos = 0; /* (non-Javadoc) * @see java.io.InputStream#read() */ public int read() throws IOException { if(bufferPos == 0) { double newSamplePos=samplePos+tickSamples; int bufferSize = (int)(newSamplePos)-(int)(samplePos); samplePos = newSamplePos; if(buffer == null || buffer.length != bufferSize*4) buffer = new byte[bufferSize*4]; // This has to be done for each tick in case of tempo changes tickSamples = getNumberOfSamples(1); //nextTick(); sequencer.nonRealtimeNextTick(); fillBuffer(); } int ret = 0xff & buffer[bufferPos++]; if(bufferPos == buffer.length) bufferPos = 0; available--; return(ret); } public int read(byte[] b, int off, int len) throws IOException { for (int i = off; i < len; i++) { if(available == 0) return i - off; if(bufferPos == 0) { double newSamplePos=samplePos+tickSamples; int bufferSize = (int)(newSamplePos)-(int)(samplePos); samplePos = newSamplePos; if(buffer == null || buffer.length != bufferSize*4) buffer = new byte[bufferSize*4]; // This has to be done for each tick in case of tempo changes tickSamples = getNumberOfSamples(1); //nextTick(); sequencer.nonRealtimeNextTick(); fillBuffer(); } b[i] = buffer[bufferPos++]; if(bufferPos == buffer.length) bufferPos = 0; available--; } return len; } public int read(byte[] b) throws IOException { return read(b, 0, b.length); } /** * PJL * @return start position of rendering in microseconds */ public double getStartTimeInMicros() { return startTimeInMicros; } }