X-Git-Url: http://git.johnwright.org/?p=fmit.git;a=blobdiff_plain;f=src%2FCaptureThreadImplALSA.cpp;fp=src%2FCaptureThreadImplALSA.cpp;h=775e99779780a28844e8045d473ebb3bd4cbe9a8;hp=0000000000000000000000000000000000000000;hb=49947d1cd4506f5574b3be62ee90e9f00227d9fd;hpb=82c9faab9421b3d87a0faa84a73f55aaccbbb689 diff --git a/src/CaptureThreadImplALSA.cpp b/src/CaptureThreadImplALSA.cpp new file mode 100644 index 0000000..775e997 --- /dev/null +++ b/src/CaptureThreadImplALSA.cpp @@ -0,0 +1,421 @@ +// 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; + +// ------------------------------ 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 = "default"; // was 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.toAscii().constData(), 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(!isRunning()) + 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.toAscii().constData(), 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.toStdString() << 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.toStdString() << 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.toStdString() << 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); + + 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 sampling rate " << 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.toStdString() << 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 == -ESTRPIPE) + continue; + + if(ret_val<0) + { + cerr << "CaptureThread: WARNING: ALSA: " << snd_strerror(ret_val) << endl; + while((ret_val = snd_pcm_prepare(m_alsa_capture_handle)) < 0) + { + if (ret_val == -ESTRPIPE) + continue; + + msleep(1000); + cerr << "ALSA: cannot prepare audio interface (" << 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.toStdString() << 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(isRunning()) + msleep(10); + +// cerr << "~CaptureThreadImplALSA::~CaptureThreadImplALSA" << endl; +} + +#endif