|  | /*
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.
*/
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include "JackPortAudioAdapter.h"
#include "JackError.h"
namespace Jack
{
int JackPortAudioAdapter::Render(const void* inputBuffer, void* outputBuffer,
                                unsigned long framesPerBuffer,
                                const PaStreamCallbackTimeInfo* timeInfo,
                                PaStreamCallbackFlags statusFlags,
                                void* userData)
{
    JackPortAudioAdapter* adapter = static_cast<JackPortAudioAdapter*>(userData);
    float** paBuffer;
    float* buffer;
    bool failure = false;
    jack_nframes_t time1, time2;
    adapter->ResampleFactor(time1, time2);
    paBuffer = (float**)inputBuffer;
    for (int i = 0; i < adapter->fCaptureChannels; i++) {
        buffer = (float*)paBuffer[i];
        adapter->fCaptureRingBuffer[i]->SetRatio(time1, time2);
        if (adapter->fCaptureRingBuffer[i]->WriteResample(buffer, framesPerBuffer) < framesPerBuffer)
            failure = true;
    }
    paBuffer = (float**)outputBuffer;
    for (int i = 0; i < adapter->fPlaybackChannels; i++) {
        buffer = (float*)paBuffer[i];
        adapter->fPlaybackRingBuffer[i]->SetRatio(time2, time1);
        if (adapter->fPlaybackRingBuffer[i]->ReadResample(buffer, framesPerBuffer) < framesPerBuffer)
            failure = true;
    }
#ifdef JACK_MONITOR
    adapter->fTable.Write(time1, time2, double(time1) / double(time2), double(time2) / double(time1),
         adapter->fCaptureRingBuffer[0]->ReadSpace(),  adapter->fPlaybackRingBuffer[0]->WriteSpace());
#endif
    // Reset all ringbuffers in case of failure
    if (failure) {
        jack_error("JackPortAudioAdapter::Render ringbuffer failure... reset");
        adapter->ResetRingBuffers();
    }
    return paContinue;
}
JackPortAudioAdapter::JackPortAudioAdapter(jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params)
                :JackAudioAdapterInterface(buffer_size, sample_rate)
{
	jack_log ( "JackPortAudioAdapter::JackPortAudioAdapter buffer_size = %d, sample_rate = %d", buffer_size, sample_rate );
    const JSList* node;
    const jack_driver_param_t* param;
	int in_max = 0;
	int out_max = 0;
    fCaptureChannels = 0;
    fPlaybackChannels = 0;
	fInputDevice = Pa_GetDefaultInputDevice();
	fOutputDevice = Pa_GetDefaultOutputDevice();
    for (node = params; node; node = jack_slist_next(node)) {
        param = (const jack_driver_param_t*) node->data;
        switch (param->character) {
            case 'i':
                fCaptureChannels = param->value.ui;
                break;
            case 'o':
                fPlaybackChannels = param->value.ui;
                break;
            case 'C':
				if ( fPaDevices.GetInputDeviceFromName(param->value.str, fInputDevice, in_max) < 0 ) {
					jack_error ( "Can't use %s, taking default input device", param->value.str );
					fInputDevice = Pa_GetDefaultInputDevice();
				}
                break;
            case 'P':
				if ( fPaDevices.GetOutputDeviceFromName(param->value.str, fOutputDevice, out_max) < 0 ) {
					jack_error ( "Can't use %s, taking default output device", param->value.str );
					fOutputDevice = Pa_GetDefaultOutputDevice();
				}
                break;
            case 'r':
                SetAudioSampleRate(param->value.ui);
                break;
            case 'd':
				if ( fPaDevices.GetInputDeviceFromName(param->value.str, fInputDevice, in_max) < 0 ) {
					jack_error ( "Can't use %s, taking default input device", param->value.str );
				}
				if ( fPaDevices.GetOutputDeviceFromName(param->value.str, fOutputDevice, out_max) < 0 ) {
					jack_error ( "Can't use %s, taking default output device", param->value.str );
				}
                break;
            case 'l':
				fPaDevices.DisplayDevicesNames();
                break;
        }
    }
	//max channels
	if ( in_max == 0 )
		in_max = fPaDevices.GetDeviceInfo(fInputDevice)->maxInputChannels;
	if ( out_max == 0 )
		out_max = fPaDevices.GetDeviceInfo(fOutputDevice)->maxOutputChannels;
	//effective channels
	if ( ( fCaptureChannels == 0 ) || ( fCaptureChannels > in_max ) )
		fCaptureChannels = in_max;
	if ( ( fPlaybackChannels == 0 ) || ( fPlaybackChannels > out_max ) )
		fPlaybackChannels = out_max;
}
int JackPortAudioAdapter::Open()
{
    PaError err;
    PaStreamParameters inputParameters;
    PaStreamParameters outputParameters;
    if (JackAudioAdapterInterface::Open() < 0)
        return -1;
	jack_log("JackPortAudioAdapter::Open fInputDevice = %d DeviceName %s", fInputDevice, fPaDevices.GetFullName(fInputDevice).c_str());
	jack_log("JackPortAudioAdapter::Open fOutputDevice = %d DeviceName %s", fOutputDevice, fPaDevices.GetFullName(fOutputDevice).c_str());
    jack_log("JackPortAudioAdapter::Open fBufferSize = %u fSampleRate %u", fBufferSize, fSampleRate);
    inputParameters.device = fInputDevice;
    inputParameters.channelCount = fCaptureChannels;
    inputParameters.sampleFormat = paFloat32 | paNonInterleaved;		// 32 bit floating point output
    inputParameters.suggestedLatency = (fInputDevice != paNoDevice)		// TODO: check how to setup this on ASIO
                                       ? fPaDevices.GetDeviceInfo(fInputDevice)->defaultLowInputLatency
                                       : 0;
    inputParameters.hostApiSpecificStreamInfo = NULL;
    outputParameters.device = fOutputDevice;
    outputParameters.channelCount = fPlaybackChannels;
    outputParameters.sampleFormat = paFloat32 | paNonInterleaved;		// 32 bit floating point output
    outputParameters.suggestedLatency = (fOutputDevice != paNoDevice)	// TODO: check how to setup this on ASIO
                                        ? fPaDevices.GetDeviceInfo(fOutputDevice)->defaultLowOutputLatency
                                        : 0;
    outputParameters.hostApiSpecificStreamInfo = NULL;
    err = Pa_OpenStream(&fStream,
                        (fInputDevice == paNoDevice) ? 0 : &inputParameters,
                        (fOutputDevice == paNoDevice) ? 0 : &outputParameters,
                        fSampleRate,
                        fBufferSize,
                        paNoFlag,  // Clipping is on...
                        Render,
                        this);
    if (err != paNoError) {
        jack_error("Pa_OpenStream error = %s", Pa_GetErrorText(err));
        return -1;
    }
    err = Pa_StartStream(fStream);
    if (err != paNoError) {
         jack_error("Pa_StartStream error = %s", Pa_GetErrorText(err));
         return -1;
    }
    jack_log("JackPortAudioAdapter::Open OK");
    return 0;
}
int JackPortAudioAdapter::Close()
{
#ifdef JACK_MONITOR
    fTable.Save();
#endif
    jack_log("JackPortAudioAdapter::Close");
    Pa_StopStream(fStream);
    jack_log("JackPortAudioAdapter:: Pa_StopStream");
    Pa_CloseStream(fStream);
    jack_log("JackPortAudioAdapter:: Pa_CloseStream");
    return JackAudioAdapterInterface::Close();
}
int JackPortAudioAdapter::SetBufferSize(jack_nframes_t buffer_size)
{
    JackAudioAdapterInterface::SetBufferSize(buffer_size);
    Close();
    return Open();
}
} // namespace
#ifdef __cplusplus
extern "C"
{
#endif
    EXPORT jack_driver_desc_t* jack_get_descriptor()
    {
        jack_driver_desc_t *desc;
        unsigned int i;
        desc = (jack_driver_desc_t*)calloc(1, sizeof(jack_driver_desc_t));
        strcpy(desc->name, "portaudio-adapter");
        desc->nparams = 7;
        desc->params = (jack_driver_param_desc_t*)calloc(desc->nparams, sizeof(jack_driver_param_desc_t));
        i = 0;
        strcpy(desc->params[i].name, "inchannels");
        desc->params[i].character = 'i';
        desc->params[i].type = JackDriverParamInt;
        desc->params[i].value.ui = 0;
        strcpy(desc->params[i].short_desc, "Maximum number of input channels");
        strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
        i++;
        strcpy(desc->params[i].name, "outchannels");
        desc->params[i].character = 'o';
        desc->params[i].type = JackDriverParamInt;
        desc->params[i].value.ui = 0;
        strcpy(desc->params[i].short_desc, "Maximum number of output channels");
        strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
        i++;
        strcpy(desc->params[i].name, "capture");
        desc->params[i].character = 'C';
        desc->params[i].type = JackDriverParamString;
        strcpy(desc->params[i].value.str, "default input device");
        strcpy(desc->params[i].short_desc, "Provide capture ports. Optionally set PortAudio device name");
        strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
        i++;
        strcpy(desc->params[i].name, "playback");
        desc->params[i].character = 'P';
        desc->params[i].type = JackDriverParamString;
        strcpy(desc->params[i].value.str, "default output device");
        strcpy(desc->params[i].short_desc, "Provide playback ports. Optionally set PortAudio device name");
        strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
        i++;
        strcpy(desc->params[i].name, "rate");
        desc->params[i].character = 'r';
        desc->params[i].type = JackDriverParamUInt;
        desc->params[i].value.ui = 44100U;
        strcpy(desc->params[i].short_desc, "Sample rate");
        strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
        i++;
        strcpy(desc->params[i].name, "device");
        desc->params[i].character = 'd';
        desc->params[i].type = JackDriverParamString;
        desc->params[i].value.ui = 128U;
        strcpy(desc->params[i].value.str, "default device");
        strcpy(desc->params[i].short_desc, "PortAudio device name");
        strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
        i++;
        strcpy(desc->params[i].name, "list-devices");
        desc->params[i].character = 'l';
        desc->params[i].type = JackDriverParamBool;
        desc->params[i].value.i = TRUE;
        strcpy(desc->params[i].short_desc, "Display available PortAudio devices");
        strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
        return desc;
    }
#ifdef __cplusplus
}
#endif
 |