[alsa-devel] [PATCH 0/3] Add support for playing a given number of samples
We introduce (patch 1) the option of playing only a given number of samples in order to reduce our simple test running time. Until now we can only limit them at 1 second (-d parameter).
This patch series also refactors the playback function (patch 2) and fixes a bug with small raw files (patch 3).
Previous discussions about this started here:
https://www.spinics.net/lists/alsa-devel/msg64463.html
Daniel Baluta (2): aplay: Refactor playback code aplay: Fix playback for small raw files
Ion-Horia Petrisor (1): aplay: Add samples argument for playing/recording a given number of samples
aplay/aplay.c | 185 +++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 143 insertions(+), 42 deletions(-)
From: Ion-Horia Petrisor ion-horia.petrisor@nxp.com
-s --samples allows aplay to be used for playback/capture a given number of samples per channel
Signed-off-by: Ion-Horia Petrisor ion-horia.petrisor@nxp.com Signed-off-by: Daniel Baluta daniel.baluta@nxp.com --- aplay/aplay.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-)
diff --git a/aplay/aplay.c b/aplay/aplay.c index 89baa9a..fb2909f 100644 --- a/aplay/aplay.c +++ b/aplay/aplay.c @@ -100,6 +100,7 @@ static struct { unsigned int rate; } hwparams, rhwparams; static int timelimit = 0; +static int sampleslimit = 0; static int quiet_mode = 0; static int file_type = FORMAT_DEFAULT; static int open_mode = 0; @@ -212,6 +213,7 @@ _("Usage: %s [OPTION]... [FILE]...\n" "-f, --format=FORMAT sample format (case insensitive)\n" "-r, --rate=# sample rate\n" "-d, --duration=# interrupt after # seconds\n" +"-s, --samples=# interrupt after # samples per channel\n" "-M, --mmap mmap stream\n" "-N, --nonblock nonblocking mode\n" "-F, --period-time=# distance between interrupts is # microseconds\n" @@ -466,8 +468,9 @@ static long parse_long(const char *str, int *err)
int main(int argc, char *argv[]) { + int duration_or_sample = 0; int option_index; - static const char short_options[] = "hnlLD:qt:c:f:r:d:MNF:A:R:T:B:vV:IPCi" + static const char short_options[] = "hnlLD:qt:c:f:r:d:s:MNF:A:R:T:B:vV:IPCi" #ifdef CONFIG_SUPPORT_CHMAP "m:" #endif @@ -485,6 +488,7 @@ int main(int argc, char *argv[]) {"format", 1, 0, 'f'}, {"rate", 1, 0, 'r'}, {"duration", 1, 0 ,'d'}, + {"samples", 1, 0, 's'}, {"mmap", 0, 0, 'M'}, {"nonblock", 0, 0, 'N'}, {"period-time", 1, 0, 'F'}, @@ -640,11 +644,28 @@ int main(int argc, char *argv[]) } break; case 'd': + if (duration_or_sample) { + error(_("duration and samples arguments cannot be used together")); + return 1; + } timelimit = parse_long(optarg, &err); if (err < 0) { error(_("invalid duration argument '%s'"), optarg); return 1; } + duration_or_sample = 1; + break; + case 's': + if (duration_or_sample) { + error(_("samples and duration arguments cannot be used together")); + return 1; + } + sampleslimit = parse_long(optarg, &err); + if (err < 0) { + error(_("invalid samples argument '%s'"), optarg); + return 1; + } + duration_or_sample = 1; break; case 'N': nonblock = 1; @@ -2477,9 +2498,12 @@ static off64_t calc_count(void) { off64_t count;
- if (timelimit == 0) { - count = pbrec_count; - } else { + if (timelimit == 0) + if (sampleslimit == 0) + count = pbrec_count; + else + count = snd_pcm_format_size(hwparams.format, sampleslimit * hwparams.channels); + else { count = snd_pcm_format_size(hwparams.format, hwparams.rate * hwparams.channels); count *= (off64_t)timelimit; } @@ -3125,7 +3149,7 @@ static void capture(char *orig_name) /* repeat the loop when format is raw without timelimit or * requested counts of data are recorded */ - } while ((file_type == FORMAT_RAW && !timelimit) || count > 0); + } while ((file_type == FORMAT_RAW && !timelimit && !sampleslimit) || count > 0); }
static void playbackv_go(int* fds, unsigned int channels, size_t loaded, off64_t count, int rtype, char **names)
From: Daniel Baluta daniel.baluta@nxp.com
This introduces read_header function which tries to read the header of an audio file in order to determine its type.
This has the following effects: (1) makes code easier to read (2) don't abort if file size is less than expected header
(2), allows us to play small files with size smaller than any supported audio file headers.
Suggested-by: Takashi Iwai tiwai@suse.de Signed-off-by: Daniel Baluta daniel.baluta@nxp.com --- aplay/aplay.c | 144 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 37 deletions(-)
diff --git a/aplay/aplay.c b/aplay/aplay.c index fb2909f..28da6c5 100644 --- a/aplay/aplay.c +++ b/aplay/aplay.c @@ -2801,6 +2801,89 @@ static void playback_go(int fd, size_t loaded, off64_t count, int rtype, char *n snd_pcm_nonblock(handle, nonblock); }
+static int read_header(int *loaded, int header_size) +{ + int ret; + struct stat buf; + + ret = fstat(fd, &buf); + if (ret < 0) { + perror("fstat"); + prg_exit(EXIT_FAILURE); + } + + /* don't be adventurous, get out if file size is smaller than + * requested header size */ + if (buf.st_size < header_size) + return -1; + + if (*loaded < header_size) { + header_size -= *loaded; + ret = safe_read(fd, audiobuf + *loaded, header_size); + if (ret != header_size) { + error(_("read error")); + prg_exit(EXIT_FAILURE); + } + *loaded += header_size; + } + return 0; +} + +static int playback_au(char *name, int *loaded) +{ + if (read_header(loaded, sizeof(AuHeader)) < 0) + return -1; + + if (test_au(fd, audiobuf) < 0) + return -1; + + rhwparams.format = hwparams.format; + pbrec_count = calc_count(); + playback_go(fd, *loaded - sizeof(AuHeader), pbrec_count, FORMAT_AU, name); + + return 0; +} + +static int playback_voc(char *name, int *loaded) +{ + int ofs; + + if (read_header(loaded, sizeof(VocHeader)) < 0) + return -1; + + if ((ofs = test_vocfile(audiobuf)) < 0) + return -1; + + pbrec_count = calc_count(); + voc_play(fd, ofs, name); + + return 0; +} + +static int playback_wave(char *name, int *loaded) +{ + ssize_t dtawave; + + if (read_header(loaded, sizeof(WaveHeader)) < 0) + return -1; + + if ((dtawave = test_wavefile(fd, audiobuf, *loaded)) < 0) + return -1; + + pbrec_count = calc_count(); + playback_go(fd, dtawave, pbrec_count, FORMAT_WAVE, name); + + return 0; +} + +static int playback_raw(char *name, int *loaded) +{ + init_raw_data(); + pbrec_count = calc_count(); + playback_go(fd, *loaded, pbrec_count, FORMAT_RAW, name); + + return 0; +}
/* * let's play or capture it (capture_type says VOC/WAVE/raw) @@ -2808,9 +2891,7 @@ static void playback_go(int fd, size_t loaded, off64_t count, int rtype, char *n
static void playback(char *name) { - int ofs; - size_t dta; - ssize_t dtawave; + int loaded = 0;
pbrec_count = LLONG_MAX; fdcount = 0; @@ -2824,40 +2905,29 @@ static void playback(char *name) prg_exit(EXIT_FAILURE); } } - /* read the file header */ - dta = sizeof(AuHeader); - if ((size_t)safe_read(fd, audiobuf, dta) != dta) { - error(_("read error")); - prg_exit(EXIT_FAILURE); - } - if (test_au(fd, audiobuf) >= 0) { - rhwparams.format = hwparams.format; - pbrec_count = calc_count(); - playback_go(fd, 0, pbrec_count, FORMAT_AU, name); - goto __end; - } - dta = sizeof(VocHeader); - if ((size_t)safe_read(fd, audiobuf + sizeof(AuHeader), - dta - sizeof(AuHeader)) != dta - sizeof(AuHeader)) { - error(_("read error")); - prg_exit(EXIT_FAILURE);; - } - if ((ofs = test_vocfile(audiobuf)) >= 0) { - pbrec_count = calc_count(); - voc_play(fd, ofs, name); - goto __end; - } - /* read bytes for WAVE-header */ - if ((dtawave = test_wavefile(fd, audiobuf, dta)) >= 0) { - pbrec_count = calc_count(); - playback_go(fd, dtawave, pbrec_count, FORMAT_WAVE, name); - } else { - /* should be raw data */ - init_raw_data(); - pbrec_count = calc_count(); - playback_go(fd, dta, pbrec_count, FORMAT_RAW, name); - } - __end: + + switch(file_type) { + case FORMAT_AU: + playback_au(name, &loaded); + break; + case FORMAT_VOC: + playback_voc(name, &loaded); + break; + case FORMAT_WAVE: + playback_wave(name, &loaded); + break; + case FORMAT_RAW: + playback_raw(name, &loaded); + break; + default: + /* parse the file header */ + if (playback_au(name, &loaded) < 0 && + playback_voc(name, &loaded) < 0 && + playback_wave(name, &loaded) < 0) + playback_raw(name, &loaded); /* should be raw data */ + break; + } + if (fd != 0) close(fd); }
This fixes a bug when trying to play files with size smaller than maximum supported header size.
Lets have a look at the following example:
$ aplay -s 2 sample.raw
-> playback_go(fd = 10, loaded = 26, count = 2, name="sample.raw") --> l = loaded = 26 --> c = count - written = 2 --> c -= l = 2 - 26 = -24 ---> r = safe_read(fd, audiobuf + 26, -24) ---> r = -1, EXIT_FAILURE
In this case we have already 'loaded' from the input file more bytes that we need to send to pcm device. So, we need to adjust the number of bytes loaded and avoid reading a negative number of bytes.
Signed-off-by: Daniel Baluta daniel.baluta@nxp.com --- aplay/aplay.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/aplay/aplay.c b/aplay/aplay.c index 28da6c5..0aa1688 100644 --- a/aplay/aplay.c +++ b/aplay/aplay.c @@ -2774,6 +2774,13 @@ static void playback_go(int fd, size_t loaded, off64_t count, int rtype, char *n c = count - written; if (c > chunk_bytes) c = chunk_bytes; + + /* c < l, there is more data loaded + * then we actually need to write + */ + if (c < l) + l = c; + c -= l;
if (c == 0)
On Wed, 09 Aug 2017 00:05:57 +0200, Daniel Baluta wrote:
We introduce (patch 1) the option of playing only a given number of samples in order to reduce our simple test running time. Until now we can only limit them at 1 second (-d parameter).
This patch series also refactors the playback function (patch 2) and fixes a bug with small raw files (patch 3).
Previous discussions about this started here:
https://www.spinics.net/lists/alsa-devel/msg64463.html
Daniel Baluta (2): aplay: Refactor playback code aplay: Fix playback for small raw files
Ion-Horia Petrisor (1): aplay: Add samples argument for playing/recording a given number of samples
Applied all three patches now. Thanks.
Takashi
participants (2)
-
Daniel Baluta
-
Takashi Iwai