X-Git-Url: http://git.johnwright.org/?p=fmit.git;a=blobdiff_plain;f=src%2FCaptureThreadImplPortAudio.cpp;fp=src%2FCaptureThreadImplPortAudio.cpp;h=b2e93edb4a0d3777fe138c44db12b611dd3b6747;hp=0000000000000000000000000000000000000000;hb=49947d1cd4506f5574b3be62ee90e9f00227d9fd;hpb=82c9faab9421b3d87a0faa84a73f55aaccbbb689 diff --git a/src/CaptureThreadImplPortAudio.cpp b/src/CaptureThreadImplPortAudio.cpp new file mode 100644 index 0000000..b2e93ed --- /dev/null +++ b/src/CaptureThreadImplPortAudio.cpp @@ -0,0 +1,325 @@ +// Copyright 2004 "Gilles Degottex" + +// This file is part of "Music" + +// "Music" is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// "Music" 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +#include "CaptureThread.h" + +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +// ------------------------------ PortAudio implementation ---------------------------- +#ifdef CAPTURE_PORTAUDIO +CaptureThreadImplPortAudio::CaptureThreadImplPortAudio(CaptureThread* capture_thread) + : CaptureThreadImpl(capture_thread, "PortAudio", QString("Portable cross-platform Audio API (lib:")+Pa_GetVersionText()+"["+QString::number(Pa_GetVersion())+"])") +{ + m_stream = NULL; + + m_source = "default"; +} + +bool CaptureThreadImplPortAudio::is_available() +{ + if(!m_stream) + { + try + { + m_err = Pa_Initialize(); + if(m_err != paNoError) throw QString("PortAudio: is_available:Pa_Initialize ")+Pa_GetErrorText(m_err); + + int numDevices; + + numDevices = Pa_GetDeviceCount(); + if(numDevices < 0) + throw QString("PortAudio: is_available:Pa_GetDeviceCount ")+Pa_GetErrorText(numDevices); + else if(numDevices == 0) + throw QString("PortAudio: is_available:Pa_GetDeviceCount no devices available")+Pa_GetErrorText(numDevices); + +/* const PaDeviceInfo *deviceInfo; + + for(int i=0; iname << endl; + cerr << deviceInfo->defaultSampleRate << endl; + }*/ + } + catch(QString error) + { + Pa_Terminate(); + m_stream = NULL; + m_status = "N/A"; + return false; + } + capture_finished(); + } + + m_status = "OK"; + + return true; +} + +void CaptureThreadImplPortAudio::setSamplingRate(int value) +{ +// cerr << "CaptureThreadImplPortAudio::setSamplingRate " << value << endl; + + assert(value>0 || value==CaptureThread::SAMPLING_RATE_MAX); + + if(m_sampling_rate!=value || value==CaptureThread::SAMPLING_RATE_MAX) + { + bool was_running = m_capture_thread->isCapturing(); + if(was_running) m_capture_thread->stopCapture(); + + m_sampling_rate = value; + + if(m_sampling_rate==CaptureThread::SAMPLING_RATE_MAX) + { + try + { + set_params(true); + } + catch(QString error) + { + cerr << "CaptureThread: ERROR: " << error.toStdString() << endl; + m_capture_thread->emitError(error); + } + + try{ + // it was just for testing + capture_finished(); + } + catch(QString error) + { + cerr << "CaptureThread: ERROR: " << error.toStdString() << endl; + m_capture_thread->emitError(error); + } + } + else + m_capture_thread->emitSamplingRateChanged(); + + if(was_running) m_capture_thread->startCapture(); + } + +// cerr << "~CaptureThreadImplPortAudio::setSamplingRate" << endl; +} + +int CaptureThreadImplPortAudio::PortAudioCallback( const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{return ((CaptureThreadImplPortAudio*)userData)->portAudioCallback(inputBuffer, framesPerBuffer, timeInfo, statusFlags);} +int CaptureThreadImplPortAudio::portAudioCallback(const void *inputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags) +{ + if(m_capture_thread->m_pause) + return 0; + + m_capture_thread->m_lock.lock(); + + float *in = (float*)inputBuffer; + + for(unsigned long i=0; im_values.push_front(*in++); +// addValue(*in++, i, m_channel_count); // TODO + + m_capture_thread->m_packet_size = framesPerBuffer; + if(m_capture_thread->m_ext_lock) + { + m_capture_thread->m_packet_size_sll = 0; + m_capture_thread->m_ext_lock = false; + } + m_capture_thread->m_packet_size_sll += framesPerBuffer; + + m_capture_thread->m_lock.unlock(); + + return 0; +} + +void CaptureThreadImplPortAudio::set_params(bool test) +{ + m_err = Pa_Initialize(); + if(m_err != paNoError) throw QString("PortAudio: set_params:Pa_Initialize ")+Pa_GetErrorText(m_err); + + PaStreamParameters params; + params.device = paNoDevice; + params.channelCount = 1; + params.sampleFormat = paFloat32; + params.suggestedLatency = 0; + params.hostApiSpecificStreamInfo = NULL; + + if(m_source!="default") // TODO hum hum + { + int numDevices = Pa_GetDeviceCount(); + const PaDeviceInfo* deviceInfo; + for(int i=0; params.device==paNoDevice && iname)==m_source) + params.device = i; + } + + if(params.device==paNoDevice) + cerr << "CaptureThread: INFO: PortAudio: cannot determine selected source \"" << m_source.toStdString() << "\"" << endl; + } + + if(!test) + { + if(params.device==paNoDevice) + cerr << "CaptureThread: INFO: PortAudio: using default device" << endl; + else + cerr << "CaptureThread: INFO: PortAudio: using \"" << m_source.toStdString() << "\"" << endl; + + setFormatDescrsAndFns(4, true, true, 1); // TODO do something if nbchannel=1 not supported + } + + if(m_sampling_rate==CaptureThread::SAMPLING_RATE_MAX || m_sampling_rate==CaptureThread::SAMPLING_RATE_UNKNOWN) + { + int old_sampling_rate = m_sampling_rate; + + cerr << "CaptureThread: INFO: PortAudio: sampling rate set to max or undefined, try to determinate it." << endl; + + list sampling_rates; + sampling_rates.push_front(8000); sampling_rates.push_front(11025); sampling_rates.push_front(16000); + sampling_rates.push_front(22050); sampling_rates.push_front(24000); sampling_rates.push_front(32000); + sampling_rates.push_front(44100); + + m_err = -1; + while(m_err!=paNoError) + { + if(sampling_rates.empty()) + throw QString("PortAudio: cannot set any sample rate (")+Pa_GetErrorText(m_err)+")"; + + m_err = Pa_Initialize(); + if(m_err != paNoError) throw QString("PortAudio: set_params:Pa_Initialize ")+Pa_GetErrorText(m_err); + + m_sampling_rate = sampling_rates.front(); + cerr << "CaptureThread: INFO: PortAudio: try sampling rate " << m_sampling_rate << " ..." << flush; + +// cerr << "nbc1 " << params.channelCount << endl; + + if(params.device==paNoDevice) + m_err = Pa_OpenDefaultStream(&m_stream, 1, 0, paFloat32, m_sampling_rate, 0, PortAudioCallback, this); + else + m_err = Pa_OpenStream(&m_stream, ¶ms, NULL, m_sampling_rate, 0, paNoFlag, PortAudioCallback, this); + + if(m_err != paNoError) cerr << " failed" << endl; + else cerr << " success" << endl; + + sampling_rates.pop_front(); + } + + if(old_sampling_rate!=m_sampling_rate) + m_capture_thread->emitSamplingRateChanged(); + } + else + { +// cerr << "nbc2 " << params.channelCount << endl; +// cerr << "dev2 " << params.device << "/" << paNoDevice << endl; + + if(params.device==paNoDevice) + { + m_err = Pa_OpenDefaultStream(&m_stream, 1, 0, paFloat32, m_sampling_rate, 0, PortAudioCallback, this); + if(m_err != paNoError) + throw QString("PortAudio: set_params:Pa_OpenDefaultStream ")+Pa_GetErrorText(m_err); + } + else + { + m_err = Pa_OpenStream(&m_stream, ¶ms, NULL, m_sampling_rate, 0, paNoFlag, PortAudioCallback, this); + if(m_err != paNoError) + throw QString("PortAudio: set_params:Pa_OpenStream ")+Pa_GetErrorText(m_err); + } + } +} + +void CaptureThreadImplPortAudio::capture_init() +{ + set_params(false); + + m_err = Pa_StartStream(m_stream); + if(m_err != paNoError) + throw QString("PortAudio: capture_init:Pa_StartStream ")+Pa_GetErrorText(m_err); + + m_capture_thread->m_capturing = true; + m_capture_thread->emitCaptureStarted(); + m_capture_thread->emitCaptureToggled(true); +} +void CaptureThreadImplPortAudio::capture_finished() +{ + if(m_stream) + { + if(!Pa_IsStreamStopped(m_stream)) + { + m_err = Pa_StopStream(m_stream); + if(m_err != paNoError) throw QString("PortAudio: capture_finished: ")+Pa_GetErrorText(m_err); + } + + if(m_stream) + { + m_err = Pa_CloseStream(m_stream); + if(m_err != paNoError) throw QString("PortAudio: capture_finished: ")+Pa_GetErrorText(m_err); + } + + m_stream = NULL; + + m_capture_thread->m_capturing = false; + m_capture_thread->emitCaptureStoped(); + m_capture_thread->emitCaptureToggled(false); + } + + m_err = Pa_Terminate(); +// if(m_err != paNoError) throw QString("PortAudio: capture_finished: ")+Pa_GetErrorText(m_err); +} +void CaptureThreadImplPortAudio::startCapture() +{ + try + { + capture_init(); + } + catch(QString error) + { + capture_finished(); + cerr << "CaptureThread: ERROR: " << error.toStdString() << endl; + m_capture_thread->emitError(error); + } +} +void CaptureThreadImplPortAudio::stopCapture() +{ + try + { + capture_finished(); + } + catch(QString error) + { + cerr << "CaptureThread: ERROR: " << error.toStdString() << endl; + m_capture_thread->emitError(error); + } +} +CaptureThreadImplPortAudio::~CaptureThreadImplPortAudio() +{ + stopCapture(); +} + +#endif