the paraslash-0.2.11 release tarball
[paraslash.git] / play.c
diff --git a/play.c b/play.c
index 99c2316..689747e 100644 (file)
--- a/play.c
+++ b/play.c
@@ -22,7 +22,6 @@
  * 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"
@@ -57,7 +56,7 @@ enum {        E_BROKEN_CONF,          /* Broken configuration for this PCM */
 
 #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;
@@ -109,13 +108,13 @@ static void set_alsa_params(void)
                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);
@@ -123,13 +122,14 @@ static void set_alsa_params(void)
                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;
@@ -151,25 +151,19 @@ static void set_alsa_params(void)
 
 /*
  * 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)
@@ -187,7 +181,7 @@ static snd_pcm_sframes_t pcm_write(u_char *data, size_t count)
 
 /*
  * 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
@@ -213,79 +207,65 @@ static int start_time_in_future(struct timeval *diff)
  * 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);
 }
 
@@ -325,7 +305,7 @@ int main(int argc, char *argv[])
        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);
@@ -336,7 +316,7 @@ int main(int argc, char *argv[])
        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;
 }