1 /***************************************************** vim:set ts=4 sw=4 sts=4:
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
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>
29 #include <config-kttsd.h>
30 #if TIME_WITH_SYS_TIME
31 # include <sys/time.h>
35 # include <sys/time.h>
43 #include <QApplication>
44 #include <QMutexLocker>
49 #include <kstandarddirs.h>
50 #include <kmessagebox.h>
53 // AlsaPlayer includes.
54 #include "alsaplayer.h"
56 #if !defined(__GNUC__) || __GNUC__ >= 3
57 #define ERR(...) do {\
59 QString s = dbgStr.sprintf( "%s:%d: ERROR ", __FUNCTION__, __LINE__); \
60 s += dbgStr.sprintf( __VA_ARGS__); \
61 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
64 #define ERR(args...) do {\
66 QString s = dbgStr.sprintf( "%s:%d: ERROR ", __FUNCTION__, __LINE__); \
67 s += dbgStr.sprintf( ##args ); \
68 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
71 #if !defined(__GNUC__) || __GNUC__ >= 3
72 #define MSG(...) do {\
73 if (m_debugLevel >= 1) {\
75 QString s = dbgStr.sprintf( "%s:%d: ", __FUNCTION__, __LINE__); \
76 s += dbgStr.sprintf( __VA_ARGS__); \
77 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
81 #define MSG(args...) do {\
82 if (m_debugLevel >= 1) {\
84 QString s = dbgStr.sprintf( "%s:%d: ", __FUNCTION__, __LINE__); \
85 s += dbgStr.sprintf( ##args ); \
86 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
91 #if !defined(__GNUC__) || __GNUC__ >= 3
92 #define DBG(...) do {\
93 if (m_debugLevel >= 2) {\
95 QString s = dbgStr.sprintf( "%s:%d: ", __FUNCTION__, __LINE__); \
96 s += dbgStr.sprintf( __VA_ARGS__); \
97 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
101 #define DBG(args...) do {\
102 if (m_debugLevel >= 2) {\
104 QString s = dbgStr.sprintf( "%s:%d: ", __FUNCTION__, __LINE__); \
105 s += dbgStr.sprintf( ##args ); \
106 kDebug() << timestamp() << "AlsaPlayer::" << s << endl; \
111 QString
AlsaPlayerThread::timestamp() const
117 tstr
= strdup(ctime(&t
));
118 tstr
[strlen(tstr
)-1] = 0;
119 gettimeofday(&tv
,NULL
);
121 ts
.sprintf(" %s [%d] ",tstr
, (int) tv
.tv_usec
);
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 ////////////////////////////////////////////////////////////////////////////////
134 ////////////////////////////////////////////////////////////////////////////////
136 AlsaPlayerThread::AlsaPlayerThread(QObject
* parent
) :
138 m_currentVolume(1.0),
139 m_pcmName("default"),
140 m_defPeriodSize(128),
143 m_simulatedPause(false)
148 AlsaPlayerThread::~AlsaPlayerThread()
156 //void AlsaPlayerThread::play(const FileHandle &file)
157 void AlsaPlayerThread::startPlay(const QString
&file
)
162 snd_pcm_pause(handle
, false);
164 m_simulatedPause
= false;
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.
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
);
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);
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
);
198 ERR("audio open error on pcm device %s: %s", pcm_name
, snd_strerror(err
));
202 if ((err
= snd_pcm_info(handle
, info
)) < 0) {
203 ERR("info error: %s", snd_strerror(err
));
210 audioBuffer
.resize(1024);
211 // audiobuf = (char *)malloc(1024);
212 audiobuf
= audioBuffer
.data();
213 if (audiobuf
== NULL
) {
214 ERR("not enough memory");
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
;
224 writei_func
= snd_pcm_writei
;
225 readi_func
= snd_pcm_readi
;
226 writen_func
= snd_pcm_writen
;
227 readn_func
= snd_pcm_readn
;
236 void AlsaPlayerThread::pause()
239 DBG("Pause requested");
240 QMutexLocker
locker(&m_mutex
);
242 // Some hardware can pause; some can't. canPause is set in set_params.
244 m_simulatedPause
= false;
245 snd_pcm_pause(handle
, true);
247 // Set a flag and cause wait_for_poll to sleep. When resumed, will get
249 m_simulatedPause
= true;
255 void AlsaPlayerThread::stop()
258 DBG("STOP! Locking mutex");
259 QMutexLocker
locker(&m_mutex
);
260 m_simulatedPause
= false;
262 /* This constant is arbitrary */
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");
270 /* Wait for thread to exit */
271 DBG("waiting for thread to exit");
274 // TODO: This seems like a bug. Why must I relock the locker
275 // since I'm about to destroy the locker?
282 * Stop playback, cleanup and exit thread.
284 void AlsaPlayerThread::stopAndExit()
286 // if (handle) snd_pcm_drop(handle);
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
309 QMutexLocker
locker(&m_mutex
);
312 snd_pcm_status_t
*status
;
313 snd_pcm_status_alloca(&status
);
315 if ((res
= snd_pcm_status(handle
, status
)) < 0)
316 ERR("status error: %s", snd_strerror(res
));
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
)));
323 result
= !m_simulatedPause
;
329 bool AlsaPlayerThread::paused() const
333 QMutexLocker
locker(&m_mutex
);
336 snd_pcm_status_t
*status
;
337 snd_pcm_status_alloca(&status
);
339 if ((res
= snd_pcm_status(handle
, status
)) < 0)
340 ERR("status error: %s", snd_strerror(res
));
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
)));
346 result
= m_simulatedPause
;
352 int AlsaPlayerThread::totalTime() const
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);
365 int AlsaPlayerThread::currentTime() const
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);
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*/)
394 void AlsaPlayerThread::seekPosition(int /*position*/)
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
)
410 int card
= -1, device
= -1;
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
);
418 result
.append("default");
420 err
= snd_card_next(&card
);
421 if (err
< 0 || card
< 0) break;
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
);
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
);
441 infoName
+= snd_pcm_info_get_name(pcminfo
);
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
);
456 void AlsaPlayerThread::setSinkName(const QString
& sinkName
) { m_pcmName
= sinkName
; }
458 /////////////////////////////////////////////////////////////////////////////////
460 /////////////////////////////////////////////////////////////////////////////////
462 void AlsaPlayerThread::init()
469 file_type
= FORMAT_DEFAULT
;
472 open_mode
= SND_PCM_NONBLOCK
;
473 stream
= SND_PCM_STREAM_PLAYBACK
;
486 pbrec_count
= LLONG_MAX
;
487 alsa_stop_pipe
[0] = 0;
488 alsa_stop_pipe
[1] = 0;
490 m_simulatedPause
= false;
493 void AlsaPlayerThread::cleanup()
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();
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();
514 * Safe read (for pipes)
517 ssize_t
AlsaPlayerThread::safe_read(int fd
, void *buf
, size_t count
)
523 if ((res
= read(fd
, buf
, count
)) == 0)
526 return result
> 0 ? result
: res
;
529 buf
= (char *)buf
+ res
;
535 * Test, if it is a .VOC file and return >=0 if ok (this is the length of rest)
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
)
560 if ((size_t)safe_read(fd
, buffer
+ *size
, reqsize
- *size
) != reqsize
- *size
) {
561 ERR("read error (called from line %i)", line
);
564 return *size
= reqsize
;
567 #define check_wavefile_space(buffer, len, blimit) \
568 if (len > blimit) { \
570 if ((buffer = (char*)realloc(buffer, blimit)) == NULL) { \
571 ERR("not enough memory"); \
577 * test, if it's a .WAV file, > 0 if ok (and set the speed, stereo etc.)
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
;
591 if (size
< sizeof(WaveHeader
))
593 if (h
->magic
!= WAV_RIFF
|| h
->type
!= WAV_WAVE
)
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
);
601 check_wavefile_space(buffer
, sizeof(WaveChunkHeader
), blimit
);
602 test_wavefile_read(fd
, buffer
, &size
, sizeof(WaveChunkHeader
), __LINE__
);
603 c
= (WaveChunkHeader
*)buffer
;
605 len
= LE_INT(c
->length
);
607 if (size
> sizeof(WaveChunkHeader
))
608 memmove(buffer
, buffer
+ sizeof(WaveChunkHeader
), size
- sizeof(WaveChunkHeader
));
609 size
-= sizeof(WaveChunkHeader
);
612 check_wavefile_space(buffer
, len
, blimit
);
613 test_wavefile_read(fd
, buffer
, &size
, len
, __LINE__
);
615 memmove(buffer
, buffer
+ len
, 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
));
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");
630 if (LE_SHORT(f
->modus
) < 1) {
631 ERR("can't play WAVE-files with %d tracks", LE_SHORT(f
->modus
));
634 hwdata
.channels
= LE_SHORT(f
->modus
);
635 switch (LE_SHORT(f
->bit_p_spl
)) {
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
;
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
;
649 switch (LE_SHORT(f
->byte_p_spl
) / hwdata
.channels
) {
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
;
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
;
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
);
668 hwdata
.format
= SND_PCM_FORMAT_S32_LE
;
671 ERR("can't play WAVE-files with sample %d bits wide", LE_SHORT(f
->bit_p_spl
));
674 hwdata
.rate
= LE_INT(f
->sample_fq
);
677 memmove(buffer
, buffer
+ len
, size
- len
);
683 check_wavefile_space(buffer
, sizeof(WaveChunkHeader
), blimit
);
684 test_wavefile_read(fd
, buffer
, &size
, sizeof(WaveChunkHeader
), __LINE__
);
685 c
= (WaveChunkHeader
*)buffer
;
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)
695 memcpy(_buffer
, buffer
, size
);
700 check_wavefile_space(buffer
, len
, blimit
);
701 test_wavefile_read(fd
, buffer
, &size
, len
, __LINE__
);
703 memmove(buffer
, buffer
+ len
, size
- len
);
707 /* shouldn't be reached */
715 int AlsaPlayerThread::test_au(int fd
, char *buffer
)
717 AuHeader
*ap
= (AuHeader
*)buffer
;
719 if (ap
->magic
!= AU_MAGIC
)
721 if (BE_INT(ap
->hdr_size
) > 128 || BE_INT(ap
->hdr_size
) < 24)
723 pbrec_count
= BE_INT(ap
->data_size
);
724 switch (BE_INT(ap
->encoding
)) {
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
;
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
;
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
;
746 hwdata
.rate
= BE_INT(ap
->sample_rate
);
747 if (hwdata
.rate
< 2000 || hwdata
.rate
> 256000)
749 hwdata
.channels
= BE_INT(ap
->channels
);
750 if (hwdata
.channels
< 1 || hwdata
.channels
> 128)
752 if ((size_t)safe_read(fd
, buffer
+ sizeof(AuHeader
), BE_INT(ap
->hdr_size
) - sizeof(AuHeader
)) != BE_INT(ap
->hdr_size
) - sizeof(AuHeader
)) {
759 void AlsaPlayerThread::set_params(void)
761 snd_pcm_hw_params_t
*hwparams
;
762 snd_pcm_uframes_t period_size
;
766 unsigned int periods
;
768 snd_pcm_hw_params_alloca(&hwparams
);
769 err
= snd_pcm_hw_params_any(handle
, hwparams
);
771 ERR("Broken configuration for this PCM: no configurations available");
775 /* Create the pipe for communication about stop requests. */
776 if (pipe(alsa_stop_pipe
)) {
777 ERR("Stop pipe creation failed (%s)", strerror(errno
));
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.");
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
));
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
;
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
);
819 err
= snd_pcm_hw_params_set_access(handle
, hwparams
,
820 SND_PCM_ACCESS_RW_NONINTERLEAVED
);
822 ERR("Error setting access type: %s", snd_strerror(err
));
825 err
= snd_pcm_hw_params_set_format(handle
, hwparams
, hwdata
.format
);
827 ERR("Error setting sample format to %i: %s", hwdata
.format
, snd_strerror(err
));
830 err
= snd_pcm_hw_params_set_channels(handle
, hwparams
, hwdata
.channels
);
832 ERR("Error setting channel count to %i: %s", hwdata
.channels
, snd_strerror(err
));
837 err
= snd_pcm_hw_params_set_periods_min(handle
, hwparams
, 2);
841 err
= snd_pcm_hw_params_set_rate_near(handle
, hwparams
, &hwdata
.rate
, 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
;
850 err
= snd_pcm_hw_params_set_period_size_near(handle
, hwparams
, &period_size
, &dir
);
852 MSG("Setting period_size to %lu failed, but continuing: %s", period_size
, snd_strerror(err
));
855 periods
= m_defPeriods
;
857 err
= snd_pcm_hw_params_set_periods_near(handle
, hwparams
, &periods
, &dir
);
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
);
864 MSG("Unable to install hw params: %s", snd_strerror(err
));
865 snd_pcm_hw_params_dump(hwparams
, log
);
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.");
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");
902 #define timersub(a, b, result) \
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; \
913 /* I/O error handler */
914 void AlsaPlayerThread::xrun()
916 snd_pcm_status_t
*status
;
919 snd_pcm_status_alloca(&status
);
920 if ((res
= snd_pcm_status(handle
, status
))<0) {
921 ERR("status error: %s", snd_strerror(res
));
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) {
934 snd_pcm_status_dump(status
, log
);
936 if ((res
= snd_pcm_prepare(handle
))<0) {
937 ERR("xrun: prepare error: %s", snd_strerror(res
));
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
));
955 if (m_debugLevel
>= 2) {
957 snd_pcm_status_dump(status
, log
);
959 ERR("read/write error, state = %s", snd_pcm_state_name(snd_pcm_status_get_state(status
)));
963 /* I/O suspend handler */
964 void AlsaPlayerThread::suspend(void)
968 MSG("Suspended. Trying resume. ");
969 while ((res
= snd_pcm_resume(handle
)) == -EAGAIN
)
970 sleep(1); /* wait until suspend flag is released */
972 MSG("Failed. Restarting stream. ");
973 if ((res
= snd_pcm_prepare(handle
)) < 0) {
974 ERR("suspend: prepare error: %s", snd_strerror(res
));
978 MSG("Suspend done.");
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
) {
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
;
1000 signed short *valp
= (signed short *)data
;
1001 signed short mask
= snd_pcm_format_silence_16(hwdata
.format
);
1003 while (count
-- > 0) {
1004 val
= *valp
++ ^ mask
;
1012 signed int *valp
= (signed int *)data
;
1013 signed int mask
= snd_pcm_format_silence_32(hwdata
.format
);
1015 while (count
-- > 0) {
1016 val
= *valp
++ ^ mask
;
1026 max
= 1 << (bits_per_sample
-1);
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);
1033 perc
= max_peak
* 100 / max
;
1034 for (val
= 0; val
< 20; val
++)
1035 if (val
<= perc
/ 5)
1046 ssize_t
AlsaPlayerThread::pcm_write(char *data
, size_t count
)
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
);
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
) {
1065 } else if (-ESTRPIPE
== r
) {
1067 } else if (-EBUSY
== r
){
1068 MSG("WARNING: sleeping while PCM BUSY");
1072 ERR("write error: %s", snd_strerror(r
));
1076 if (m_debugLevel
>= 2 > 1)
1077 compute_max_peak(data
, r
* hwdata
.channels
);
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);
1088 ERR("Wait for poll() failed");
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
);
1097 ERR("snd_pcm_drop() failed: %s", snd_strerror(err
));
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
;
1117 if (size
> chunk_bytes
- buffer_pos
)
1118 size
= chunk_bytes
- buffer_pos
;
1119 memcpy(audiobuf
+ buffer_pos
, data
, size
);
1123 if ((size_t)buffer_pos
== chunk_bytes
) {
1124 if ((size_t)(r
= pcm_write(audiobuf
, chunk_size
)) != chunk_size
)
1132 void AlsaPlayerThread::voc_write_silence(unsigned x
)
1137 QByteArray
buffer(chunk_bytes
, '\0');
1138 // buf = (char *) malloc(chunk_bytes);
1139 buf
= buffer
.data();
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
);
1149 if (voc_pcm_write((u_char
*)buf
, l
) != (ssize_t
)l
) {
1158 void AlsaPlayerThread::voc_pcm_flush(void)
1160 if (buffer_pos
> 0) {
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");
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
)
1181 size_t nextblock
, in_buffer
;
1183 char was_extended
= 0, output
= 0;
1184 u_short
*sp
, repeat
= 0;
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();
1196 ERR("malloc error");
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
) {
1209 if (safe_read(fd
, buf
, ofs
) != ofs
) {
1214 hwdata
.format
= DEFAULT_FORMAT
;
1215 hwdata
.channels
= 1;
1216 hwdata
.rate
= DEFAULT_SPEED
;
1219 in_buffer
= nextblock
= 0;
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 */
1225 memcpy(buf
, data
, in_buffer
);
1227 if ((l
= safe_read(fd
, buf
+ in_buffer
, chunk_bytes
- in_buffer
)) > 0)
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;
1239 while (!nextblock
) { /* this is a new block */
1240 if (in_buffer
< sizeof(VocBlockType
))
1242 bp
= (VocBlockType
*) data
;
1243 COUNT1(sizeof(VocBlockType
));
1244 nextblock
= VOC_DATALEN(bp
);
1246 MSG(" "); /* write /n after ASCII-out */
1253 return; /* VOC-file stop */
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
);
1263 MSG("Channel data %d Hz", dsp_speed
);
1265 if (vd
->pack
) { /* /dev/dsp can't it */
1266 ERR("can't play packed .voc files");
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;
1277 case 2: /* nothing to do, pure data */
1279 MSG("Channel continuation");
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
);
1287 hwdata
.rate
= 1000000 / (256 - hwdata
.rate
);
1289 silence
= (((size_t) * sp
) * 1000) / hwdata
.rate
;
1291 MSG("Silence for %d ms", (int) silence
);
1293 voc_write_silence(*sp
);
1295 case 4: /* a marker for syncronisation, no effect */
1296 sp
= (u_short
*) data
;
1297 COUNT1(sizeof(u_short
));
1299 MSG("Marker %d", *sp
);
1302 case 5: /* ASCII text, we copy to stderr */
1305 MSG("ASCII - text :");
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
));
1314 MSG("Repeat loop %d times", repeat
);
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
);
1321 filepos
-= in_buffer
; /* set filepos after repeat */
1327 case 7: /* ok, lets repeat that be rewinding tape */
1329 if (repeat
!= 0xFFFF) {
1331 MSG("Repeat loop %d", repeat
);
1337 MSG("Neverending loop");
1339 lseek64(fd
, filepos
, 0);
1340 in_buffer
= 0; /* clear the buffer */
1341 goto Fill_the_buffer
;
1345 MSG("End repeat loop");
1348 case 8: /* the extension to play Stereo, I have SB 1.0 :-( */
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");
1362 MSG("Extended block %s %d Hz",
1363 (eb
->mode
? "Stereo" : "Mono"), dsp_speed
);
1367 ERR("unknown blocktype %d. terminate.", bp
->type
);
1369 } /* switch (bp->type) */
1370 } /* while (! nextblock) */
1371 /* put nextblock data bytes to dsp */
1373 if (nextblock
< (size_t)l
)
1377 if (write(2, data
, l
) != l
) { /* to stderr */
1382 if (voc_pcm_write(data
, l
) != l
) {
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)
1402 /* calculate the data count to read from/to dsp */
1403 off64_t
AlsaPlayerThread::calc_count(void)
1407 if (timelimit
== 0) {
1408 count
= pbrec_count
;
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,
1423 if (hwdata
.channels
== 1)
1425 else if (hwdata
.channels
== 2)
1426 channels
= "Stereo";
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
),
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
)
1441 off64_t written
= 0;
1444 if (m_debugLevel
>= 1) header(rtype
, name
);
1447 while (loaded
> chunk_bytes
&& written
< count
) {
1448 if (pcm_write(audiobuf
+ written
, chunk_size
) <= 0)
1450 written
+= chunk_bytes
;
1451 loaded
-= chunk_bytes
;
1453 if (written
> 0 && loaded
> 0)
1454 memmove(audiobuf
, audiobuf
+ written
, loaded
);
1457 while (written
< count
) {
1459 c
= count
- written
;
1460 if (c
> chunk_bytes
)
1466 r
= safe_read(fd
, audiobuf
+ l
, c
);
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
);
1483 r
= r
* bits_per_frame
/ 8;
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. */
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
);
1498 ERR("Unable to get current swparams: %s", snd_strerror(err
));
1501 DBG("Setting avail min to %lu", buffer_size
);
1502 err
= snd_pcm_sw_params_set_avail_min(handle
, swparams
, buffer_size
);
1504 ERR("Unable to set avail min for playback: %s", snd_strerror(err
));
1507 /* write the parameters to the playback device */
1508 DBG("Writing swparams");
1509 err
= snd_pcm_sw_params(handle
, swparams
);
1511 ERR("Unable to set sw params for playback: %s", snd_strerror(err
));
1515 DBG("Waiting for poll");
1516 err
= wait_for_poll(1);
1518 ERR("Wait for poll() failed");
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
);
1527 ERR("snd_pcm_drop() failed: %s", snd_strerror(err
));
1531 DBG("Draining completed");
1535 * let's play or capture it (capture_type says VOC/WAVE/raw)
1538 void AlsaPlayerThread::playback(int fd
)
1544 pbrec_count
= LLONG_MAX
;
1547 /* read the file header */
1548 dta
= sizeof(AuHeader
);
1549 if ((size_t)safe_read(fd
, audiobuf
, dta
) != dta
) {
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
);
1559 dta
= sizeof(VocHeader
);
1560 if ((size_t)safe_read(fd
, audiobuf
+ sizeof(AuHeader
),
1561 dta
- sizeof(AuHeader
)) != dta
- sizeof(AuHeader
)) {
1565 if ((ofs
= test_vocfile(audiobuf
)) >= 0) {
1566 pbrec_count
= calc_count();
1567 voc_play(fd
, ofs
, audiofile_name
);
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
);
1575 /* should be raw data */
1577 pbrec_count
= calc_count();
1578 playback_go(fd
, dta
, pbrec_count
, FORMAT_RAW
, audiofile_name
);
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
;
1593 DBG("Waiting for poll");
1595 /* Wait for certain events */
1597 /* Simulated pause by not writing to alsa device, which will lead to an XRUN
1599 if (m_simulatedPause
)
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");
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
){
1623 MSG("WARNING: Buffer underrun detected!");
1627 DBG("Playback terminated");
1632 if (SND_PCM_STATE_SUSPENDED
== state
){
1633 DBG("WARNING: Suspend detected!");
1638 /* Check for errors */
1639 if (revents
& POLLERR
) {
1640 DBG("poll revents says POLLERR");
1644 /* Is ALSA ready for more input? */
1645 if ((revents
& POLLOUT
)){
1646 DBG("Ready for more input");
1653 // ====================================================================
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"
1699 // vim: sw=4 ts=8 et