/* Copyright (C) 2008 Grame This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __APPLE__ #include #endif #include "JackAudioAdapter.h" #ifndef MY_TARGET_OS_IPHONE #include "JackLibSampleRateResampler.h" #endif #include "JackTime.h" #include "JackError.h" #include namespace Jack { #ifdef JACK_MONITOR void MeasureTable::Write(int time1, int time2, float r1, float r2, int pos1, int pos2) { int pos = (++fCount) % TABLE_MAX; fTable[pos].time1 = time1; fTable[pos].time2 = time2; fTable[pos].r1 = r1; fTable[pos].r2 = r2; fTable[pos].pos1 = pos1; fTable[pos].pos2 = pos2; } void MeasureTable::Save(unsigned int fHostBufferSize, unsigned int fHostSampleRate, unsigned int fAdaptedSampleRate, unsigned int fAdaptedBufferSize) { FILE* file = fopen("JackAudioAdapter.log", "w"); int max = (fCount) % TABLE_MAX - 1; for (int i = 1; i < max; i++) { fprintf(file, "%d \t %d \t %d \t %f \t %f \t %d \t %d \n", fTable[i].delta, fTable[i].time1, fTable[i].time2, fTable[i].r1, fTable[i].r2, fTable[i].pos1, fTable[i].pos2); } fclose(file); // No used for now // Adapter timing 1 file = fopen("AdapterTiming1.plot", "w"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 2 title \"Ringbuffer error\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 3 title \"Ringbuffer error with timing correction\" with lines"); fprintf(file, "\n unset multiplot\n"); fprintf(file, "set output 'AdapterTiming1.svg\n"); fprintf(file, "set terminal svg\n"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 2 title \"Consumer interrupt period\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 3 title \"Producer interrupt period\" with lines\n"); fprintf(file, "unset multiplot\n"); fprintf(file, "unset output\n"); fclose(file); // Adapter timing 2 file = fopen("AdapterTiming2.plot", "w"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"resampling ratio\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with lines"); fprintf(file, "\n unset multiplot\n"); fprintf(file, "set output 'AdapterTiming2.svg\n"); fprintf(file, "set terminal svg\n"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"resampling ratio\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with lines\n"); fprintf(file, "unset multiplot\n"); fprintf(file, "unset output\n"); fclose(file); // Adapter timing 3 file = fopen("AdapterTiming3.plot", "w"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with lines"); fprintf(file, "\n unset multiplot\n"); fprintf(file, "set output 'AdapterTiming3.svg\n"); fprintf(file, "set terminal svg\n"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with lines\n"); fprintf(file, "unset multiplot\n"); fprintf(file, "unset output\n"); fclose(file); } #endif void JackAudioAdapterInterface::GrowRingBufferSize() { fRingbufferCurSize *= 2; } void JackAudioAdapterInterface::AdaptRingBufferSize() { if (fHostBufferSize > fAdaptedBufferSize) { fRingbufferCurSize = 4 * fHostBufferSize; } else { fRingbufferCurSize = 4 * fAdaptedBufferSize; } } void JackAudioAdapterInterface::ResetRingBuffers() { if (fRingbufferCurSize > DEFAULT_RB_SIZE) { fRingbufferCurSize = DEFAULT_RB_SIZE; } for (int i = 0; i < fCaptureChannels; i++) { fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); } for (int i = 0; i < fPlaybackChannels; i++) { fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); } } void JackAudioAdapterInterface::Reset() { ResetRingBuffers(); fRunning = false; } #ifdef MY_TARGET_OS_IPHONE void JackAudioAdapterInterface::Create() {} #else void JackAudioAdapterInterface::Create() { //ringbuffers fCaptureRingBuffer = new JackResampler*[fCaptureChannels]; fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels]; if (fAdaptative) { AdaptRingBufferSize(); jack_info("Ringbuffer automatic adaptative mode size = %d frames", fRingbufferCurSize); } else { if (fRingbufferCurSize > DEFAULT_RB_SIZE) { fRingbufferCurSize = DEFAULT_RB_SIZE; } jack_info("Fixed ringbuffer size = %d frames", fRingbufferCurSize); } for (int i = 0; i < fCaptureChannels; i++ ) { fCaptureRingBuffer[i] = new JackLibSampleRateResampler(fQuality); fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); } for (int i = 0; i < fPlaybackChannels; i++ ) { fPlaybackRingBuffer[i] = new JackLibSampleRateResampler(fQuality); fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); } if (fCaptureChannels > 0) { jack_log("ReadSpace = %ld", fCaptureRingBuffer[0]->ReadSpace()); } if (fPlaybackChannels > 0) { jack_log("WriteSpace = %ld", fPlaybackRingBuffer[0]->WriteSpace()); } } #endif void JackAudioAdapterInterface::Destroy() { for (int i = 0; i < fCaptureChannels; i++) { delete(fCaptureRingBuffer[i]); } for (int i = 0; i < fPlaybackChannels; i++) { delete (fPlaybackRingBuffer[i]); } delete[] fCaptureRingBuffer; delete[] fPlaybackRingBuffer; } int JackAudioAdapterInterface::PushAndPull(float** inputBuffer, float** outputBuffer, unsigned int frames) { bool failure = false; fRunning = true; // Finer estimation of the position in the ringbuffer int delta_frames = (fPullAndPushTime > 0) ? (int)((float(long(GetMicroSeconds() - fPullAndPushTime)) * float(fAdaptedSampleRate)) / 1000000.f) : 0; double ratio = 1; // TODO : done like this just to avoid crash when input only or output only... if (fCaptureChannels > 0) { ratio = fPIControler.GetRatio(fCaptureRingBuffer[0]->GetError() - delta_frames); } else if (fPlaybackChannels > 0) { ratio = fPIControler.GetRatio(fPlaybackRingBuffer[0]->GetError() - delta_frames); } #ifdef JACK_MONITOR if (fCaptureRingBuffer && fCaptureRingBuffer[0] != NULL) fTable.Write(fCaptureRingBuffer[0]->GetError(), fCaptureRingBuffer[0]->GetError() - delta_frames, ratio, 1/ratio, fCaptureRingBuffer[0]->ReadSpace(), fCaptureRingBuffer[0]->ReadSpace()); #endif // Push/pull from ringbuffer for (int i = 0; i < fCaptureChannels; i++) { fCaptureRingBuffer[i]->SetRatio(ratio); if (inputBuffer[i]) { if (fCaptureRingBuffer[i]->WriteResample(inputBuffer[i], frames) < frames) { failure = true; } } } for (int i = 0; i < fPlaybackChannels; i++) { fPlaybackRingBuffer[i]->SetRatio(1/ratio); if (outputBuffer[i]) { if (fPlaybackRingBuffer[i]->ReadResample(outputBuffer[i], frames) < frames) { failure = true; } } } // Reset all ringbuffers in case of failure if (failure) { jack_error("JackAudioAdapterInterface::PushAndPull ringbuffer failure... reset"); if (fAdaptative) { GrowRingBufferSize(); jack_info("Ringbuffer size = %d frames", fRingbufferCurSize); } ResetRingBuffers(); return -1; } else { return 0; } } int JackAudioAdapterInterface::PullAndPush(float** inputBuffer, float** outputBuffer, unsigned int frames) { fPullAndPushTime = GetMicroSeconds(); if (!fRunning) { return 0; } int res = 0; // Push/pull from ringbuffer for (int i = 0; i < fCaptureChannels; i++) { if (inputBuffer[i]) { if (fCaptureRingBuffer[i]->Read(inputBuffer[i], frames) < frames) { res = -1; } } } for (int i = 0; i < fPlaybackChannels; i++) { if (outputBuffer[i]) { if (fPlaybackRingBuffer[i]->Write(outputBuffer[i], frames) < frames) { res = -1; } } } return res; } int JackAudioAdapterInterface::SetHostBufferSize(jack_nframes_t buffer_size) { fHostBufferSize = buffer_size; if (fAdaptative) { AdaptRingBufferSize(); } return 0; } int JackAudioAdapterInterface::SetAdaptedBufferSize(jack_nframes_t buffer_size) { fAdaptedBufferSize = buffer_size; if (fAdaptative) { AdaptRingBufferSize(); } return 0; } int JackAudioAdapterInterface::SetBufferSize(jack_nframes_t buffer_size) { SetHostBufferSize(buffer_size); SetAdaptedBufferSize(buffer_size); return 0; } int JackAudioAdapterInterface::SetHostSampleRate(jack_nframes_t sample_rate) { fHostSampleRate = sample_rate; fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); return 0; } int JackAudioAdapterInterface::SetAdaptedSampleRate(jack_nframes_t sample_rate) { fAdaptedSampleRate = sample_rate; fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); return 0; } int JackAudioAdapterInterface::SetSampleRate(jack_nframes_t sample_rate) { SetHostSampleRate(sample_rate); SetAdaptedSampleRate(sample_rate); return 0; } void JackAudioAdapterInterface::SetInputs(int inputs) { jack_log("JackAudioAdapterInterface::SetInputs %d", inputs); fCaptureChannels = inputs; } void JackAudioAdapterInterface::SetOutputs(int outputs) { jack_log("JackAudioAdapterInterface::SetOutputs %d", outputs); fPlaybackChannels = outputs; } int JackAudioAdapterInterface::GetInputs() { //jack_log("JackAudioAdapterInterface::GetInputs %d", fCaptureChannels); return fCaptureChannels; } int JackAudioAdapterInterface::GetOutputs() { //jack_log ("JackAudioAdapterInterface::GetOutputs %d", fPlaybackChannels); return fPlaybackChannels; } } // namespace