Import upstream version 0.99.2
[fmit.git] / src / CaptureThreadImplPortAudio.cpp
diff --git a/src/CaptureThreadImplPortAudio.cpp b/src/CaptureThreadImplPortAudio.cpp
new file mode 100644 (file)
index 0000000..b2e93ed
--- /dev/null
@@ -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 <cassert>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <iostream>
+#include <vector>
+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; i<numDevices; i++ )
+                       {
+                               deviceInfo = Pa_GetDeviceInfo( i );
+                               cerr << deviceInfo->name << 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; i<framesPerBuffer; i++)
+               m_capture_thread->m_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 && i<numDevices; i++)
+               {
+                       deviceInfo = Pa_GetDeviceInfo(i);
+                       if(QString(deviceInfo->name)==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<int> 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, &params, 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, &params, 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