* based on the vplay program by Michael Beck.
*/
-#define BUFFER_SIZE 1000 * 1000
#define WAV_HEADER_LEN 44
#include <sys/time.h> /* gettimeofday */
#include "para.h"
#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;
EXIT(E_SAMPLE_FORMAT);
if (snd_pcm_hw_params_set_channels(handle, hwparams, conf.channels_arg) < 0)
EXIT(E_CHANNEL_COUNT);
- if (snd_pcm_hw_params_set_rate_near(handle, hwparams, (unsigned int*) &conf.sample_rate_arg, 0) < 0)
+ if (snd_pcm_hw_params_set_rate_near(handle, hwparams,
+ (unsigned int*) &conf.sample_rate_arg, 0) < 0)
EXIT(E_SET_RATE);
err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0);
if (err < 0 || !buffer_time)
EXIT(E_GET_BUFFER_TIME);
- if (buffer_time > 500000)
- buffer_time = 500000;
+ fprintf(stderr, "buffer time: %d\n", buffer_time);
if (snd_pcm_hw_params_set_buffer_time_near(handle, hwparams,
&buffer_time, 0) < 0)
EXIT(E_SET_BUFFER_TIME);
EXIT(E_HW_PARAMS);
snd_pcm_hw_params_get_period_size(hwparams, &chunk_size, 0);
snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
+ fprintf(stderr, "buffer size: %lu, period_size: %lu\n", buffer_size, chunk_size);
if (chunk_size == buffer_size)
EXIT(E_BAD_PERIOD);
snd_pcm_sw_params_current(handle, swparams);
err = snd_pcm_sw_params_get_xfer_align(swparams, &xfer_align);
if (err < 0 || !xfer_align)
EXIT(E_GET_XFER);
- snd_pcm_sw_params_set_sleep_min(handle, swparams, 0);
+// snd_pcm_sw_params_set_sleep_min(handle, swparams, 0);
snd_pcm_sw_params_set_avail_min(handle, swparams, chunk_size);
/* round to closest transfer boundary */
start_threshold = (buffer_size / xfer_align) * xfer_align;
/*
* 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;
-#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);
+ 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)
/*
* 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
* 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
- * @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.
*/
-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, prebuf_size;
+ ssize_t ret;
+ unsigned char *p;
+ struct timeval delay;
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 = (conf.bufsize_arg * 1024 / chunk_bytes) * chunk_bytes;
+ audiobuf = realloc(audiobuf, bufsize);
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;
- }
- }
-// 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;;
+ prebuf_size = conf.prebuffer_arg * bufsize / 100;
+again:
+ if (!written) {
+ if (loaded < prebuf_size)
+ goto read;
+ if (start_time && start_time_in_future(&delay)) {
+ do_initial_delay(&delay);
+ start_time = NULL;
}
- 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);
}
-out:
+ p = audiobuf;
+ while (loaded >= chunk_bytes) {
+// fprintf(stderr, "write (loaded = %d)\n", loaded);
+ ret = pcm_write(p, chunk_size) * bytes_per_frame;
+ p += ret;
+ written += ret;
+ loaded -= ret;
+ }
+ if (loaded && p != audiobuf) {
+// fprintf(stderr, "memcpy: %d@%d\n", loaded, p - audiobuf);
+ memcpy(audiobuf, p, loaded);
+ }
+read:
+ ret = read(STDIN_FILENO, audiobuf + loaded, bufsize - loaded);
+ if (ret < 0)
+ EXIT(E_READ);
+ if (ret) {
+ loaded += ret;
+ goto again;
+ }
snd_pcm_drain(handle);
}
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);
play_pcm(check_wave());
snd_pcm_close(handle);
free(audiobuf);
- snd_output_close(log);
+// snd_output_close(log);
snd_config_update_free_global();
return EXIT_SUCCESS;
}