Rename to new qt4header
[kdeaccessibility.git] / kttsd / players / alsaplayer / alsaplayer.cpp
blob192dd701bf73cc1e083f534cbd9bb8c3a9773764
1 /***************************************************** vim:set ts=4 sw=4 sts=4:
2 ALSA player.
3 -------------------
4 Copyright:
5 (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
6 Portions based on aplay.c in alsa-utils
7 Copyright (c) by Jaroslav Kysela <perex@suse.cz>
8 Based on vplay program by Michael Beck
9 -------------------
10 Original author: Gary Cramblitt <garycramblitt@comcast.net>
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 ******************************************************************************/
27 // #include <sys/wait.h>
28 // System includes.
29 #include <config.h>
30 #if TIME_WITH_SYS_TIME
31 # include <sys/time.h>
32 # include <time.h>
33 #else
34 # if HAVE_SYS_TIME_H
35 # include <sys/time.h>
36 # else
37 # include <time.h>
38 # endif
39 #endif
41 // Qt includes.
42 #include <QDir>
43 #include <QApplication>
44 #include <QMutexLocker>
46 // KDE includes.
47 #include <kdebug.h>
48 #include <kconfig.h>
49 #include <kstandarddirs.h>
50 #include <kmessagebox.h>
51 #include <klocale.h>
53 // AlsaPlayer includes.
54 #include "alsaplayer.h"
56 #if !defined(__GNUC__) || __GNUC__ >= 3
57 #define ERR(...) do {\
58 QString dbgStr;\
59 QString s = dbgStr.sprintf( "%s:%d: ERROR ", __FUNCTION__, __LINE__); \
60 s += dbgStr.sprintf( __VA_ARGS__); \
61 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
62 } while (0)
63 #else
64 #define ERR(args...) do {\
65 QString dbgStr;\
66 QString s = dbgStr.sprintf( "%s:%d: ERROR ", __FUNCTION__, __LINE__); \
67 s += dbgStr.sprintf( ##args ); \
68 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
69 } while (0)
70 #endif
71 #if !defined(__GNUC__) || __GNUC__ >= 3
72 #define MSG(...) do {\
73 if (m_debugLevel >= 1) {\
74 QString dbgStr; \
75 QString s = dbgStr.sprintf( "%s:%d: ", __FUNCTION__, __LINE__); \
76 s += dbgStr.sprintf( __VA_ARGS__); \
77 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
78 }; \
79 } while (0)
80 #else
81 #define MSG(args...) do {\
82 if (m_debugLevel >= 1) {\
83 QString dbgStr; \
84 QString s = dbgStr.sprintf( "%s:%d: ", __FUNCTION__, __LINE__); \
85 s += dbgStr.sprintf( ##args ); \
86 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
87 }; \
88 } while (0)
89 #endif
91 #if !defined(__GNUC__) || __GNUC__ >= 3
92 #define DBG(...) do {\
93 if (m_debugLevel >= 2) {\
94 QString dbgStr; \
95 QString s = dbgStr.sprintf( "%s:%d: ", __FUNCTION__, __LINE__); \
96 s += dbgStr.sprintf( __VA_ARGS__); \
97 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
98 }; \
99 } while (0)
100 #else
101 #define DBG(args...) do {\
102 if (m_debugLevel >= 2) {\
103 QString dbgStr; \
104 QString s = dbgStr.sprintf( "%s:%d: ", __FUNCTION__, __LINE__); \
105 s += dbgStr.sprintf( ##args ); \
106 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
107 }; \
108 } while (0)
109 #endif
111 QString AlsaPlayerThread::timestamp() const
113 time_t t;
114 struct timeval tv;
115 char *tstr;
116 t = time(NULL);
117 tstr = strdup(ctime(&t));
118 tstr[strlen(tstr)-1] = 0;
119 gettimeofday(&tv,NULL);
120 QString ts;
121 ts.sprintf(" %s [%d] ",tstr, (int) tv.tv_usec);
122 free(tstr);
123 return ts;
126 ////////////////////////////////////////////////////////////////////////////////
127 // public methods
128 ////////////////////////////////////////////////////////////////////////////////
130 AlsaPlayerThread::AlsaPlayerThread(QObject* parent) :
131 QThread(parent),
132 m_currentVolume(1.0),
133 m_pcmName("default"),
134 m_defPeriodSize(128),
135 m_defPeriods(8),
136 m_debugLevel(1),
137 m_simulatedPause(false)
139 init();
142 AlsaPlayerThread::~AlsaPlayerThread()
144 if (isRunning()) {
145 stop();
146 wait();
150 //void AlsaPlayerThread::play(const FileHandle &file)
151 void AlsaPlayerThread::startPlay(const QString &file)
153 if (isRunning()) {
154 if (paused()) {
155 if (canPause)
156 snd_pcm_pause(handle, false);
157 else
158 m_simulatedPause = false;
160 return;
162 audiofile.setName(file);
163 audiofile.open(QIODevice::ReadOnly);
164 fd = audiofile.handle();
165 // Start thread running.
166 start();
169 /*virtual*/ void AlsaPlayerThread::run()
171 QString pName = m_pcmName.section(" ", 0, 0);
172 DBG("pName = %s", pName.ascii());
173 pcm_name = qstrdup(pName.ascii());
174 int err;
175 snd_pcm_info_t *info;
177 m_simulatedPause = false;
179 snd_pcm_info_alloca(&info);
181 err = snd_output_stdio_attach(&log, stderr, 0);
182 assert(err >= 0);
184 rhwdata.format = DEFAULT_FORMAT;
185 rhwdata.rate = DEFAULT_SPEED;
186 rhwdata.channels = 1;
188 err = snd_pcm_open(&handle, pcm_name, stream, open_mode);
189 if (err < 0) {
190 ERR("audio open error on pcm device %s: %s", pcm_name, snd_strerror(err));
191 return;
194 if ((err = snd_pcm_info(handle, info)) < 0) {
195 ERR("info error: %s", snd_strerror(err));
196 return;
199 chunk_size = 1024;
200 hwdata = rhwdata;
202 audioBuffer.resize(1024);
203 // audiobuf = (char *)malloc(1024);
204 audiobuf = audioBuffer.data();
205 if (audiobuf == NULL) {
206 ERR("not enough memory");
207 return;
210 if (mmap_flag) {
211 writei_func = snd_pcm_mmap_writei;
212 readi_func = snd_pcm_mmap_readi;
213 writen_func = snd_pcm_mmap_writen;
214 readn_func = snd_pcm_mmap_readn;
215 } else {
216 writei_func = snd_pcm_writei;
217 readi_func = snd_pcm_readi;
218 writen_func = snd_pcm_writen;
219 readn_func = snd_pcm_readn;
223 playback(fd);
224 cleanup();
225 return;
228 void AlsaPlayerThread::pause()
230 if (isRunning()) {
231 DBG("Pause requested");
232 QMutexLocker locker(&m_mutex);
233 if (handle) {
234 // Some hardware can pause; some can't. canPause is set in set_params.
235 if (canPause) {
236 m_simulatedPause = false;
237 snd_pcm_pause(handle, true);
238 } else {
239 // Set a flag and cause wait_for_poll to sleep. When resumed, will get
240 // an underrun.
241 m_simulatedPause = true;
242 locker.unlock();
248 void AlsaPlayerThread::stop()
250 if (isRunning()) {
251 DBG("STOP! Locking mutex");
252 QMutexLocker locker(&m_mutex);
253 m_simulatedPause = false;
254 if (handle) {
255 /* This constant is arbitrary */
256 char buf = 42;
257 DBG("Request for stop, device state is %s",
258 snd_pcm_state_name(snd_pcm_state(handle)));
259 write(alsa_stop_pipe[1], &buf, 1);
261 DBG("unlocking mutex");
262 locker.unlock();
263 /* Wait for thread to exit */
264 DBG("waiting for thread to exit");
265 wait();
266 DBG("cleaning up");
268 cleanup();
272 * Stop playback, cleanup and exit thread.
274 void AlsaPlayerThread::stopAndExit()
276 // if (handle) snd_pcm_drop(handle);
277 cleanup();
278 exit();
281 void AlsaPlayerThread::setVolume(float volume)
283 m_currentVolume = volume;
286 float AlsaPlayerThread::volume() const
288 return m_currentVolume;
291 /////////////////////////////////////////////////////////////////////////////////
292 // player status functions
293 /////////////////////////////////////////////////////////////////////////////////
295 bool AlsaPlayerThread::playing() const
297 bool result = false;
298 if (isRunning()) {
299 QMutexLocker locker(&m_mutex);
300 if (handle) {
301 if (canPause) {
302 snd_pcm_status_t *status;
303 snd_pcm_status_alloca(&status);
304 int res;
305 if ((res = snd_pcm_status(handle, status)) < 0)
306 ERR("status error: %s", snd_strerror(res));
307 else {
308 result = (SND_PCM_STATE_RUNNING == snd_pcm_status_get_state(status))
309 || (SND_PCM_STATE_DRAINING == snd_pcm_status_get_state(status));
310 DBG("state = %s", snd_pcm_state_name(snd_pcm_status_get_state(status)));
312 } else
313 result = !m_simulatedPause;
316 return result;
319 bool AlsaPlayerThread::paused() const
321 bool result = false;
322 if (isRunning()) {
323 QMutexLocker locker(&m_mutex);
324 if (handle) {
325 if (canPause) {
326 snd_pcm_status_t *status;
327 snd_pcm_status_alloca(&status);
328 int res;
329 if ((res = snd_pcm_status(handle, status)) < 0)
330 ERR("status error: %s", snd_strerror(res));
331 else {
332 result = (SND_PCM_STATE_PAUSED == snd_pcm_status_get_state(status));
333 DBG("state = %s", snd_pcm_state_name(snd_pcm_status_get_state(status)));
335 } else
336 result = m_simulatedPause;
339 return result;
342 int AlsaPlayerThread::totalTime() const
344 int total = 0;
345 int rate = hwdata.rate;
346 int channels = hwdata.channels;
347 if (rate > 0 && channels > 0) {
348 total = int((double(pbrec_count) / rate) / channels);
349 // DBG("pbrec_count = %i rate =%i channels = %i", pbrec_count, rate, channels);
350 // DBG("totalTime = %i", total);
352 return total;
355 int AlsaPlayerThread::currentTime() const
357 int current = 0;
358 int rate = hwdata.rate;
359 int channels = hwdata.channels;
360 if (rate > 0 && channels > 0) {
361 current = int((double(fdcount) / rate) / channels);
362 // DBG("fdcount = %i rate = %i channels = %i", fdcount, rate, channels);
363 // DBG("currentTime = %i", current);
365 return current;
368 int AlsaPlayerThread::position() const
370 // TODO: Make this more accurate by adding frames that have been so-far
371 // played within the Alsa ring buffer.
372 return pbrec_count > 0 ? int(double(fdcount) * 1000 / pbrec_count + .5) : 0;
375 /////////////////////////////////////////////////////////////////////////////////
376 // player seek functions
377 /////////////////////////////////////////////////////////////////////////////////
379 void AlsaPlayerThread::seek(int /*seekTime*/)
381 // TODO:
384 void AlsaPlayerThread::seekPosition(int /*position*/)
386 // TODO:
390 * Returns a list of PCM devices.
391 * This function fills the specified list with ALSA hardware soundcards found on the system.
392 * It uses plughw:xx instead of hw:xx for specifiers, because hw:xx are not practical to
393 * use (e.g. they require a resampler/channel mixer in the application).
395 QStringList AlsaPlayerThread::getPluginList( const QByteArray& classname )
397 Q_UNUSED(classname);
399 int err = 0;
400 int card = -1, device = -1;
401 snd_ctl_t *handle;
402 snd_ctl_card_info_t *info;
403 snd_pcm_info_t *pcminfo;
404 snd_ctl_card_info_alloca(&info);
405 snd_pcm_info_alloca(&pcminfo);
406 QStringList result;
408 result.append("default");
409 for (;;) {
410 err = snd_card_next(&card);
411 if (err < 0 || card < 0) break;
412 if (card >= 0) {
413 char name[32];
414 sprintf(name, "hw:%i", card);
415 if ((err = snd_ctl_open(&handle, name, 0)) < 0) continue;
416 if ((err = snd_ctl_card_info(handle, info)) < 0) {
417 snd_ctl_close(handle);
418 continue;
420 for (int devCnt=0;;++devCnt) {
421 err = snd_ctl_pcm_next_device(handle, &device);
422 if (err < 0 || device < 0) break;
424 snd_pcm_info_set_device(pcminfo, device);
425 snd_pcm_info_set_subdevice(pcminfo, 0);
426 snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
427 if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) continue;
428 QString infoName = " ";
429 infoName += snd_ctl_card_info_get_name(info);
430 infoName += " (";
431 infoName += snd_pcm_info_get_name(pcminfo);
432 infoName += ")";
433 if (0 == devCnt) {
434 QString pcmName = QString("default:%1").arg(card);
435 result.append(pcmName + infoName);
437 QString pcmName = QString("plughw:%1,%2").arg(card).arg(device);
438 result.append(pcmName + infoName);
440 snd_ctl_close(handle);
443 return result;
446 void AlsaPlayerThread::setSinkName(const QString& sinkName) { m_pcmName = sinkName; }
448 /////////////////////////////////////////////////////////////////////////////////
449 // private
450 /////////////////////////////////////////////////////////////////////////////////
452 void AlsaPlayerThread::init()
454 pcm_name = 0;
455 handle = 0;
456 canPause = false;
457 timelimit = 0;
458 file_type = FORMAT_DEFAULT;
459 sleep_min = 0;
460 // open_mode = 0;
461 open_mode = SND_PCM_NONBLOCK;
462 stream = SND_PCM_STREAM_PLAYBACK;
463 mmap_flag = 0;
464 interleaved = 1;
465 audiobuf = NULL;
466 chunk_size = 0;
467 period_time = 0;
468 buffer_time = 0;
469 avail_min = -1;
470 start_delay = 0;
471 stop_delay = 0;
472 buffer_pos = 0;
473 log = 0;
474 fd = -1;
475 pbrec_count = LLONG_MAX;
476 alsa_stop_pipe[0] = 0;
477 alsa_stop_pipe[1] = 0;
478 alsa_poll_fds = 0;
479 m_simulatedPause = false;
482 void AlsaPlayerThread::cleanup()
484 DBG("cleaning up");
485 QMutexLocker locker(&m_mutex);
486 if (pcm_name) free(pcm_name);
487 if (fd >= 0) audiofile.close();
488 if (handle) {
489 snd_pcm_drop(handle);
490 snd_pcm_close(handle);
492 if (alsa_stop_pipe[0]) close(alsa_stop_pipe[0]);
493 if (alsa_stop_pipe[1]) close(alsa_stop_pipe[1]);
494 if (audiobuf) audioBuffer.resize(0);
495 if (alsa_poll_fds) alsa_poll_fds_barray.resize(0);
496 if (log) snd_output_close(log);
497 snd_config_update_free_global();
498 init();
502 * Safe read (for pipes)
505 ssize_t AlsaPlayerThread::safe_read(int fd, void *buf, size_t count)
507 ssize_t result = 0;
508 ssize_t res;
510 while (count > 0) {
511 if ((res = read(fd, buf, count)) == 0)
512 break;
513 if (res < 0)
514 return result > 0 ? result : res;
515 count -= res;
516 result += res;
517 buf = (char *)buf + res;
519 return result;
523 * Test, if it is a .VOC file and return >=0 if ok (this is the length of rest)
524 * < 0 if not
526 int AlsaPlayerThread::test_vocfile(void *buffer)
528 VocHeader *vp = (VocHeader*)buffer;
530 if (!memcmp(vp->magic, VOC_MAGIC_STRING, 20)) {
531 vocminor = LE_SHORT(vp->version) & 0xFF;
532 vocmajor = LE_SHORT(vp->version) / 256;
533 if (LE_SHORT(vp->version) != (0x1233 - LE_SHORT(vp->coded_ver)))
534 return -2; /* coded version mismatch */
535 return LE_SHORT(vp->headerlen) - sizeof(VocHeader); /* 0 mostly */
537 return -1; /* magic string fail */
541 * helper for test_wavefile
544 ssize_t AlsaPlayerThread::test_wavefile_read(int fd, char *buffer, size_t *size, size_t reqsize, int line)
546 if (*size >= reqsize)
547 return *size;
548 if ((size_t)safe_read(fd, buffer + *size, reqsize - *size) != reqsize - *size) {
549 ERR("read error (called from line %i)", line);
550 stopAndExit();
552 return *size = reqsize;
555 #define check_wavefile_space(buffer, len, blimit) \
556 if (len > blimit) { \
557 blimit = len; \
558 if ((buffer = (char*)realloc(buffer, blimit)) == NULL) { \
559 ERR("not enough memory"); \
560 stopAndExit(); \
565 * test, if it's a .WAV file, > 0 if ok (and set the speed, stereo etc.)
566 * == 0 if not
567 * Value returned is bytes to be discarded.
569 ssize_t AlsaPlayerThread::test_wavefile(int fd, char *_buffer, size_t size)
571 WaveHeader *h = (WaveHeader *)_buffer;
572 char *buffer = NULL;
573 size_t blimit = 0;
574 WaveFmtBody *f;
575 WaveChunkHeader *c;
576 u_int type;
577 u_int len;
579 if (size < sizeof(WaveHeader))
580 return -1;
581 if (h->magic != WAV_RIFF || h->type != WAV_WAVE)
582 return -1;
583 if (size > sizeof(WaveHeader)) {
584 check_wavefile_space(buffer, size - sizeof(WaveHeader), blimit);
585 memcpy(buffer, _buffer + sizeof(WaveHeader), size - sizeof(WaveHeader));
587 size -= sizeof(WaveHeader);
588 while (1) {
589 check_wavefile_space(buffer, sizeof(WaveChunkHeader), blimit);
590 test_wavefile_read(fd, buffer, &size, sizeof(WaveChunkHeader), __LINE__);
591 c = (WaveChunkHeader*)buffer;
592 type = c->type;
593 len = LE_INT(c->length);
594 len += len % 2;
595 if (size > sizeof(WaveChunkHeader))
596 memmove(buffer, buffer + sizeof(WaveChunkHeader), size - sizeof(WaveChunkHeader));
597 size -= sizeof(WaveChunkHeader);
598 if (type == WAV_FMT)
599 break;
600 check_wavefile_space(buffer, len, blimit);
601 test_wavefile_read(fd, buffer, &size, len, __LINE__);
602 if (size > len)
603 memmove(buffer, buffer + len, size - len);
604 size -= len;
607 if (len < sizeof(WaveFmtBody)) {
608 ERR("unknown length of 'fmt ' chunk (read %u, should be %u at least)", len, (u_int)sizeof(WaveFmtBody));
609 stopAndExit();
611 check_wavefile_space(buffer, len, blimit);
612 test_wavefile_read(fd, buffer, &size, len, __LINE__);
613 f = (WaveFmtBody*) buffer;
614 if (LE_SHORT(f->format) != WAV_PCM_CODE) {
615 ERR("can't play not PCM-coded WAVE-files");
616 stopAndExit();
618 if (LE_SHORT(f->modus) < 1) {
619 ERR("can't play WAVE-files with %d tracks", LE_SHORT(f->modus));
620 stopAndExit();
622 hwdata.channels = LE_SHORT(f->modus);
623 switch (LE_SHORT(f->bit_p_spl)) {
624 case 8:
625 if (hwdata.format != DEFAULT_FORMAT &&
626 hwdata.format != SND_PCM_FORMAT_U8)
627 MSG("Warning: format is changed to U8");
628 hwdata.format = SND_PCM_FORMAT_U8;
629 break;
630 case 16:
631 if (hwdata.format != DEFAULT_FORMAT &&
632 hwdata.format != SND_PCM_FORMAT_S16_LE)
633 MSG("Warning: format is changed to S16_LE");
634 hwdata.format = SND_PCM_FORMAT_S16_LE;
635 break;
636 case 24:
637 switch (LE_SHORT(f->byte_p_spl) / hwdata.channels) {
638 case 3:
639 if (hwdata.format != DEFAULT_FORMAT &&
640 hwdata.format != SND_PCM_FORMAT_S24_3LE)
641 MSG("Warning: format is changed to S24_3LE");
642 hwdata.format = SND_PCM_FORMAT_S24_3LE;
643 break;
644 case 4:
645 if (hwdata.format != DEFAULT_FORMAT &&
646 hwdata.format != SND_PCM_FORMAT_S24_LE)
647 MSG("Warning: format is changed to S24_LE");
648 hwdata.format = SND_PCM_FORMAT_S24_LE;
649 break;
650 default:
651 ERR("can't play WAVE-files with sample %d bits in %d bytes wide (%d channels)", LE_SHORT(f->bit_p_spl), LE_SHORT(f->byte_p_spl), hwdata.channels);
652 stopAndExit();
654 break;
655 case 32:
656 hwdata.format = SND_PCM_FORMAT_S32_LE;
657 break;
658 default:
659 ERR("can't play WAVE-files with sample %d bits wide", LE_SHORT(f->bit_p_spl));
660 stopAndExit();
662 hwdata.rate = LE_INT(f->sample_fq);
664 if (size > len)
665 memmove(buffer, buffer + len, size - len);
666 size -= len;
668 while (1) {
669 u_int type, len;
671 check_wavefile_space(buffer, sizeof(WaveChunkHeader), blimit);
672 test_wavefile_read(fd, buffer, &size, sizeof(WaveChunkHeader), __LINE__);
673 c = (WaveChunkHeader*)buffer;
674 type = c->type;
675 len = LE_INT(c->length);
676 if (size > sizeof(WaveChunkHeader))
677 memmove(buffer, buffer + sizeof(WaveChunkHeader), size - sizeof(WaveChunkHeader));
678 size -= sizeof(WaveChunkHeader);
679 if (type == WAV_DATA) {
680 if (len < pbrec_count && len < 0x7ffffffe)
681 pbrec_count = len;
682 if (size > 0)
683 memcpy(_buffer, buffer, size);
684 free(buffer);
685 return size;
687 len += len % 2;
688 check_wavefile_space(buffer, len, blimit);
689 test_wavefile_read(fd, buffer, &size, len, __LINE__);
690 if (size > len)
691 memmove(buffer, buffer + len, size - len);
692 size -= len;
695 /* shouldn't be reached */
696 return -1;
700 * Test for AU file.
703 int AlsaPlayerThread::test_au(int fd, char *buffer)
705 AuHeader *ap = (AuHeader*)buffer;
707 if (ap->magic != AU_MAGIC)
708 return -1;
709 if (BE_INT(ap->hdr_size) > 128 || BE_INT(ap->hdr_size) < 24)
710 return -1;
711 pbrec_count = BE_INT(ap->data_size);
712 switch (BE_INT(ap->encoding)) {
713 case AU_FMT_ULAW:
714 if (hwdata.format != DEFAULT_FORMAT &&
715 hwdata.format != SND_PCM_FORMAT_MU_LAW)
716 MSG("Warning: format is changed to MU_LAW");
717 hwdata.format = SND_PCM_FORMAT_MU_LAW;
718 break;
719 case AU_FMT_LIN8:
720 if (hwdata.format != DEFAULT_FORMAT &&
721 hwdata.format != SND_PCM_FORMAT_U8)
722 MSG("Warning: format is changed to U8");
723 hwdata.format = SND_PCM_FORMAT_U8;
724 break;
725 case AU_FMT_LIN16:
726 if (hwdata.format != DEFAULT_FORMAT &&
727 hwdata.format != SND_PCM_FORMAT_S16_BE)
728 MSG("Warning: format is changed to S16_BE");
729 hwdata.format = SND_PCM_FORMAT_S16_BE;
730 break;
731 default:
732 return -1;
734 hwdata.rate = BE_INT(ap->sample_rate);
735 if (hwdata.rate < 2000 || hwdata.rate > 256000)
736 return -1;
737 hwdata.channels = BE_INT(ap->channels);
738 if (hwdata.channels < 1 || hwdata.channels > 128)
739 return -1;
740 if ((size_t)safe_read(fd, buffer + sizeof(AuHeader), BE_INT(ap->hdr_size) - sizeof(AuHeader)) != BE_INT(ap->hdr_size) - sizeof(AuHeader)) {
741 ERR("read error");
742 stopAndExit();
744 return 0;
747 void AlsaPlayerThread::set_params(void)
749 snd_pcm_hw_params_t *hwparams;
750 snd_pcm_uframes_t period_size;
751 int err;
752 int dir;
753 unsigned int rate;
754 unsigned int periods;
756 snd_pcm_hw_params_alloca(&hwparams);
757 err = snd_pcm_hw_params_any(handle, hwparams);
758 if (err < 0) {
759 ERR("Broken configuration for this PCM: no configurations available");
760 stopAndExit();
763 /* Create the pipe for communication about stop requests. */
764 if (pipe(alsa_stop_pipe)) {
765 ERR("Stop pipe creation failed (%s)", strerror(errno));
766 stopAndExit();
769 /* Find how many descriptors we will get for poll(). */
770 alsa_fd_count = snd_pcm_poll_descriptors_count(handle);
771 if (alsa_fd_count <= 0){
772 ERR("Invalid poll descriptors count returned from ALSA.");
773 stopAndExit();
776 /* Create and fill in struct pollfd *alsa_poll_fds with ALSA descriptors. */
777 // alsa_poll_fds = (pollfd *)malloc ((alsa_fd_count + 1) * sizeof(struct pollfd));
778 alsa_poll_fds_barray.resize((alsa_fd_count + 1) * sizeof(struct pollfd));
779 alsa_poll_fds = (pollfd *)alsa_poll_fds_barray.data();
780 assert(alsa_poll_fds);
781 if ((err = snd_pcm_poll_descriptors(handle, alsa_poll_fds, alsa_fd_count)) < 0) {
782 ERR("Unable to obtain poll descriptors for playback: %s", snd_strerror(err));
783 stopAndExit();
786 /* Create a new pollfd structure for requests by alsa_stop(). */
787 struct pollfd alsa_stop_pipe_pfd;
788 alsa_stop_pipe_pfd.fd = alsa_stop_pipe[0];
789 alsa_stop_pipe_pfd.events = POLLIN;
790 alsa_stop_pipe_pfd.revents = 0;
792 /* Join this our own pollfd to the ALSAs ones. */
793 alsa_poll_fds[alsa_fd_count] = alsa_stop_pipe_pfd;
794 ++alsa_fd_count;
796 if (mmap_flag) {
797 snd_pcm_access_mask_t *mask = (snd_pcm_access_mask_t *)alloca(snd_pcm_access_mask_sizeof());
798 snd_pcm_access_mask_none(mask);
799 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
800 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
801 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
802 err = snd_pcm_hw_params_set_access_mask(handle, hwparams, mask);
803 } else if (interleaved)
804 err = snd_pcm_hw_params_set_access(handle, hwparams,
805 SND_PCM_ACCESS_RW_INTERLEAVED);
806 else
807 err = snd_pcm_hw_params_set_access(handle, hwparams,
808 SND_PCM_ACCESS_RW_NONINTERLEAVED);
809 if (err < 0) {
810 ERR("Error setting access type: %s", snd_strerror(err));
811 stopAndExit();
813 err = snd_pcm_hw_params_set_format(handle, hwparams, hwdata.format);
814 if (err < 0) {
815 ERR("Error setting sample format to %i: %s", hwdata.format, snd_strerror(err));
816 stopAndExit();
818 err = snd_pcm_hw_params_set_channels(handle, hwparams, hwdata.channels);
819 if (err < 0) {
820 ERR("Error setting channel count to %i: %s", hwdata.channels, snd_strerror(err));
821 stopAndExit();
824 #if 0
825 err = snd_pcm_hw_params_set_periods_min(handle, hwparams, 2);
826 assert(err >= 0);
827 #endif
828 rate = hwdata.rate;
829 err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &hwdata.rate, 0);
830 assert(err >= 0);
831 if ((float)rate * 1.05 < hwdata.rate || (float)rate * 0.95 > hwdata.rate) {
832 MSG("Warning: rate is not accurate (requested = %iHz, got = %iHz)", rate, hwdata.rate);
833 MSG(" please, try the plug plugin (-Dplug:%s)", snd_pcm_name(handle));
836 period_size = m_defPeriodSize;
837 dir = 1;
838 err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_size, &dir);
839 if (err < 0) {
840 MSG("Setting period_size to %lu failed, but continuing: %s", period_size, snd_strerror(err));
843 periods = m_defPeriods;
844 dir = 1;
845 err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &periods, &dir);
846 if (err < 0)
847 MSG("Unable to set number of periods to %i, but continuing: %s", periods, snd_strerror(err));
849 /* Install hw parameters. */
850 err = snd_pcm_hw_params(handle, hwparams);
851 if (err < 0) {
852 MSG("Unable to install hw params: %s", snd_strerror(err));
853 snd_pcm_hw_params_dump(hwparams, log);
854 stopAndExit();
857 /* Determine if device can pause. */
858 canPause = (1 == snd_pcm_hw_params_can_pause(hwparams));
860 /* Get final buffer size and calculate the chunk size we will pass to device. */
861 snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
862 chunk_size = periods * period_size;
864 if (0 == chunk_size) {
865 ERR("Invalid periods or period_size. Cannot continue.");
866 stopAndExit();
869 if (chunk_size == buffer_size)
870 MSG("WARNING: Shouldn't use chunk_size equal to buffer_size (%lu). Continuing anyway.", chunk_size);
872 DBG("Final buffer_size = %lu, chunk_size = %lu, periods = %i, period_size = %lu, canPause = %i",
873 buffer_size, chunk_size, periods, period_size, canPause);
875 if (m_debugLevel >= 2)
876 snd_pcm_dump(handle, log);
878 bits_per_sample = snd_pcm_format_physical_width(hwdata.format);
879 bits_per_frame = bits_per_sample * hwdata.channels;
880 chunk_bytes = chunk_size * bits_per_frame / 8;
881 audioBuffer.resize(chunk_bytes);
882 audiobuf = audioBuffer.data();
883 if (audiobuf == NULL) {
884 ERR("not enough memory");
885 stopAndExit();
889 #ifndef timersub
890 #define timersub(a, b, result) \
891 do { \
892 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
893 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
894 if ((result)->tv_usec < 0) { \
895 --(result)->tv_sec; \
896 (result)->tv_usec += 1000000; \
898 } while (0)
899 #endif
901 /* I/O error handler */
902 void AlsaPlayerThread::xrun()
904 snd_pcm_status_t *status;
905 int res;
907 snd_pcm_status_alloca(&status);
908 if ((res = snd_pcm_status(handle, status))<0) {
909 ERR("status error: %s", snd_strerror(res));
910 stopAndExit();
912 if (SND_PCM_STATE_XRUN == snd_pcm_status_get_state(status)) {
913 struct timeval now, diff, tstamp;
914 gettimeofday(&now, 0);
915 snd_pcm_status_get_trigger_tstamp(status, &tstamp);
916 timersub(&now, &tstamp, &diff);
917 MSG("%s!!! (at least %.3f ms long)",
918 stream == SND_PCM_STREAM_PLAYBACK ? "underrun" : "overrun",
919 diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
920 if (m_debugLevel >= 2) {
921 DBG("Status:");
922 snd_pcm_status_dump(status, log);
924 if ((res = snd_pcm_prepare(handle))<0) {
925 ERR("xrun: prepare error: %s", snd_strerror(res));
926 stopAndExit();
928 return; /* ok, data should be accepted again */
929 } if (SND_PCM_STATE_DRAINING == snd_pcm_status_get_state(status)) {
930 if (m_debugLevel >= 2) {
931 DBG("Status(DRAINING):");
932 snd_pcm_status_dump(status, log);
934 if (stream == SND_PCM_STREAM_CAPTURE) {
935 MSG("capture stream format change? attempting recover...");
936 if ((res = snd_pcm_prepare(handle))<0) {
937 ERR("xrun(DRAINING): prepare error: %s", snd_strerror(res));
938 stopAndExit();
940 return;
943 if (m_debugLevel >= 2) {
944 DBG("Status(R/W):");
945 snd_pcm_status_dump(status, log);
947 ERR("read/write error, state = %s", snd_pcm_state_name(snd_pcm_status_get_state(status)));
948 stopAndExit();
951 /* I/O suspend handler */
952 void AlsaPlayerThread::suspend(void)
954 int res;
956 MSG("Suspended. Trying resume. ");
957 while ((res = snd_pcm_resume(handle)) == -EAGAIN)
958 sleep(1); /* wait until suspend flag is released */
959 if (res < 0) {
960 MSG("Failed. Restarting stream. ");
961 if ((res = snd_pcm_prepare(handle)) < 0) {
962 ERR("suspend: prepare error: %s", snd_strerror(res));
963 stopAndExit();
966 MSG("Suspend done.");
969 /* peak handler */
970 void AlsaPlayerThread::compute_max_peak(char *data, size_t count)
972 signed int val, max, max_peak = 0, perc;
973 size_t ocount = count;
975 switch (bits_per_sample) {
976 case 8: {
977 signed char *valp = (signed char *)data;
978 signed char mask = snd_pcm_format_silence(hwdata.format);
979 while (count-- > 0) {
980 val = *valp++ ^ mask;
981 val = abs(val);
982 if (max_peak < val)
983 max_peak = val;
985 break;
987 case 16: {
988 signed short *valp = (signed short *)data;
989 signed short mask = snd_pcm_format_silence_16(hwdata.format);
990 count /= 2;
991 while (count-- > 0) {
992 val = *valp++ ^ mask;
993 val = abs(val);
994 if (max_peak < val)
995 max_peak = val;
997 break;
999 case 32: {
1000 signed int *valp = (signed int *)data;
1001 signed int mask = snd_pcm_format_silence_32(hwdata.format);
1002 count /= 4;
1003 while (count-- > 0) {
1004 val = *valp++ ^ mask;
1005 val = abs(val);
1006 if (max_peak < val)
1007 max_peak = val;
1009 break;
1011 default:
1012 break;
1014 max = 1 << (bits_per_sample-1);
1015 if (max <= 0)
1016 max = 0x7fffffff;
1017 DBG("Max peak (%li samples): %05i (0x%04x) ", (long)ocount, max_peak, max_peak);
1018 if (bits_per_sample > 16)
1019 perc = max_peak / (max / 100);
1020 else
1021 perc = max_peak * 100 / max;
1022 for (val = 0; val < 20; val++)
1023 if (val <= perc / 5)
1024 kDebug() << '#';
1025 else
1026 kDebug() << ' ';
1027 DBG(" %i%%", perc);
1031 * write function
1034 ssize_t AlsaPlayerThread::pcm_write(char *data, size_t count)
1036 ssize_t r;
1037 ssize_t result = 0;
1039 if (sleep_min == 0 && count < chunk_size) {
1040 DBG("calling snd_pcm_format_set_silence");
1041 snd_pcm_format_set_silence(hwdata.format, data + count * bits_per_frame / 8, (chunk_size - count) * hwdata.channels);
1042 count = chunk_size;
1044 while (count > 0) {
1045 DBG("calling writei_func, count = %i", count);
1046 r = writei_func(handle, data, count);
1047 DBG("writei_func returned %i", r);
1048 if (-EAGAIN == r || (r >= 0 && (size_t)r < count)) {
1049 DBG("r = %i calling snd_pcm_wait", r);
1050 snd_pcm_wait(handle, 100);
1051 } else if (-EPIPE == r) {
1052 xrun();
1053 } else if (-ESTRPIPE == r) {
1054 suspend();
1055 } else if (-EBUSY == r){
1056 MSG("WARNING: sleeping while PCM BUSY");
1057 usleep(1000);
1058 continue;
1059 } else if (r < 0) {
1060 ERR("write error: %s", snd_strerror(r));
1061 stopAndExit();
1063 if (r > 0) {
1064 if (m_debugLevel >= 2 > 1)
1065 compute_max_peak(data, r * hwdata.channels);
1066 result += r;
1067 count -= r;
1068 data += r * bits_per_frame / 8;
1070 /* Report current state */
1071 DBG("PCM state before polling: %s",
1072 snd_pcm_state_name(snd_pcm_state(handle)));
1074 int err = wait_for_poll(0);
1075 if (err < 0) {
1076 ERR("Wait for poll() failed");
1077 return -1;
1079 else if (err == 1){
1080 MSG("Playback stopped");
1081 /* Drop the playback on the sound device (probably
1082 still in progress up till now) */
1083 err = snd_pcm_drop(handle);
1084 if (err < 0) {
1085 ERR("snd_pcm_drop() failed: %s", snd_strerror(err));
1086 return -1;
1088 return -1;
1091 return result;
1095 * ok, let's play a .voc file
1098 ssize_t AlsaPlayerThread::voc_pcm_write(u_char *data, size_t count)
1100 ssize_t result = count, r;
1101 size_t size;
1103 while (count > 0) {
1104 size = count;
1105 if (size > chunk_bytes - buffer_pos)
1106 size = chunk_bytes - buffer_pos;
1107 memcpy(audiobuf + buffer_pos, data, size);
1108 data += size;
1109 count -= size;
1110 buffer_pos += size;
1111 if ((size_t)buffer_pos == chunk_bytes) {
1112 if ((size_t)(r = pcm_write(audiobuf, chunk_size)) != chunk_size)
1113 return r;
1114 buffer_pos = 0;
1117 return result;
1120 void AlsaPlayerThread::voc_write_silence(unsigned x)
1122 unsigned l;
1123 char *buf;
1125 QByteArray buffer(chunk_bytes);
1126 // buf = (char *) malloc(chunk_bytes);
1127 buf = buffer.data();
1128 if (buf == NULL) {
1129 ERR("can't allocate buffer for silence");
1130 return; /* not fatal error */
1132 snd_pcm_format_set_silence(hwdata.format, buf, chunk_size * hwdata.channels);
1133 while (x > 0) {
1134 l = x;
1135 if (l > chunk_size)
1136 l = chunk_size;
1137 if (voc_pcm_write((u_char*)buf, l) != (ssize_t)l) {
1138 ERR("write error");
1139 stopAndExit();
1141 x -= l;
1143 // free(buf);
1146 void AlsaPlayerThread::voc_pcm_flush(void)
1148 if (buffer_pos > 0) {
1149 size_t b;
1150 if (sleep_min == 0) {
1151 if (snd_pcm_format_set_silence(hwdata.format, audiobuf + buffer_pos, chunk_bytes - buffer_pos * 8 / bits_per_sample) < 0)
1152 MSG("voc_pcm_flush - silence error");
1153 b = chunk_size;
1154 } else {
1155 b = buffer_pos * 8 / bits_per_frame;
1157 if (pcm_write(audiobuf, b) != (ssize_t)b)
1158 ERR("voc_pcm_flush error");
1160 snd_pcm_drain(handle);
1163 void AlsaPlayerThread::voc_play(int fd, int ofs, const char* name)
1165 int l;
1166 VocBlockType *bp;
1167 VocVoiceData *vd;
1168 VocExtBlock *eb;
1169 size_t nextblock, in_buffer;
1170 u_char *data, *buf;
1171 char was_extended = 0, output = 0;
1172 u_short *sp, repeat = 0;
1173 size_t silence;
1174 off64_t filepos = 0;
1176 #define COUNT(x) nextblock -= x; in_buffer -= x; data += x
1177 #define COUNT1(x) in_buffer -= x; data += x
1179 QByteArray buffer(64 * 1024);
1180 // data = buf = (u_char *)malloc(64 * 1024);
1181 data = buf = (u_char*)buffer.data();
1182 buffer_pos = 0;
1183 if (data == NULL) {
1184 ERR("malloc error");
1185 stopAndExit();
1187 MSG("Playing Creative Labs Channel file '%s'...", name);
1188 /* first we waste the rest of header, ugly but we don't need seek */
1189 while (ofs > (ssize_t)chunk_bytes) {
1190 if ((size_t)safe_read(fd, buf, chunk_bytes) != chunk_bytes) {
1191 ERR("read error");
1192 stopAndExit();
1194 ofs -= chunk_bytes;
1196 if (ofs) {
1197 if (safe_read(fd, buf, ofs) != ofs) {
1198 ERR("read error");
1199 stopAndExit();
1202 hwdata.format = DEFAULT_FORMAT;
1203 hwdata.channels = 1;
1204 hwdata.rate = DEFAULT_SPEED;
1205 set_params();
1207 in_buffer = nextblock = 0;
1208 while (1) {
1209 Fill_the_buffer: /* need this for repeat */
1210 if (in_buffer < 32) {
1211 /* move the rest of buffer to pos 0 and fill the buf up */
1212 if (in_buffer)
1213 memcpy(buf, data, in_buffer);
1214 data = buf;
1215 if ((l = safe_read(fd, buf + in_buffer, chunk_bytes - in_buffer)) > 0)
1216 in_buffer += l;
1217 else if (!in_buffer) {
1218 /* the file is truncated, so simulate 'Terminator'
1219 and reduce the datablock for safe landing */
1220 nextblock = buf[0] = 0;
1221 if (l == -1) {
1222 // perror(name);
1223 stopAndExit();
1227 while (!nextblock) { /* this is a new block */
1228 if (in_buffer < sizeof(VocBlockType))
1229 goto __end;
1230 bp = (VocBlockType *) data;
1231 COUNT1(sizeof(VocBlockType));
1232 nextblock = VOC_DATALEN(bp);
1233 if (output)
1234 MSG(" "); /* write /n after ASCII-out */
1235 output = 0;
1236 switch (bp->type) {
1237 case 0:
1238 #if 0
1239 MSG("Terminator");
1240 #endif
1241 return; /* VOC-file stop */
1242 case 1:
1243 vd = (VocVoiceData *) data;
1244 COUNT1(sizeof(VocVoiceData));
1245 /* we need a SYNC, before we can set new SPEED, STEREO ... */
1247 if (!was_extended) {
1248 hwdata.rate = (int) (vd->tc);
1249 hwdata.rate = 1000000 / (256 - hwdata.rate);
1250 #if 0
1251 MSG("Channel data %d Hz", dsp_speed);
1252 #endif
1253 if (vd->pack) { /* /dev/dsp can't it */
1254 ERR("can't play packed .voc files");
1255 return;
1257 if (hwdata.channels == 2) /* if we are in Stereo-Mode, switch back */
1258 hwdata.channels = 1;
1259 } else { /* there was extended block */
1260 hwdata.channels = 2;
1261 was_extended = 0;
1263 set_params();
1264 break;
1265 case 2: /* nothing to do, pure data */
1266 #if 0
1267 MSG("Channel continuation");
1268 #endif
1269 break;
1270 case 3: /* a silence block, no data, only a count */
1271 sp = (u_short *) data;
1272 COUNT1(sizeof(u_short));
1273 hwdata.rate = (int) (*data);
1274 COUNT1(1);
1275 hwdata.rate = 1000000 / (256 - hwdata.rate);
1276 set_params();
1277 silence = (((size_t) * sp) * 1000) / hwdata.rate;
1278 #if 0
1279 MSG("Silence for %d ms", (int) silence);
1280 #endif
1281 voc_write_silence(*sp);
1282 break;
1283 case 4: /* a marker for syncronisation, no effect */
1284 sp = (u_short *) data;
1285 COUNT1(sizeof(u_short));
1286 #if 0
1287 MSG("Marker %d", *sp);
1288 #endif
1289 break;
1290 case 5: /* ASCII text, we copy to stderr */
1291 output = 1;
1292 #if 0
1293 MSG("ASCII - text :");
1294 #endif
1295 break;
1296 case 6: /* repeat marker, says repeatcount */
1297 /* my specs don't say it: maybe this can be recursive, but
1298 I don't think somebody use it */
1299 repeat = *(u_short *) data;
1300 COUNT1(sizeof(u_short));
1301 #if 0
1302 MSG("Repeat loop %d times", repeat);
1303 #endif
1304 if (filepos >= 0) { /* if < 0, one seek fails, why test another */
1305 if ((filepos = lseek64(fd, 0, 1)) < 0) {
1306 ERR("can't play loops; %s isn't seekable", name);
1307 repeat = 0;
1308 } else {
1309 filepos -= in_buffer; /* set filepos after repeat */
1311 } else {
1312 repeat = 0;
1314 break;
1315 case 7: /* ok, lets repeat that be rewinding tape */
1316 if (repeat) {
1317 if (repeat != 0xFFFF) {
1318 #if 0
1319 MSG("Repeat loop %d", repeat);
1320 #endif
1321 --repeat;
1323 #if 0
1324 else
1325 MSG("Neverending loop");
1326 #endif
1327 lseek64(fd, filepos, 0);
1328 in_buffer = 0; /* clear the buffer */
1329 goto Fill_the_buffer;
1331 #if 0
1332 else
1333 MSG("End repeat loop");
1334 #endif
1335 break;
1336 case 8: /* the extension to play Stereo, I have SB 1.0 :-( */
1337 was_extended = 1;
1338 eb = (VocExtBlock *) data;
1339 COUNT1(sizeof(VocExtBlock));
1340 hwdata.rate = (int) (eb->tc);
1341 hwdata.rate = 256000000L / (65536 - hwdata.rate);
1342 hwdata.channels = eb->mode == VOC_MODE_STEREO ? 2 : 1;
1343 if (hwdata.channels == 2)
1344 hwdata.rate = hwdata.rate >> 1;
1345 if (eb->pack) { /* /dev/dsp can't it */
1346 ERR("can't play packed .voc files");
1347 return;
1349 #if 0
1350 MSG("Extended block %s %d Hz",
1351 (eb->mode ? "Stereo" : "Mono"), dsp_speed);
1352 #endif
1353 break;
1354 default:
1355 ERR("unknown blocktype %d. terminate.", bp->type);
1356 return;
1357 } /* switch (bp->type) */
1358 } /* while (! nextblock) */
1359 /* put nextblock data bytes to dsp */
1360 l = in_buffer;
1361 if (nextblock < (size_t)l)
1362 l = nextblock;
1363 if (l) {
1364 if (output) {
1365 if (write(2, data, l) != l) { /* to stderr */
1366 ERR("write error");
1367 stopAndExit();
1369 } else {
1370 if (voc_pcm_write(data, l) != l) {
1371 ERR("write error");
1372 stopAndExit();
1375 COUNT(l);
1377 } /* while(1) */
1378 __end:
1379 voc_pcm_flush();
1380 // free(buf);
1382 /* that was a big one, perhaps somebody split it :-) */
1384 /* setting the globals for playing raw data */
1385 void AlsaPlayerThread::init_raw_data(void)
1387 hwdata = rhwdata;
1390 /* calculate the data count to read from/to dsp */
1391 off64_t AlsaPlayerThread::calc_count(void)
1393 off64_t count;
1395 if (timelimit == 0) {
1396 count = pbrec_count;
1397 } else {
1398 count = snd_pcm_format_size(hwdata.format, hwdata.rate * hwdata.channels);
1399 count *= (off64_t)timelimit;
1401 return count < pbrec_count ? count : pbrec_count;
1404 void AlsaPlayerThread::header(int /*rtype*/, const char* /*name*/)
1406 // fprintf(stderr, "%s %s '%s' : ",
1407 // (stream == SND_PCM_STREAM_PLAYBACK) ? "Playing" : "Recording",
1408 // fmt_rec_table[rtype].what,
1409 // name);
1410 QString channels;
1411 if (hwdata.channels == 1)
1412 channels = "Mono";
1413 else if (hwdata.channels == 2)
1414 channels = "Stereo";
1415 else
1416 channels = QString("Channels %1").arg(hwdata.channels);
1417 DBG("Format: %s, Rate %d Hz, %s",
1418 snd_pcm_format_description(hwdata.format),
1419 hwdata.rate,
1420 channels.ascii());
1423 /* playing raw data */
1425 void AlsaPlayerThread::playback_go(int fd, size_t loaded, off64_t count, int rtype, const char *name)
1427 int l, r;
1428 off64_t written = 0;
1429 off64_t c;
1431 if (m_debugLevel >= 1) header(rtype, name);
1432 set_params();
1434 while (loaded > chunk_bytes && written < count) {
1435 if (pcm_write(audiobuf + written, chunk_size) <= 0)
1436 return;
1437 written += chunk_bytes;
1438 loaded -= chunk_bytes;
1440 if (written > 0 && loaded > 0)
1441 memmove(audiobuf, audiobuf + written, loaded);
1443 l = loaded;
1444 while (written < count) {
1445 do {
1446 c = count - written;
1447 if (c > chunk_bytes)
1448 c = chunk_bytes;
1449 c -= l;
1451 if (c == 0)
1452 break;
1453 r = safe_read(fd, audiobuf + l, c);
1454 if (r < 0) {
1455 // perror(name);
1456 stopAndExit();
1458 fdcount += r;
1459 if (r == 0)
1460 break;
1461 l += r;
1462 } while (sleep_min == 0 && (size_t)l < chunk_bytes);
1463 l = l * 8 / bits_per_frame;
1464 DBG("calling pcm_write with %i frames.", l);
1465 r = pcm_write(audiobuf, l);
1466 DBG("pcm_write returned r = %i", r);
1467 if (r < 0) return;
1468 if (r != l)
1469 break;
1470 r = r * bits_per_frame / 8;
1471 written += r;
1472 l = 0;
1475 DBG("Draining...");
1477 /* We want the next "device ready" notification only when the buffer is completely empty. */
1478 /* Do this by setting the avail_min to the buffer size. */
1479 int err;
1480 DBG("Getting swparams");
1481 snd_pcm_sw_params_t *swparams;
1482 snd_pcm_sw_params_alloca(&swparams);
1483 err = snd_pcm_sw_params_current(handle, swparams);
1484 if (err < 0) {
1485 ERR("Unable to get current swparams: %s", snd_strerror(err));
1486 return;
1488 DBG("Setting avail min to %lu", buffer_size);
1489 err = snd_pcm_sw_params_set_avail_min(handle, swparams, buffer_size);
1490 if (err < 0) {
1491 ERR("Unable to set avail min for playback: %s", snd_strerror(err));
1492 return;
1494 /* write the parameters to the playback device */
1495 DBG("Writing swparams");
1496 err = snd_pcm_sw_params(handle, swparams);
1497 if (err < 0) {
1498 ERR("Unable to set sw params for playback: %s", snd_strerror(err));
1499 return;
1502 DBG("Waiting for poll");
1503 err = wait_for_poll(1);
1504 if (err < 0) {
1505 ERR("Wait for poll() failed");
1506 return;
1507 } else if (err == 1){
1508 MSG("Playback stopped while draining");
1510 /* Drop the playback on the sound device (probably
1511 still in progress up till now) */
1512 err = snd_pcm_drop(handle);
1513 if (err < 0) {
1514 ERR("snd_pcm_drop() failed: %s", snd_strerror(err));
1515 return;
1518 DBG("Draining completed");
1522 * let's play or capture it (capture_type says VOC/WAVE/raw)
1525 void AlsaPlayerThread::playback(int fd)
1527 int ofs;
1528 size_t dta;
1529 ssize_t dtawave;
1531 pbrec_count = LLONG_MAX;
1532 fdcount = 0;
1534 /* read the file header */
1535 dta = sizeof(AuHeader);
1536 if ((size_t)safe_read(fd, audiobuf, dta) != dta) {
1537 ERR("read error");
1538 stopAndExit();
1540 if (test_au(fd, audiobuf) >= 0) {
1541 rhwdata.format = hwdata.format;
1542 pbrec_count = calc_count();
1543 playback_go(fd, 0, pbrec_count, FORMAT_AU, name.ascii());
1544 goto __end;
1546 dta = sizeof(VocHeader);
1547 if ((size_t)safe_read(fd, audiobuf + sizeof(AuHeader),
1548 dta - sizeof(AuHeader)) != dta - sizeof(AuHeader)) {
1549 ERR("read error");
1550 stopAndExit();
1552 if ((ofs = test_vocfile(audiobuf)) >= 0) {
1553 pbrec_count = calc_count();
1554 voc_play(fd, ofs, name.ascii());
1555 goto __end;
1557 /* read bytes for WAVE-header */
1558 if ((dtawave = test_wavefile(fd, audiobuf, dta)) >= 0) {
1559 pbrec_count = calc_count();
1560 playback_go(fd, dtawave, pbrec_count, FORMAT_WAVE, name.ascii());
1561 } else {
1562 /* should be raw data */
1563 init_raw_data();
1564 pbrec_count = calc_count();
1565 playback_go(fd, dta, pbrec_count, FORMAT_RAW, name.ascii());
1567 __end:
1568 return;
1571 /* Wait until ALSA is ready for more samples or stop() was called.
1572 @return 0 if ALSA is ready for more input, +1 if a request to stop
1573 the sound output was received and a negative value on error. */
1574 int AlsaPlayerThread::wait_for_poll(int draining)
1576 unsigned short revents;
1577 snd_pcm_state_t state;
1578 int ret;
1580 DBG("Waiting for poll");
1582 /* Wait for certain events */
1583 while (1) {
1584 /* Simulated pause by not writing to alsa device, which will lead to an XRUN
1585 when resumed. */
1586 if (m_simulatedPause)
1587 msleep(500);
1588 else {
1590 ret = poll(alsa_poll_fds, alsa_fd_count, -1);
1591 DBG("activity on %d descriptors", ret);
1593 /* Check for stop request from alsa_stop on the last file descriptors. */
1594 if ((revents = alsa_poll_fds[alsa_fd_count-1].revents)) {
1595 if (revents & POLLIN){
1596 DBG("stop requested");
1597 return 1;
1601 /* Check the first count-1 descriptors for ALSA events */
1602 snd_pcm_poll_descriptors_revents(handle, alsa_poll_fds, alsa_fd_count-1, &revents);
1604 /* Ensure we are in the right state */
1605 state = snd_pcm_state(handle);
1606 DBG("State after poll returned is %s", snd_pcm_state_name(state));
1608 if (SND_PCM_STATE_XRUN == state){
1609 if (!draining){
1610 MSG("WARNING: Buffer underrun detected!");
1611 xrun();
1612 return 0;
1613 }else{
1614 DBG("Playback terminated");
1615 return 0;
1619 if (SND_PCM_STATE_SUSPENDED == state){
1620 DBG("WARNING: Suspend detected!");
1621 suspend();
1622 return 0;
1625 /* Check for errors */
1626 if (revents & POLLERR) {
1627 DBG("poll revents says POLLERR");
1628 return -EIO;
1631 /* Is ALSA ready for more input? */
1632 if ((revents & POLLOUT)){
1633 DBG("Ready for more input");
1634 return 0;
1640 // ====================================================================
1641 // AlsaPlayer
1643 // AlsaPlayer is nothing more than a container for AlsaPlayerThread
1644 // in order to avoid ambiguous QObject, since both Player and QThread
1645 // derive from QObject. Is there a better solution?
1647 AlsaPlayer::AlsaPlayer(QObject* parent, const char* name, const QStringList& args):
1648 Player(parent, name, args)
1650 m_AlsaPlayerThread = new AlsaPlayerThread(this);
1653 AlsaPlayer::~AlsaPlayer()
1655 delete m_AlsaPlayerThread;
1658 /*virtual*/ void AlsaPlayer::startPlay(const QString& file) { m_AlsaPlayerThread->startPlay(file); }
1659 /*virtual*/ void AlsaPlayer::pause() { m_AlsaPlayerThread->pause(); }
1660 /*virtual*/ void AlsaPlayer::stop() { m_AlsaPlayerThread->stop(); }
1662 /*virtual*/ void AlsaPlayer::setVolume(float volume) { m_AlsaPlayerThread->setVolume(volume); }
1663 /*virtual*/ float AlsaPlayer::volume() const { return m_AlsaPlayerThread->volume(); }
1665 /*virtual*/ bool AlsaPlayer::playing() const { return m_AlsaPlayerThread->playing(); }
1666 /*virtual*/ bool AlsaPlayer::paused() const { return m_AlsaPlayerThread->paused(); }
1668 /*virtual*/ int AlsaPlayer::totalTime() const { return m_AlsaPlayerThread->totalTime(); }
1669 /*virtual*/ int AlsaPlayer::currentTime() const { return m_AlsaPlayerThread->currentTime(); }
1670 /*virtual*/ int AlsaPlayer::position() const { return m_AlsaPlayerThread->position(); } // in this case not really the percent
1672 /*virtual*/ void AlsaPlayer::seek(int seekTime) { m_AlsaPlayerThread->seek(seekTime); }
1673 /*virtual*/ void AlsaPlayer::seekPosition(int position) { m_AlsaPlayerThread->seekPosition(position); }
1675 /*virtual*/ QStringList AlsaPlayer::getPluginList( const QByteArray& classname )
1676 { return m_AlsaPlayerThread->getPluginList(classname); }
1677 /*virtual*/ void AlsaPlayer::setSinkName(const QString &sinkName)
1678 { m_AlsaPlayerThread->setSinkName(sinkName); }
1680 #include "alsaplayer.moc"
1682 #undef DBG
1683 #undef MSG
1684 #undef ERR
1686 // vim: sw=4 ts=8 et