]> git.alsa-project.org Git - alsa-utils.git/commitdiff
axfer: add options for buffer arrangement
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Tue, 13 Nov 2018 06:41:37 +0000 (15:41 +0900)
committerTakashi Iwai <tiwai@suse.de>
Tue, 13 Nov 2018 11:04:47 +0000 (12:04 +0100)
In ALSA PCM interface, two parameters are used for size of intermediate
buffer for data frames; period size and buffer size. Actual effects of
these sizes differs depending on hardware, but basically the size of
period is used for intervals of hardware interrupts and the size of buffer
is used to maintain the intermediate buffer as ring buffer. These
parameters can be configured as a part of hardware parameters by
data frame unit or micro second. PCM API in alsa-lib also includes
helper functions to configure them by the two units.

This commit adds support for options to the parameters by both units. When
no options are given, default values are applied according to current
aplay; available maximum size of buffer up to 500msec, a quarter of the
size of buffer for period size. However, these calculation should be
reconsidered somehow.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
axfer/xfer-libasound.c
axfer/xfer-libasound.h
axfer/xfer-options.c

index 61ae11512f15d6d79b371d3a7158ab6cb0541b99..a23021ef767ebc51f99ec0cf34a1f41cf89c35a9 100644 (file)
 
 enum no_short_opts {
         // 200 or later belong to non us-ascii character set.
-       OPT_FATAL_ERRORS = 200,
+       OPT_PERIOD_SIZE = 200,
+       OPT_BUFFER_SIZE,
+       OPT_FATAL_ERRORS,
        OPT_TEST_NOWAIT,
 };
 
-#define S_OPTS "D:NM"
+#define S_OPTS "D:NMF:B:"
 static const struct option l_opts[] = {
        {"device",              1, 0, 'D'},
        {"nonblock",            0, 0, 'N'},
        {"mmap",                0, 0, 'M'},
+       {"period-time",         1, 0, 'F'},
+       {"buffer-time",         1, 0, 'B'},
+       {"period-size",         1, 0, OPT_PERIOD_SIZE},
+       {"buffer-size",         1, 0, OPT_BUFFER_SIZE},
        // For debugging.
        {"fatal-errors",        0, 0, OPT_FATAL_ERRORS},
        {"test-nowait",         0, 0, OPT_TEST_NOWAIT},
@@ -54,6 +60,14 @@ static int xfer_libasound_parse_opt(struct xfer_context *xfer, int key,
                state->nonblock = true;
        else if (key == 'M')
                state->mmap = true;
+       else if (key == 'F')
+               state->msec_per_period = arg_parse_decimal_num(optarg, &err);
+       else if (key == 'B')
+               state->msec_per_buffer = arg_parse_decimal_num(optarg, &err);
+       else if (key == OPT_PERIOD_SIZE)
+               state->frames_per_period = arg_parse_decimal_num(optarg, &err);
+       else if (key == OPT_BUFFER_SIZE)
+               state->frames_per_buffer = arg_parse_decimal_num(optarg, &err);
        else if (key == OPT_FATAL_ERRORS)
                state->finish_at_xrun = true;
        else if (key == OPT_TEST_NOWAIT)
@@ -93,6 +107,20 @@ int xfer_libasound_validate_opts(struct xfer_context *xfer)
                }
        }
 
+       if (state->msec_per_period > 0 && state->msec_per_buffer > 0) {
+               if (state->msec_per_period > state->msec_per_buffer) {
+                       state->msec_per_period = state->msec_per_buffer;
+                       state->msec_per_buffer = 0;
+               }
+       }
+
+       if (state->frames_per_period > 0 && state->frames_per_buffer > 0) {
+               if (state->frames_per_period > state->frames_per_buffer) {
+                       state->frames_per_period = state->frames_per_buffer;
+                       state->frames_per_buffer = 0;
+               }
+       }
+
        return err;
 }
 
