Duuuh, the --device option never worked because "plug:swmix" was harcoded
in play.c instead of using the value from the configuration. Even worse,
the default in play.ggo was _also_ set to "plug:swmix". Replace that by
"plughw:0,0".
This patch also simplifies and optimizes play_pcm().
Anyway, software mixing is still recommended. Here's an aprropriate
/etc/asound.conf:
---------------------------------------------------- /etc/asound.conf
pcm.swmix {
type dmix
# any unique number here
ipc_key 313
ipc_perm 0666
slave {
pcm "hw:0,0"
# these settings may require tweaking for different sound
# cards; this is for the Powerbook's built-in snd-powermac
# probably not required at all for well-behaved cards...
period_time 0
period_size 1024
buffer_size 8192
# mentioning rate fixes wrong speed/pitch in native ALSA stuff
# rate 44100
}
}
pcm.dsp0 {
type plug
slave.pcm "swmix"
}
ctl.mixer0 {
type hw
card 0
}
pcm.!default {
type plug
slave.pcm "swmix"
}
---------------------------------------------------------------------
a Use the
: Use a large input
buffer and For one, the
#define EXIT(EXP) \
do { if (EXP) \
#define EXIT(EXP) \
do { if (EXP) \
- fprintf (stderr, "error: " #EXP "\n"); exit(EXP);} \
+ fprintf (stderr, "error: " #EXP "\n"); exit(EXP);} \
while (0)
static snd_pcm_t *handle;
while (0)
static snd_pcm_t *handle;
/*
* pcm_write - push out pcm frames
/*
* pcm_write - push out pcm frames
- * @data: pointer do data to be written
- * @count: number of frames
+ * \param data pointer do data to be written
+ * \param count number of frames
- * Return value: Number of bytes written. Exit on errors.
+ * \return Number of bytes written. Exit on errors.
*/
static snd_pcm_sframes_t pcm_write(u_char *data, size_t count)
{
snd_pcm_sframes_t r, result = 0;
*/
static snd_pcm_sframes_t pcm_write(u_char *data, size_t count)
{
snd_pcm_sframes_t r, result = 0;
-#if 0
- if (count < chunk_size) {
- snd_pcm_format_set_silence(FORMAT, data
- + count * bytes_per_frame,
- (chunk_size - count) * conf.channels_arg);
- count = chunk_size;
- }
-#endif
while (count > 0) {
/* write interleaved frames */
r = snd_pcm_writei(handle, data, count);
while (count > 0) {
/* write interleaved frames */
r = snd_pcm_writei(handle, data, count);
+ if (r < 0)
+ fprintf(stderr, "write error: %s\n", snd_strerror(r));
if (r == -EAGAIN || (r >= 0 && r < count))
snd_pcm_wait(handle, 1);
else if (r == -EPIPE)
if (r == -EAGAIN || (r >= 0 && r < count))
snd_pcm_wait(handle, 1);
else if (r == -EPIPE)
/*
* start_time_in_future - check if current time is later than start_time
/*
* start_time_in_future - check if current time is later than start_time
- * @diff: pointer to write remaining time to
+ * \param diff pointer to write remaining time to
*
* If start_time was not given, or current time is later than given
* start_time, return 0. Otherwise, return 1 and write the time
*
* If start_time was not given, or current time is later than given
* start_time, return 0. Otherwise, return 1 and write the time
* or if the given start time is in the past.
*
*/
* or if the given start time is in the past.
*
*/
-static void do_initial_delay(void)
+static void do_initial_delay(struct timeval *delay)
- struct timeval diff;
- int ret;
-
- fprintf(stderr, "initial delay\n");
- if (!conf.start_time_given)
- return;
-again:
- if (!start_time_in_future(&diff))
- return;
- ret = select(1, NULL, NULL, NULL, &diff);
- if (ret < 0 && errno == EINTR)
- goto again;
+ fprintf(stderr, "sleeping %lums\n", tv2ms(delay));
+ do
+ select(1, NULL, NULL, NULL, delay);
+ while (start_time_in_future(delay));
/*
* play_pcm - play raw pcm data
/*
* play_pcm - play raw pcm data
- * @l: number of bytes already loaded
+ * \param loaded number of bytes already loaded
*
* If start_time was given, prebuffer data until buffer is full or
* start_time is reached. In any case, do not start playing before
* start_time.
*/
*
* If start_time was given, prebuffer data until buffer is full or
* start_time is reached. In any case, do not start playing before
* start_time.
*/
-static void play_pcm(size_t l)
+static void play_pcm(size_t loaded)
- ssize_t r, w;
- unsigned long written = 0;
- size_t chunk_bytes;
+ size_t chunk_bytes, bufsize, written = 0;
+ ssize_t ret;
+ unsigned char *p;
+ int dont_write;
+ struct timeval delay;
set_alsa_params();
chunk_bytes = chunk_size * bytes_per_frame;
set_alsa_params();
chunk_bytes = chunk_size * bytes_per_frame;
- audiobuf = realloc(audiobuf, BUFFER_SIZE);
-// fprintf(stderr, "loaded: %d, chunk_bytes: %d\n", l, chunk_bytes);
+ bufsize = chunk_bytes * 1024;
+ audiobuf = realloc(audiobuf, bufsize);
if (!audiobuf)
EXIT(E_MEM);
if (!audiobuf)
EXIT(E_MEM);
- for (;;) {
- for (;;) {
- if (l >= chunk_bytes) {
- if (written)
- break;
- if (!start_time)
- break;
- if (!start_time_in_future(NULL))
- break;
- if (l > BUFFER_SIZE) {
- do_initial_delay();
- break;
- }
+again:
+ dont_write = 0;
+ if (!written && start_time)
+ dont_write = start_time_in_future(&delay);
+ if (!dont_write) {
+ p = audiobuf;
+ while (loaded >= chunk_bytes) {
+ ret = (ssize_t) pcm_write(p, chunk_size) * bytes_per_frame;
+ if (ret <= 0) {
+ fprintf(stderr, "write error: %d\n", ret);
+ EXIT(E_WRITE);
-// fprintf(stderr, "l = %d, chunk_Bytes = %d\n", l, chunk_bytes);
- r = read(STDIN_FILENO, audiobuf + l, BUFFER_SIZE -l);
- if (r < 0)
- EXIT(E_READ);
- l += r;
-// fprintf(stderr, "loaded: %d, r= %d\n", l, r);
- if (!r)
- goto out;;
+ p += ret;
+ written += ret;
+ loaded -= ret;
- w = MIN(chunk_bytes, l);
-// fprintf(stderr, "play: writing %d\n", w);
- r = (ssize_t) pcm_write(audiobuf, w / bytes_per_frame) * bytes_per_frame;
-// fprintf(stderr, "wrote %d\n", r);
- if (r < 0)
- EXIT(E_WRITE);
- written += r;
- l -= r;
- if (l)
- memmove(audiobuf, audiobuf + r, l);
-// fprintf(stderr, "written %lu, loaded : %d\n", written, l);
+ if (loaded && p != audiobuf) {
+ fprintf(stderr, "memcpy: %d\n", loaded);
+ memcpy(audiobuf, p, loaded);
+ }
+ }
+ if (dont_write && loaded >= bufsize) {
+ do_initial_delay(&delay);
+ start_time = NULL;
+ goto again;
+ }
+ ret = read(STDIN_FILENO, audiobuf, bufsize - loaded);
+ if (ret < 0)
+ EXIT(E_READ);
+ if (ret) {
+ loaded += ret;
+ goto again;
snd_pcm_info_alloca(&info);
if (snd_output_stdio_attach(&log, stderr, 0) < 0)
EXIT(E_LOG);
snd_pcm_info_alloca(&info);
if (snd_output_stdio_attach(&log, stderr, 0) < 0)
EXIT(E_LOG);
- err = snd_pcm_open(&handle, "plug:swmix",
+ err = snd_pcm_open(&handle, conf.device_arg,
SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0)
EXIT(E_PCM_OPEN);
SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0)
EXIT(E_PCM_OPEN);
play_pcm(check_wave());
snd_pcm_close(handle);
free(audiobuf);
play_pcm(check_wave());
snd_pcm_close(handle);
free(audiobuf);
+// snd_output_close(log);
snd_config_update_free_global();
return EXIT_SUCCESS;
}
snd_config_update_free_global();
return EXIT_SUCCESS;
}
section "general options"
option "start_time" t "start playback at given time which must be in a:b format where a denotes seconds and b denotes microseconds since the epoch" string typestr="timeval" no
section "general options"
option "start_time" t "start playback at given time which must be in a:b format where a denotes seconds and b denotes microseconds since the epoch" string typestr="timeval" no
-option "device" d "set PCM device" string typestr="device" default="plug:swmix" no
+option "device" d "set PCM device" string typestr="device" default="plughw:0,0" no
option "channels" c "number of channels (only neccessary for raw audio)" int typestr="num" default="2" no
option "sample_rate" s "force given sample rate (only neccessary for raw audio)" int typestr="num" default="44100" no
option "channels" c "number of channels (only neccessary for raw audio)" int typestr="num" default="2" no
option "sample_rate" s "force given sample rate (only neccessary for raw audio)" int typestr="num" default="44100" no