- remove FindAlsa and its config-alsa.h skeleton, already provided by kdelibs
[kdeaccessibility.git] / kttsd / players / alsaplayer / alsaplayer.cpp
blobdd9be1edda0b1b363cff309b86beae096f5a7eb0
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-kttsd.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 static snd_pcm_sframes_t (*readi_func)(snd_pcm_t *handle, void *buffer, snd_pcm_uframes_t size);
127 static snd_pcm_sframes_t (*writei_func)(snd_pcm_t *handle, const void *buffer, snd_pcm_uframes_t size);
128 static snd_pcm_sframes_t (*readn_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);
129 static snd_pcm_sframes_t (*writen_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);
132 ////////////////////////////////////////////////////////////////////////////////
133 // public methods
134 ////////////////////////////////////////////////////////////////////////////////
136 AlsaPlayerThread::AlsaPlayerThread(QObject* parent) :
137 QThread(parent),
138 m_currentVolume(1.0),
139 m_pcmName("default"),
140 m_defPeriodSize(128),
141 m_defPeriods(8),
142 m_debugLevel(1),
143 m_simulatedPause(false)
145 init();
148 AlsaPlayerThread::~AlsaPlayerThread()
150 if (isRunning()) {
151 stop();
152 wait();
156 //void AlsaPlayerThread::play(const FileHandle &file)
157 void AlsaPlayerThread::startPlay(const QString &file)
159 if (isRunning()) {
160 if (paused()) {
161 if (canPause)
162 snd_pcm_pause(handle, false);
163 else
164 m_simulatedPause = false;
166 return;
168 audiofile.setFileName(file);
169 audiofile.open(QIODevice::ReadOnly);
170 fd = audiofile.handle();
171 if (audiofile_name) free(audiofile_name);
172 audiofile_name = qstrdup(file.toAscii().constData());
173 // Start thread running.
174 start();
177 /*virtual*/ void AlsaPlayerThread::run()
179 QString pName = m_pcmName.section(" ", 0, 0);
180 pcm_name = qstrdup(pName.toAscii().constData());
181 DBG("pName = %s", pcm_name);
182 int err;
183 snd_pcm_info_t *info;
185 m_simulatedPause = false;
187 snd_pcm_info_alloca(&info);
189 err = snd_output_stdio_attach(&log, stderr, 0);
190 assert(err >= 0);
192 rhwdata.format = DEFAULT_FORMAT;
193 rhwdata.rate = DEFAULT_SPEED;
194 rhwdata.channels = 1;
196 err = snd_pcm_open(&handle, pcm_name, stream, open_mode);
197 if (err < 0) {
198 ERR("audio open error on pcm device %s: %s", pcm_name, snd_strerror(err));
199 return;
202 if ((err = snd_pcm_info(handle, info)) < 0) {
203 ERR("info error: %s", snd_strerror(err));
204 return;
207 chunk_size = 1024;
208 hwdata = rhwdata;
210 audioBuffer.resize(1024);
211 // audiobuf = (char *)malloc(1024);
212 audiobuf = audioBuffer.data();
213 if (audiobuf == NULL) {
214 ERR("not enough memory");
215 return;
218 if (mmap_flag) {
219 writei_func = snd_pcm_mmap_writei;
220 readi_func = snd_pcm_mmap_readi;
221 writen_func = snd_pcm_mmap_writen;
222 readn_func = snd_pcm_mmap_readn;
223 } else {
224 writei_func = snd_pcm_writei;
225 readi_func = snd_pcm_readi;
226 writen_func = snd_pcm_writen;
227 readn_func = snd_pcm_readn;
231 playback(fd);
232 cleanup();
233 return;
236 void AlsaPlayerThread::pause()
238 if (isRunning()) {
239 DBG("Pause requested");
240 QMutexLocker locker(&m_mutex);
241 if (handle) {
242 // Some hardware can pause; some can't. canPause is set in set_params.
243 if (canPause) {
244 m_simulatedPause = false;
245 snd_pcm_pause(handle, true);
246 } else {
247 // Set a flag and cause wait_for_poll to sleep. When resumed, will get
248 // an underrun.
249 m_simulatedPause = true;
255 void AlsaPlayerThread::stop()
257 if (isRunning()) {
258 DBG("STOP! Locking mutex");
259 QMutexLocker locker(&m_mutex);
260 m_simulatedPause = false;
261 if (handle) {
262 /* This constant is arbitrary */
263 char buf = 42;
264 DBG("Request for stop, device state is %s",
265 snd_pcm_state_name(snd_pcm_state(handle)));
266 write(alsa_stop_pipe[1], &buf, 1);
268 DBG("unlocking mutex");
269 locker.unlock();
270 /* Wait for thread to exit */
271 DBG("waiting for thread to exit");
272 wait();
273 DBG("cleaning up");
274 // TODO: This seems like a bug. Why must I relock the locker
275 // since I'm about to destroy the locker?
276 locker.relock();
278 cleanup();
282 * Stop playback, cleanup and exit thread.
284 void AlsaPlayerThread::stopAndExit()
286 // if (handle) snd_pcm_drop(handle);
287 cleanup();
288 exit();
291 void AlsaPlayerThread::setVolume(float volume)
293 m_currentVolume = volume;
296 float AlsaPlayerThread::volume() const
298 return m_currentVolume;
301 /////////////////////////////////////////////////////////////////////////////////
302 // player status functions
303 /////////////////////////////////////////////////////////////////////////////////
305 bool AlsaPlayerThread::playing() const
307 bool result = false;
308 if (isRunning()) {
309 QMutexLocker locker(&m_mutex);
310 if (handle) {
311 if (canPause) {
312 snd_pcm_status_t *status;
313 snd_pcm_status_alloca(&status);
314 int res;
315 if ((res = snd_pcm_status(handle, status)) < 0)
316 ERR("status error: %s", snd_strerror(res));
317 else {
318 result = (SND_PCM_STATE_RUNNING == snd_pcm_status_get_state(status))
319 || (SND_PCM_STATE_DRAINING == snd_pcm_status_get_state(status));
320 DBG("state = %s", snd_pcm_state_name(snd_pcm_status_get_state(status)));
322 } else
323 result = !m_simulatedPause;
326 return result;
329 bool AlsaPlayerThread::paused() const
331 bool result = false;
332 if (isRunning()) {
333 QMutexLocker locker(&m_mutex);
334 if (handle) {
335 if (canPause) {
336 snd_pcm_status_t *status;
337 snd_pcm_status_alloca(&status);
338 int res;
339 if ((res = snd_pcm_status(handle, status)) < 0)
340 ERR("status error: %s", snd_strerror(res));
341 else {
342 result = (SND_PCM_STATE_PAUSED == snd_pcm_status_get_state(status));
343 DBG("state = %s", snd_pcm_state_name(snd_pcm_status_get_state(status)));
345 } else
346 result = m_simulatedPause;
349 return result;
352 int AlsaPlayerThread::totalTime() const
354 int total = 0;
355 int rate = hwdata.rate;
356 int channels = hwdata.channels;
357 if (rate > 0 && channels > 0) {
358 total = int((double(pbrec_count) / rate) / channels);
359 // DBG("pbrec_count = %i rate =%i channels = %i", pbrec_count, rate, channels);
360 // DBG("totalTime = %i", total);
362 return total;
365 int AlsaPlayerThread::currentTime() const
367 int current = 0;
368 int rate = hwdata.rate;
369 int channels = hwdata.channels;
370 if (rate > 0 && channels > 0) {
371 current = int((double(fdcount) / rate) / channels);
372 // DBG("fdcount = %i rate = %i channels = %i", fdcount, rate, channels);
373 // DBG("currentTime = %i", current);
375 return current;
378 int AlsaPlayerThread::position() const
380 // TODO: Make this more accurate by adding frames that have been so-far
381 // played within the Alsa ring buffer.
382 return pbrec_count > 0 ? int(double(fdcount) * 1000 / pbrec_count + .5) : 0;
385 /////////////////////////////////////////////////////////////////////////////////
386 // player seek functions
387 /////////////////////////////////////////////////////////////////////////////////
389 void AlsaPlayerThread::seek(int /*seekTime*/)
391 // TODO:
394 void AlsaPlayerThread::seekPosition(int /*position*/)
396 // TODO:
400 * Returns a list of PCM devices.
401 * This function fills the specified list with ALSA hardware soundcards found on the system.
402 * It uses plughw:xx instead of hw:xx for specifiers, because hw:xx are not practical to
403 * use (e.g. they require a resampler/channel mixer in the application).
405 QStringList AlsaPlayerThread::getPluginList( const QByteArray& classname )
407 Q_UNUSED(classname);
409 int err = 0;
410 int card = -1, device = -1;
411 snd_ctl_t *handle;
412 snd_ctl_card_info_t *info;
413 snd_pcm_info_t *pcminfo;
414 snd_ctl_card_info_alloca(&info);
415 snd_pcm_info_alloca(&pcminfo);
416 QStringList result;
418 result.append("default");
419 for (;;) {
420 err = snd_card_next(&card);
421 if (err < 0 || card < 0) break;
422 if (card >= 0) {
423 char name[32];
424 sprintf(name, "hw:%i", card);
425 if ((err = snd_ctl_open(&handle, name, 0)) < 0) continue;
426 if ((err = snd_ctl_card_info(handle, info)) < 0) {
427 snd_ctl_close(handle);
428 continue;
430 for (int devCnt=0;;++devCnt) {
431 err = snd_ctl_pcm_next_device(handle, &device);
432 if (err < 0 || device < 0) break;
434 snd_pcm_info_set_device(pcminfo, device);
435 snd_pcm_info_set_subdevice(pcminfo, 0);
436 snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
437 if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) continue;
438 QString infoName = " ";
439 infoName += snd_ctl_card_info_get_name(info);
440 infoName += " (";
441 infoName += snd_pcm_info_get_name(pcminfo);
442 infoName += ')';
443 if (0 == devCnt) {
444 QString pcmName = QString("default:%1").arg(card);
445 result.append(pcmName + infoName);
447 QString pcmName = QString("plughw:%1,%2").arg(card).arg(device);
448 result.append(pcmName + infoName);
450 snd_ctl_close(handle);
453 return result;
456 void AlsaPlayerThread::setSinkName(const QString& sinkName) { m_pcmName = sinkName; }
458 /////////////////////////////////////////////////////////////////////////////////
459 // private
460 /////////////////////////////////////////////////////////////////////////////////
462 void AlsaPlayerThread::init()
464 pcm_name = 0;
465 audiofile_name = 0;
466 handle = 0;
467 canPause = false;
468 timelimit = 0;
469 file_type = FORMAT_DEFAULT;
470 sleep_min = 0;
471 // open_mode = 0;
472 open_mode = SND_PCM_NONBLOCK;
473 stream = SND_PCM_STREAM_PLAYBACK;
474 mmap_flag = 0;
475 interleaved = 1;
476 audiobuf = NULL;
477 chunk_size = 0;
478 period_time = 0;
479 buffer_time = 0;
480 avail_min = -1;
481 start_delay = 0;
482 stop_delay = 0;
483 buffer_pos = 0;
484 log = 0;
485 fd = -1;
486 pbrec_count = LLONG_MAX;
487 alsa_stop_pipe[0] = 0;
488 alsa_stop_pipe[1] = 0;
489 alsa_poll_fds = 0;
490 m_simulatedPause = false;
493 void AlsaPlayerThread::cleanup()
495 DBG("cleaning up");
496 QMutexLocker locker(&m_mutex);
497 if (pcm_name) free(pcm_name);
498 if (audiofile_name) free(audiofile_name);
499 if (fd >= 0) audiofile.close();
500 if (handle) {
501 snd_pcm_drop(handle);
502 snd_pcm_close(handle);
504 if (alsa_stop_pipe[0]) close(alsa_stop_pipe[0]);
505 if (alsa_stop_pipe[1]) close(alsa_stop_pipe[1]);
506 if (audiobuf) audioBuffer.resize(0);
507 if (alsa_poll_fds) alsa_poll_fds_barray.resize(0);
508 if (log) snd_output_close(log);
509 snd_config_update_free_global();
510 init();
514 * Safe read (for pipes)
517 ssize_t AlsaPlayerThread::safe_read(int fd, void *buf, size_t count)
519 ssize_t result = 0;
520 ssize_t res;
522 while (count > 0) {
523 if ((res = read(fd, buf, count)) == 0)
524 break;
525 if (res < 0)
526 return result > 0 ? result : res;
527 count -= res;
528 result += res;
529 buf = (char *)buf + res;
531 return result;
535 * Test, if it is a .VOC file and return >=0 if ok (this is the length of rest)
536 * < 0 if not
538 int AlsaPlayerThread::test_vocfile(void *buffer)
540 VocHeader *vp = (VocHeader*)buffer;
542 if (!memcmp(vp->magic, VOC_MAGIC_STRING, 20)) {
543 vocminor = LE_SHORT(vp->version) & 0xFF;
544 vocmajor = LE_SHORT(vp->version) / 256;
545 if (LE_SHORT(vp->version) != (0x1233 - LE_SHORT(vp->coded_ver)))
546 return -2; /* coded version mismatch */
547 return LE_SHORT(vp->headerlen) - sizeof(VocHeader); /* 0 mostly */
549 return -1; /* magic string fail */
553 * helper for test_wavefile
556 ssize_t AlsaPlayerThread::test_wavefile_read(int fd, char *buffer, size_t *size, size_t reqsize, int line)
558 if (*size >= reqsize)
559 return *size;
560 if ((size_t)safe_read(fd, buffer + *size, reqsize - *size) != reqsize - *size) {
561 ERR("read error (called from line %i)", line);
562 stopAndExit();
564 return *size = reqsize;
567 #define check_wavefile_space(buffer, len, blimit) \
568 if (len > blimit) { \
569 blimit = len; \
570 if ((buffer = (char*)realloc(buffer, blimit)) == NULL) { \
571 ERR("not enough memory"); \
572 stopAndExit(); \
577 * test, if it's a .WAV file, > 0 if ok (and set the speed, stereo etc.)
578 * == 0 if not
579 * Value returned is bytes to be discarded.
581 ssize_t AlsaPlayerThread::test_wavefile(int fd, char *_buffer, size_t size)
583 WaveHeader *h = (WaveHeader *)_buffer;
584 char *buffer = NULL;
585 size_t blimit = 0;
586 WaveFmtBody *f;
587 WaveChunkHeader *c;
588 u_int type;
589 u_int len;
591 if (size < sizeof(WaveHeader))
592 return -1;
593 if (h->magic != WAV_RIFF || h->type != WAV_WAVE)
594 return -1;
595 if (size > sizeof(WaveHeader)) {
596 check_wavefile_space(buffer, size - sizeof(WaveHeader), blimit);
597 memcpy(buffer, _buffer + sizeof(WaveHeader), size - sizeof(WaveHeader));
599 size -= sizeof(WaveHeader);
600 while (1) {
601 check_wavefile_space(buffer, sizeof(WaveChunkHeader), blimit);
602 test_wavefile_read(fd, buffer, &size, sizeof(WaveChunkHeader), __LINE__);
603 c = (WaveChunkHeader*)buffer;
604 type = c->type;
605 len = LE_INT(c->length);
606 len += len % 2;
607 if (size > sizeof(WaveChunkHeader))
608 memmove(buffer, buffer + sizeof(WaveChunkHeader), size - sizeof(WaveChunkHeader));
609 size -= sizeof(WaveChunkHeader);
610 if (type == WAV_FMT)
611 break;
612 check_wavefile_space(buffer, len, blimit);
613 test_wavefile_read(fd, buffer, &size, len, __LINE__);
614 if (size > len)
615 memmove(buffer, buffer + len, size - len);
616 size -= len;
619 if (len < sizeof(WaveFmtBody)) {
620 ERR("unknown length of 'fmt ' chunk (read %u, should be %u at least)", len, (u_int)sizeof(WaveFmtBody));
621 stopAndExit();
623 check_wavefile_space(buffer, len, blimit);
624 test_wavefile_read(fd, buffer, &size, len, __LINE__);
625 f = (WaveFmtBody*) buffer;
626 if (LE_SHORT(f->format) != WAV_PCM_CODE) {
627 ERR("can't play not PCM-coded WAVE-files");
628 stopAndExit();
630 if (LE_SHORT(f->modus) < 1) {
631 ERR("can't play WAVE-files with %d tracks", LE_SHORT(f->modus));
632 stopAndExit();
634 hwdata.channels = LE_SHORT(f->modus);
635 switch (LE_SHORT(f->bit_p_spl)) {
636 case 8:
637 if (hwdata.format != DEFAULT_FORMAT &&
638 hwdata.format != SND_PCM_FORMAT_U8)
639 MSG("Warning: format is changed to U8");
640 hwdata.format = SND_PCM_FORMAT_U8;
641 break;
642 case 16:
643 if (hwdata.format != DEFAULT_FORMAT &&
644 hwdata.format != SND_PCM_FORMAT_S16_LE)
645 MSG("Warning: format is changed to S16_LE");
646 hwdata.format = SND_PCM_FORMAT_S16_LE;
647 break;
648 case 24:
649 switch (LE_SHORT(f->byte_p_spl) / hwdata.channels) {
650 case 3:
651 if (hwdata.format != DEFAULT_FORMAT &&
652 hwdata.format != SND_PCM_FORMAT_S24_3LE)
653 MSG("Warning: format is changed to S24_3LE");
654 hwdata.format = SND_PCM_FORMAT_S24_3LE;
655 break;
656 case 4:
657 if (hwdata.format != DEFAULT_FORMAT &&
658 hwdata.format != SND_PCM_FORMAT_S24_LE)
659 MSG("Warning: format is changed to S24_LE");
660 hwdata.format = SND_PCM_FORMAT_S24_LE;
661 break;
662 default:
663 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);
664 stopAndExit();
666 break;
667 case 32:
668 hwdata.format = SND_PCM_FORMAT_S32_LE;
669 break;
670 default:
671 ERR("can't play WAVE-files with sample %d bits wide", LE_SHORT(f->bit_p_spl));
672 stopAndExit();
674 hwdata.rate = LE_INT(f->sample_fq);
676 if (size > len)
677 memmove(buffer, buffer + len, size - len);
678 size -= len;
680 while (1) {
681 u_int type, len;
683 check_wavefile_space(buffer, sizeof(WaveChunkHeader), blimit);
684 test_wavefile_read(fd, buffer, &size, sizeof(WaveChunkHeader), __LINE__);
685 c = (WaveChunkHeader*)buffer;
686 type = c->type;
687 len = LE_INT(c->length);
688 if (size > sizeof(WaveChunkHeader))
689 memmove(buffer, buffer + sizeof(WaveChunkHeader), size - sizeof(WaveChunkHeader));
690 size -= sizeof(WaveChunkHeader);
691 if (type == WAV_DATA) {
692 if (len < pbrec_count && len < 0x7ffffffe)
693 pbrec_count = len;
694 if (size > 0)
695 memcpy(_buffer, buffer, size);
696 free(buffer);
697 return size;
699 len += len % 2;
700 check_wavefile_space(buffer, len, blimit);
701 test_wavefile_read(fd, buffer, &size, len, __LINE__);
702 if (size > len)
703 memmove(buffer, buffer + len, size - len);
704 size -= len;
707 /* shouldn't be reached */
708 return -1;
712 * Test for AU file.
715 int AlsaPlayerThread::test_au(int fd, char *buffer)
717 AuHeader *ap = (AuHeader*)buffer;
719 if (ap->magic != AU_MAGIC)
720 return -1;
721 if (BE_INT(ap->hdr_size) > 128 || BE_INT(ap->hdr_size) < 24)
722 return -1;
723 pbrec_count = BE_INT(ap->data_size);
724 switch (BE_INT(ap->encoding)) {
725 case AU_FMT_ULAW:
726 if (hwdata.format != DEFAULT_FORMAT &&
727 hwdata.format != SND_PCM_FORMAT_MU_LAW)
728 MSG("Warning: format is changed to MU_LAW");
729 hwdata.format = SND_PCM_FORMAT_MU_LAW;
730 break;
731 case AU_FMT_LIN8:
732 if (hwdata.format != DEFAULT_FORMAT &&
733 hwdata.format != SND_PCM_FORMAT_U8)
734 MSG("Warning: format is changed to U8");
735 hwdata.format = SND_PCM_FORMAT_U8;
736 break;
737 case AU_FMT_LIN16:
738 if (hwdata.format != DEFAULT_FORMAT &&
739 hwdata.format != SND_PCM_FORMAT_S16_BE)
740 MSG("Warning: format is changed to S16_BE");
741 hwdata.format = SND_PCM_FORMAT_S16_BE;
742 break;
743 default:
744 return -1;
746 hwdata.rate = BE_INT(ap->sample_rate);
747 if (hwdata.rate < 2000 || hwdata.rate > 256000)
748 return -1;
749 hwdata.channels = BE_INT(ap->channels);
750 if (hwdata.channels < 1 || hwdata.channels > 128)
751 return -1;
752 if ((size_t)safe_read(fd, buffer + sizeof(AuHeader), BE_INT(ap->hdr_size) - sizeof(AuHeader)) != BE_INT(ap->hdr_size) - sizeof(AuHeader)) {
753 ERR("read error");
754 stopAndExit();
756 return 0;
759 void AlsaPlayerThread::set_params(void)
761 snd_pcm_hw_params_t *hwparams;
762 snd_pcm_uframes_t period_size;
763 int err;
764 int dir;
765 unsigned int rate;
766 unsigned int periods;
768 snd_pcm_hw_params_alloca(&hwparams);
769 err = snd_pcm_hw_params_any(handle, hwparams);
770 if (err < 0) {
771 ERR("Broken configuration for this PCM: no configurations available");
772 stopAndExit();
775 /* Create the pipe for communication about stop requests. */
776 if (pipe(alsa_stop_pipe)) {
777 ERR("Stop pipe creation failed (%s)", strerror(errno));
778 stopAndExit();
781 /* Find how many descriptors we will get for poll(). */
782 alsa_fd_count = snd_pcm_poll_descriptors_count(handle);
783 if (alsa_fd_count <= 0){
784 ERR("Invalid poll descriptors count returned from ALSA.");
785 stopAndExit();
788 /* Create and fill in struct pollfd *alsa_poll_fds with ALSA descriptors. */
789 // alsa_poll_fds = (pollfd *)malloc ((alsa_fd_count + 1) * sizeof(struct pollfd));
790 alsa_poll_fds_barray.resize((alsa_fd_count + 1) * sizeof(struct pollfd));
791 alsa_poll_fds = (pollfd *)alsa_poll_fds_barray.data();
792 assert(alsa_poll_fds);
793 if ((err = snd_pcm_poll_descriptors(handle, alsa_poll_fds, alsa_fd_count)) < 0) {
794 ERR("Unable to obtain poll descriptors for playback: %s", snd_strerror(err));
795 stopAndExit();
798 /* Create a new pollfd structure for requests by alsa_stop(). */
799 struct pollfd alsa_stop_pipe_pfd;
800 alsa_stop_pipe_pfd.fd = alsa_stop_pipe[0];
801 alsa_stop_pipe_pfd.events = POLLIN;
802 alsa_stop_pipe_pfd.revents = 0;
804 /* Join this our own pollfd to the ALSAs ones. */
805 alsa_poll_fds[alsa_fd_count] = alsa_stop_pipe_pfd;
806 ++alsa_fd_count;
808 if (mmap_flag) {
809 snd_pcm_access_mask_t *mask = (snd_pcm_access_mask_t *)alloca(snd_pcm_access_mask_sizeof());
810 snd_pcm_access_mask_none(mask);
811 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
812 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
813 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
814 err = snd_pcm_hw_params_set_access_mask(handle, hwparams, mask);
815 } else if (interleaved)
816 err = snd_pcm_hw_params_set_access(handle, hwparams,
817 SND_PCM_ACCESS_RW_INTERLEAVED);
818 else
819 err = snd_pcm_hw_params_set_access(handle, hwparams,
820 SND_PCM_ACCESS_RW_NONINTERLEAVED);
821 if (err < 0) {
822 ERR("Error setting access type: %s", snd_strerror(err));
823 stopAndExit();
825 err = snd_pcm_hw_params_set_format(handle, hwparams, hwdata.format);
826 if (err < 0) {
827 ERR("Error setting sample format to %i: %s", hwdata.format, snd_strerror(err));
828 stopAndExit();
830 err = snd_pcm_hw_params_set_channels(handle, hwparams, hwdata.channels);
831 if (err < 0) {
832 ERR("Error setting channel count to %i: %s", hwdata.channels, snd_strerror(err));
833 stopAndExit();
836 #if 0
837 err = snd_pcm_hw_params_set_periods_min(handle, hwparams, 2);
838 assert(err >= 0);
839 #endif
840 rate = hwdata.rate;
841 err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &hwdata.rate, 0);
842 assert(err >= 0);
843 if ((float)rate * 1.05 < hwdata.rate || (float)rate * 0.95 > hwdata.rate) {
844 MSG("Warning: rate is not accurate (requested = %iHz, got = %iHz)", rate, hwdata.rate);
845 MSG(" please, try the plug plugin (-Dplug:%s)", snd_pcm_name(handle));
848 period_size = m_defPeriodSize;
849 dir = 1;
850 err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_size, &dir);
851 if (err < 0) {
852 MSG("Setting period_size to %lu failed, but continuing: %s", period_size, snd_strerror(err));
855 periods = m_defPeriods;
856 dir = 1;
857 err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &periods, &dir);
858 if (err < 0)
859 MSG("Unable to set number of periods to %i, but continuing: %s", periods, snd_strerror(err));
861 /* Install hw parameters. */
862 err = snd_pcm_hw_params(handle, hwparams);
863 if (err < 0) {
864 MSG("Unable to install hw params: %s", snd_strerror(err));
865 snd_pcm_hw_params_dump(hwparams, log);
866 stopAndExit();
869 /* Determine if device can pause. */
870 canPause = (1 == snd_pcm_hw_params_can_pause(hwparams));
872 /* Get final buffer size and calculate the chunk size we will pass to device. */
873 snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
874 chunk_size = periods * period_size;
876 if (0 == chunk_size) {
877 ERR("Invalid periods or period_size. Cannot continue.");
878 stopAndExit();
881 if (chunk_size == buffer_size)
882 MSG("WARNING: Shouldn't use chunk_size equal to buffer_size (%lu). Continuing anyway.", chunk_size);
884 DBG("Final buffer_size = %lu, chunk_size = %lu, periods = %i, period_size = %lu, canPause = %i",
885 buffer_size, chunk_size, periods, period_size, canPause);
887 if (m_debugLevel >= 2)
888 snd_pcm_dump(handle, log);
890 bits_per_sample = snd_pcm_format_physical_width(hwdata.format);
891 bits_per_frame = bits_per_sample * hwdata.channels;
892 chunk_bytes = chunk_size * bits_per_frame / 8;
893 audioBuffer.resize(chunk_bytes);
894 audiobuf = audioBuffer.data();
895 if (audiobuf == NULL) {
896 ERR("not enough memory");
897 stopAndExit();
901 #ifndef timersub
902 #define timersub(a, b, result) \
903 do { \
904 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
905 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
906 if ((result)->tv_usec < 0) { \
907 --(result)->tv_sec; \
908 (result)->tv_usec += 1000000; \
910 } while (0)
911 #endif
913 /* I/O error handler */
914 void AlsaPlayerThread::xrun()
916 snd_pcm_status_t *status;
917 int res;
919 snd_pcm_status_alloca(&status);
920 if ((res = snd_pcm_status(handle, status))<0) {
921 ERR("status error: %s", snd_strerror(res));
922 stopAndExit();
924 if (SND_PCM_STATE_XRUN == snd_pcm_status_get_state(status)) {
925 struct timeval now, diff, tstamp;
926 gettimeofday(&now, 0);
927 snd_pcm_status_get_trigger_tstamp(status, &tstamp);
928 timersub(&now, &tstamp, &diff);
929 MSG("%s!!! (at least %.3f ms long)",
930 stream == SND_PCM_STREAM_PLAYBACK ? "underrun" : "overrun",
931 diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
932 if (m_debugLevel >= 2) {
933 DBG("Status:");
934 snd_pcm_status_dump(status, log);
936 if ((res = snd_pcm_prepare(handle))<0) {
937 ERR("xrun: prepare error: %s", snd_strerror(res));
938 stopAndExit();
940 return; /* ok, data should be accepted again */
941 } if (SND_PCM_STATE_DRAINING == snd_pcm_status_get_state(status)) {
942 if (m_debugLevel >= 2) {
943 DBG("Status(DRAINING):");
944 snd_pcm_status_dump(status, log);
946 if (stream == SND_PCM_STREAM_CAPTURE) {
947 MSG("capture stream format change? attempting recover...");
948 if ((res = snd_pcm_prepare(handle))<0) {
949 ERR("xrun(DRAINING): prepare error: %s", snd_strerror(res));
950 stopAndExit();
952 return;
955 if (m_debugLevel >= 2) {
956 DBG("Status(R/W):");
957 snd_pcm_status_dump(status, log);
959 ERR("read/write error, state = %s", snd_pcm_state_name(snd_pcm_status_get_state(status)));
960 stopAndExit();
963 /* I/O suspend handler */
964 void AlsaPlayerThread::suspend(void)
966 int res;
968 MSG("Suspended. Trying resume. ");
969 while ((res = snd_pcm_resume(handle)) == -EAGAIN)
970 sleep(1); /* wait until suspend flag is released */
971 if (res < 0) {
972 MSG("Failed. Restarting stream. ");
973 if ((res = snd_pcm_prepare(handle)) < 0) {
974 ERR("suspend: prepare error: %s", snd_strerror(res));
975 stopAndExit();
978 MSG("Suspend done.");
981 /* peak handler */
982 void AlsaPlayerThread::compute_max_peak(char *data, size_t count)
984 signed int val, max, max_peak = 0, perc;
985 size_t ocount = count;
987 switch (bits_per_sample) {
988 case 8: {
989 signed char *valp = (signed char *)data;
990 signed char mask = snd_pcm_format_silence(hwdata.format);
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 16: {
1000 signed short *valp = (signed short *)data;
1001 signed short mask = snd_pcm_format_silence_16(hwdata.format);
1002 count /= 2;
1003 while (count-- > 0) {
1004 val = *valp++ ^ mask;
1005 val = abs(val);
1006 if (max_peak < val)
1007 max_peak = val;
1009 break;
1011 case 32: {
1012 signed int *valp = (signed int *)data;
1013 signed int mask = snd_pcm_format_silence_32(hwdata.format);
1014 count /= 4;
1015 while (count-- > 0) {
1016 val = *valp++ ^ mask;
1017 val = abs(val);
1018 if (max_peak < val)
1019 max_peak = val;
1021 break;
1023 default:
1024 break;
1026 max = 1 << (bits_per_sample-1);
1027 if (max <= 0)
1028 max = 0x7fffffff;
1029 DBG("Max peak (%li samples): %05i (0x%04x) ", (long)ocount, max_peak, max_peak);
1030 if (bits_per_sample > 16)
1031 perc = max_peak / (max / 100);
1032 else
1033 perc = max_peak * 100 / max;
1034 for (val = 0; val < 20; val++)
1035 if (val <= perc / 5)
1036 kDebug() << '#';
1037 else
1038 kDebug() << ' ';
1039 DBG(" %i%%", perc);
1043 * write function
1046 ssize_t AlsaPlayerThread::pcm_write(char *data, size_t count)
1048 ssize_t r;
1049 ssize_t result = 0;
1051 if (sleep_min == 0 && count < chunk_size) {
1052 DBG("calling snd_pcm_format_set_silence");
1053 snd_pcm_format_set_silence(hwdata.format, data + count * bits_per_frame / 8, (chunk_size - count) * hwdata.channels);
1054 count = chunk_size;
1056 while (count > 0) {
1057 DBG("calling writei_func, count = %i", count);
1058 r = writei_func(handle, data, count);
1059 DBG("writei_func returned %i", r);
1060 if (-EAGAIN == r || (r >= 0 && (size_t)r < count)) {
1061 DBG("r = %i calling snd_pcm_wait", r);
1062 snd_pcm_wait(handle, 100);
1063 } else if (-EPIPE == r) {
1064 xrun();
1065 } else if (-ESTRPIPE == r) {
1066 suspend();
1067 } else if (-EBUSY == r){
1068 MSG("WARNING: sleeping while PCM BUSY");
1069 usleep(1000);
1070 continue;
1071 } else if (r < 0) {
1072 ERR("write error: %s", snd_strerror(r));
1073 stopAndExit();
1075 if (r > 0) {
1076 if (m_debugLevel >= 2 > 1)
1077 compute_max_peak(data, r * hwdata.channels);
1078 result += r;
1079 count -= r;
1080 data += r * bits_per_frame / 8;
1082 /* Report current state */
1083 DBG("PCM state before polling: %s",
1084 snd_pcm_state_name(snd_pcm_state(handle)));
1086 int err = wait_for_poll(0);
1087 if (err < 0) {
1088 ERR("Wait for poll() failed");
1089 return -1;
1091 else if (err == 1){
1092 MSG("Playback stopped");
1093 /* Drop the playback on the sound device (probably
1094 still in progress up till now) */
1095 err = snd_pcm_drop(handle);
1096 if (err < 0) {
1097 ERR("snd_pcm_drop() failed: %s", snd_strerror(err));
1098 return -1;
1100 return -1;
1103 return result;
1107 * ok, let's play a .voc file
1110 ssize_t AlsaPlayerThread::voc_pcm_write(u_char *data, size_t count)
1112 ssize_t result = count, r;
1113 size_t size;
1115 while (count > 0) {
1116 size = count;
1117 if (size > chunk_bytes - buffer_pos)
1118 size = chunk_bytes - buffer_pos;
1119 memcpy(audiobuf + buffer_pos, data, size);
1120 data += size;
1121 count -= size;
1122 buffer_pos += size;
1123 if ((size_t)buffer_pos == chunk_bytes) {
1124 if ((size_t)(r = pcm_write(audiobuf, chunk_size)) != chunk_size)
1125 return r;
1126 buffer_pos = 0;
1129 return result;
1132 void AlsaPlayerThread::voc_write_silence(unsigned x)
1134 unsigned l;
1135 char *buf;
1137 QByteArray buffer(chunk_bytes, '\0');
1138 // buf = (char *) malloc(chunk_bytes);
1139 buf = buffer.data();
1140 if (buf == NULL) {
1141 ERR("can't allocate buffer for silence");
1142 return; /* not fatal error */
1144 snd_pcm_format_set_silence(hwdata.format, buf, chunk_size * hwdata.channels);
1145 while (x > 0) {
1146 l = x;
1147 if (l > chunk_size)
1148 l = chunk_size;
1149 if (voc_pcm_write((u_char*)buf, l) != (ssize_t)l) {
1150 ERR("write error");
1151 stopAndExit();
1153 x -= l;
1155 // free(buf);
1158 void AlsaPlayerThread::voc_pcm_flush(void)
1160 if (buffer_pos > 0) {
1161 size_t b;
1162 if (sleep_min == 0) {
1163 if (snd_pcm_format_set_silence(hwdata.format, audiobuf + buffer_pos, chunk_bytes - buffer_pos * 8 / bits_per_sample) < 0)
1164 MSG("voc_pcm_flush - silence error");
1165 b = chunk_size;
1166 } else {
1167 b = buffer_pos * 8 / bits_per_frame;
1169 if (pcm_write(audiobuf, b) != (ssize_t)b)
1170 ERR("voc_pcm_flush error");
1172 snd_pcm_drain(handle);
1175 void AlsaPlayerThread::voc_play(int fd, int ofs, const char* name)
1177 int l;
1178 VocBlockType *bp;
1179 VocVoiceData *vd;
1180 VocExtBlock *eb;
1181 size_t nextblock, in_buffer;
1182 u_char *data, *buf;
1183 char was_extended = 0, output = 0;
1184 u_short *sp, repeat = 0;
1185 size_t silence;
1186 off64_t filepos = 0;
1188 #define COUNT(x) nextblock -= x; in_buffer -= x; data += x
1189 #define COUNT1(x) in_buffer -= x; data += x
1191 QByteArray buffer(64 * 1024, '\0');
1192 // data = buf = (u_char *)malloc(64 * 1024);
1193 data = buf = (u_char*)buffer.data();
1194 buffer_pos = 0;
1195 if (data == NULL) {
1196 ERR("malloc error");
1197 stopAndExit();
1199 MSG("Playing Creative Labs Channel file '%s'...", name);
1200 /* first we waste the rest of header, ugly but we don't need seek */
1201 while (ofs > (ssize_t)chunk_bytes) {
1202 if ((size_t)safe_read(fd, buf, chunk_bytes) != chunk_bytes) {
1203 ERR("read error");
1204 stopAndExit();
1206 ofs -= chunk_bytes;
1208 if (ofs) {
1209 if (safe_read(fd, buf, ofs) != ofs) {
1210 ERR("read error");
1211 stopAndExit();
1214 hwdata.format = DEFAULT_FORMAT;
1215 hwdata.channels = 1;
1216 hwdata.rate = DEFAULT_SPEED;
1217 set_params();
1219 in_buffer = nextblock = 0;
1220 while (1) {
1221 Fill_the_buffer: /* need this for repeat */
1222 if (in_buffer < 32) {
1223 /* move the rest of buffer to pos 0 and fill the buf up */
1224 if (in_buffer)
1225 memcpy(buf, data, in_buffer);
1226 data = buf;
1227 if ((l = safe_read(fd, buf + in_buffer, chunk_bytes - in_buffer)) > 0)
1228 in_buffer += l;
1229 else if (!in_buffer) {
1230 /* the file is truncated, so simulate 'Terminator'
1231 and reduce the datablock for safe landing */
1232 nextblock = buf[0] = 0;
1233 if (l == -1) {
1234 // perror(name);
1235 stopAndExit();
1239 while (!nextblock) { /* this is a new block */
1240 if (in_buffer < sizeof(VocBlockType))
1241 goto __end;
1242 bp = (VocBlockType *) data;
1243 COUNT1(sizeof(VocBlockType));
1244 nextblock = VOC_DATALEN(bp);
1245 if (output)
1246 MSG(" "); /* write /n after ASCII-out */
1247 output = 0;
1248 switch (bp->type) {
1249 case 0:
1250 #if 0
1251 MSG("Terminator");
1252 #endif
1253 return; /* VOC-file stop */
1254 case 1:
1255 vd = (VocVoiceData *) data;
1256 COUNT1(sizeof(VocVoiceData));
1257 /* we need a SYNC, before we can set new SPEED, STEREO ... */
1259 if (!was_extended) {
1260 hwdata.rate = (int) (vd->tc);
1261 hwdata.rate = 1000000 / (256 - hwdata.rate);
1262 #if 0
1263 MSG("Channel data %d Hz", dsp_speed);
1264 #endif
1265 if (vd->pack) { /* /dev/dsp can't it */
1266 ERR("can't play packed .voc files");
1267 return;
1269 if (hwdata.channels == 2) /* if we are in Stereo-Mode, switch back */
1270 hwdata.channels = 1;
1271 } else { /* there was extended block */
1272 hwdata.channels = 2;
1273 was_extended = 0;
1275 set_params();
1276 break;
1277 case 2: /* nothing to do, pure data */
1278 #if 0
1279 MSG("Channel continuation");
1280 #endif
1281 break;
1282 case 3: /* a silence block, no data, only a count */
1283 sp = (u_short *) data;
1284 COUNT1(sizeof(u_short));
1285 hwdata.rate = (int) (*data);
1286 COUNT1(1);
1287 hwdata.rate = 1000000 / (256 - hwdata.rate);
1288 set_params();
1289 silence = (((size_t) * sp) * 1000) / hwdata.rate;
1290 #if 0
1291 MSG("Silence for %d ms", (int) silence);
1292 #endif
1293 voc_write_silence(*sp);
1294 break;
1295 case 4: /* a marker for syncronisation, no effect */
1296 sp = (u_short *) data;
1297 COUNT1(sizeof(u_short));
1298 #if 0
1299 MSG("Marker %d", *sp);
1300 #endif
1301 break;
1302 case 5: /* ASCII text, we copy to stderr */
1303 output = 1;
1304 #if 0
1305 MSG("ASCII - text :");
1306 #endif
1307 break;
1308 case 6: /* repeat marker, says repeatcount */
1309 /* my specs don't say it: maybe this can be recursive, but
1310 I don't think somebody use it */
1311 repeat = *(u_short *) data;
1312 COUNT1(sizeof(u_short));
1313 #if 0
1314 MSG("Repeat loop %d times", repeat);
1315 #endif
1316 if (filepos >= 0) { /* if < 0, one seek fails, why test another */
1317 if ((filepos = lseek64(fd, 0, 1)) < 0) {
1318 ERR("can't play loops; %s isn't seekable", name);
1319 repeat = 0;
1320 } else {
1321 filepos -= in_buffer; /* set filepos after repeat */
1323 } else {
1324 repeat = 0;
1326 break;
1327 case 7: /* ok, lets repeat that be rewinding tape */
1328 if (repeat) {
1329 if (repeat != 0xFFFF) {
1330 #if 0
1331 MSG("Repeat loop %d", repeat);
1332 #endif
1333 --repeat;
1335 #if 0
1336 else
1337 MSG("Neverending loop");
1338 #endif
1339 lseek64(fd, filepos, 0);
1340 in_buffer = 0; /* clear the buffer */
1341 goto Fill_the_buffer;
1343 #if 0
1344 else
1345 MSG("End repeat loop");
1346 #endif
1347 break;
1348 case 8: /* the extension to play Stereo, I have SB 1.0 :-( */
1349 was_extended = 1;
1350 eb = (VocExtBlock *) data;
1351 COUNT1(sizeof(VocExtBlock));
1352 hwdata.rate = (int) (eb->tc);
1353 hwdata.rate = 256000000L / (65536 - hwdata.rate);
1354 hwdata.channels = eb->mode == VOC_MODE_STEREO ? 2 : 1;
1355 if (hwdata.channels == 2)
1356 hwdata.rate = hwdata.rate >> 1;
1357 if (eb->pack) { /* /dev/dsp can't it */
1358 ERR("can't play packed .voc files");
1359 return;
1361 #if 0
1362 MSG("Extended block %s %d Hz",
1363 (eb->mode ? "Stereo" : "Mono"), dsp_speed);
1364 #endif
1365 break;
1366 default:
1367 ERR("unknown blocktype %d. terminate.", bp->type);
1368 return;
1369 } /* switch (bp->type) */
1370 } /* while (! nextblock) */
1371 /* put nextblock data bytes to dsp */
1372 l = in_buffer;
1373 if (nextblock < (size_t)l)
1374 l = nextblock;
1375 if (l) {
1376 if (output) {
1377 if (write(2, data, l) != l) { /* to stderr */
1378 ERR("write error");
1379 stopAndExit();
1381 } else {
1382 if (voc_pcm_write(data, l) != l) {
1383 ERR("write error");
1384 stopAndExit();
1387 COUNT(l);
1389 } /* while(1) */
1390 __end:
1391 voc_pcm_flush();
1392 // free(buf);
1394 /* that was a big one, perhaps somebody split it :-) */
1396 /* setting the globals for playing raw data */
1397 void AlsaPlayerThread::init_raw_data(void)
1399 hwdata = rhwdata;
1402 /* calculate the data count to read from/to dsp */
1403 off64_t AlsaPlayerThread::calc_count(void)
1405 off64_t count;
1407 if (timelimit == 0) {
1408 count = pbrec_count;
1409 } else {
1410 count = snd_pcm_format_size(hwdata.format, hwdata.rate * hwdata.channels);
1411 count *= (off64_t)timelimit;
1413 return count < pbrec_count ? count : pbrec_count;
1416 void AlsaPlayerThread::header(int /*rtype*/, const char* /*name*/)
1418 // fprintf(stderr, "%s %s '%s' : ",
1419 // (stream == SND_PCM_STREAM_PLAYBACK) ? "Playing" : "Recording",
1420 // fmt_rec_table[rtype].what,
1421 // name);
1422 QString channels;
1423 if (hwdata.channels == 1)
1424 channels = "Mono";
1425 else if (hwdata.channels == 2)
1426 channels = "Stereo";
1427 else
1428 channels = QString("Channels %1").arg(hwdata.channels);
1429 QByteArray asciiChannels = channels.toAscii();
1430 DBG("Format: %s, Rate %d Hz, %s",
1431 snd_pcm_format_description(hwdata.format),
1432 hwdata.rate,
1433 asciiChannels.constData());
1436 /* playing raw data */
1438 void AlsaPlayerThread::playback_go(int fd, size_t loaded, off64_t count, int rtype, const char *name)
1440 int l, r;
1441 off64_t written = 0;
1442 off64_t c;
1444 if (m_debugLevel >= 1) header(rtype, name);
1445 set_params();
1447 while (loaded > chunk_bytes && written < count) {
1448 if (pcm_write(audiobuf + written, chunk_size) <= 0)
1449 return;
1450 written += chunk_bytes;
1451 loaded -= chunk_bytes;
1453 if (written > 0 && loaded > 0)
1454 memmove(audiobuf, audiobuf + written, loaded);
1456 l = loaded;
1457 while (written < count) {
1458 do {
1459 c = count - written;
1460 if (c > chunk_bytes)
1461 c = chunk_bytes;
1462 c -= l;
1464 if (c == 0)
1465 break;
1466 r = safe_read(fd, audiobuf + l, c);
1467 if (r < 0) {
1468 // perror(name);
1469 stopAndExit();
1471 fdcount += r;
1472 if (r == 0)
1473 break;
1474 l += r;
1475 } while (sleep_min == 0 && (size_t)l < chunk_bytes);
1476 l = l * 8 / bits_per_frame;
1477 DBG("calling pcm_write with %i frames.", l);
1478 r = pcm_write(audiobuf, l);
1479 DBG("pcm_write returned r = %i", r);
1480 if (r < 0) return;
1481 if (r != l)
1482 break;
1483 r = r * bits_per_frame / 8;
1484 written += r;
1485 l = 0;
1488 DBG("Draining...");
1490 /* We want the next "device ready" notification only when the buffer is completely empty. */
1491 /* Do this by setting the avail_min to the buffer size. */
1492 int err;
1493 DBG("Getting swparams");
1494 snd_pcm_sw_params_t *swparams;
1495 snd_pcm_sw_params_alloca(&swparams);
1496 err = snd_pcm_sw_params_current(handle, swparams);
1497 if (err < 0) {
1498 ERR("Unable to get current swparams: %s", snd_strerror(err));
1499 return;
1501 DBG("Setting avail min to %lu", buffer_size);
1502 err = snd_pcm_sw_params_set_avail_min(handle, swparams, buffer_size);
1503 if (err < 0) {
1504 ERR("Unable to set avail min for playback: %s", snd_strerror(err));
1505 return;
1507 /* write the parameters to the playback device */
1508 DBG("Writing swparams");
1509 err = snd_pcm_sw_params(handle, swparams);
1510 if (err < 0) {
1511 ERR("Unable to set sw params for playback: %s", snd_strerror(err));
1512 return;
1515 DBG("Waiting for poll");
1516 err = wait_for_poll(1);
1517 if (err < 0) {
1518 ERR("Wait for poll() failed");
1519 return;
1520 } else if (err == 1){
1521 MSG("Playback stopped while draining");
1523 /* Drop the playback on the sound device (probably
1524 still in progress up till now) */
1525 err = snd_pcm_drop(handle);
1526 if (err < 0) {
1527 ERR("snd_pcm_drop() failed: %s", snd_strerror(err));
1528 return;
1531 DBG("Draining completed");
1535 * let's play or capture it (capture_type says VOC/WAVE/raw)
1538 void AlsaPlayerThread::playback(int fd)
1540 int ofs;
1541 size_t dta;
1542 ssize_t dtawave;
1544 pbrec_count = LLONG_MAX;
1545 fdcount = 0;
1547 /* read the file header */
1548 dta = sizeof(AuHeader);
1549 if ((size_t)safe_read(fd, audiobuf, dta) != dta) {
1550 ERR("read error");
1551 stopAndExit();
1553 if (test_au(fd, audiobuf) >= 0) {
1554 rhwdata.format = hwdata.format;
1555 pbrec_count = calc_count();
1556 playback_go(fd, 0, pbrec_count, FORMAT_AU, audiofile_name);
1557 goto __end;
1559 dta = sizeof(VocHeader);
1560 if ((size_t)safe_read(fd, audiobuf + sizeof(AuHeader),
1561 dta - sizeof(AuHeader)) != dta - sizeof(AuHeader)) {
1562 ERR("read error");
1563 stopAndExit();
1565 if ((ofs = test_vocfile(audiobuf)) >= 0) {
1566 pbrec_count = calc_count();
1567 voc_play(fd, ofs, audiofile_name);
1568 goto __end;
1570 /* read bytes for WAVE-header */
1571 if ((dtawave = test_wavefile(fd, audiobuf, dta)) >= 0) {
1572 pbrec_count = calc_count();
1573 playback_go(fd, dtawave, pbrec_count, FORMAT_WAVE, audiofile_name);
1574 } else {
1575 /* should be raw data */
1576 init_raw_data();
1577 pbrec_count = calc_count();
1578 playback_go(fd, dta, pbrec_count, FORMAT_RAW, audiofile_name);
1580 __end:
1581 return;
1584 /* Wait until ALSA is ready for more samples or stop() was called.
1585 @return 0 if ALSA is ready for more input, +1 if a request to stop
1586 the sound output was received and a negative value on error. */
1587 int AlsaPlayerThread::wait_for_poll(int draining)
1589 unsigned short revents;
1590 snd_pcm_state_t state;
1591 int ret;
1593 DBG("Waiting for poll");
1595 /* Wait for certain events */
1596 while (1) {
1597 /* Simulated pause by not writing to alsa device, which will lead to an XRUN
1598 when resumed. */
1599 if (m_simulatedPause)
1600 msleep(500);
1601 else {
1603 ret = poll(alsa_poll_fds, alsa_fd_count, -1);
1604 DBG("activity on %d descriptors", ret);
1606 /* Check for stop request from alsa_stop on the last file descriptors. */
1607 if ((revents = alsa_poll_fds[alsa_fd_count-1].revents)) {
1608 if (revents & POLLIN){
1609 DBG("stop requested");
1610 return 1;
1614 /* Check the first count-1 descriptors for ALSA events */
1615 snd_pcm_poll_descriptors_revents(handle, alsa_poll_fds, alsa_fd_count-1, &revents);
1617 /* Ensure we are in the right state */
1618 state = snd_pcm_state(handle);
1619 DBG("State after poll returned is %s", snd_pcm_state_name(state));
1621 if (SND_PCM_STATE_XRUN == state){
1622 if (!draining){
1623 MSG("WARNING: Buffer underrun detected!");
1624 xrun();
1625 return 0;
1626 }else{
1627 DBG("Playback terminated");
1628 return 0;
1632 if (SND_PCM_STATE_SUSPENDED == state){
1633 DBG("WARNING: Suspend detected!");
1634 suspend();
1635 return 0;
1638 /* Check for errors */
1639 if (revents & POLLERR) {
1640 DBG("poll revents says POLLERR");
1641 return -EIO;
1644 /* Is ALSA ready for more input? */
1645 if ((revents & POLLOUT)){
1646 DBG("Ready for more input");
1647 return 0;
1653 // ====================================================================
1654 // AlsaPlayer
1656 // AlsaPlayer is nothing more than a container for AlsaPlayerThread
1657 // in order to avoid ambiguous QObject, since both Player and QThread
1658 // derive from QObject. Is there a better solution?
1660 AlsaPlayer::AlsaPlayer(QObject* parent, const QStringList& args):
1661 Player(parent, "alsaplayer", args)
1663 m_AlsaPlayerThread = new AlsaPlayerThread(this);
1666 AlsaPlayer::~AlsaPlayer()
1668 delete m_AlsaPlayerThread;
1671 /*virtual*/ void AlsaPlayer::startPlay(const QString& file) { m_AlsaPlayerThread->startPlay(file); }
1672 /*virtual*/ void AlsaPlayer::pause() { m_AlsaPlayerThread->pause(); }
1673 /*virtual*/ void AlsaPlayer::stop() { m_AlsaPlayerThread->stop(); }
1675 /*virtual*/ void AlsaPlayer::setVolume(float volume) { m_AlsaPlayerThread->setVolume(volume); }
1676 /*virtual*/ float AlsaPlayer::volume() const { return m_AlsaPlayerThread->volume(); }
1678 /*virtual*/ bool AlsaPlayer::playing() const { return m_AlsaPlayerThread->playing(); }
1679 /*virtual*/ bool AlsaPlayer::paused() const { return m_AlsaPlayerThread->paused(); }
1681 /*virtual*/ int AlsaPlayer::totalTime() const { return m_AlsaPlayerThread->totalTime(); }
1682 /*virtual*/ int AlsaPlayer::currentTime() const { return m_AlsaPlayerThread->currentTime(); }
1683 /*virtual*/ int AlsaPlayer::position() const { return m_AlsaPlayerThread->position(); } // in this case not really the percent
1685 /*virtual*/ void AlsaPlayer::seek(int seekTime) { m_AlsaPlayerThread->seek(seekTime); }
1686 /*virtual*/ void AlsaPlayer::seekPosition(int position) { m_AlsaPlayerThread->seekPosition(position); }
1688 /*virtual*/ QStringList AlsaPlayer::getPluginList( const QByteArray& classname )
1689 { return m_AlsaPlayerThread->getPluginList(classname); }
1690 /*virtual*/ void AlsaPlayer::setSinkName(const QString &sinkName)
1691 { m_AlsaPlayerThread->setSinkName(sinkName); }
1693 #include "alsaplayer.moc"
1695 #undef DBG
1696 #undef MSG
1697 #undef ERR
1699 // vim: sw=4 ts=8 et