@@ -161,7 +189,11 @@ static int open_handle(struct xfer_context *xfer)
 static int configure_hw_params(struct libasound_state *state,
                               snd_pcm_format_t format,
                               unsigned int samples_per_frame,
-                              unsigned int frames_per_second)
+                              unsigned int frames_per_second,
+                              unsigned int msec_per_period,
+                              unsigned int msec_per_buffer,
+                              snd_pcm_uframes_t frames_per_period,
+                              snd_pcm_uframes_t frames_per_buffer)
 {
        int err;
 
@@ -232,6 +264,84 @@ static int configure_hw_params(struct libasound_state *state,
                return err;
        }
 
+       // Keep one of 'frames_per_buffer' and 'msec_per_buffer'.
+       if (frames_per_buffer == 0) {
+               if (msec_per_buffer == 0) {
+                       err = snd_pcm_hw_params_get_buffer_time_max(
+                               state->hw_params, &msec_per_buffer, NULL);
+                       if (err < 0) {
+                               logging(state,
+                                       "The maximum msec per buffer is not "
+                                       "available.\n");
+                               return err;
+                       }
+                       if (msec_per_buffer > 500000)
+                               msec_per_buffer = 500000;
+               }
+       } else if (msec_per_buffer > 0) {
+               uint64_t msec;
+
+               msec = 1000000 * frames_per_buffer / frames_per_second;
+               if (msec < msec_per_buffer)
+                       msec_per_buffer = 0;
+       }
+
+       // Keep one of 'frames_per_period' and 'msec_per_period'.
+       if (frames_per_period == 0) {
+               if (msec_per_period == 0) {
+                       if (msec_per_buffer > 0)
+                               msec_per_period = msec_per_buffer / 4;
+                       else
+                               frames_per_period = frames_per_buffer / 4;
+               }
+       } else if (msec_per_period > 0) {
+               uint64_t msec;
+
+               msec = 1000000 * frames_per_period / frames_per_second;
+               if (msec < msec_per_period)
+                       msec_per_period = 0;
+       }
+
+       if (msec_per_period) {
+               err = snd_pcm_hw_params_set_period_time_near(state->handle,
+                               state->hw_params, &msec_per_period, NULL);
+               if (err < 0) {
+                       logging(state,
+                               "Fail to configure period time: %u msec\n",
+                               msec_per_period);
+                       return err;
+               }
+       } else {
+               err = snd_pcm_hw_params_set_period_size_near(state->handle,
+                               state->hw_params, &frames_per_period, NULL);
+               if (err < 0) {
+                       logging(state,
+                               "Fail to configure period size: %lu frames\n",
+                               frames_per_period);
+                       return err;
+               }
+       }
+
+       if (msec_per_buffer) {
+               err = snd_pcm_hw_params_set_buffer_time_near(state->handle,
+                               state->hw_params, &msec_per_buffer, NULL);
+               if (err < 0) {
+                       logging(state,
+                               "Fail to configure buffer time: %u msec\n",
+                               msec_per_buffer);
+                       return err;
+               }
+       } else {
+               err = snd_pcm_hw_params_set_buffer_size_near(state->handle,
+                                       state->hw_params, &frames_per_buffer);
+               if (err < 0) {
+                       logging(state,
+                               "Fail to configure buffer size: %lu frames\n",
+                               frames_per_buffer);
+                       return err;
+               }
+       }
+
        return snd_pcm_hw_params(state->handle, state->hw_params);
 }
 
@@ -287,7 +397,11 @@ static int xfer_libasound_pre_process(struct xfer_context *xfer,
                return -ENXIO;
 
        err = configure_hw_params(state, *format, *samples_per_frame,
-                                 *frames_per_second);
+                                 *frames_per_second,
+                                 state->msec_per_period,
+                                 state->msec_per_buffer,
+                                 state->frames_per_period,
+                                 state->frames_per_buffer);
        if (err < 0) {
                logging(state, "Current hardware parameters:\n");
                snd_pcm_hw_params_dump(state->hw_params, state->log);
index f3ce73f142ebbf19e7faddb5392f956e4be1326c..4456fabd453a4b835069d34422951f3bf0d5acf2 100644 (file)
@@ -30,6 +30,11 @@ struct libasound_state {
 
        char *node_literal;
 
+       unsigned int msec_per_period;
+       unsigned int msec_per_buffer;
+       unsigned int frames_per_period;
+       unsigned int frames_per_buffer;
+
        bool finish_at_xrun:1;
        bool nonblock:1;
        bool mmap:1;
index 9233647d74784b2dbf5ac7df7a51aa71b0e39d2f..d899134c6b92d6261582896f4463628cda40ae80 100644 (file)
@@ -17,6 +17,8 @@ enum no_short_opts {
        // 128 or later belong to non us-ascii character set.
        OPT_XFER_TYPE = 128,
        OPT_DUMP_HW_PARAMS,
+       OPT_PERIOD_SIZE,
+       OPT_BUFFER_SIZE,
        // Obsoleted.
        OPT_MAX_FILE_TIME,
 };