1 // Copyright 2004 "Gilles Degottex"
3 // This file is part of "Music"
5 // "Music" is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation; either version 2.1 of the License, or
8 // (at your option) any later version.
10 // "Music" is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include "CaptureThread.h"
32 // ------------------------------ ALSA implementation ----------------------------
35 #define ALSA_BUFF_SIZE 1024
37 void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
39 cerr << "alsa_error_handler: " << file << ":" << line << " " << function << " err=" << err << endl;
42 CaptureThreadImplALSA::CaptureThreadImplALSA(CaptureThread* capture_thread)
43 : CaptureThreadImpl(capture_thread, "ALSA", QString("Advanced Linux Sound Architecture (lib:")+snd_asoundlib_version()+")")
45 m_alsa_capture_handle = NULL;
46 m_alsa_hw_params = NULL;
48 m_format = SND_PCM_FORMAT_UNKNOWN;
50 m_source = "default"; // was hw:0
56 // snd_lib_error_set_handler(alsa_error_handler);
59 bool CaptureThreadImplALSA::is_available()
61 if(m_alsa_capture_handle==NULL)
66 if((err=snd_pcm_open(&m_alsa_capture_handle, m_source.toAscii().constData(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0)
68 if(err==-19) // TODO risks of changes for the error code
69 throw QString("invalid source '")+m_source+"'";
71 throw QString("device '")+m_source+"' busy";
73 throw QString("cannot open pcm: ")+QString(snd_strerror(err));
78 m_alsa_capture_handle = NULL;
80 m_status = "N/A ("+error+")";
85 if(m_alsa_capture_handle!=NULL)
87 snd_pcm_close(m_alsa_capture_handle);
88 m_alsa_capture_handle = NULL;
94 // cerr << "CaptureThread: INFO: ALSA seems available" << endl;
99 void CaptureThreadImplALSA::startCapture()
106 m_wait_for_start = true;
107 while(m_wait_for_start) // some implementations take a long time to start
110 void CaptureThreadImplALSA::stopCapture()
118 void CaptureThreadImplALSA::set_params(bool test)
120 // cerr << "ALSA: Recognized sample formats are" << endl;
121 // for (int k = 0; k < SND_PCM_FORMAT_LAST; ++(unsigned long) k) {
122 // const char *s = snd_pcm_format_name((snd_pcm_format_t)k);
123 // if (s) cerr << s << endl;
128 throw QString("ALSA: set the source first");
129 if((err=snd_pcm_open(&m_alsa_capture_handle, m_source.toAscii().constData(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0)
131 // cerr << "err=" << err << ":" << snd_strerror(err) << endl;
133 if(err==-19) // TODO risks of changes for the error code
134 throw QString("ALSA: Invalid Source '")+m_source+"'";
136 throw QString("ALSA: Device '")+m_source+"' busy";
138 throw QString("ALSA: Cannot open pcm: ")+QString(snd_strerror(err));
141 snd_pcm_hw_params_alloca(&m_alsa_hw_params);
143 if((err=snd_pcm_hw_params_any(m_alsa_capture_handle, m_alsa_hw_params)) < 0)
144 throw QString("ALSA: cannot initialize hardware parameter structure (")+QString(snd_strerror(err))+")";
146 if((err=snd_pcm_hw_params_set_access(m_alsa_capture_handle, m_alsa_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
147 throw QString("ALSA: cannot set access type (")+QString(snd_strerror(err))+")";
154 list<snd_pcm_format_t> formats;
155 formats.push_back(SND_PCM_FORMAT_S16); formats.push_back(SND_PCM_FORMAT_U16);
156 formats.push_back(SND_PCM_FORMAT_S8); formats.push_back(SND_PCM_FORMAT_U8);
162 throw QString("ALSA: cannot set any format (")+QString(snd_strerror(err))+")";
164 m_format = formats.front();
165 cerr << "CaptureThread: INFO: ALSA: try to set format to " << snd_pcm_format_description(m_format) << flush;
166 err=snd_pcm_hw_params_set_format(m_alsa_capture_handle, m_alsa_hw_params, m_format);
168 if(err<0) cerr << " failed" << endl;
169 else cerr << " success" << endl;
176 if((err=snd_pcm_hw_params_set_format(m_alsa_capture_handle, m_alsa_hw_params, m_format))<0)
178 QString err_msg = QString("ALSA: cannot set format (")+QString(snd_strerror(err))+")";
179 cerr << "CaptureThread: ERROR: " << err_msg.toStdString() << endl;
184 unsigned int channel_count = 1;
185 if((err=snd_pcm_hw_params_set_channels_near(m_alsa_capture_handle, m_alsa_hw_params, &channel_count)) < 0)
187 QString err_msg = QString("ALSA: cannot set channel count (")+QString(snd_strerror(err))+")";
188 cerr << "CaptureThread: WARNING: " << err_msg.toStdString() << endl;
192 QString err_msg = QString("ALSA: cannot set channel count to one (")+QString::number(channel_count)+" instead)";
193 cerr << "CaptureThread: WARNING: " << err_msg.toStdString() << endl;
196 setFormatDescrsAndFns(snd_pcm_format_width(m_format)/8, snd_pcm_format_signed(m_format), false, channel_count);
199 if(m_sampling_rate==CaptureThread::SAMPLING_RATE_MAX || m_sampling_rate==CaptureThread::SAMPLING_RATE_UNKNOWN)
201 int old_sampling_rate = m_sampling_rate;
203 cerr << "CaptureThread: INFO: ALSA: sampling rate set to max or undefined, try to determinate it." << endl;
205 list<int> sampling_rates;
206 sampling_rates.push_front(8000); sampling_rates.push_front(11025); sampling_rates.push_front(16000);
207 sampling_rates.push_front(22050); sampling_rates.push_front(24000); sampling_rates.push_front(32000);
208 sampling_rates.push_front(44100);
213 if(sampling_rates.empty())
214 throw QString("ALSA: cannot set any sample rate (")+QString(snd_strerror(err))+")";
216 m_sampling_rate = sampling_rates.front();
217 cerr << "CaptureThread: INFO: ALSA: try sampling rate " << m_sampling_rate << " ..." << flush;
218 unsigned int rrate = m_sampling_rate;
219 err = snd_pcm_hw_params_set_rate(m_alsa_capture_handle, m_alsa_hw_params, rrate, 0);
221 if(err<0) cerr << " failed" << endl;
222 else cerr << " success" << endl;
224 sampling_rates.pop_front();
227 if(old_sampling_rate!=m_sampling_rate)
228 m_capture_thread->emitSamplingRateChanged();
234 unsigned int rrate = m_sampling_rate;
235 if((err = snd_pcm_hw_params_set_rate_near(m_alsa_capture_handle, m_alsa_hw_params, &rrate, &dir))<0)
236 throw QString("ALSA: cannot set sampling rate (")+QString(snd_strerror(err))+")";
237 if(m_sampling_rate!=rrate)
238 m_capture_thread->emitSamplingRateChanged();
239 m_sampling_rate = rrate;
242 if((err=snd_pcm_hw_params(m_alsa_capture_handle, m_alsa_hw_params)) < 0)
243 throw QString("ALSA: cannot set parameters (")+QString(snd_strerror(err))+")";
246 void CaptureThreadImplALSA::setSamplingRate(int value)
248 // cerr << "CaptureThreadImplALSA::setSamplingRate " << value << endl;
250 assert(value>0 || value==CaptureThread::SAMPLING_RATE_MAX);
252 if(m_sampling_rate!=value || value==CaptureThread::SAMPLING_RATE_MAX)
254 bool was_running = m_capture_thread->isCapturing();
255 if(was_running) m_capture_thread->stopCapture();
257 m_sampling_rate = value;
259 if(m_sampling_rate==CaptureThread::SAMPLING_RATE_MAX)
267 cerr << "CaptureThread: ERROR: " << error.toStdString() << endl;
268 m_capture_thread->emitError(error);
271 // it was just for testing
275 m_capture_thread->emitSamplingRateChanged();
277 if(was_running) m_capture_thread->startCapture();
280 // cerr << "~CaptureThreadImplALSA::setSamplingRate" << endl;
283 void CaptureThreadImplALSA::capture_init()
287 snd_pcm_nonblock(m_alsa_capture_handle, 0);
289 m_alsa_buffer = new char[m_channel_count*ALSA_BUFF_SIZE*snd_pcm_format_width(m_format)/8];
293 if((err=snd_pcm_prepare(m_alsa_capture_handle)) < 0)
294 throw QString("ALSA: cannot prepare audio interface for use (")+QString(snd_strerror(err))+")";
296 void CaptureThreadImplALSA::capture_loop()
298 // cerr << "CaptureThreadImplALSA::capture_loop" << endl;
300 m_wait_for_start = false;
303 int ret_val = snd_pcm_readi(m_alsa_capture_handle, m_alsa_buffer, ALSA_BUFF_SIZE);
304 if (ret_val == -ESTRPIPE)
309 cerr << "CaptureThread: WARNING: ALSA: " << snd_strerror(ret_val) << endl;
310 while((ret_val = snd_pcm_prepare(m_alsa_capture_handle)) < 0)
312 if (ret_val == -ESTRPIPE)
316 cerr << "ALSA: cannot prepare audio interface (" << snd_strerror(ret_val) << ")" << endl;
317 // throw QString("ALSA: cannot prepare audio interface (")+QString(snd_strerror(ret_val))+")";
322 if(!m_capture_thread->m_pause)
324 m_capture_thread->m_lock.lock();
326 // cerr << "CaptureThreadImplALSA::capture_loop " << m_capture_thread->m_values.size() << endl;
328 for(int i=0; i<ret_val*m_channel_count; i++)
329 addValue(this, decodeValue(m_alsa_buffer, i), i);
331 m_capture_thread->m_packet_size = ret_val;
332 if(m_capture_thread->m_ext_lock)
334 m_capture_thread->m_packet_size_sll = 0;
335 m_capture_thread->m_ext_lock = false;
337 m_capture_thread->m_packet_size_sll += ret_val;
339 m_capture_thread->m_lock.unlock();
344 // cerr << "~CaptureThreadImplALSA::capture_loop" << endl;
346 void CaptureThreadImplALSA::capture_finished()
348 if(m_alsa_buffer!=NULL)
350 delete[] m_alsa_buffer;
351 m_alsa_buffer = NULL;
354 if(m_alsa_capture_handle!=NULL)
356 snd_pcm_hw_free(m_alsa_capture_handle);
357 snd_pcm_close(m_alsa_capture_handle);
358 m_alsa_capture_handle = NULL;
362 void CaptureThreadImplALSA::run()
364 // cerr << "CaptureThread: INFO: ALSA: capture thread entered" << endl;
366 // while(m_alive) // TODO need to keep alsa thread alive to let PortAudio working after ALSA !!
368 while(m_alive && !m_loop)
375 // cerr << "CaptureThread: INFO: capture thread running" << endl;
379 m_capture_thread->m_capturing = true;
380 m_capture_thread->emitCaptureStarted();
381 m_capture_thread->emitCaptureToggled(true);
385 m_capture_thread->m_capturing = false;
386 m_capture_thread->emitCaptureStoped();
387 m_capture_thread->emitCaptureToggled(false);
392 cerr << "CaptureThread: ERROR: " << error.toStdString() << endl;
393 m_capture_thread->emitError(error);
395 m_wait_for_start = false;
401 // cerr << "CaptureThread: INFO: capture thread stop running" << endl;
404 // cerr << "CaptureThread: INFO: ALSA: capture thread exited" << endl;
407 CaptureThreadImplALSA::~CaptureThreadImplALSA()
409 // cerr << "CaptureThreadImplALSA::~CaptureThreadImplALSA" << endl;
418 // cerr << "~CaptureThreadImplALSA::~CaptureThreadImplALSA" << endl;