// 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 #include using namespace std; #include double DecodeUnsigned8Bits(void* buffer, int i) {return 2*(((unsigned char*)buffer)[i])/256.0 - 1;} double DecodeSigned8Bits(void* buffer, int i) {return (((signed char*)buffer)[i])/128.0;} double DecodeUnsigned16Bits(void* buffer, int i){return 2*((unsigned short*)buffer)[i]/65536.0 - 1;} double DecodeSigned16Bits(void* buffer, int i) {return ((signed short*)buffer)[i]/32768.0;} void AddValue2ChannelFirst(CaptureThreadImpl* impl, double value, int i) { if(i%2==0) impl->m_capture_thread->m_values.push_front(value); } void AddValue2ChannelMix(CaptureThreadImpl* impl, double value, int i) { if(i%2==0) impl->m_capture_thread->m_values.push_front(value); else impl->m_capture_thread->m_values[0] = (impl->m_capture_thread->m_values[0]+value)/2.0; } void AddValue1Channel(CaptureThreadImpl* impl, double value, int i) { impl->m_capture_thread->m_values.push_front(value); } CaptureThread::CaptureThread(const QString& name) { m_current_impl = NULL; m_capturing = false; m_packet_size = 0; m_ext_lock = false; m_packet_size_sll = 0; m_pause = false; m_mix_multiple_channel = false; m_name = name; #ifdef CAPTURE_JACK m_impls.push_back(new CaptureThreadImplJACK(this)); #endif #ifdef CAPTURE_ALSA m_impls.push_back(new CaptureThreadImplALSA(this)); #endif #ifdef CAPTURE_OSS m_impls.push_back(new CaptureThreadImplOSS(this)); #endif #ifdef CAPTURE_PORTAUDIO m_impls.push_back(new CaptureThreadImplPortAudio(this)); #endif listTransports(); } void CaptureThread::autoDetectTransport() { bool was_capturing = isCapturing(); if(was_capturing) stopCapture(); QString old_name; if(m_current_impl!=NULL) old_name = m_current_impl->m_name; cerr << "CaptureThread: INFO: Auto detecting a working transport ... " << flush; CaptureThreadImpl* impl = NULL; for(vector::iterator it=m_impls.begin(); impl==NULL && it!=m_impls.end(); it++) if((*it)->is_available()) impl = *it; if(impl!=NULL) { m_current_impl = impl; cerr << "using " << m_current_impl->m_name << endl; if(m_current_impl->m_name!=old_name) emit(transportChanged(m_current_impl->m_name)); if(was_capturing) startCapture(); } else { cerr << "no working transport !" << endl; if(old_name!="") emit(transportChanged("")); } } void CaptureThread::selectTransport(const QString& name) { cerr << "CaptureThread: INFO: using " << name << " transport" << endl; if(getCurrentTransport() && name==getCurrentTransport()->getName()) return; bool was_capturing = isCapturing(); if(was_capturing) stopCapture(); QString old_name; if(m_current_impl!=NULL) old_name = m_current_impl->m_name; CaptureThreadImpl* impl = NULL; for(vector::iterator it=m_impls.begin(); impl==NULL && it!=m_impls.end(); it++) if((*it)->m_name==name) impl = *it; if(impl==NULL) { cerr << "CaptureThread: ERROR: unknown transport '" << name << "'" << endl; throw QString("CaptureThread: ERROR: unknown transport '")+name+"'"; } m_current_impl = impl; if(m_current_impl->m_name!=old_name) emit(transportChanged(m_current_impl->m_name)); if(was_capturing) startCapture(); } void CaptureThread::selectTransport(int index) { assert(index>=0 && indexgetName() << " transport" << endl; bool was_capturing = isCapturing(); if(was_capturing) stopCapture(); m_current_impl = m_impls[index]; emit(transportChanged(m_current_impl->m_name)); if(was_capturing) startCapture(); } const vector& CaptureThread::getTransports() const { return m_impls; } void CaptureThread::listTransports() { cerr << "CaptureThread: INFO: Built in transports" << endl; for(vector::iterator it=m_impls.begin(); it!=m_impls.end(); it++) cerr << "CaptureThread: INFO: " << (*it)->getStatus() << " " << (*it)->m_name << " " << (*it)->m_descr << endl; } const CaptureThreadImpl* CaptureThread::getCurrentTransport() const { return m_current_impl; } int CaptureThread::getCurrentTransportIndex() const { for(int i=0; im_descr; } const CaptureThreadImpl* CaptureThread::getTransport(const QString& name) const { for(vector::const_iterator it=m_impls.begin(); it!=m_impls.end(); it++) if((*it)->m_name==name) return *it; return NULL; } QString CaptureThread::getFormatDescr() const { if(m_current_impl==NULL) return ""; return ""; // TODO } void CaptureThread::emitError(const QString& error) { emit(errorRaised(error)); } void CaptureThread::emitSamplingRateChanged() { if(m_current_impl->m_sampling_rate>0) emit(samplingRateChanged(m_current_impl->m_sampling_rate)); } void CaptureThread::emitCaptureStarted() { emit(captureStarted()); } void CaptureThread::emitCaptureStoped() { emit(captureStoped()); } void CaptureThread::emitCaptureToggled(bool value) { emit(captureToggled(value)); } void CaptureThread::startCapture() { if(m_current_impl==NULL) return; m_current_impl->startCapture(); } void CaptureThread::stopCapture() { // cerr << "CaptureThread::stopCapture" << endl; if(m_current_impl==NULL) return; m_current_impl->stopCapture(); // cerr << "/CaptureThread::stopCapture" << endl; } void CaptureThread::toggleCapture(bool run) { if(run && !m_capturing) startCapture(); if(!run && m_capturing) stopCapture(); } void CaptureThread::reset() { stopCapture(); startCapture(); } void CaptureThread::togglePause(bool pause) { m_pause = pause; } int CaptureThread::getSamplingRate() const { if(m_current_impl==NULL) return SAMPLING_RATE_UNKNOWN; return m_current_impl->m_sampling_rate; } void CaptureThread::setSamplingRate(int rate) { if(m_current_impl!=NULL) m_current_impl->setSamplingRate(rate); } void CaptureThread::setSource(const QString& name) { if(m_current_impl==NULL) { cerr << "CaptureThread: setSource: ERROR: select a transport first" << endl; return; } if(name!=m_current_impl->m_source) { m_current_impl->m_source = name; if(isCapturing()) { stopCapture(); startCapture(); } emit(sourceChanged(m_current_impl->m_source)); } } void CaptureThread::setMixMultipleChannels(bool mix) { m_mix_multiple_channel = mix; } CaptureThread::~CaptureThread() { stopCapture(); for(vector::iterator it=m_impls.begin(); it!=m_impls.end(); it++) delete *it; } // -------------------------------- implementation ------------------------------ CaptureThreadImpl::CaptureThreadImpl(CaptureThread* capture_thread, const QString& name, const QString& descr) : m_capture_thread(capture_thread) { m_name = name; m_descr = descr; m_status = ""; m_sampling_rate = CaptureThread::SAMPLING_RATE_UNKNOWN; m_format_size = 0; m_format_signed = true; m_channel_count = 0; m_source = ""; } const QString& CaptureThreadImpl::getStatus() { if(m_status=="") is_available(); return m_status; } void CaptureThreadImpl::setFormatDescrsAndFns(int format_size, bool format_signed, bool format_float, int channel_count) { m_format_size = format_size; m_format_signed = format_signed; m_format_float = format_float; m_channel_count = channel_count; if(m_format_size==2) // 16bits { if(m_format_signed) decodeValue = DecodeSigned16Bits; else decodeValue = DecodeUnsigned16Bits; } else // 8bits { if(m_format_signed) decodeValue = DecodeSigned8Bits; else decodeValue = DecodeUnsigned8Bits; } if(m_channel_count==1) addValue = AddValue1Channel; else if(m_channel_count==2) { if(m_capture_thread->m_mix_multiple_channel) addValue = AddValue2ChannelMix; else addValue = AddValue2ChannelFirst; } cerr << "CaptureThread: INFO: format is " << (m_format_signed?"signed":"unsigned") << " " << (m_format_float?"float":"integer") << " " << m_format_size*8 << "bits with " << m_channel_count << " channel(s)" << endl; } // ------------------------------ ALSA implementation ---------------------------- #ifdef CAPTURE_ALSA #define ALSA_BUFF_SIZE 1024 void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...) { cerr << "alsa_error_handler: " << file << ":" << line << " " << function << " err=" << err << endl; } CaptureThreadImplALSA::CaptureThreadImplALSA(CaptureThread* capture_thread) : CaptureThreadImpl(capture_thread, "ALSA", QString("Advanced Linux Sound Architecture (lib:")+snd_asoundlib_version()+")") { m_alsa_capture_handle = NULL; m_alsa_hw_params = NULL; m_alsa_buffer = NULL; m_format = SND_PCM_FORMAT_UNKNOWN; m_source = "hw:0"; m_alive = true; m_in_run = false; m_loop = false; // snd_lib_error_set_handler(alsa_error_handler); } bool CaptureThreadImplALSA::is_available() { if(m_alsa_capture_handle==NULL) { try { int err = -1; if((err=snd_pcm_open(&m_alsa_capture_handle, m_source.latin1(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { if(err==-19) // TODO risks of changes for the error code throw QString("invalid source '")+m_source+"'"; else if(err==-16) throw QString("device '")+m_source+"' busy"; else throw QString("cannot open pcm: ")+QString(snd_strerror(err)); } } catch(QString error) { m_alsa_capture_handle = NULL; m_status = "N/A ("+error+")"; return false; } if(m_alsa_capture_handle!=NULL) { snd_pcm_close(m_alsa_capture_handle); m_alsa_capture_handle = NULL; } } m_status = "OK"; // cerr << "CaptureThread: INFO: ALSA seems available" << endl; return true; } void CaptureThreadImplALSA::startCapture() { if(!running()) start(); m_loop = true; m_wait_for_start = true; while(m_wait_for_start) // some implementations take a long time to start msleep(10); } void CaptureThreadImplALSA::stopCapture() { m_loop = false; while(m_in_run) msleep(10); } void CaptureThreadImplALSA::set_params(bool test) { // cerr << "ALSA: Recognized sample formats are" << endl; // for (int k = 0; k < SND_PCM_FORMAT_LAST; ++(unsigned long) k) { // const char *s = snd_pcm_format_name((snd_pcm_format_t)k); // if (s) cerr << s << endl; // } int err=0; if(m_source=="") throw QString("ALSA: set the source first"); if((err=snd_pcm_open(&m_alsa_capture_handle, m_source.latin1(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { // cerr << "err=" << err << ":" << snd_strerror(err) << endl; if(err==-19) // TODO risks of changes for the error code throw QString("ALSA: Invalid Source '")+m_source+"'"; else if(err==-16) throw QString("ALSA: Device '")+m_source+"' busy"; else throw QString("ALSA: Cannot open pcm: ")+QString(snd_strerror(err)); } snd_pcm_hw_params_alloca(&m_alsa_hw_params); if((err=snd_pcm_hw_params_any(m_alsa_capture_handle, m_alsa_hw_params)) < 0) throw QString("ALSA: cannot initialize hardware parameter structure (")+QString(snd_strerror(err))+")"; if((err=snd_pcm_hw_params_set_access(m_alsa_capture_handle, m_alsa_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) throw QString("ALSA: cannot set access type (")+QString(snd_strerror(err))+")"; if(!test) { // Formats if(m_format==-1) { list formats; formats.push_back(SND_PCM_FORMAT_S16); formats.push_back(SND_PCM_FORMAT_U16); formats.push_back(SND_PCM_FORMAT_S8); formats.push_back(SND_PCM_FORMAT_U8); err = -1; while(err<0) { if(formats.empty()) throw QString("ALSA: cannot set any format (")+QString(snd_strerror(err))+")"; m_format = formats.front(); cerr << "CaptureThread: INFO: ALSA: try to set format to " << snd_pcm_format_description(m_format) << flush; err=snd_pcm_hw_params_set_format(m_alsa_capture_handle, m_alsa_hw_params, m_format); if(err<0) cerr << " failed" << endl; else cerr << " success" << endl; formats.pop_front(); } } else { if((err=snd_pcm_hw_params_set_format(m_alsa_capture_handle, m_alsa_hw_params, m_format))<0) { QString err_msg = QString("ALSA: cannot set format (")+QString(snd_strerror(err))+")"; cerr << "CaptureThread: ERROR: " << err_msg << endl; } } // Channel count unsigned int channel_count = 1; if((err=snd_pcm_hw_params_set_channels_near(m_alsa_capture_handle, m_alsa_hw_params, &channel_count)) < 0) { QString err_msg = QString("ALSA: cannot set channel count (")+QString(snd_strerror(err))+")"; cerr << "CaptureThread: WARNING: " << err_msg << endl; } if(channel_count>1) { QString err_msg = QString("ALSA: cannot set channel count to one (")+QString::number(channel_count)+" instead)"; cerr << "CaptureThread: WARNING: " << err_msg << endl; } setFormatDescrsAndFns(snd_pcm_format_width(m_format)/8, snd_pcm_format_signed(m_format), false, channel_count); } 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: ALSA: 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); sampling_rates.push_front(48000); sampling_rates.push_front(96000); err = -1; while(err<0) { if(sampling_rates.empty()) throw QString("ALSA: cannot set any sample rate (")+QString(snd_strerror(err))+")"; m_sampling_rate = sampling_rates.front(); cerr << "CaptureThread: INFO: ALSA: try to set sampling rate to " << m_sampling_rate << flush; unsigned int rrate = m_sampling_rate; err = snd_pcm_hw_params_set_rate(m_alsa_capture_handle, m_alsa_hw_params, rrate, 0); if(err<0) cerr << " failed" << endl; else cerr << " success" << endl; sampling_rates.pop_front(); } if(old_sampling_rate!=m_sampling_rate) m_capture_thread->emitSamplingRateChanged(); } else { int err; int dir = 0; unsigned int rrate = m_sampling_rate; if((err = snd_pcm_hw_params_set_rate_near(m_alsa_capture_handle, m_alsa_hw_params, &rrate, &dir))<0) throw QString("ALSA: cannot set sampling rate (")+QString(snd_strerror(err))+")"; if(m_sampling_rate!=rrate) m_capture_thread->emitSamplingRateChanged(); m_sampling_rate = rrate; } if((err=snd_pcm_hw_params(m_alsa_capture_handle, m_alsa_hw_params)) < 0) throw QString("ALSA: cannot set parameters (")+QString(snd_strerror(err))+")"; } void CaptureThreadImplALSA::setSamplingRate(int value) { // cerr << "CaptureThreadImplALSA::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 << endl; m_capture_thread->emitError(error); } // it was just for testing capture_finished(); } else m_capture_thread->emitSamplingRateChanged(); if(was_running) m_capture_thread->startCapture(); } // cerr << "~CaptureThreadImplALSA::setSamplingRate" << endl; } void CaptureThreadImplALSA::capture_init() { set_params(); snd_pcm_nonblock(m_alsa_capture_handle, 0); m_alsa_buffer = new char[m_channel_count*ALSA_BUFF_SIZE*snd_pcm_format_width(m_format)/8]; int err=0; if((err=snd_pcm_prepare(m_alsa_capture_handle)) < 0) throw QString("ALSA: cannot prepare audio interface for use (")+QString(snd_strerror(err))+")"; } void CaptureThreadImplALSA::capture_loop() { // cerr << "CaptureThreadImplALSA::capture_loop" << endl; m_wait_for_start = false; while(m_loop) { int ret_val = snd_pcm_readi(m_alsa_capture_handle, m_alsa_buffer, ALSA_BUFF_SIZE); if(ret_val<0) { cerr << "CaptureThread: WARNING: ALSA: " << snd_strerror(ret_val) << endl; while((ret_val = snd_pcm_prepare(m_alsa_capture_handle)) < 0) { msleep(1000); cerr << QString("ALSA: cannot prepare audio interface (")+QString(snd_strerror(ret_val))+")" << endl; // throw QString("ALSA: cannot prepare audio interface (")+QString(snd_strerror(ret_val))+")"; } } else { if(!m_capture_thread->m_pause) { m_capture_thread->m_lock.lock(); // cerr << "CaptureThreadImplALSA::capture_loop " << m_capture_thread->m_values.size() << endl; for(int i=0; im_packet_size = ret_val; 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 += ret_val; m_capture_thread->m_lock.unlock(); } } } // cerr << "~CaptureThreadImplALSA::capture_loop" << endl; } void CaptureThreadImplALSA::capture_finished() { if(m_alsa_buffer!=NULL) { delete[] m_alsa_buffer; m_alsa_buffer = NULL; } if(m_alsa_capture_handle!=NULL) { snd_pcm_hw_free(m_alsa_capture_handle); snd_pcm_close(m_alsa_capture_handle); m_alsa_capture_handle = NULL; } } void CaptureThreadImplALSA::run() { // cerr << "CaptureThread: INFO: ALSA: capture thread entered" << endl; // while(m_alive) // TODO need to keep alsa thread alive to let PortAudio working after ALSA !! { while(m_alive && !m_loop) msleep(10); m_in_run = true; try { // cerr << "CaptureThread: INFO: capture thread running" << endl; capture_init(); m_capture_thread->m_capturing = true; m_capture_thread->emitCaptureStarted(); m_capture_thread->emitCaptureToggled(true); capture_loop(); m_capture_thread->m_capturing = false; m_capture_thread->emitCaptureStoped(); m_capture_thread->emitCaptureToggled(false); } catch(QString error) { m_loop = false; cerr << "CaptureThread: ERROR: " << error << endl; m_capture_thread->emitError(error); } m_wait_for_start = false; capture_finished(); m_in_run = false; // cerr << "CaptureThread: INFO: capture thread stop running" << endl; } // cerr << "CaptureThread: INFO: ALSA: capture thread exited" << endl; } CaptureThreadImplALSA::~CaptureThreadImplALSA() { // cerr << "CaptureThreadImplALSA::~CaptureThreadImplALSA" << endl; m_alive = false; stopCapture(); while(running()) msleep(10); // cerr << "~CaptureThreadImplALSA::~CaptureThreadImplALSA" << endl; } #endif // ------------------------------ JACK implementation ---------------------------- #ifdef CAPTURE_JACK CaptureThreadImplJACK::CaptureThreadImplJACK(CaptureThread* capture_thread) : CaptureThreadImpl(capture_thread, "JACK", "Jack Audio Connection Kit") { m_jack_client = NULL; m_jack_port = NULL; } bool CaptureThreadImplJACK::is_available() { if(m_jack_client==NULL) { try { m_jack_client = jack_client_new((m_capture_thread->m_name+"_test").latin1()); if(m_jack_client==NULL) throw QString("unknown reason"); } catch(QString error) { m_jack_client = NULL; m_status = "N/A"; return false; } capture_finished(); } m_status = "available"; return true; } void CaptureThreadImplJACK::setSamplingRate(int value) { cerr << "CaptureThread: ERROR: JACK: setSamplingRate not available with JACK ! change the JACK server sampling rate instead" << endl; } void CaptureThreadImplJACK::startCapture() { try { capture_init(); } catch(QString error) { capture_finished(); cerr << "CaptureThread: ERROR: " << error << endl; m_capture_thread->emitError(error); } } void CaptureThreadImplJACK::stopCapture() { try { capture_finished(); } catch(QString error) { cerr << "CaptureThread: ERROR: " << error << endl; m_capture_thread->emitError(error); } } void CaptureThreadImplJACK::JackShutdown(void* arg){((CaptureThreadImplJACK*)arg)->jackShutdown();} void CaptureThreadImplJACK::jackShutdown() { m_jack_client = NULL; m_capture_thread->emitError("JACK: server shutdown !"); } int CaptureThreadImplJACK::JackSampleRate(jack_nframes_t nframes, void* arg){return ((CaptureThreadImplJACK*)arg)->jackSampleRate(nframes);} int CaptureThreadImplJACK::jackSampleRate(jack_nframes_t nframes) { if(m_sampling_rate!=int(nframes)) { m_sampling_rate = nframes; m_capture_thread->emitSamplingRateChanged(); } return 0; } int CaptureThreadImplJACK::JackProcess(jack_nframes_t nframes, void* arg){return ((CaptureThreadImplJACK*)arg)->jackProcess(nframes);} int CaptureThreadImplJACK::jackProcess(jack_nframes_t nframes) { // cerr << "'" << nframes << "'" << endl; if(m_capture_thread->m_pause || !m_capture_thread->m_capturing || nframes<=0) return 0; void* pin = jack_port_get_buffer(m_jack_port, nframes); if(!pin) return 0; jack_default_audio_sample_t* in = (jack_default_audio_sample_t*)pin; m_capture_thread->m_lock.lock(); for(jack_nframes_t i=0; im_values.push_front(in[i]); m_capture_thread->m_lock.unlock(); m_capture_thread->m_packet_size = nframes; 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 += nframes; return 0; } void CaptureThreadImplJACK::capture_init() { m_jack_client = jack_client_new(m_capture_thread->m_name.latin1()); if(!m_jack_client) throw QString("JACK: cannot create client, JACK deamon is running ?"); jack_set_process_callback(m_jack_client, JackProcess, (void*)this); jack_on_shutdown(m_jack_client, JackShutdown, (void*)this); jack_set_error_function(jack_error_callback); jack_set_sample_rate_callback(m_jack_client, JackSampleRate, (void*)this); int err=0; if((err=jack_activate(m_jack_client))!=0) throw QString("JACK: cannot activate client"); setFormatDescrsAndFns(sizeof(jack_default_audio_sample_t), true, true, 1); m_jack_port = jack_port_register(m_jack_client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput,0); if(m_source!="") if((err=jack_connect(m_jack_client, m_source.latin1(), (m_capture_thread->m_name+":input").latin1()))!=0) m_capture_thread->emitError(QString("JACK: Invalid source '")+m_source+"'"); int old_sampling_rate = m_sampling_rate; m_sampling_rate = jack_get_sample_rate(m_jack_client); if(m_sampling_rate!=old_sampling_rate) m_capture_thread->emitSamplingRateChanged(); m_capture_thread->m_capturing = true; m_capture_thread->emitCaptureStarted(); m_capture_thread->emitCaptureToggled(true); } void CaptureThreadImplJACK::capture_finished() { // cerr << "CaptureThreadImplJACK::capture_finished" << endl; if(m_jack_client!=NULL) { jack_client_close(m_jack_client); m_jack_client = NULL; m_capture_thread->m_capturing = false; m_capture_thread->emitCaptureStoped(); m_capture_thread->emitCaptureToggled(false); } } #endif // ------------------------------ 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); PaError 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 << endl; m_capture_thread->emitError(error); } try{ // it was just for testing capture_finished(); } catch(QString error) { cerr << "CaptureThread: ERROR: " << error << 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 ) {((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; int index = -1; 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 << "\"" << endl; } if(!test) { if(params.device==paNoDevice) cerr << "CaptureThread: INFO: PortAudio: using default device" << endl; else cerr << "CaptureThread: INFO: PortAudio: using \"" << m_source << "\"" << endl; setFormatDescrsAndFns(4, true, true, 1); } 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); sampling_rates.push_front(48000); sampling_rates.push_front(96000); 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 to set sampling rate to " << 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 << endl; m_capture_thread->emitError(error); } } void CaptureThreadImplPortAudio::stopCapture() { try { capture_finished(); } catch(QString error) { cerr << "CaptureThread: ERROR: " << error << endl; m_capture_thread->emitError(error); } } CaptureThreadImplPortAudio::~CaptureThreadImplPortAudio() { stopCapture(); } #endif // ------------------------------ OSS implementation ---------------------------- #ifdef CAPTURE_OSS #include #include #include #include #include #include #include #define OSS_BUFF_SIZE 1024 CaptureThreadImplOSS::CaptureThreadImplOSS(CaptureThread* capture_thread) : CaptureThreadImpl(capture_thread, "OSS", QString("Open Sound System")) { m_fd_in = 0; m_oss_buffer = NULL; m_format = -1; m_source = "/dev/dsp"; // oss_sysinfo si; // definition ?? // ioctl(m_fd_in, OSS_SYSINFO, &si); // m_fd_in = open(m_source, O_RDONLY, 0)); // m_descr = QString("Open Sound System (")+QString::number(SOUND_VERSION, 16)+":"+si.version+")"); // close(m_fd_in); // m_fd_in = 0; m_descr = QString("Open Sound System (lib:")+QString::number(SOUND_VERSION, 16)+")"; m_alive = true; m_in_run = false; m_loop = false; } bool CaptureThreadImplOSS::is_available() { if(m_fd_in==0) { try { if((m_fd_in = open (m_source.latin1(), O_RDONLY, 0)) == -1) throw QString(strerror(errno)); } catch(QString error) { m_fd_in = 0; m_status = "N/A ("+error+")"; return false; } capture_finished(); } m_status = "OK"; // cerr << "CaptureThread: INFO: OSS seems available" << endl; return true; } void CaptureThreadImplOSS::startCapture() { if(!running()) start(); m_loop = true; m_wait_for_start = true; while(m_wait_for_start) // some implementations take a long time to start msleep(10); } void CaptureThreadImplOSS::stopCapture() { m_loop = false; while(m_in_run) msleep(10); } void CaptureThreadImplOSS::set_params(bool test) { if(m_source=="") throw QString("OSS: set the source first"); if((m_fd_in = open (m_source.latin1(), O_RDONLY, 0))==-1) throw QString("OSS: ")+QString(strerror(errno)); if(!test) { if(m_format==-1) { // Formats m_format = AFMT_S16_NE; /* Native 16 bits */ if(ioctl(m_fd_in, SNDCTL_DSP_SETFMT, &m_format)==-1) throw QString("OSS: cannot set format (")+strerror(errno)+")"; if(m_format != AFMT_S16_NE) throw QString("OSS: cannot set format to signed 16bits"); } else { if(ioctl(m_fd_in, SNDCTL_DSP_SETFMT, &m_format)==-1) throw QString("OSS: cannot set format (")+strerror(errno)+")"; } m_format_size = 2; m_format_signed = true; m_format_float = false; // Channel count unsigned int channel_count = 1; if(ioctl(m_fd_in, SNDCTL_DSP_CHANNELS, &channel_count)==-1) throw QString("OSS: cannot set channel count to 1 (")+strerror(errno)+")"; if(channel_count != 1) throw QString("OSS: the device doesn't support mono mode"); /* if(m_channel_count>1) // TODO { QString err_msg = QString("OSS: cannot set channel count to one (")+QString::number(m_channel_count)+" instead)"; cerr << "CaptureThread: WARNING: " << err_msg << endl; }*/ setFormatDescrsAndFns(2, true, false, channel_count); } 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: OSS: 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); sampling_rates.push_front(48000); sampling_rates.push_front(96000); int err = -1; while(err<0) { if(sampling_rates.empty()) throw QString("OSS: cannot set any sample rate (")+strerror(errno)+")"; m_sampling_rate = sampling_rates.front(); cerr << "CaptureThread: INFO: OSS: try to set sampling rate to " << m_sampling_rate << flush; err = ioctl(m_fd_in, SNDCTL_DSP_SPEED, &m_sampling_rate); if(err==-1) cerr << " failed" << endl; else cerr << " success" << endl; sampling_rates.pop_front(); } if(old_sampling_rate!=m_sampling_rate) m_capture_thread->emitSamplingRateChanged(); } else { if(ioctl(m_fd_in, SNDCTL_DSP_SPEED, &m_sampling_rate)==-1) throw QString("OSS: cannot set sampling rate (")+QString(strerror(errno))+")"; } } void CaptureThreadImplOSS::setSamplingRate(int value) { // cerr << "CaptureThreadImplOSS::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 << endl; m_capture_thread->emitError(error); } // it was just for testing capture_finished(); } else m_capture_thread->emitSamplingRateChanged(); if(was_running) m_capture_thread->startCapture(); } // cerr << "~CaptureThreadImplOSS::setSamplingRate" << endl; } void CaptureThreadImplOSS::capture_init() { set_params(false); m_oss_buffer = new char[m_channel_count*OSS_BUFF_SIZE*16/8]; } void CaptureThreadImplOSS::capture_loop() { // cerr << "CaptureThreadImplOSS::capture_loop" << endl; bool format_signed = true; int l=0; m_wait_for_start = false; while(m_loop) { int ret_val = read(m_fd_in, m_oss_buffer, sizeof(m_oss_buffer)); if(ret_val==-1) { cerr << "CaptureThread: WARNING: OSS: " << strerror(errno) << endl; msleep(1000); // TODO which behavior ? // m_loop = false;// TODO which behavior ? } else { ret_val /= m_format_size; if(!m_capture_thread->m_pause) { m_capture_thread->m_lock.lock(); for(int i=0; im_packet_size = ret_val; 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 += ret_val; m_capture_thread->m_lock.unlock(); } } } // cerr << "~CaptureThreadImplOSS::capture_loop" << endl; } void CaptureThreadImplOSS::capture_finished() { if(m_oss_buffer!=NULL) { delete[] m_oss_buffer; m_oss_buffer = NULL; } if(m_fd_in!=0) { close(m_fd_in); m_fd_in = 0; } } void CaptureThreadImplOSS::run() { // cerr << "CaptureThread: INFO: OSS: capture thread entered" << endl; // while(m_alive) // TODO ?? need to keep oss thread alive to let PortAudio working after ALSA ?? { while(m_alive && !m_loop) msleep(10); m_in_run = true; try { // cerr << "CaptureThread: INFO: capture thread running" << endl; capture_init(); m_capture_thread->m_capturing = true; m_capture_thread->emitCaptureStarted(); m_capture_thread->emitCaptureToggled(true); capture_loop(); m_capture_thread->m_capturing = false; m_capture_thread->emitCaptureStoped(); m_capture_thread->emitCaptureToggled(false); } catch(QString error) { m_loop = false; cerr << "CaptureThread: ERROR: " << error << endl; m_capture_thread->emitError(error); } m_wait_for_start = false; capture_finished(); m_in_run = false; // cerr << "CaptureThread: INFO: capture thread stop running" << endl; } // cerr << "CaptureThread: INFO: OSS: capture thread exited" << endl; } CaptureThreadImplOSS::~CaptureThreadImplOSS() { // cerr << "CaptureThreadImplOSS::~CaptureThreadImplOSS" << endl; m_alive = false; stopCapture(); while(running()) msleep(10); // cerr << "~CaptureThreadImplOSS::~CaptureThreadImplOSS" << endl; } #endif