From 1bf35a6162d6199df6a27f53cf3be6609ee2311c Mon Sep 17 00:00:00 2001 From: Mikhail Kupchik Date: Wed, 8 Jun 2022 18:28:42 +0300 Subject: mmap support in tinycap Added mmap (-M option) support to tinycap utility. This is necessary for some hardware to work (e.g. Plugable HDMI capture card with USB ID 1bcf:2c99). --- utils/tinycap.1 | 5 +++++ utils/tinycap.c | 26 ++++++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/utils/tinycap.1 b/utils/tinycap.1 index ad60a2e..d18dd12 100644 --- a/utils/tinycap.1 +++ b/utils/tinycap.1 @@ -23,6 +23,11 @@ The default is 0. Device number of the PCM. The default is 0. +.TP +\fB\-M\fR +Use memory-mapped I/O method. +If this option is not specified, then read and write I/O method will be used. + .TP \fB\-c\fR \fIchannels\fR Number of channels the PCM will have. diff --git a/utils/tinycap.c b/utils/tinycap.c index 7d4b8a4..617d16a 100644 --- a/utils/tinycap.c +++ b/utils/tinycap.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -64,7 +65,7 @@ int capturing = 1; int prinfo = 1; unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device, - unsigned int channels, unsigned int rate, + bool use_mmap, unsigned int channels, unsigned int rate, enum pcm_format format, unsigned int period_size, unsigned int period_count, unsigned int capture_time); @@ -88,12 +89,13 @@ int main(int argc, char **argv) unsigned int period_size = 1024; unsigned int period_count = 4; unsigned int capture_time = UINT_MAX; + bool use_mmap = false; enum pcm_format format; int no_header = 0, c; struct optparse opts; if (argc < 2) { - fprintf(stderr, "Usage: %s {file.wav | --} [-D card] [-d device] [-c channels] " + fprintf(stderr, "Usage: %s {file.wav | --} [-D card] [-d device] [-M] [-c channels] " "[-r rate] [-b bits] [-p period_size] [-n n_periods] [-t time_in_seconds]\n\n" "Use -- for filename to send raw PCM to stdout\n", argv[0]); return 1; @@ -113,7 +115,7 @@ int main(int argc, char **argv) /* parse command line arguments */ optparse_init(&opts, argv + 1); - while ((c = optparse(&opts, "D:d:c:r:b:p:n:t:")) != -1) { + while ((c = optparse(&opts, "D:d:c:r:b:p:n:t:M")) != -1) { switch (c) { case 'd': device = atoi(opts.optarg); @@ -139,6 +141,9 @@ int main(int argc, char **argv) case 't': capture_time = atoi(opts.optarg); break; + case 'M': + use_mmap = true; + break; case '?': fprintf(stderr, "%s\n", opts.errmsg); return EXIT_FAILURE; @@ -182,9 +187,9 @@ int main(int argc, char **argv) /* install signal handler and begin capturing */ signal(SIGINT, sigint_handler); - frames = capture_sample(file, card, device, header.num_channels, - header.sample_rate, format, - period_size, period_count, capture_time); + frames = capture_sample(file, card, device, use_mmap, + header.num_channels, header.sample_rate, + format, period_size, period_count, capture_time); if (prinfo) { printf("Captured %u frames\n", frames); } @@ -203,11 +208,12 @@ int main(int argc, char **argv) } unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device, - unsigned int channels, unsigned int rate, + bool use_mmap, unsigned int channels, unsigned int rate, enum pcm_format format, unsigned int period_size, unsigned int period_count, unsigned int capture_time) { struct pcm_config config; + unsigned int pcm_open_flags; struct pcm *pcm; char *buffer; unsigned int size; @@ -225,7 +231,11 @@ unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device, config.stop_threshold = 0; config.silence_threshold = 0; - pcm = pcm_open(card, device, PCM_IN, &config); + pcm_open_flags = PCM_IN; + if (use_mmap) + pcm_open_flags |= PCM_MMAP; + + pcm = pcm_open(card, device, pcm_open_flags, &config); if (!pcm || !pcm_is_ready(pcm)) { fprintf(stderr, "Unable to open PCM device (%s)\n", pcm_get_error(pcm)); -- cgit v1.2.3 From 563016b7fe31a8918b3db2a2f2cbb67607ecd153 Mon Sep 17 00:00:00 2001 From: Roman Stratiienko Date: Tue, 26 Jul 2022 17:07:51 +0300 Subject: pcm: Propagate error message to the bad_pcm in case of failure Signed-off-by: Roman Stratiienko --- src/pcm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pcm.c b/src/pcm.c index d460593..d681563 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1092,8 +1092,10 @@ struct pcm *pcm_open(unsigned int card, unsigned int device, } pcm->subdevice = info.subdevice; - if (pcm_set_config(pcm, config) != 0) + if (pcm_set_config(pcm, config) != 0) { + memcpy(bad_pcm.error, pcm->error, sizeof(pcm->error)); goto fail_close; + } rc = pcm_hw_mmap_status(pcm); if (rc < 0) { -- cgit v1.2.3 From f5368cdf08be50d8de01155e17a2dfdcb3c9beb3 Mon Sep 17 00:00:00 2001 From: Michael Forney Date: Mon, 31 Oct 2022 00:07:42 -0700 Subject: tinyplay: fix playback of 24-bit and 8-bit pcm The bits-to-format function returned bool, which happened to work for PCM_FORMAT_S16_LE (0) and PCM_FORMAT_S32_LE (1). However, all other formats were incorrectly mapped to PCM_FORMAT_S32_LE. Return enum pcm_format instead. --- utils/tinyplay.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/tinyplay.c b/utils/tinyplay.c index 9f72bbb..d617074 100644 --- a/utils/tinyplay.c +++ b/utils/tinyplay.c @@ -112,7 +112,7 @@ static bool is_wave_file(const char *filetype) return filetype != NULL && strcmp(filetype, "wav") == 0; } -static bool signed_pcm_bits_to_format(int bits) +static enum pcm_format signed_pcm_bits_to_format(int bits) { switch (bits) { case 8: -- cgit v1.2.3 From bc3af517534346742a2e753f753a0ad21f51513b Mon Sep 17 00:00:00 2001 From: Guodong Hu Date: Tue, 15 Aug 2023 13:37:26 +0800 Subject: Support pcm drain ops Signed-off-by: Guodong Hu --- include/tinyalsa/pcm.h | 2 ++ include/tinyalsa/plugin.h | 5 ++++- src/pcm.c | 16 ++++++++++++++++ src/pcm_plugin.c | 13 +++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/include/tinyalsa/pcm.h b/include/tinyalsa/pcm.h index 9fca92d..35318a5 100644 --- a/include/tinyalsa/pcm.h +++ b/include/tinyalsa/pcm.h @@ -361,6 +361,8 @@ int pcm_prepare(struct pcm *pcm); int pcm_start(struct pcm *pcm); +int pcm_drain(struct pcm *pcm); + int pcm_stop(struct pcm *pcm); int pcm_wait(struct pcm *pcm, int timeout); diff --git a/include/tinyalsa/plugin.h b/include/tinyalsa/plugin.h index b2f97b9..055734c 100644 --- a/include/tinyalsa/plugin.h +++ b/include/tinyalsa/plugin.h @@ -124,7 +124,10 @@ struct pcm_plugin_ops { int (*prepare) (struct pcm_plugin *plugin); /** Start data transfer from/to the plugin */ int (*start) (struct pcm_plugin *plugin); - /** Drop pcm frames */ + /** Signal the plugin to drain PCM */ + int (*drain) (struct pcm_plugin *plugin); + /** Stop a PCM dropping pending frames if drain() is NOT called. + * Stop a PCM preserving pending frames if drain() is called. */ int (*drop) (struct pcm_plugin *plugin); /** Any custom or alsa specific ioctl implementation */ int (*ioctl) (struct pcm_plugin *plugin, diff --git a/src/pcm.c b/src/pcm.c index d681563..1b2103a 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1218,6 +1218,22 @@ int pcm_start(struct pcm *pcm) return 0; } +/** Drains a PCM. + * @param pcm A PCM handle. + * @return On success, zero; on failure, a negative number. + * @ingroup libtinyalsa-pcm + */ +int pcm_drain(struct pcm *pcm) +{ + if (!pcm_is_ready(pcm)) + return -1; + + if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_DRAIN) < 0) + return oops(pcm, errno, "cannot drain channel"); + + return 0; +} + /** Stops a PCM. * @param pcm A PCM handle. * @return On success, zero; on failure, a negative number. diff --git a/src/pcm_plugin.c b/src/pcm_plugin.c index b6b69aa..4d2651c 100644 --- a/src/pcm_plugin.c +++ b/src/pcm_plugin.c @@ -622,6 +622,16 @@ static int pcm_plug_drop(struct pcm_plug_data *plug_data) return rc; } +static int pcm_plug_drain(struct pcm_plug_data *plug_data) +{ + struct pcm_plugin *plugin = plug_data->plugin; + + if (plugin->state != PCM_PLUG_STATE_RUNNING) + return -EBADFD; + + return plug_data->ops->drain(plugin); +} + static int pcm_plug_ioctl(void *data, unsigned int cmd, ...) { struct pcm_plug_data *plug_data = data; @@ -659,6 +669,9 @@ static int pcm_plug_ioctl(void *data, unsigned int cmd, ...) case SNDRV_PCM_IOCTL_START: ret = pcm_plug_start(plug_data); break; + case SNDRV_PCM_IOCTL_DRAIN: + ret = pcm_plug_drain(plug_data); + break; case SNDRV_PCM_IOCTL_DROP: ret = pcm_plug_drop(plug_data); break; -- cgit v1.2.3 From a8a581fb422c9c2af2d8858a678c008db80e1e3c Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Tue, 30 May 2023 20:27:35 +0530 Subject: mixer: add support for pcm device specific mixer controls Mixer control such as "Playback channel map" can be registered by multiple pcm device nodes and is distinguished by device in snd_ctl_elem_id. Add support to get the control handle associated with mixer ctl name and device number. Also, introduce API to get the device number associated with specific mixer_ctl handle. --- include/tinyalsa/mixer.h | 6 +++++ src/mixer.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/include/tinyalsa/mixer.h b/include/tinyalsa/mixer.h index 7d0580f..149b395 100644 --- a/include/tinyalsa/mixer.h +++ b/include/tinyalsa/mixer.h @@ -103,6 +103,10 @@ struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id); struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name); +struct mixer_ctl *mixer_get_ctl_by_name_and_device(struct mixer *mixer, + const char *name, + unsigned int device); + struct mixer_ctl *mixer_get_ctl_by_name_and_index(struct mixer *mixer, const char *name, unsigned int index); @@ -153,6 +157,8 @@ int mixer_ctl_get_range_min(const struct mixer_ctl *ctl); int mixer_ctl_get_range_max(const struct mixer_ctl *ctl); +unsigned int mixer_ctl_get_device(const struct mixer_ctl *ctl); + int mixer_read_event(struct mixer *mixer, struct mixer_ctl_event *event); int mixer_consume_event(struct mixer *mixer); diff --git a/src/mixer.c b/src/mixer.c index 029fc84..f2c21c3 100644 --- a/src/mixer.c +++ b/src/mixer.c @@ -760,6 +760,55 @@ struct mixer_ctl *mixer_get_ctl_by_name_and_index(struct mixer *mixer, return NULL; } +/** Gets an instance of mixer control handle, by the mixer control's name and device. + * For instance, if two controls have same name, + * e.g. 'Playback Channel map', then PCM device returns the specific control. + * @param mixer An initialized mixer handle. + * @param name The control's name in the given mixer. + * @param device The PCM device + * @returns A handle to the mixer control. + * @ingroup libtinyalsa-mixer + */ +struct mixer_ctl *mixer_get_ctl_by_name_and_device(struct mixer *mixer, + const char *name, + unsigned int device) +{ + struct mixer_ctl_group *grp; + unsigned int n; + struct mixer_ctl *ctl; + + if (!mixer || !name) { + return NULL; + } + + if (mixer->h_grp) { + grp = mixer->h_grp; + ctl = grp->ctl; + + for (n = 0; n < grp->count; n++) { + if (!strcmp(name, (char*) ctl[n].info.id.name) && + device == ctl[n].info.id.device) { + return ctl + n; + } + } + } + +#ifdef TINYALSA_USES_PLUGINS + if (mixer->v_grp) { + grp = mixer->v_grp; + ctl = grp->ctl; + + for (n = 0; n < grp->count; n++) { + if (!strcmp(name, (char*) ctl[n].info.id.name) && + device == ctl[n].info.id.device) { + return ctl + n; + } + } + } +#endif + return NULL; +} + /** Updates the control's info. * This is useful for a program that may be idle for a period of time. * @param ctl An initialized control handle. @@ -822,6 +871,14 @@ const char *mixer_ctl_get_name(const struct mixer_ctl *ctl) return (const char *)ctl->info.id.name; } +unsigned int mixer_ctl_get_device(const struct mixer_ctl *ctl) +{ + if (!ctl) + return UINT_MAX; + + return ctl->info.id.device; +} + /** Gets the value type of the control. * @param ctl An initialized control handle * @returns On success, the type of mixer control. -- cgit v1.2.3 From 4516c73951d861e974f338540b026285148dc426 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Tue, 30 May 2023 20:34:51 +0530 Subject: utils: tinymix: Print device number associated with mixer controls For mixer controls with same name and different device number, there is no clarity in tinymix output. Print device number along with mixer control name to distinguish between mixer controls with same name. --- utils/tinymix.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/utils/tinymix.c b/utils/tinymix.c index e272ade..f0e1d95 100644 --- a/utils/tinymix.c +++ b/utils/tinymix.c @@ -201,7 +201,7 @@ static void list_controls(struct mixer *mixer, int print_all) { struct mixer_ctl *ctl; const char *name, *type; - unsigned int num_ctls, num_values; + unsigned int num_ctls, num_values, device; unsigned int i; num_ctls = mixer_get_num_ctls(mixer); @@ -209,9 +209,9 @@ static void list_controls(struct mixer *mixer, int print_all) printf("Number of controls: %u\n", num_ctls); if (print_all) - printf("ctl\ttype\tnum\t%-40svalue\n", "name"); + printf("ctl\ttype\tnum\t%-40s\tdevice\tvalue\n", "name"); else - printf("ctl\ttype\tnum\t%-40s\n", "name"); + printf("ctl\ttype\tnum\t%-40s\tdevice\n", "name"); for (i = 0; i < num_ctls; i++) { ctl = mixer_get_ctl(mixer, i); @@ -219,7 +219,8 @@ static void list_controls(struct mixer *mixer, int print_all) name = mixer_ctl_get_name(ctl); type = mixer_ctl_get_type_string(ctl); num_values = mixer_ctl_get_num_values(ctl); - printf("%u\t%s\t%u\t%-40s", i, type, num_values, name); + device = mixer_ctl_get_dev_num(ctl); + printf("%u\t%s\t%u\t%-40s\t%u", i, type, num_values, name, device); if (print_all) print_control_values(ctl); printf("\n"); -- cgit v1.2.3 From 895322d47a5180772190b75859bd54409dfc8eb6 Mon Sep 17 00:00:00 2001 From: dvdli Date: Mon, 18 Sep 2023 12:47:13 +0800 Subject: fix tinymix --- utils/tinymix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/tinymix.c b/utils/tinymix.c index f0e1d95..edeb6ad 100644 --- a/utils/tinymix.c +++ b/utils/tinymix.c @@ -219,7 +219,7 @@ static void list_controls(struct mixer *mixer, int print_all) name = mixer_ctl_get_name(ctl); type = mixer_ctl_get_type_string(ctl); num_values = mixer_ctl_get_num_values(ctl); - device = mixer_ctl_get_dev_num(ctl); + device = mixer_ctl_get_device(ctl); printf("%u\t%s\t%u\t%-40s\t%u", i, type, num_values, name, device); if (print_all) print_control_values(ctl); -- cgit v1.2.3