Release version 0.99.2-1
[fmit.git] / src / CaptureThreadImplALSA.cpp
1 // Copyright 2004 "Gilles Degottex"
2
3 // This file is part of "Music"
4
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.
9 //
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.
14 //
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
18
19
20 #include "CaptureThread.h"
21
22 #include <cassert>
23 #include <time.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <iostream>
28 #include <fstream>
29 #include <vector>
30 using namespace std;
31
32 // ------------------------------ ALSA implementation ----------------------------
33 #ifdef CAPTURE_ALSA
34
35 #define ALSA_BUFF_SIZE 1024
36
37 void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
38 {
39         cerr << "alsa_error_handler: " << file << ":" << line << " " << function << " err=" << err << endl;
40 }
41
42 CaptureThreadImplALSA::CaptureThreadImplALSA(CaptureThread* capture_thread)
43         : CaptureThreadImpl(capture_thread, "ALSA", QString("Advanced Linux Sound Architecture (lib:")+snd_asoundlib_version()+")")
44 {
45         m_alsa_capture_handle = NULL;
46         m_alsa_hw_params = NULL;
47         m_alsa_buffer = NULL;
48         m_format = SND_PCM_FORMAT_UNKNOWN;
49
50         m_source = "default"; // was hw:0
51
52         m_alive = true;
53         m_in_run = false;
54         m_loop = false;
55
56 //      snd_lib_error_set_handler(alsa_error_handler);
57 }
58
59 bool CaptureThreadImplALSA::is_available()
60 {
61         if(m_alsa_capture_handle==NULL)
62         {
63                 try
64                 {
65                         int err = -1;
66                         if((err=snd_pcm_open(&m_alsa_capture_handle, m_source.toAscii().constData(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0)
67                         {
68                                 if(err==-19)    // TODO risks of changes for the error code
69                                         throw QString("invalid source '")+m_source+"'";
70                                 else if(err==-16)
71                                         throw QString("device '")+m_source+"' busy";
72                                 else
73                                         throw QString("cannot open pcm: ")+QString(snd_strerror(err));
74                         }
75                 }
76                 catch(QString error)
77                 {
78                         m_alsa_capture_handle = NULL;
79
80                         m_status = "N/A ("+error+")";
81
82                         return false;
83                 }
84
85                 if(m_alsa_capture_handle!=NULL)
86                 {
87                         snd_pcm_close(m_alsa_capture_handle);
88                         m_alsa_capture_handle = NULL;
89                 }
90         }
91
92         m_status = "OK";
93
94         //      cerr << "CaptureThread: INFO: ALSA seems available" << endl;
95
96         return true;
97 }
98
99 void CaptureThreadImplALSA::startCapture()
100 {
101         if(!isRunning())
102                 start();
103
104         m_loop = true;
105
106         m_wait_for_start = true;
107         while(m_wait_for_start) // some implementations take a long time to start
108                 msleep(10);
109 }
110 void CaptureThreadImplALSA::stopCapture()
111 {
112         m_loop = false;
113
114         while(m_in_run)
115                 msleep(10);
116 }
117
118 void CaptureThreadImplALSA::set_params(bool test)
119 {
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;
124 //      }
125         int err=0;
126
127         if(m_source=="")
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)
130         {
131                 //                                      cerr << "err=" << err << ":" << snd_strerror(err) << endl;
132
133                 if(err==-19)    // TODO risks of changes for the error code
134                         throw QString("ALSA: Invalid Source '")+m_source+"'";
135                 else if(err==-16)
136                         throw QString("ALSA: Device '")+m_source+"' busy";
137                 else
138                         throw QString("ALSA: Cannot open pcm: ")+QString(snd_strerror(err));
139         }
140
141         snd_pcm_hw_params_alloca(&m_alsa_hw_params);
142
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))+")";
145
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))+")";
148
149         if(!test)
150         {
151                 // Formats
152                 if(m_format==-1)
153                 {
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);
157
158                         err = -1;
159                         while(err<0)
160                         {
161                                 if(formats.empty())
162                                         throw QString("ALSA: cannot set any format (")+QString(snd_strerror(err))+")";
163
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);
167
168                                 if(err<0)       cerr << " failed" << endl;
169                                 else            cerr << " success" << endl;
170
171                                 formats.pop_front();
172                         }
173                 }
174                 else
175                 {
176                         if((err=snd_pcm_hw_params_set_format(m_alsa_capture_handle, m_alsa_hw_params, m_format))<0)
177                         {
178                                 QString err_msg = QString("ALSA: cannot set format (")+QString(snd_strerror(err))+")";
179                                 cerr << "CaptureThread: ERROR: " << err_msg.toStdString() << endl;
180                         }
181                 }
182
183                 // Channel count
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)
186                 {
187                         QString err_msg = QString("ALSA: cannot set channel count (")+QString(snd_strerror(err))+")";
188                         cerr << "CaptureThread: WARNING: " << err_msg.toStdString() << endl;
189                 }
190                 if(channel_count>1)
191                 {
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;
194                 }
195
196                 setFormatDescrsAndFns(snd_pcm_format_width(m_format)/8, snd_pcm_format_signed(m_format), false, channel_count);
197         }
198
199         if(m_sampling_rate==CaptureThread::SAMPLING_RATE_MAX || m_sampling_rate==CaptureThread::SAMPLING_RATE_UNKNOWN)
200         {
201                 int old_sampling_rate = m_sampling_rate;
202
203                 cerr << "CaptureThread: INFO: ALSA: sampling rate set to max or undefined, try to determinate it." << endl;
204
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);
209
210                 err = -1;
211                 while(err<0)
212                 {
213                         if(sampling_rates.empty())
214                                 throw QString("ALSA: cannot set any sample rate (")+QString(snd_strerror(err))+")";
215
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);
220
221                         if(err<0)       cerr << " failed" << endl;
222                         else            cerr << " success" << endl;
223
224                         sampling_rates.pop_front();
225                 }
226
227                 if(old_sampling_rate!=m_sampling_rate)
228                         m_capture_thread->emitSamplingRateChanged();
229         }
230         else
231         {
232                 int err;
233                 int dir = 0;
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;
240         }
241
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))+")";
244 }
245
246 void CaptureThreadImplALSA::setSamplingRate(int value)
247 {
248 //      cerr << "CaptureThreadImplALSA::setSamplingRate " << value << endl;
249
250         assert(value>0 || value==CaptureThread::SAMPLING_RATE_MAX);
251
252         if(m_sampling_rate!=value || value==CaptureThread::SAMPLING_RATE_MAX)
253         {
254                 bool was_running = m_capture_thread->isCapturing();
255                 if(was_running) m_capture_thread->stopCapture();
256
257                 m_sampling_rate = value;
258
259                 if(m_sampling_rate==CaptureThread::SAMPLING_RATE_MAX)
260                 {
261                         try
262                         {
263                                 set_params(true);
264                         }
265                         catch(QString error)
266                         {
267                                 cerr << "CaptureThread: ERROR: " << error.toStdString() << endl;
268                                 m_capture_thread->emitError(error);
269                         }
270
271                         // it was just for testing
272                         capture_finished();
273                 }
274                 else
275                         m_capture_thread->emitSamplingRateChanged();
276
277                 if(was_running) m_capture_thread->startCapture();
278         }
279
280 //      cerr << "~CaptureThreadImplALSA::setSamplingRate" << endl;
281 }
282
283 void CaptureThreadImplALSA::capture_init()
284 {
285         set_params();
286
287         snd_pcm_nonblock(m_alsa_capture_handle, 0);
288
289         m_alsa_buffer = new char[m_channel_count*ALSA_BUFF_SIZE*snd_pcm_format_width(m_format)/8];
290
291         int err=0;
292
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))+")";
295 }
296 void CaptureThreadImplALSA::capture_loop()
297 {
298 //      cerr << "CaptureThreadImplALSA::capture_loop" << endl;
299
300         m_wait_for_start = false;
301         while(m_loop)
302         {
303                 int ret_val = snd_pcm_readi(m_alsa_capture_handle, m_alsa_buffer, ALSA_BUFF_SIZE);
304         if (ret_val == -ESTRPIPE)
305             continue;
306
307                 if(ret_val<0)
308                 {
309                         cerr << "CaptureThread: WARNING: ALSA: " << snd_strerror(ret_val) << endl;
310                         while((ret_val = snd_pcm_prepare(m_alsa_capture_handle)) < 0)
311                         {
312                 if (ret_val == -ESTRPIPE)
313                     continue;
314
315                                 msleep(1000);
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))+")";
318                         }
319                 }
320                 else
321                 {
322                         if(!m_capture_thread->m_pause)
323                         {
324                                 m_capture_thread->m_lock.lock();
325
326 //                              cerr << "CaptureThreadImplALSA::capture_loop " << m_capture_thread->m_values.size() << endl;
327
328                                 for(int i=0; i<ret_val*m_channel_count; i++)
329                                         addValue(this, decodeValue(m_alsa_buffer, i), i);
330
331                                 m_capture_thread->m_packet_size = ret_val;
332                                 if(m_capture_thread->m_ext_lock)
333                                 {
334                                         m_capture_thread->m_packet_size_sll = 0;
335                                         m_capture_thread->m_ext_lock = false;
336                                 }
337                                 m_capture_thread->m_packet_size_sll += ret_val;
338
339                                 m_capture_thread->m_lock.unlock();
340                         }
341                 }
342         }
343
344 //      cerr << "~CaptureThreadImplALSA::capture_loop" << endl;
345 }
346 void CaptureThreadImplALSA::capture_finished()
347 {
348         if(m_alsa_buffer!=NULL)
349         {
350                 delete[] m_alsa_buffer;
351                 m_alsa_buffer = NULL;
352         }
353
354         if(m_alsa_capture_handle!=NULL)
355         {
356                 snd_pcm_hw_free(m_alsa_capture_handle);
357                 snd_pcm_close(m_alsa_capture_handle);
358                 m_alsa_capture_handle = NULL;
359         }
360 }
361
362 void CaptureThreadImplALSA::run()
363 {
364 //      cerr << "CaptureThread: INFO: ALSA: capture thread entered" << endl;
365
366 //      while(m_alive)  // TODO need to keep alsa thread alive to let PortAudio working after ALSA !!
367         {
368                 while(m_alive && !m_loop)
369                         msleep(10);
370
371                 m_in_run = true;
372
373                 try
374                 {
375                         //                      cerr << "CaptureThread: INFO: capture thread running" << endl;
376
377                         capture_init();
378
379                         m_capture_thread->m_capturing = true;
380                         m_capture_thread->emitCaptureStarted();
381                         m_capture_thread->emitCaptureToggled(true);
382
383                         capture_loop();
384
385                         m_capture_thread->m_capturing = false;
386                         m_capture_thread->emitCaptureStoped();
387                         m_capture_thread->emitCaptureToggled(false);
388                 }
389                 catch(QString error)
390                 {
391                         m_loop = false;
392                         cerr << "CaptureThread: ERROR: " << error.toStdString() << endl;
393                         m_capture_thread->emitError(error);
394                 }
395                 m_wait_for_start = false;
396
397                 capture_finished();
398
399                 m_in_run = false;
400
401                 //              cerr << "CaptureThread: INFO: capture thread stop running" << endl;
402         }
403
404 //      cerr << "CaptureThread: INFO: ALSA: capture thread exited" << endl;
405 }
406
407 CaptureThreadImplALSA::~CaptureThreadImplALSA()
408 {
409 //      cerr << "CaptureThreadImplALSA::~CaptureThreadImplALSA" << endl;
410
411         m_alive = false;
412
413         stopCapture();
414
415         while(isRunning())
416                 msleep(10);
417
418 //      cerr << "~CaptureThreadImplALSA::~CaptureThreadImplALSA" << endl;
419 }
420
421 #endif