jack2/common/JackAudioAdapterInterface.cpp

394 lines
14 KiB
C++

/*
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 <TargetConditionals.h>
#endif
#include "JackAudioAdapter.h"
#ifndef MY_TARGET_OS_IPHONE
#include "JackLibSampleRateResampler.h"
#endif
#include "JackTime.h"
#include "JackError.h"
#include <stdio.h>
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