more krazy fixes (include own header first, and missing e-mail addresses
[kdeaccessibility.git] / kttsd / players / alsaplayer / alsaplayer.cpp
blob2b7489cfb47a16ef1ab9421fd8bc0fe4a92b2daa
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 // AlsaPlayer includes.
28 #include "alsaplayer.h"
30 #include <assert.h>
32 // #include <sys/wait.h>
33 // System includes.
34 #include <config-kttsd.h>
35 #if TIME_WITH_SYS_TIME
36 # include <sys/time.h>
37 # include <time.h>
38 #else
39 # if HAVE_SYS_TIME_H
40 # include <sys/time.h>
41 # else
42 # include <time.h>
43 # endif
44 #endif
46 // Qt includes.
47 #include <QtCore/QDir>
48 #include <QtGui/QApplication>
49 #include <QtCore/QMutexLocker>
51 // KDE includes.
52 #include <kdebug.h>
53 #include <kconfig.h>
54 #include <kstandarddirs.h>
55 #include <kmessagebox.h>
56 #include <klocale.h>
58 #define DBG if (m_debugLevel >= 2) kDebug() << timestamp()
60 QString AlsaPlayerThread::timestamp() const
62 time_t t;
63 struct timeval tv;
64 char *tstr;
65 t = time(NULL);
66 tstr = strdup(ctime(&t));
67 tstr[strlen(tstr)-1] = 0;
68 gettimeofday(&tv,NULL);
69 QString ts;
70 ts.sprintf(" %s [%d] ",tstr, (int) tv.tv_usec);
71 free(tstr);
72 return ts;
75 static snd_pcm_sframes_t (*readi_func)(snd_pcm_t *handle, void *buffer, snd_pcm_uframes_t size);
76 static snd_pcm_sframes_t (*writei_func)(snd_pcm_t *handle, const void *buffer, snd_pcm_uframes_t size);
77 static snd_pcm_sframes_t (*readn_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);
78 static snd_pcm_sframes_t (*writen_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);
81 ////////////////////////////////////////////////////////////////////////////////
82 // public methods
83 ////////////////////////////////////////////////////////////////////////////////
85 AlsaPlayerThread::AlsaPlayerThread(QObject* parent) :
86 QThread(parent),
87 m_currentVolume(1.0),
88 m_pcmName("default"),
89 m_defPeriodSize(128),
90 m_defPeriods(8),
91 m_debugLevel(1),
92 m_simulatedPause(false)
94 init();
97 AlsaPlayerThread::~AlsaPlayerThread()
99 if (isRunning()) {
100 stop();
101 wait();
105 //void AlsaPlayerThread::play(const FileHandle &file)
106 void AlsaPlayerThread::startPlay(const QString &file)
108 if (isRunning()) {
109 if (paused()) {
110 if (canPause)
111 snd_pcm_pause(handle, false);
112 else
113 m_simulatedPause = false;
115 return;
117 audiofile.setFileName(file);
118 audiofile.open(QIODevice::ReadOnly);
119 fd = audiofile.handle();
120 if (audiofile_name) free(audiofile_name);
121 audiofile_name = qstrdup(file.toAscii().constData());
122 // Start thread running.
123 start();
126 /*virtual*/ void AlsaPlayerThread::run()
128 QString pName = m_pcmName.section(" ", 0, 0);
129 pcm_name = qstrdup(pName.toAscii().constData());
130 DBG << "pName = " << pcm_name << endl;
131 int err;
132 snd_pcm_info_t *info;
134 m_simulatedPause = false;
136 snd_pcm_info_alloca(&info);
138 err = snd_output_stdio_attach(&log, stderr, 0);
139 assert(err >= 0);
141 rhwdata.format = DEFAULT_FORMAT;
142 rhwdata.rate = DEFAULT_SPEED;
143 rhwdata.channels = 1;
145 err = snd_pcm_open(&handle, pcm_name, stream, open_mode);
146 if (err < 0) {
147 kError() << "audio open error on pcm device " << pcm_name << ": " << snd_strerror(err)
148 << endl;
149 return;
152 if ((err = snd_pcm_info(handle, info)) < 0) {
153 kError() << "info error: " << snd_strerror(err) << endl;
154 return;
157 chunk_size = 1024;
158 hwdata = rhwdata;
160 audioBuffer.resize(1024);
161 // audiobuf = (char *)malloc(1024);
162 audiobuf = audioBuffer.data();
163 if (audiobuf == NULL)
164 return;
166 if (mmap_flag) {
167 writei_func = snd_pcm_mmap_writei;
168 readi_func = snd_pcm_mmap_readi;
169 writen_func = snd_pcm_mmap_writen;
170 readn_func = snd_pcm_mmap_readn;
171 } else {
172 writei_func = snd_pcm_writei;
173 readi_func = snd_pcm_readi;
174 writen_func = snd_pcm_writen;
175 readn_func = snd_pcm_readn;
179 playback(fd);
180 cleanup();
181 return;
184 void AlsaPlayerThread::pause()
186 if (isRunning()) {
187 kDebug() << "Pause requested";
188 QMutexLocker locker(&m_mutex);
189 if (handle) {
190 // Some hardware can pause; some can't. canPause is set in set_params.
191 if (canPause) {
192 m_simulatedPause = false;
193 snd_pcm_pause(handle, true);
194 } else {
195 // Set a flag and cause wait_for_poll to sleep. When resumed, will get
196 // an underrun.
197 m_simulatedPause = true;
203 void AlsaPlayerThread::stop()
205 if (isRunning()) {
206 DBG << "STOP! Locking mutex" << endl;
207 QMutexLocker locker(&m_mutex);
208 m_simulatedPause = false;
209 if (handle) {
210 /* This constant is arbitrary */
211 char buf = 42;
212 DBG << "Request for stop, device state is "
213 << snd_pcm_state_name(snd_pcm_state(handle)) << endl;
214 write(alsa_stop_pipe[1], &buf, 1);
216 DBG << "unlocking mutex" << endl;
217 locker.unlock();
218 /* Wait for thread to exit */
219 DBG << "waiting for thread to exit" << endl;
220 wait();
221 DBG << "cleaning up" << endl;
222 // TODO: This seems like a bug. Why must I relock the locker
223 // since I'm about to destroy the locker?
224 locker.relock();
226 cleanup();
230 * Stop playback, cleanup and exit thread.
232 void AlsaPlayerThread::stopAndExit()
234 // if (handle) snd_pcm_drop(handle);
235 cleanup();
236 exit();
239 void AlsaPlayerThread::setVolume(float volume)
241 m_currentVolume = volume;
244 float AlsaPlayerThread::volume() const
246 return m_currentVolume;
249 /////////////////////////////////////////////////////////////////////////////////
250 // player status functions
251 /////////////////////////////////////////////////////////////////////////////////
253 bool AlsaPlayerThread::playing() const
255 bool result = false;
256 if (isRunning()) {
257 QMutexLocker locker(&m_mutex);
258 if (handle) {
259 if (canPause) {
260 snd_pcm_status_t *status;
261 snd_pcm_status_alloca(&status);
262 int res;
263 if ((res = snd_pcm_status(handle, status)) < 0)
264 kError() << "status error: " << snd_strerror(res) << endl;
265 else {
266 result = (SND_PCM_STATE_RUNNING == snd_pcm_status_get_state(status))
267 || (SND_PCM_STATE_DRAINING == snd_pcm_status_get_state(status));
268 DBG << "state = " << snd_pcm_state_name(snd_pcm_status_get_state(status)) <<
269 endl;
271 } else
272 result = !m_simulatedPause;
275 return result;
278 bool AlsaPlayerThread::paused() const
280 bool result = false;
281 if (isRunning()) {
282 QMutexLocker locker(&m_mutex);
283 if (handle) {
284 if (canPause) {
285 snd_pcm_status_t *status;
286 snd_pcm_status_alloca(&status);
287 int res;
288 if ((res = snd_pcm_status(handle, status)) < 0)
289 kError() << "status error: " << snd_strerror(res) << endl;
290 else {
291 result = (SND_PCM_STATE_PAUSED == snd_pcm_status_get_state(status));
292 DBG << "state = " << snd_pcm_state_name(snd_pcm_status_get_state(status)) <<
293 endl;
295 } else
296 result = m_simulatedPause;
299 return result;
302 int AlsaPlayerThread::totalTime() const
304 int total = 0;
305 int rate = hwdata.rate;
306 int channels = hwdata.channels;
307 if (rate > 0 && channels > 0) {
308 total = int((double(pbrec_count) / rate) / channels);
309 // DBG("pbrec_count = %i rate =%i channels = %i", pbrec_count, rate, channels);
310 // DBG("totalTime = %i", total);
312 return total;
315 int AlsaPlayerThread::currentTime() const
317 int current = 0;
318 int rate = hwdata.rate;
319 int channels = hwdata.channels;
320 if (rate > 0 && channels > 0) {
321 current = int((double(fdcount) / rate) / channels);
322 // DBG("fdcount = %i rate = %i channels = %i", fdcount, rate, channels);
323 // DBG("currentTime = %i", current);
325 return current;
328 int AlsaPlayerThread::position() const
330 // TODO: Make this more accurate by adding frames that have been so-far
331 // played within the Alsa ring buffer.
332 return pbrec_count > 0 ? int(double(fdcount) * 1000 / pbrec_count + .5) : 0;
335 /////////////////////////////////////////////////////////////////////////////////
336 // player seek functions
337 /////////////////////////////////////////////////////////////////////////////////
339 void AlsaPlayerThread::seek(int /*seekTime*/)
341 // TODO:
344 void AlsaPlayerThread::seekPosition(int /*position*/)
346 // TODO:
350 * Returns a list of PCM devices.
351 * This function fills the specified list with ALSA hardware soundcards found on the system.
352 * It uses plughw:xx instead of hw:xx for specifiers, because hw:xx are not practical to
353 * use (e.g. they require a resampler/channel mixer in the application).
355 QStringList AlsaPlayerThread::getPluginList( const QByteArray& classname )
357 Q_UNUSED(classname);
359 int err = 0;
360 int card = -1, device = -1;
361 snd_ctl_t *handle;
362 snd_ctl_card_info_t *info;
363 snd_pcm_info_t *pcminfo;
364 snd_ctl_card_info_alloca(&info);
365 snd_pcm_info_alloca(&pcminfo);
366 QStringList result;
368 result.append("default");
369 for (;;) {
370 err = snd_card_next(&card);
371 if (err < 0 || card < 0) break;
372 if (card >= 0) {
373 char name[32];
374 sprintf(name, "hw:%i", card);
375 if ((err = snd_ctl_open(&handle, name, 0)) < 0) continue;
376 if ((err = snd_ctl_card_info(handle, info)) < 0) {
377 snd_ctl_close(handle);
378 continue;
380 for (int devCnt=0;;++devCnt) {
381 err = snd_ctl_pcm_next_device(handle, &device);
382 if (err < 0 || device < 0) break;
384 snd_pcm_info_set_device(pcminfo, device);
385 snd_pcm_info_set_subdevice(pcminfo, 0);
386 snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
387 if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) continue;
388 QString infoName = " ";
389 infoName += snd_ctl_card_info_get_name(info);
390 infoName += " (";
391 infoName += snd_pcm_info_get_name(pcminfo);
392 infoName += ')';
393 if (0 == devCnt) {
394 QString pcmName = QString("default:%1").arg(card);
395 result.append(pcmName + infoName);
397 QString pcmName = QString("plughw:%1,%2").arg(card).arg(device);
398 result.append(pcmName + infoName);
400 snd_ctl_close(handle);
403 return result;
406 void AlsaPlayerThread::setSinkName(const QString& sinkName) { m_pcmName = sinkName; }
408 /////////////////////////////////////////////////////////////////////////////////
409 // private
410 /////////////////////////////////////////////////////////////////////////////////
412 void AlsaPlayerThread::init()
414 pcm_name = 0;
415 audiofile_name = 0;
416 handle = 0;
417 canPause = false;
418 timelimit = 0;
419 file_type = FORMAT_DEFAULT;
420 sleep_min = 0;
421 // open_mode = 0;
422 open_mode = SND_PCM_NONBLOCK;
423 stream = SND_PCM_STREAM_PLAYBACK;
424 mmap_flag = 0;
425 interleaved = 1;
426 audiobuf = NULL;
427 chunk_size = 0;
428 period_time = 0;
429 buffer_time = 0;
430 avail_min = -1;
431 start_delay = 0;
432 stop_delay = 0;
433 buffer_pos = 0;
434 log = 0;
435 fd = -1;
436 pbrec_count = LLONG_MAX;
437 alsa_stop_pipe[0] = 0;
438 alsa_stop_pipe[1] = 0;
439 alsa_poll_fds = 0;
440 m_simulatedPause = false;
443 void AlsaPlayerThread::cleanup()
445 DBG << "cleaning up" << endl;
446 QMutexLocker locker(&m_mutex);
447 if (pcm_name) free(pcm_name);
448 if (audiofile_name) free(audiofile_name);
449 if (fd >= 0) audiofile.close();
450 if (handle) {
451 snd_pcm_drop(handle);
452 snd_pcm_close(handle);
454 if (alsa_stop_pipe[0]) close(alsa_stop_pipe[0]);
455 if (alsa_stop_pipe[1]) close(alsa_stop_pipe[1]);
456 if (audiobuf) audioBuffer.resize(0);
457 if (alsa_poll_fds) alsa_poll_fds_barray.resize(0);
458 if (log) snd_output_close(log);
459 snd_config_update_free_global();
460 init();
464 * Safe read (for pipes)
467 ssize_t AlsaPlayerThread::safe_read(int fd, void *buf, size_t count)
469 ssize_t result = 0;
470 ssize_t res;
472 while (count > 0) {
473 if ((res = read(fd, buf, count)) == 0)
474 break;
475 if (res < 0)
476 return result > 0 ? result : res;
477 count -= res;
478 result += res;
479 buf = (char *)buf + res;
481 return result;
485 * Test, if it is a .VOC file and return >=0 if ok (this is the length of rest)
486 * < 0 if not
488 int AlsaPlayerThread::test_vocfile(void *buffer)
490 VocHeader *vp = (VocHeader*)buffer;
492 if (!memcmp(vp->magic, VOC_MAGIC_STRING, 20)) {
493 vocminor = LE_SHORT(vp->version) & 0xFF;
494 vocmajor = LE_SHORT(vp->version) / 256;
495 if (LE_SHORT(vp->version) != (0x1233 - LE_SHORT(vp->coded_ver)))
496 return -2; /* coded version mismatch */
497 return LE_SHORT(vp->headerlen) - sizeof(VocHeader); /* 0 mostly */
499 return -1; /* magic string fail */
503 * helper for test_wavefile
506 ssize_t AlsaPlayerThread::test_wavefile_read(int fd, char *buffer, size_t *size, size_t reqsize, int line)
508 if (*size >= reqsize)
509 return *size;
510 if ((size_t)safe_read(fd, buffer + *size, reqsize - *size) != reqsize - *size) {
511 kError() << "read error (called from line " << line << endl;
512 stopAndExit();
514 return *size = reqsize;
517 #define check_wavefile_space(buffer, len, blimit) \
518 if (len > blimit) { \
519 blimit = len; \
520 if ((buffer = (char*)realloc(buffer, blimit)) == NULL) { \
521 stopAndExit(); \
526 * test, if it's a .WAV file, > 0 if ok (and set the speed, stereo etc.)
527 * == 0 if not
528 * Value returned is bytes to be discarded.
530 ssize_t AlsaPlayerThread::test_wavefile(int fd, char *_buffer, size_t size)
532 WaveHeader *h = (WaveHeader *)_buffer;
533 char *buffer = NULL;
534 size_t blimit = 0;
535 WaveFmtBody *f;
536 WaveChunkHeader *c;
537 u_int type;
538 u_int len;
540 if (size < sizeof(WaveHeader))
541 return -1;
542 if (h->magic != WAV_RIFF || h->type != WAV_WAVE)
543 return -1;
544 if (size > sizeof(WaveHeader)) {
545 check_wavefile_space(buffer, size - sizeof(WaveHeader), blimit);
546 memcpy(buffer, _buffer + sizeof(WaveHeader), size - sizeof(WaveHeader));
548 size -= sizeof(WaveHeader);
549 while (1) {
550 check_wavefile_space(buffer, sizeof(WaveChunkHeader), blimit);
551 test_wavefile_read(fd, buffer, &size, sizeof(WaveChunkHeader), __LINE__);
552 c = (WaveChunkHeader*)buffer;
553 type = c->type;
554 len = LE_INT(c->length);
555 len += len % 2;
556 if (size > sizeof(WaveChunkHeader))
557 memmove(buffer, buffer + sizeof(WaveChunkHeader), size - sizeof(WaveChunkHeader));
558 size -= sizeof(WaveChunkHeader);
559 if (type == WAV_FMT)
560 break;
561 check_wavefile_space(buffer, len, blimit);
562 test_wavefile_read(fd, buffer, &size, len, __LINE__);
563 if (size > len)
564 memmove(buffer, buffer + len, size - len);
565 size -= len;
568 if (len < sizeof(WaveFmtBody)) {
569 kError() << "unknown length of 'fmt ' chunk (read " << len << ", should be "
570 << sizeof(WaveFmtBody) << " at least" << endl;
571 stopAndExit();
573 check_wavefile_space(buffer, len, blimit);
574 test_wavefile_read(fd, buffer, &size, len, __LINE__);
575 f = (WaveFmtBody*) buffer;
576 if (LE_SHORT(f->format) != WAV_PCM_CODE) {
577 kError() << "can't play not PCM-coded WAVE-files" << endl;
578 stopAndExit();
580 if (LE_SHORT(f->modus) < 1) {
581 kError() << "can't play WAVE-files with " << LE_SHORT (f->modus) << " tracks" << endl;
582 stopAndExit();
584 hwdata.channels = LE_SHORT(f->modus);
585 switch (LE_SHORT(f->bit_p_spl)) {
586 case 8:
587 if (hwdata.format != DEFAULT_FORMAT &&
588 hwdata.format != SND_PCM_FORMAT_U8)
589 kDebug() << "Warning: format is changed to U8";
590 hwdata.format = SND_PCM_FORMAT_U8;
591 break;
592 case 16:
593 if (hwdata.format != DEFAULT_FORMAT &&
594 hwdata.format != SND_PCM_FORMAT_S16_LE)
595 kDebug() << "Warning: format is changed to S16_LE";
596 hwdata.format = SND_PCM_FORMAT_S16_LE;
597 break;
598 case 24:
599 switch (LE_SHORT(f->byte_p_spl) / hwdata.channels) {
600 case 3:
601 if (hwdata.format != DEFAULT_FORMAT &&
602 hwdata.format != SND_PCM_FORMAT_S24_3LE)
603 kDebug() << "Warning: format is changed to S24_3LE";
604 hwdata.format = SND_PCM_FORMAT_S24_3LE;
605 break;
606 case 4:
607 if (hwdata.format != DEFAULT_FORMAT &&
608 hwdata.format != SND_PCM_FORMAT_S24_LE)
609 kDebug() << "Warning: format is changed to S24_LE";
610 hwdata.format = SND_PCM_FORMAT_S24_LE;
611 break;
612 default:
613 kError () << "can not play WAVE-files with sample " <<
614 LE_SHORT (f->bit_p_spl) << " bits in " << LE_SHORT (f->byte_p_spl)
615 << "(" << hwdata.channels << " channels)" << endl;
616 stopAndExit();
618 break;
619 case 32:
620 hwdata.format = SND_PCM_FORMAT_S32_LE;
621 break;
622 default:
623 kError() << "can't play WAVE-files with sample "
624 << LE_SHORT (f->bit_p_spl) << endl;
625 stopAndExit();
627 hwdata.rate = LE_INT(f->sample_fq);
629 if (size > len)
630 memmove(buffer, buffer + len, size - len);
631 size -= len;
633 while (1) {
634 u_int type, len;
636 check_wavefile_space(buffer, sizeof(WaveChunkHeader), blimit);
637 test_wavefile_read(fd, buffer, &size, sizeof(WaveChunkHeader), __LINE__);
638 c = (WaveChunkHeader*)buffer;
639 type = c->type;
640 len = LE_INT(c->length);
641 if (size > sizeof(WaveChunkHeader))
642 memmove(buffer, buffer + sizeof(WaveChunkHeader), size - sizeof(WaveChunkHeader));
643 size -= sizeof(WaveChunkHeader);
644 if (type == WAV_DATA) {
645 if (len < pbrec_count && len < 0x7ffffffe)
646 pbrec_count = len;
647 if (size > 0)
648 memcpy(_buffer, buffer, size);
649 free(buffer);
650 return size;
652 len += len % 2;
653 check_wavefile_space(buffer, len, blimit);
654 test_wavefile_read(fd, buffer, &size, len, __LINE__);
655 if (size > len)
656 memmove(buffer, buffer + len, size - len);
657 size -= len;
660 /* shouldn't be reached */
661 return -1;
665 * Test for AU file.
668 int AlsaPlayerThread::test_au(int fd, char *buffer)
670 AuHeader *ap = (AuHeader*)buffer;
672 if (ap->magic != AU_MAGIC)
673 return -1;
674 if (BE_INT(ap->hdr_size) > 128 || BE_INT(ap->hdr_size) < 24)
675 return -1;
676 pbrec_count = BE_INT(ap->data_size);
677 switch (BE_INT(ap->encoding)) {
678 case AU_FMT_ULAW:
679 if (hwdata.format != DEFAULT_FORMAT &&
680 hwdata.format != SND_PCM_FORMAT_MU_LAW)
681 kDebug() << "Warning: format is changed to MU_LAW";
682 hwdata.format = SND_PCM_FORMAT_MU_LAW;
683 break;
684 case AU_FMT_LIN8:
685 if (hwdata.format != DEFAULT_FORMAT &&
686 hwdata.format != SND_PCM_FORMAT_U8)
687 kDebug() << "Warning: format is changed to U8";
688 hwdata.format = SND_PCM_FORMAT_U8;
689 break;
690 case AU_FMT_LIN16:
691 if (hwdata.format != DEFAULT_FORMAT &&
692 hwdata.format != SND_PCM_FORMAT_S16_BE)
693 kDebug() << "Warning: format is changed to S16_BE";
694 hwdata.format = SND_PCM_FORMAT_S16_BE;
695 break;
696 default:
697 return -1;
699 hwdata.rate = BE_INT(ap->sample_rate);
700 if (hwdata.rate < 2000 || hwdata.rate > 256000)
701 return -1;
702 hwdata.channels = BE_INT(ap->channels);
703 if (hwdata.channels < 1 || hwdata.channels > 128)
704 return -1;
705 if ((size_t)safe_read(fd, buffer + sizeof(AuHeader), BE_INT(ap->hdr_size) - sizeof(AuHeader)) != BE_INT(ap->hdr_size) - sizeof(AuHeader)) {
706 kError() << "read error" << endl;
707 stopAndExit();
709 return 0;
712 void AlsaPlayerThread::set_params(void)
714 snd_pcm_hw_params_t *hwparams;
715 snd_pcm_uframes_t period_size;
716 int err;
717 int dir;
718 unsigned int rate;
719 unsigned int periods;
721 snd_pcm_hw_params_alloca(&hwparams);
722 err = snd_pcm_hw_params_any(handle, hwparams);
723 if (err < 0) {
724 kError() << "Broken configuration for this PCM: no configurations available" << endl;
725 stopAndExit();
728 /* Create the pipe for communication about stop requests. */
729 if (pipe(alsa_stop_pipe)) {
730 kError() << "Stop pipe creation failed (" << strerror(errno) << ")" << endl;
731 stopAndExit();
734 /* Find how many descriptors we will get for poll(). */
735 alsa_fd_count = snd_pcm_poll_descriptors_count(handle);
736 if (alsa_fd_count <= 0) {
737 kError() << "Invalid poll descriptors count returned from ALSA." << endl;
738 stopAndExit();
741 /* Create and fill in struct pollfd *alsa_poll_fds with ALSA descriptors. */
742 // alsa_poll_fds = (pollfd *)malloc ((alsa_fd_count + 1) * sizeof(struct pollfd));
743 alsa_poll_fds_barray.resize((alsa_fd_count + 1) * sizeof(struct pollfd));
744 alsa_poll_fds = (pollfd *)alsa_poll_fds_barray.data();
745 assert(alsa_poll_fds);
746 if ((err = snd_pcm_poll_descriptors(handle, alsa_poll_fds, alsa_fd_count)) < 0) {
747 kError() << "Unable to obtain poll descriptors for playback: " << snd_strerror(err) << endl;
748 stopAndExit();
751 /* Create a new pollfd structure for requests by alsa_stop(). */
752 struct pollfd alsa_stop_pipe_pfd;
753 alsa_stop_pipe_pfd.fd = alsa_stop_pipe[0];
754 alsa_stop_pipe_pfd.events = POLLIN;
755 alsa_stop_pipe_pfd.revents = 0;
757 /* Join this our own pollfd to the ALSAs ones. */
758 alsa_poll_fds[alsa_fd_count] = alsa_stop_pipe_pfd;
759 ++alsa_fd_count;
761 if (mmap_flag) {
762 snd_pcm_access_mask_t *mask = (snd_pcm_access_mask_t *)alloca(snd_pcm_access_mask_sizeof());
763 snd_pcm_access_mask_none(mask);
764 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
765 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
766 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
767 err = snd_pcm_hw_params_set_access_mask(handle, hwparams, mask);
768 } else if (interleaved)
769 err = snd_pcm_hw_params_set_access(handle, hwparams,
770 SND_PCM_ACCESS_RW_INTERLEAVED);
771 else
772 err = snd_pcm_hw_params_set_access(handle, hwparams,
773 SND_PCM_ACCESS_RW_NONINTERLEAVED);
774 if (err < 0) {
775 kError() << "Error setting access type: " << snd_strerror(err) << endl;
776 stopAndExit();
778 err = snd_pcm_hw_params_set_format(handle, hwparams, hwdata.format);
779 if (err < 0) {
780 kError() << "Error setting sample format to " <<
781 hwdata.format << ": " << snd_strerror(err) << endl;
782 stopAndExit();
784 err = snd_pcm_hw_params_set_channels(handle, hwparams, hwdata.channels);
785 if (err < 0) {
786 kError() << "Error setting channel count to "
787 << hwdata.channels << ": " << snd_strerror(err) << endl;
788 stopAndExit();
791 #if 0
792 err = snd_pcm_hw_params_set_periods_min(handle, hwparams, 2);
793 assert(err >= 0);
794 #endif
795 rate = hwdata.rate;
796 err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &hwdata.rate, 0);
797 assert(err >= 0);
798 if ((float)rate * 1.05 < hwdata.rate || (float)rate * 0.95 > hwdata.rate) {
799 kDebug() << "Warning: rate is not accurate (requested = " << rate << "Hz, got = " <<
800 hwdata.rate << ")" << endl;
801 kDebug() << " please, try the plug plugin (-Dplug:" << snd_pcm_name << ")";
804 period_size = m_defPeriodSize;
805 dir = 1;
806 err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_size, &dir);
807 if (err < 0) {
808 kDebug() << "Setting period_size to " << period_size << " failed, but continuing: "
809 << snd_strerror(err);
812 periods = m_defPeriods;
813 dir = 1;
814 err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &periods, &dir);
815 if (err < 0)
816 kDebug() << "Unable to set number of periods to " << periods << ", but continuing: "
817 << snd_strerror(err);
819 /* Install hw parameters. */
820 err = snd_pcm_hw_params(handle, hwparams);
821 if (err < 0) {
822 kDebug() << "Unable to install hw params: " << snd_strerror(err);
823 snd_pcm_hw_params_dump(hwparams, log);
824 stopAndExit();
827 /* Determine if device can pause. */
828 canPause = (1 == snd_pcm_hw_params_can_pause(hwparams));
830 /* Get final buffer size and calculate the chunk size we will pass to device. */
831 snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
832 chunk_size = periods * period_size;
834 if (0 == chunk_size) {
835 kError() << "Invalid periods or period_size. Cannot continue.";
836 stopAndExit();
839 if (chunk_size == buffer_size)
840 kDebug() << "WARNING: Should not use chunk_size equal to buffer_size (" << chunk_size
841 << "). Continuing anyway." ;
843 DBG << "Final buffer_size = " <<
844 buffer_size << " chunk_size = " << chunk_size << " periods = " << periods
845 << " period_size = " << period_size << " canPause = " << canPause << endl;
847 if (m_debugLevel >= 2)
848 snd_pcm_dump(handle, log);
850 bits_per_sample = snd_pcm_format_physical_width(hwdata.format);
851 bits_per_frame = bits_per_sample * hwdata.channels;
852 chunk_bytes = chunk_size * bits_per_frame / 8;
853 audioBuffer.resize(chunk_bytes);
854 audiobuf = audioBuffer.data();
855 if (audiobuf == NULL) {
856 stopAndExit();
860 #ifndef timersub
861 #define timersub(a, b, result) \
862 do { \
863 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
864 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
865 if ((result)->tv_usec < 0) { \
866 --(result)->tv_sec; \
867 (result)->tv_usec += 1000000; \
869 } while (0)
870 #endif
872 /* I/O error handler */
873 void AlsaPlayerThread::xrun()
875 snd_pcm_status_t *status;
876 int res;
878 snd_pcm_status_alloca(&status);
879 if ((res = snd_pcm_status(handle, status))<0) {
880 kError() << "status error: " << snd_strerror(res) << endl;
881 stopAndExit();
883 if (SND_PCM_STATE_XRUN == snd_pcm_status_get_state(status)) {
884 struct timeval now, diff, tstamp;
885 gettimeofday(&now, 0);
886 snd_pcm_status_get_trigger_tstamp(status, &tstamp);
887 timersub(&now, &tstamp, &diff);
888 kDebug() << (stream == SND_PCM_STREAM_PLAYBACK ? "underrun" : "overrun")
889 << "!!! at least " << diff.tv_sec * 1000 + diff.tv_usec / 1000.0 << " ms long)" << endl;
890 if (m_debugLevel >= 2) {
891 DBG << "Status:" << snd_pcm_status_dump(status, log) << endl;
893 if ((res = snd_pcm_prepare(handle))<0) {
894 kError() << "xrun: prepare error: " << snd_strerror(res) << endl;
895 stopAndExit();
897 return; /* ok, data should be accepted again */
898 } if (SND_PCM_STATE_DRAINING == snd_pcm_status_get_state(status)) {
899 if (m_debugLevel >= 2) {
900 DBG << "Status(DRAINING):" << endl;
901 snd_pcm_status_dump(status, log);
903 if (stream == SND_PCM_STREAM_CAPTURE) {
904 kDebug() << "capture stream format change? attempting recover...";
905 if ((res = snd_pcm_prepare(handle))<0) {
906 kError() << "xrun(DRAINING): prepare error: " << snd_strerror(res) << endl;
907 stopAndExit();
909 return;
912 if (m_debugLevel >= 2) {
913 DBG << "Status(R/W):" << endl;
914 snd_pcm_status_dump(status, log);
916 kError() << "read/write error, state = " << snd_pcm_state_name(snd_pcm_status_get_state(status))
917 << endl;
918 stopAndExit();
921 /* I/O suspend handler */
922 void AlsaPlayerThread::suspend(void)
924 int res;
926 kDebug() << "Suspended. Trying resume. ";
927 while ((res = snd_pcm_resume(handle)) == -EAGAIN)
928 sleep(1); /* wait until suspend flag is released */
929 if (res < 0) {
930 kDebug() << "Failed. Restarting stream. ";
931 if ((res = snd_pcm_prepare(handle)) < 0) {
932 kError() << "suspend: prepare error: " << snd_strerror(res) << endl;
933 stopAndExit();
936 kDebug() << "Suspend done.";
939 /* peak handler */
940 void AlsaPlayerThread::compute_max_peak(char *data, size_t count)
942 signed int val, max, max_peak = 0, perc;
943 size_t ocount = count;
945 switch (bits_per_sample) {
946 case 8: {
947 signed char *valp = (signed char *)data;
948 signed char mask = snd_pcm_format_silence(hwdata.format);
949 while (count-- > 0) {
950 val = *valp++ ^ mask;
951 val = abs(val);
952 if (max_peak < val)
953 max_peak = val;
955 break;
957 case 16: {
958 signed short *valp = (signed short *)data;
959 signed short mask = snd_pcm_format_silence_16(hwdata.format);
960 count /= 2;
961 while (count-- > 0) {
962 val = *valp++ ^ mask;
963 val = abs(val);
964 if (max_peak < val)
965 max_peak = val;
967 break;
969 case 32: {
970 signed int *valp = (signed int *)data;
971 signed int mask = snd_pcm_format_silence_32(hwdata.format);
972 count /= 4;
973 while (count-- > 0) {
974 val = *valp++ ^ mask;
975 val = abs(val);
976 if (max_peak < val)
977 max_peak = val;
979 break;
981 default:
982 break;
984 max = 1 << (bits_per_sample-1);
985 if (max <= 0)
986 max = 0x7fffffff;
987 DBG << "Max peak (" << ocount << " samples): " << max_peak << " (0x"
988 << max_peak << ")" << endl;
989 if (bits_per_sample > 16)
990 perc = max_peak / (max / 100);
991 else
992 perc = max_peak * 100 / max;
993 for (val = 0; val < 20; ++val)
994 if (val <= perc / 5)
995 kDebug() << '#';
996 else
997 kDebug() << ' ';
998 DBG << perc << "%" << endl;
1002 * write function
1005 ssize_t AlsaPlayerThread::pcm_write(char *data, size_t count)
1007 ssize_t r;
1008 ssize_t result = 0;
1010 if (sleep_min == 0 && count < chunk_size) {
1011 DBG << "calling snd_pcm_format_set_silence" << endl;
1012 snd_pcm_format_set_silence(hwdata.format, data + count * bits_per_frame / 8, (chunk_size - count) * hwdata.channels);
1013 count = chunk_size;
1015 while (count > 0) {
1016 DBG << "calling writei_func, count = " << count << endl;
1017 r = writei_func(handle, data, count);
1018 DBG << "writei_func returned " << r << endl;
1019 if (-EAGAIN == r || (r >= 0 && (size_t)r < count)) {
1020 DBG << "r = " << r << " calling snd_pcm_wait" << endl;
1021 snd_pcm_wait(handle, 100);
1022 } else if (-EPIPE == r) {
1023 xrun();
1024 } else if (-ESTRPIPE == r) {
1025 suspend();
1026 } else if (-EBUSY == r){
1027 kDebug() << "WARNING: sleeping while PCM BUSY";
1028 usleep(1000);
1029 continue;
1030 } else if (r < 0) {
1031 kError() << "write error: " << snd_strerror(r);
1032 stopAndExit();
1034 if (r > 0) {
1035 if (m_debugLevel >= 2)
1036 compute_max_peak(data, r * hwdata.channels);
1037 result += r;
1038 count -= r;
1039 data += r * bits_per_frame / 8;
1041 /* Report current state */
1042 DBG << "PCM state before polling: " << snd_pcm_state_name(snd_pcm_state(handle)) << endl;
1044 int err = wait_for_poll(0);
1045 if (err < 0) {
1046 kError() << "Wait for poll() failed" << endl;
1047 return -1;
1049 else if (err == 1){
1050 kDebug () << "Playback stopped";
1051 /* Drop the playback on the sound device (probably
1052 still in progress up till now) */
1053 err = snd_pcm_drop(handle);
1054 if (err < 0) {
1055 kError() << "snd_pcm_drop() failed: " << snd_strerror(err) << endl;
1056 return -1;
1058 return -1;
1061 return result;
1065 * ok, let's play a .voc file
1068 ssize_t AlsaPlayerThread::voc_pcm_write(u_char *data, size_t count)
1070 ssize_t result = count, r;
1071 size_t size;
1073 while (count > 0) {
1074 size = count;
1075 if (size > chunk_bytes - buffer_pos)
1076 size = chunk_bytes - buffer_pos;
1077 memcpy(audiobuf + buffer_pos, data, size);
1078 data += size;
1079 count -= size;
1080 buffer_pos += size;
1081 if ((size_t)buffer_pos == chunk_bytes) {
1082 if ((size_t)(r = pcm_write(audiobuf, chunk_size)) != chunk_size)
1083 return r;
1084 buffer_pos = 0;
1087 return result;
1090 void AlsaPlayerThread::voc_write_silence(unsigned x)
1092 unsigned l;
1093 char *buf;
1095 QByteArray buffer(chunk_bytes, '\0');
1096 // buf = (char *) malloc(chunk_bytes);
1097 buf = buffer.data();
1098 if (buf == NULL) {
1099 kError() << "can't allocate buffer for silence";
1100 return; /* not fatal error */
1102 snd_pcm_format_set_silence(hwdata.format, buf, chunk_size * hwdata.channels);
1103 while (x > 0) {
1104 l = x;
1105 if (l > chunk_size)
1106 l = chunk_size;
1107 if (voc_pcm_write((u_char*)buf, l) != (ssize_t)l) {
1108 kError() << "write error" << endl;
1109 stopAndExit();
1111 x -= l;
1113 // free(buf);
1116 void AlsaPlayerThread::voc_pcm_flush(void)
1118 if (buffer_pos > 0) {
1119 size_t b;
1120 if (sleep_min == 0) {
1121 if (snd_pcm_format_set_silence(hwdata.format, audiobuf + buffer_pos, chunk_bytes - buffer_pos * 8 / bits_per_sample) < 0)
1122 kDebug() << "voc_pcm_flush - silence error";
1123 b = chunk_size;
1124 } else {
1125 b = buffer_pos * 8 / bits_per_frame;
1127 if (pcm_write(audiobuf, b) != (ssize_t)b)
1128 kError() << "voc_pcm_flush error" << endl;
1130 snd_pcm_drain(handle);
1133 void AlsaPlayerThread::voc_play(int fd, int ofs, const char* name)
1135 int l;
1136 VocBlockType *bp;
1137 VocVoiceData *vd;
1138 VocExtBlock *eb;
1139 size_t nextblock, in_buffer;
1140 u_char *data, *buf;
1141 char was_extended = 0, output = 0;
1142 u_short *sp, repeat = 0;
1143 size_t silence;
1144 off64_t filepos = 0;
1146 #define COUNT(x) nextblock -= x; in_buffer -= x; data += x
1147 #define COUNT1(x) in_buffer -= x; data += x
1149 QByteArray buffer(64 * 1024, '\0');
1150 // data = buf = (u_char *)malloc(64 * 1024);
1151 data = buf = (u_char*)buffer.data();
1152 buffer_pos = 0;
1153 if (data == NULL) {
1154 stopAndExit();
1156 kDebug() << "Playing Creative Labs Channel file '" << name << "'...";
1157 /* first we waste the rest of header, ugly but we don't need seek */
1158 while (ofs > (ssize_t)chunk_bytes) {
1159 if ((size_t)safe_read(fd, buf, chunk_bytes) != chunk_bytes) {
1160 kError() << "read error" << endl;
1161 stopAndExit();
1163 ofs -= chunk_bytes;
1165 if (ofs) {
1166 if (safe_read(fd, buf, ofs) != ofs) {
1167 kError() << "read error" << endl;
1168 stopAndExit();
1171 hwdata.format = DEFAULT_FORMAT;
1172 hwdata.channels = 1;
1173 hwdata.rate = DEFAULT_SPEED;
1174 set_params();
1176 in_buffer = nextblock = 0;
1177 while (1) {
1178 Fill_the_buffer: /* need this for repeat */
1179 if (in_buffer < 32) {
1180 /* move the rest of buffer to pos 0 and fill the buf up */
1181 if (in_buffer)
1182 memcpy(buf, data, in_buffer);
1183 data = buf;
1184 if ((l = safe_read(fd, buf + in_buffer, chunk_bytes - in_buffer)) > 0)
1185 in_buffer += l;
1186 else if (!in_buffer) {
1187 /* the file is truncated, so simulate 'Terminator'
1188 and reduce the datablock for safe landing */
1189 nextblock = buf[0] = 0;
1190 if (l == -1) {
1191 // perror(name);
1192 stopAndExit();
1196 while (!nextblock) { /* this is a new block */
1197 if (in_buffer < sizeof(VocBlockType))
1198 goto __end;
1199 bp = (VocBlockType *) data;
1200 COUNT1(sizeof(VocBlockType));
1201 nextblock = VOC_DATALEN(bp);
1202 output = 0;
1203 switch (bp->type) {
1204 case 0:
1205 #if 0
1206 MSG("Terminator");
1207 #endif
1208 return; /* VOC-file stop */
1209 case 1:
1210 vd = (VocVoiceData *) data;
1211 COUNT1(sizeof(VocVoiceData));
1212 /* we need a SYNC, before we can set new SPEED, STEREO ... */
1214 if (!was_extended) {
1215 hwdata.rate = (int) (vd->tc);
1216 hwdata.rate = 1000000 / (256 - hwdata.rate);
1217 #if 0
1218 MSG("Channel data %d Hz", dsp_speed);
1219 #endif
1220 if (vd->pack) { /* /dev/dsp can't it */
1221 kError() << "can't play packed .voc files" << endl;
1222 return;
1224 if (hwdata.channels == 2) /* if we are in Stereo-Mode, switch back */
1225 hwdata.channels = 1;
1226 } else { /* there was extended block */
1227 hwdata.channels = 2;
1228 was_extended = 0;
1230 set_params();
1231 break;
1232 case 2: /* nothing to do, pure data */
1233 #if 0
1234 MSG("Channel continuation");
1235 #endif
1236 break;
1237 case 3: /* a silence block, no data, only a count */
1238 sp = (u_short *) data;
1239 COUNT1(sizeof(u_short));
1240 hwdata.rate = (int) (*data);
1241 COUNT1(1);
1242 hwdata.rate = 1000000 / (256 - hwdata.rate);
1243 set_params();
1244 silence = (((size_t) * sp) * 1000) / hwdata.rate;
1245 #if 0
1246 MSG("Silence for %d ms", (int) silence);
1247 #endif
1248 voc_write_silence(*sp);
1249 break;
1250 case 4: /* a marker for syncronisation, no effect */
1251 sp = (u_short *) data;
1252 COUNT1(sizeof(u_short));
1253 #if 0
1254 MSG("Marker %d", *sp);
1255 #endif
1256 break;
1257 case 5: /* ASCII text, we copy to stderr */
1258 output = 1;
1259 #if 0
1260 MSG("ASCII - text :");
1261 #endif
1262 break;
1263 case 6: /* repeat marker, says repeatcount */
1264 /* my specs don't say it: maybe this can be recursive, but
1265 I don't think somebody use it */
1266 repeat = *(u_short *) data;
1267 COUNT1(sizeof(u_short));
1268 #if 0
1269 MSG("Repeat loop %d times", repeat);
1270 #endif
1271 if (filepos >= 0) { /* if < 0, one seek fails, why test another */
1272 if ((filepos = lseek64(fd, 0, 1)) < 0) {
1273 kError() << "can't play loops; " << name << "isn't seekable";
1274 repeat = 0;
1275 } else {
1276 filepos -= in_buffer; /* set filepos after repeat */
1278 } else {
1279 repeat = 0;
1281 break;
1282 case 7: /* ok, lets repeat that be rewinding tape */
1283 if (repeat) {
1284 if (repeat != 0xFFFF) {
1285 #if 0
1286 MSG("Repeat loop %d", repeat);
1287 #endif
1288 --repeat;
1290 #if 0
1291 else
1292 MSG("Neverending loop");
1293 #endif
1294 lseek64(fd, filepos, 0);
1295 in_buffer = 0; /* clear the buffer */
1296 goto Fill_the_buffer;
1298 #if 0
1299 else
1300 MSG("End repeat loop");
1301 #endif
1302 break;
1303 case 8: /* the extension to play Stereo, I have SB 1.0 :-( */
1304 was_extended = 1;
1305 eb = (VocExtBlock *) data;
1306 COUNT1(sizeof(VocExtBlock));
1307 hwdata.rate = (int) (eb->tc);
1308 hwdata.rate = 256000000L / (65536 - hwdata.rate);
1309 hwdata.channels = eb->mode == VOC_MODE_STEREO ? 2 : 1;
1310 if (hwdata.channels == 2)
1311 hwdata.rate = hwdata.rate >> 1;
1312 if (eb->pack) { /* /dev/dsp can't it */
1313 kError() << "can't play packed .voc files" << endl;
1314 return;
1316 #if 0
1317 MSG("Extended block %s %d Hz",
1318 (eb->mode ? "Stereo" : "Mono"), dsp_speed);
1319 #endif
1320 break;
1321 default:
1322 kError() << "unknown blocktype " << bp->type << ". terminate." << endl;
1323 return;
1324 } /* switch (bp->type) */
1325 } /* while (! nextblock) */
1326 /* put nextblock data bytes to dsp */
1327 l = in_buffer;
1328 if (nextblock < (size_t)l)
1329 l = nextblock;
1330 if (l) {
1331 if (output) {
1332 if (write(2, data, l) != l) { /* to stderr */
1333 kError() << "write error";
1334 stopAndExit();
1336 } else {
1337 if (voc_pcm_write(data, l) != l) {
1338 kError() << "write error" << endl;
1339 stopAndExit();
1342 COUNT(l);
1344 } /* while(1) */
1345 __end:
1346 voc_pcm_flush();
1347 // free(buf);
1349 /* that was a big one, perhaps somebody split it :-) */
1351 /* setting the globals for playing raw data */
1352 void AlsaPlayerThread::init_raw_data(void)
1354 hwdata = rhwdata;
1357 /* calculate the data count to read from/to dsp */
1358 off64_t AlsaPlayerThread::calc_count(void)
1360 off64_t count;
1362 if (timelimit == 0) {
1363 count = pbrec_count;
1364 } else {
1365 count = snd_pcm_format_size(hwdata.format, hwdata.rate * hwdata.channels);
1366 count *= (off64_t)timelimit;
1368 return count < pbrec_count ? count : pbrec_count;
1371 void AlsaPlayerThread::header(int /*rtype*/, const char* /*name*/)
1373 // fprintf(stderr, "%s %s '%s' : ",
1374 // (stream == SND_PCM_STREAM_PLAYBACK) ? "Playing" : "Recording",
1375 // fmt_rec_table[rtype].what,
1376 // name);
1377 QString channels;
1378 if (hwdata.channels == 1)
1379 channels = "Mono";
1380 else if (hwdata.channels == 2)
1381 channels = "Stereo";
1382 else
1383 channels = QString("Channels %1").arg(hwdata.channels);
1384 QByteArray asciiChannels = channels.toAscii();
1385 DBG << "Format: "
1386 << snd_pcm_format_description(hwdata.format)
1387 << ", Rate "
1388 << hwdata.rate
1389 << ", "
1390 << asciiChannels.constData() << endl;
1393 /* playing raw data */
1395 void AlsaPlayerThread::playback_go(int fd, size_t loaded, off64_t count, int rtype, const char *name)
1397 int l, r;
1398 off64_t written = 0;
1399 off64_t c;
1401 if (m_debugLevel >= 1) header(rtype, name);
1402 set_params();
1404 while (loaded > chunk_bytes && written < count) {
1405 if (pcm_write(audiobuf + written, chunk_size) <= 0)
1406 return;
1407 written += chunk_bytes;
1408 loaded -= chunk_bytes;
1410 if (written > 0 && loaded > 0)
1411 memmove(audiobuf, audiobuf + written, loaded);
1413 l = loaded;
1414 while (written < count) {
1415 do {
1416 c = count - written;
1417 if (c > chunk_bytes)
1418 c = chunk_bytes;
1419 c -= l;
1421 if (c == 0)
1422 break;
1423 r = safe_read(fd, audiobuf + l, c);
1424 if (r < 0) {
1425 // perror(name);
1426 stopAndExit();
1428 fdcount += r;
1429 if (r == 0)
1430 break;
1431 l += r;
1432 } while (sleep_min == 0 && (size_t)l < chunk_bytes);
1433 l = l * 8 / bits_per_frame;
1434 DBG << "calling pcm_write with " << l << " frames." << endl;
1435 r = pcm_write(audiobuf, l);
1436 DBG << "pcm_write returned r = " << r << endl;
1437 if (r < 0) return;
1438 if (r != l)
1439 break;
1440 r = r * bits_per_frame / 8;
1441 written += r;
1442 l = 0;
1445 DBG << "Draining..." << endl;
1447 /* We want the next "device ready" notification only when the buffer is completely empty. */
1448 /* Do this by setting the avail_min to the buffer size. */
1449 int err;
1450 DBG << "Getting swparams" << endl;
1451 snd_pcm_sw_params_t *swparams;
1452 snd_pcm_sw_params_alloca(&swparams);
1453 err = snd_pcm_sw_params_current(handle, swparams);
1454 if (err < 0) {
1455 kError() << "Unable to get current swparams: " << snd_strerror(err) << endl;
1456 return;
1458 DBG << "Setting avail min to " << buffer_size << endl;
1459 err = snd_pcm_sw_params_set_avail_min(handle, swparams, buffer_size);
1460 if (err < 0) {
1461 kError() << "Unable to set avail min for playback: " << snd_strerror(err) << endl;
1462 return;
1464 /* write the parameters to the playback device */
1465 DBG << "Writing swparams" << endl;
1466 err = snd_pcm_sw_params(handle, swparams);
1467 if (err < 0) {
1468 kError() << "Unable to set sw params for playback: " << snd_strerror(err) << endl;
1469 return;
1472 DBG << "Waiting for poll" << endl;
1473 err = wait_for_poll(1);
1474 if (err < 0) {
1475 kError() << "Wait for poll() failed" << endl;
1476 return;
1477 } else if (err == 1){
1478 kError() << "Playback stopped while draining" << endl;
1480 /* Drop the playback on the sound device (probably
1481 still in progress up till now) */
1482 err = snd_pcm_drop(handle);
1483 if (err < 0) {
1484 kError() << "snd_pcm_drop() failed: " << snd_strerror(err) << endl;
1485 return;
1488 DBG << "Draining completed" << endl;
1492 * let's play or capture it (capture_type says VOC/WAVE/raw)
1495 void AlsaPlayerThread::playback(int fd)
1497 int ofs;
1498 size_t dta;
1499 ssize_t dtawave;
1501 pbrec_count = LLONG_MAX;
1502 fdcount = 0;
1504 /* read the file header */
1505 dta = sizeof(AuHeader);
1506 if ((size_t)safe_read(fd, audiobuf, dta) != dta) {
1507 kError() << "read error" << endl;
1508 stopAndExit();
1510 if (test_au(fd, audiobuf) >= 0) {
1511 rhwdata.format = hwdata.format;
1512 pbrec_count = calc_count();
1513 playback_go(fd, 0, pbrec_count, FORMAT_AU, audiofile_name);
1514 goto __end;
1516 dta = sizeof(VocHeader);
1517 if ((size_t)safe_read(fd, audiobuf + sizeof(AuHeader),
1518 dta - sizeof(AuHeader)) != dta - sizeof(AuHeader)) {
1519 kError() << "read error" << endl;
1520 stopAndExit();
1522 if ((ofs = test_vocfile(audiobuf)) >= 0) {
1523 pbrec_count = calc_count();
1524 voc_play(fd, ofs, audiofile_name);
1525 goto __end;
1527 /* read bytes for WAVE-header */
1528 if ((dtawave = test_wavefile(fd, audiobuf, dta)) >= 0) {
1529 pbrec_count = calc_count();
1530 playback_go(fd, dtawave, pbrec_count, FORMAT_WAVE, audiofile_name);
1531 } else {
1532 /* should be raw data */
1533 init_raw_data();
1534 pbrec_count = calc_count();
1535 playback_go(fd, dta, pbrec_count, FORMAT_RAW, audiofile_name);
1537 __end:
1538 return;
1541 /* Wait until ALSA is ready for more samples or stop() was called.
1542 @return 0 if ALSA is ready for more input, +1 if a request to stop
1543 the sound output was received and a negative value on error. */
1544 int AlsaPlayerThread::wait_for_poll(int draining)
1546 unsigned short revents;
1547 snd_pcm_state_t state;
1548 int ret;
1550 DBG << "Waiting for poll" << endl;
1552 /* Wait for certain events */
1553 while (1) {
1554 /* Simulated pause by not writing to alsa device, which will lead to an XRUN
1555 when resumed. */
1556 if (m_simulatedPause)
1557 msleep(500);
1558 else {
1560 ret = poll(alsa_poll_fds, alsa_fd_count, -1);
1561 DBG << "activity on " << ret << " descriptors" << endl;
1563 /* Check for stop request from alsa_stop on the last file descriptors. */
1564 if ((revents = alsa_poll_fds[alsa_fd_count-1].revents)) {
1565 if (revents & POLLIN){
1566 DBG << "stop requested" << endl;
1567 return 1;
1571 /* Check the first count-1 descriptors for ALSA events */
1572 snd_pcm_poll_descriptors_revents(handle, alsa_poll_fds, alsa_fd_count-1, &revents);
1574 /* Ensure we are in the right state */
1575 state = snd_pcm_state(handle);
1576 DBG << "State after poll returned is " << snd_pcm_state_name(state);
1578 if (SND_PCM_STATE_XRUN == state){
1579 if (!draining){
1580 kDebug() << "WARNING: Buffer underrun detected!";
1581 xrun();
1582 return 0;
1583 }else{
1584 DBG << "Playback terminated" << endl;
1585 return 0;
1589 if (SND_PCM_STATE_SUSPENDED == state){
1590 DBG << "WARNING: Suspend detected!" << endl;
1591 suspend();
1592 return 0;
1595 /* Check for errors */
1596 if (revents & POLLERR) {
1597 DBG << "poll revents says POLLERR" << endl;
1598 return -EIO;
1601 /* Is ALSA ready for more input? */
1602 if ((revents & POLLOUT)){
1603 DBG << "Ready for more input" << endl;
1604 return 0;
1610 // ====================================================================
1611 // AlsaPlayer
1613 // AlsaPlayer is nothing more than a container for AlsaPlayerThread
1614 // in order to avoid ambiguous QObject, since both Player and QThread
1615 // derive from QObject. Is there a better solution?
1617 AlsaPlayer::AlsaPlayer(QObject* parent, const QStringList& args):
1618 Player(parent, "alsaplayer", args)
1620 m_AlsaPlayerThread = new AlsaPlayerThread(this);
1623 AlsaPlayer::~AlsaPlayer()
1625 delete m_AlsaPlayerThread;
1628 /*virtual*/ void AlsaPlayer::startPlay(const QString& file) { m_AlsaPlayerThread->startPlay(file); }
1629 /*virtual*/ void AlsaPlayer::pause() { m_AlsaPlayerThread->pause(); }
1630 /*virtual*/ void AlsaPlayer::stop() { m_AlsaPlayerThread->stop(); }
1632 /*virtual*/ void AlsaPlayer::setVolume(float volume) { m_AlsaPlayerThread->setVolume(volume); }
1633 /*virtual*/ float AlsaPlayer::volume() const { return m_AlsaPlayerThread->volume(); }
1635 /*virtual*/ bool AlsaPlayer::playing() const { return m_AlsaPlayerThread->playing(); }
1636 /*virtual*/ bool AlsaPlayer::paused() const { return m_AlsaPlayerThread->paused(); }
1638 /*virtual*/ int AlsaPlayer::totalTime() const { return m_AlsaPlayerThread->totalTime(); }
1639 /*virtual*/ int AlsaPlayer::currentTime() const { return m_AlsaPlayerThread->currentTime(); }
1640 /*virtual*/ int AlsaPlayer::position() const { return m_AlsaPlayerThread->position(); } // in this case not really the percent
1642 /*virtual*/ void AlsaPlayer::seek(int seekTime) { m_AlsaPlayerThread->seek(seekTime); }
1643 /*virtual*/ void AlsaPlayer::seekPosition(int position) { m_AlsaPlayerThread->seekPosition(position); }
1645 /*virtual*/ QStringList AlsaPlayer::getPluginList( const QByteArray& classname )
1646 { return m_AlsaPlayerThread->getPluginList(classname); }
1647 /*virtual*/ void AlsaPlayer::setSinkName(const QString &sinkName)
1648 { m_AlsaPlayerThread->setSinkName(sinkName); }
1650 #include "alsaplayer.moc"
1652 #undef DBG
1654 // vim: sw=4 ts=8 et