From 2a736a0d2543f206fd2653aaae8a08a4c42eb917 Mon Sep 17 00:00:00 2001 From: Jan Palus Date: Tue, 30 Jan 2024 14:24:25 +0100 Subject: [PATCH 001/117] topology: correct version script path contrary to libasound, version script for libatopology is a regular source file. while it's often the case that $(builddir) and $(srcdir) point to the same directory, they don't always have to. therefore path needs to point explicitly to $(srcdir) for Versions script in topology Closes: https://github.com/alsa-project/alsa-lib/pull/383 Fixes: GH-382 Fixes: dc7da761f3a2 ("topology: separate Versions linker script") Signed-off-by: Jan Palus Signed-off-by: Jaroslav Kysela --- src/topology/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index 04299588..e0b78373 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -2,7 +2,7 @@ EXTRA_DIST = Versions COMPATNUM=@LIBTOOL_VERSION_INFO@ if VERSIONED_SYMBOLS -VSYMS = -Wl,--version-script=Versions +VSYMS = -Wl,--version-script=$(srcdir)/Versions else VSYMS = endif From d8ce72f2561f23293ad0d98d30060c4a80a74c36 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 9 Feb 2024 11:21:04 +0100 Subject: [PATCH 002/117] pcm: plug - add automatic conversion for iec958 subframe samples As Pavel noted, a possibility to automatically convert standard linear samples to iec958 subframe format would be handy for latest Raspberry HDMI driver. Link: https://lore.kernel.org/alsa-devel/81b0be0a-5ab7-db91-21cb-0c59a55291e9@ivitera.com/ Suggested-by: Pavel Hofman Signed-off-by: Jaroslav Kysela --- configure.ac | 3 +++ include/pcm_plugin.h | 13 +++++++++++++ src/pcm/pcm_plug.c | 31 ++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 7a152a4a..3f238302 100644 --- a/configure.ac +++ b/configure.ac @@ -642,6 +642,9 @@ fi if test "$build_pcm_alaw" = "yes"; then AC_DEFINE([BUILD_PCM_PLUGIN_ALAW], "1", [Build PCM alaw plugin]) fi +if test "$build_pcm_iec958" = "yes"; then + AC_DEFINE([BUILD_PCM_PLUGIN_IEC958], "1", [Build PCM iec958 plugin]) +fi if test "$build_pcm_mmap_emul" = "yes"; then AC_DEFINE([BUILD_PCM_PLUGIN_MMAP_EMUL], "1", [Build PCM mmap-emul plugin]) fi diff --git a/include/pcm_plugin.h b/include/pcm_plugin.h index 2d014394..f3e8f3b5 100644 --- a/include/pcm_plugin.h +++ b/include/pcm_plugin.h @@ -133,6 +133,19 @@ int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, const char *name, snd_config_t *root, snd_config_t *conf, snd_pcm_stream_t stream, int mode); +/* + * IEC958 subframe conversion plugin + */ +int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, + snd_pcm_format_t sformat, snd_pcm_t *slave, + int close_slave, + const unsigned char *status_bits, + const unsigned char *preamble_vals, + int hdmi_mode); +int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, + snd_config_t *root, snd_config_t *conf, + snd_pcm_stream_t stream, int mode); + /* * Route plugin for linear formats */ diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index e5a3a189..bd681a9f 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -186,7 +186,8 @@ static const snd_pcm_format_t linear_preferred_formats[] = { #if defined(BUILD_PCM_PLUGIN_MULAW) || \ defined(BUILD_PCM_PLUGIN_ALAW) || \ - defined(BUILD_PCM_PLUGIN_ADPCM) + defined(BUILD_PCM_PLUGIN_ADPCM) || \ + defined(BUILD_PCM_PLUGIN_IEC958) #define BUILD_PCM_NONLINEAR #endif @@ -201,6 +202,10 @@ static const snd_pcm_format_t nonlinear_preferred_formats[] = { #ifdef BUILD_PCM_PLUGIN_ADPCM SND_PCM_FORMAT_IMA_ADPCM, #endif +#ifdef BUILD_PCM_PLUGIN_IEC958 + SND_PCM_FORMAT_IEC958_SUBFRAME_LE, + SND_PCM_FORMAT_IEC958_SUBFRAME_BE, +#endif }; #endif @@ -490,6 +495,18 @@ static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm } #endif +#ifdef BUILD_PCM_PLUGIN_IEC958 +static int iec958_open(snd_pcm_t **pcmp, const char *name, + snd_pcm_format_t sformat, snd_pcm_t *slave, + int close_slave) +{ + unsigned char preamble_vals[3] = { + 0x08, 0x02, 0x04 /* Z, X, Y */ + }; + return snd_pcm_iec958_open(pcmp, name, sformat, slave, close_slave, NULL, preamble_vals, 0); +} +#endif + static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv) { snd_pcm_plug_t *plug = pcm->private_data; @@ -526,6 +543,12 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_p case SND_PCM_FORMAT_IMA_ADPCM: f = snd_pcm_adpcm_open; break; +#endif +#ifdef BUILD_PCM_PLUGIN_IEC958 + case SND_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_BE: + f = iec958_open; + break; #endif default: #ifdef BUILD_PCM_PLUGIN_LFLOAT @@ -566,6 +589,12 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_p case SND_PCM_FORMAT_IMA_ADPCM: f = snd_pcm_adpcm_open; break; +#endif +#ifdef BUILD_PCM_PLUGIN_IEC958 + case SND_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_BE: + f = iec958_open; + break; #endif default: return -EINVAL; From 19be3b23b38abad5861eff00e1b01341369ee8ef Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 22 Feb 2024 18:57:33 +0100 Subject: [PATCH 003/117] pcm: snd_pcm_(physical_)format_width() - change documentation The word "nominal" is not so correct here. Use similar wording as we use in the kernel space (the bit-width of the format). Link: https://github.com/larsimmisch/pyalsaaudio/pull/146 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_misc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pcm/pcm_misc.c b/src/pcm/pcm_misc.c index 04b13125..3cff4326 100644 --- a/src/pcm/pcm_misc.c +++ b/src/pcm/pcm_misc.c @@ -203,9 +203,9 @@ int snd_pcm_format_cpu_endian(snd_pcm_format_t format) } /** - * \brief Return nominal bits per a PCM sample + * \brief Return the bit-width of the format * \param format Sample format - * \return bits per sample, a negative error code if not applicable + * \return the bit-width of the format, or a negative error code if not applicable */ int snd_pcm_format_width(snd_pcm_format_t format) { @@ -270,9 +270,9 @@ int snd_pcm_format_width(snd_pcm_format_t format) } /** - * \brief Return bits needed to store a PCM sample + * \brief Return the physical bit-width of the format (bits needed to store a PCM sample) * \param format Sample format - * \return bits per sample, a negative error code if not applicable + * \return the physical bit-width of the format, or a negative error code if not applicable */ int snd_pcm_format_physical_width(snd_pcm_format_t format) { From da744ef116d3a21e418e37bfd58f3f777f65b3a7 Mon Sep 17 00:00:00 2001 From: Marc Aldorasi Date: Thu, 22 Feb 2024 23:42:57 -0500 Subject: [PATCH 004/117] pcm: ladspa - Skip missing ladspa directories Closes: https://github.com/alsa-project/alsa-lib/pull/385 Signed-off-by: Marc Aldorasi Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_ladspa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pcm/pcm_ladspa.c b/src/pcm/pcm_ladspa.c index 56ee138f..25eac76f 100644 --- a/src/pcm/pcm_ladspa.c +++ b/src/pcm/pcm_ladspa.c @@ -1210,7 +1210,7 @@ static int snd_pcm_ladspa_look_for_plugin(snd_pcm_ladspa_plugin_t * const plugin return err; err = snd_pcm_ladspa_check_dir(plugin, fullpath, label, ladspa_id); free(fullpath); - if (err < 0) + if (err < 0 && err != -ENOENT) return err; if (err > 0) return 0; From 431f69a8c3d9f482d2d15d038d84711eeda0819d Mon Sep 17 00:00:00 2001 From: Giulio Moro Date: Wed, 31 Jan 2024 14:54:30 -0500 Subject: [PATCH 005/117] tests: latency.c - fix copy-n-paste typos (sw -> hw params) in error messages Closes: https://github.com/alsa-project/alsa-lib/pull/384 Signed-off-by: Giulio Moro Signed-off-by: Jaroslav Kysela --- test/latency.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/latency.c b/test/latency.c index ae37c11a..c40b2ae7 100644 --- a/test/latency.c +++ b/test/latency.c @@ -260,11 +260,11 @@ int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int *bufsize) return -1; __set_it: if ((err = setparams_bufsize(phandle, p_params, pt_params, *bufsize, "playback")) < 0) { - printf("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err)); + printf("Unable to set hw parameters for playback stream: %s\n", snd_strerror(err)); exit(0); } if ((err = setparams_bufsize(chandle, c_params, ct_params, *bufsize, "capture")) < 0) { - printf("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err)); + printf("Unable to set hw parameters for capture stream: %s\n", snd_strerror(err)); exit(0); } From 13057b74c84c0916557cfc0b999aa4ff837ede7e Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 23 Feb 2024 21:50:01 +0100 Subject: [PATCH 006/117] pcm: clarify and fix default sbits (msbits) value for all formats As described in the kernel patch (link bellow), the significant (resolution) bits should be related to the usable sample bits not the physical sample bits. Link: https://lore.kernel.org/linux-sound/20240222173649.1447549-1-perex@perex.cz/ Link: https://github.com/larsimmisch/pyalsaaudio/pull/146 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 7 ++++++- src/pcm/pcm_hw.c | 22 +++++++++++++++++++--- src/pcm/pcm_params.c | 6 ++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index ab3bbda7..bd1fecbf 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -215,7 +215,8 @@ range, thus you may get the significant bits for linear samples via #snd_pcm_hw_params_get_sbits() function. The example: ICE1712 chips support 32-bit sample processing, but low byte is ignored (playback) or zero (capture). The function snd_pcm_hw_params_get_sbits() -returns 24 in this case. +returns 24 in this case. The significant bits are related to the usable +sample bits (width) not the physical sample space. \section alsa_transfers ALSA transfers @@ -3905,6 +3906,10 @@ int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params, * \param params Configuration space * \return signification bits in sample otherwise a negative error code if the info is not available * + * Significant bits are related to usable sample bits (width) not the + * physical sample bits (width). For non-linear formats, this value may have + * a special meaning which may be defined in future. + * * This function should only be called when the configuration space * contains a single configuration. Call #snd_pcm_hw_params to choose * a single configuration from the configuration space. diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index bd3ecfc9..f3fbcb6e 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -379,12 +379,28 @@ static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) return 0; } -static inline int hw_params_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params) +#define hw_param_mask(params,var) \ + &((params)->masks[(var) - SND_PCM_HW_PARAM_FIRST_MASK]) + +static int hw_params_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params) { + int err; + /* check for new hw_params structure; it's available from 2.0.2 version of PCM API */ if (SNDRV_PROTOCOL_VERSION(2, 0, 2) <= pcm_hw->version) - return ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params); - return use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_PARAMS_OLD, params); + err = ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params); + else + err = use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_PARAMS_OLD, params); + if (err >= 0 && pcm_hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 17) && params->msbits > 0) { + snd_mask_t *m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + if (snd_mask_single(m)) { + snd_pcm_format_t format = snd_mask_min(m); + int width = snd_pcm_format_width(format); + if (width > 0 && params->msbits > (unsigned int)width) + params->msbits = width; + } + } + return err; } static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) diff --git a/src/pcm/pcm_params.c b/src/pcm/pcm_params.c index c2f78634..05bfe3b2 100644 --- a/src/pcm/pcm_params.c +++ b/src/pcm/pcm_params.c @@ -2075,6 +2075,7 @@ int snd_pcm_hw_refine_soft(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t { unsigned int k; snd_interval_t *i; + snd_mask_t *m; unsigned int rstamps[RULES]; unsigned int vstamps[SND_PCM_HW_PARAM_LAST_INTERVAL + 1]; unsigned int stamp = 2; @@ -2159,6 +2160,11 @@ int snd_pcm_hw_refine_soft(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t i = hw_param_interval(params, SND_PCM_HW_PARAM_SAMPLE_BITS); if (snd_interval_single(i)) params->msbits = snd_interval_value(i); + m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); + if (snd_mask_single(m)) { + snd_pcm_format_t format = snd_mask_min(m); + params->msbits = snd_pcm_format_width(format); + } } if (!params->rate_den) { From 4489923d2ca2ee776c79454ce352a26b89ecca18 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 26 Feb 2024 14:52:02 +0100 Subject: [PATCH 007/117] pcm: extend doc for snd_pcm_hw_params_get_sbits() Try to explain more the output value and its relation between format and sample bits defined by the format. Link: https://github.com/larsimmisch/pyalsaaudio/pull/146 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index bd1fecbf..62e76e7e 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -3904,11 +3904,16 @@ int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params, /** * \brief Get sample resolution info from a configuration space * \param params Configuration space - * \return signification bits in sample otherwise a negative error code if the info is not available + * \return sample resolution (in bits) otherwise a negative error code if the info is not available * - * Significant bits are related to usable sample bits (width) not the - * physical sample bits (width). For non-linear formats, this value may have - * a special meaning which may be defined in future. + * For linear formats, this function returns sample resolution - + * used bits starting from the first usable significant bit defined by + * the format (e.g. bit 31 for S32_LE format or bit 23 for S24_LE format - + * starting from bit zero). Application may use full sample bit range defined + * by the format, but additional bits (outside this sample resolution) are + * stripped (not processed). + * + * For non-linear formats, this value may have a special meaning which may be defined in future. * * This function should only be called when the configuration space * contains a single configuration. Call #snd_pcm_hw_params to choose From 6f17ec50b3292b3359fb7a70faec4dca93e81440 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 4 Mar 2024 18:59:35 +1030 Subject: [PATCH 008/117] conf: USB-Audio: Add more Scarlett devices to the IEC958 blacklist The Scarlett Solo and 2i2 don't have S/PDIF outputs. Link: https://lore.kernel.org/alsa-devel/ZeWGbxZIc7AF96h0@m.b4.vu/ Signed-off-by: Geoffrey D. Bennett Signed-off-by: Jaroslav Kysela --- src/conf/cards/USB-Audio.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/conf/cards/USB-Audio.conf b/src/conf/cards/USB-Audio.conf index 80631b2e..05384a32 100644 --- a/src/conf/cards/USB-Audio.conf +++ b/src/conf/cards/USB-Audio.conf @@ -66,8 +66,11 @@ USB-Audio.pcm.iec958_device { "Plantronics USB Headset" 999 "Plantronics Wireless Audio" 999 "SB WoW Headset" 999 + "Scarlett 2i2 4th Gen" 999 "Scarlett 2i2 USB" 999 "Scarlett 2i4 USB" 999 + "Scarlett Solo 4th Gen" 999 + "Scarlett Solo USB" 999 "Sennheiser USB headset" 999 "SWTOR Gaming Headset by Razer" 999 "ThinkStation P620 Main" 999 From 08bf1a7b0c7fb353c7ca453a73a38b2979d27a72 Mon Sep 17 00:00:00 2001 From: David Senoner Date: Mon, 26 Feb 2024 15:14:03 +0100 Subject: [PATCH 009/117] conf: USB-Audio: Add Corsair HS60 Pro to the IEC958 blacklist This device is just an external USB soundcard with a female 3.5mm jack for a headset, no S/PDIF | IEC958 connector. Add it to the blacklist to prevent it being opened. Link: https://lore.kernel.org/alsa-devel/ZeWGbxZIc7AF96h0@m.b4.vu/ Signed-off-by: David Senoner Signed-off-by: Jaroslav Kysela --- src/conf/cards/USB-Audio.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/src/conf/cards/USB-Audio.conf b/src/conf/cards/USB-Audio.conf index 05384a32..2f6d2ee0 100644 --- a/src/conf/cards/USB-Audio.conf +++ b/src/conf/cards/USB-Audio.conf @@ -53,6 +53,7 @@ USB-Audio.pcm.iec958_device { "Blue Snowball" 999 "C-Media USB Headphone Set" 999 "Cmedia Audio" 999 + "Corsair HS60 PRO Surround USB S" 999 "DELL PROFESSIONAL SOUND BAR AE5" 999 "HP Digital Stereo Headset" 999 "GN 9330" 999 From dcc5d6a8bd961ac61c2d65d9cab21f6dfccf4c65 Mon Sep 17 00:00:00 2001 From: Ryan Carsten Schmidt Date: Sat, 16 Mar 2024 03:34:32 -0500 Subject: [PATCH 010/117] GitHub Actions: Use actions/checkout@v4 Fixes: #389 --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d8a025b4..b01ad81d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: dnf -y upgrade dnf -y install @development-tools libtool bzip2 - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Safe git directory @@ -75,7 +75,7 @@ jobs: image: ubuntu:latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Prepare environment run: | export DEBIAN_FRONTEND=noninteractive From de529410da2f8b2905eda7d5eff52bc69e148fe3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 23 Apr 2024 10:09:46 +0300 Subject: [PATCH 011/117] conf: aliases: hdmi: Include unconditionally the The hdmi.conf contains the high level macro to be used by cards to create the hdmi: device. Instead of including it in different config files, include it in the main aliases.conf and remove it's inclusion by other config files. This change is needed to add support for the hdmi: device mapping via UCM. Closes: https://github.com/alsa-project/alsa-lib/pull/393 Suggested-by: Jaroslav Kysela Signed-off-by: Peter Ujfalusi Signed-off-by: Jaroslav Kysela --- src/conf/cards/HDA-Intel.conf | 2 -- src/conf/cards/HdmiLpeAudio.conf | 2 -- src/conf/cards/aliases.conf | 1 + src/conf/cards/vc4-hdmi.conf | 2 -- 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/conf/cards/HDA-Intel.conf b/src/conf/cards/HDA-Intel.conf index d5a65906..5451606f 100644 --- a/src/conf/cards/HDA-Intel.conf +++ b/src/conf/cards/HDA-Intel.conf @@ -163,8 +163,6 @@ HDA-Intel.pcm.iec958.0 { hint.device 1 } - - HDA-Intel.pcm.hdmi.common { @args [ CARD DEVICE CTLINDEX AES0 AES1 AES2 AES3 ] @args.CARD { diff --git a/src/conf/cards/HdmiLpeAudio.conf b/src/conf/cards/HdmiLpeAudio.conf index a1e493da..a9104f43 100644 --- a/src/conf/cards/HdmiLpeAudio.conf +++ b/src/conf/cards/HdmiLpeAudio.conf @@ -2,8 +2,6 @@ # Configuration for the Intel HDMI/DP LPE audio # - - HdmiLpeAudio.pcm.hdmi.0 { @args [ CARD AES0 AES1 AES2 AES3 ] @args.CARD { diff --git a/src/conf/cards/aliases.conf b/src/conf/cards/aliases.conf index 7e352f76..a54824ae 100644 --- a/src/conf/cards/aliases.conf +++ b/src/conf/cards/aliases.conf @@ -62,3 +62,4 @@ VC4-HDMI cards.vc4-hdmi + diff --git a/src/conf/cards/vc4-hdmi.conf b/src/conf/cards/vc4-hdmi.conf index af87b3a8..0f313d82 100644 --- a/src/conf/cards/vc4-hdmi.conf +++ b/src/conf/cards/vc4-hdmi.conf @@ -3,8 +3,6 @@ # subframe conversion # - - vc4-hdmi.pcm.hdmi.0 { @args [ CARD AES0 AES1 AES2 AES3 ] @args.CARD { From ef6463a20914e4cdcd22917d3206a06b86718c3f Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 24 Apr 2024 13:01:04 +0200 Subject: [PATCH 012/117] ucm: fix Path condition - substitute Path and Mode fields The Path and Mode fields should be also substituted for the runtime evaluation. See Fixes. Fixes: https://github.com/alsa-project/alsa-lib/issues/395 Signed-off-by: Jaroslav Kysela --- src/ucm/ucm_cond.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/ucm/ucm_cond.c b/src/ucm/ucm_cond.c index 985a366b..70effda0 100644 --- a/src/ucm/ucm_cond.c +++ b/src/ucm/ucm_cond.c @@ -274,6 +274,7 @@ static int if_eval_path(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) { const char *path, *mode = ""; int err, amode = F_OK; + char *s; if (uc_mgr->conf_format < 4) { uc_error("Path condition is supported in v4+ syntax"); @@ -292,27 +293,34 @@ static int if_eval_path(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) return -EINVAL; } - if (strncasecmp(mode, "exist", 5) == 0) { + err = uc_mgr_get_substituted_value(uc_mgr, &s, mode); + if (err < 0) + return err; + if (strncasecmp(s, "exist", 5) == 0) { amode = F_OK; - } else if (strcasecmp(mode, "read") == 0) { + } else if (strcasecmp(s, "read") == 0) { amode = R_OK; - } else if (strcasecmp(mode, "write") == 0) { + } else if (strcasecmp(s, "write") == 0) { amode = W_OK; - } else if (strcasecmp(mode, "exec") == 0) { + } else if (strcasecmp(s, "exec") == 0) { amode = X_OK; } else { - uc_error("Path unknown mode (If.Condition.Mode)"); + uc_error("Path unknown mode '%s' (If.Condition.Mode)", s); + free(s); return -EINVAL; } + free(s); + err = uc_mgr_get_substituted_value(uc_mgr, &s, path); + if (err < 0) + return err; #ifdef HAVE_EACCESS - if (eaccess(path, amode)) + err = eaccess(path, amode); #else - if (access(path, amode)) + err = access(path, amode); #endif - return 0; - - return 1; + free(s); + return err ? 0 : 1; } static int if_eval(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) From 3fd24db22dd3b09fd70de2ae1c58804251737ff4 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 24 Apr 2024 14:47:27 +0200 Subject: [PATCH 013/117] ucm: fix Path condition - substitute Path Use the appropriate variable for access/eaccess call. Fixes: https://github.com/alsa-project/alsa-lib/issues/395 Fixes: ef6463a2 ("ucm: fix Path condition - substitute Path and Mode fields") Signed-off-by: Jaroslav Kysela --- src/ucm/ucm_cond.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ucm/ucm_cond.c b/src/ucm/ucm_cond.c index 70effda0..bc4444e3 100644 --- a/src/ucm/ucm_cond.c +++ b/src/ucm/ucm_cond.c @@ -315,9 +315,9 @@ static int if_eval_path(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) if (err < 0) return err; #ifdef HAVE_EACCESS - err = eaccess(path, amode); + err = eaccess(s, amode); #else - err = access(path, amode); + err = access(s, amode); #endif free(s); return err ? 0 : 1; From 99499a6ebc3960e695acb6a4a6fc5d067d596d1a Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 24 Apr 2024 15:08:48 +0200 Subject: [PATCH 014/117] ucm: do argument value substitution for Macros Link: https://github.com/alsa-project/alsa-ucm-conf/pull/411 Signed-off-by: Jaroslav Kysela --- src/ucm/parser.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 98acd97e..7da8060a 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -415,7 +415,7 @@ static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr, snd_config_iterator_t i, next; snd_config_t *m, *mc, *a, *n; const char *mid, *id; - char name[128], *var; + char name[128], *var, *var2; const char *s; int err; @@ -449,9 +449,13 @@ static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr, err = snd_config_get_ascii(n, &var); if (err < 0) goto __err_path; - snprintf(name, sizeof(name), "__%s", id); - err = uc_mgr_set_variable(uc_mgr, name, var); + err = uc_mgr_get_substituted_value(uc_mgr, &var2, var); free(var); + if (err >= 0) { + snprintf(name, sizeof(name), "__%s", id); + err = uc_mgr_set_variable(uc_mgr, name, var2); + free(var2); + } if (err < 0) goto __err_path; } From c6cd83bd0afe4ae0e5a64af51b87e9bc51f40563 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 24 Apr 2024 15:15:48 +0200 Subject: [PATCH 015/117] ucm: raise error when macro argument is already defined (used) Link: https://github.com/alsa-project/alsa-ucm-conf/pull/411 Signed-off-by: Jaroslav Kysela --- src/ucm/parser.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 7da8060a..02f01a10 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -446,13 +446,17 @@ static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr, err = snd_config_get_id(n, &id); if (err < 0) goto __err_path; + snprintf(name, sizeof(name), "__%s", id); + if (uc_mgr_get_variable(uc_mgr, name)) { + uc_error("Macro argument '%s' is already defined", name); + goto __err_path; + } err = snd_config_get_ascii(n, &var); if (err < 0) goto __err_path; err = uc_mgr_get_substituted_value(uc_mgr, &var2, var); free(var); if (err >= 0) { - snprintf(name, sizeof(name), "__%s", id); err = uc_mgr_set_variable(uc_mgr, name, var2); free(var2); } From 3864f7d95f7ad97690ed8e823aab3c96abfe5e48 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 24 Apr 2024 15:33:40 +0200 Subject: [PATCH 016/117] ucm: define and describe Syntax 7 - for new macro argument substitution - for new Path condition fields substitutions Link: https://github.com/alsa-project/alsa-ucm-conf/pull/411 Link: https://github.com/alsa-project/alsa-lib/issues/395 Signed-off-by: Jaroslav Kysela --- src/ucm/parser.c | 15 ++++++++++----- src/ucm/ucm_cond.c | 26 ++++++++++++++++++-------- src/ucm/ucm_confdoc.h | 14 ++++++++++++-- src/ucm/ucm_local.h | 2 +- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 02f01a10..488a5c62 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -454,11 +454,16 @@ static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr, err = snd_config_get_ascii(n, &var); if (err < 0) goto __err_path; - err = uc_mgr_get_substituted_value(uc_mgr, &var2, var); - free(var); - if (err >= 0) { - err = uc_mgr_set_variable(uc_mgr, name, var2); - free(var2); + if (uc_mgr->conf_format < 7) { + err = uc_mgr_set_variable(uc_mgr, name, var); + free(var); + } else { + err = uc_mgr_get_substituted_value(uc_mgr, &var2, var); + free(var); + if (err >= 0) { + err = uc_mgr_set_variable(uc_mgr, name, var2); + free(var2); + } } if (err < 0) goto __err_path; diff --git a/src/ucm/ucm_cond.c b/src/ucm/ucm_cond.c index bc4444e3..87bc9762 100644 --- a/src/ucm/ucm_cond.c +++ b/src/ucm/ucm_cond.c @@ -293,9 +293,13 @@ static int if_eval_path(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) return -EINVAL; } - err = uc_mgr_get_substituted_value(uc_mgr, &s, mode); - if (err < 0) - return err; + if (uc_mgr->conf_format < 7) { + s = (char *)mode; + } else { + err = uc_mgr_get_substituted_value(uc_mgr, &s, mode); + if (err < 0) + return err; + } if (strncasecmp(s, "exist", 5) == 0) { amode = F_OK; } else if (strcasecmp(s, "read") == 0) { @@ -309,17 +313,23 @@ static int if_eval_path(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) free(s); return -EINVAL; } - free(s); + if (s != mode) + free(s); - err = uc_mgr_get_substituted_value(uc_mgr, &s, path); - if (err < 0) - return err; + if (uc_mgr->conf_format < 7) { + s = (char *)path; + } else { + err = uc_mgr_get_substituted_value(uc_mgr, &s, path); + if (err < 0) + return err; + } #ifdef HAVE_EACCESS err = eaccess(s, amode); #else err = access(s, amode); #endif - free(s); + if (s != path) + free(s); return err ? 0 : 1; } diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index 7a907934..eb12a714 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -63,7 +63,7 @@ use case verbs for that sound card. i.e.: # Example master file for blah sound card # By Joe Blogs -Syntax 6 +Syntax 7 # Use Case name for user interface Comment "Nice Abstracted Soundcard" @@ -489,7 +489,8 @@ DefineMacro.macro1 { The arguments in the macro are refered as the variables with the double underscore name prefix (like *__variable*). The configuration block in the DefineMacro subtree is always evaluated (including arguments and variables) -at the time of the instantiation. +at the time of the instantiation. Argument string substitutions +(for multiple macro call levels) were added in *Syntax* version *7*. The macros can be instantiated (expanded) using: @@ -563,6 +564,15 @@ Field | Description String | string Regex | regex expression (extended posix, ignore case) +#### Path is present (Type Path) + +Field | Description +---------------------|----------------------- +Path | path (filename) +Mode | exist,read,write,exec + +Note: Substitution for Path and Mode fields were added in *Syntax* version *7*. + #### ALSA control element exists (Type ControlExists) Field | Description diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index fc249058..464d1154 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -40,7 +40,7 @@ #include #include "use-case.h" -#define SYNTAX_VERSION_MAX 6 +#define SYNTAX_VERSION_MAX 7 #define MAX_CARD_SHORT_NAME 32 #define MAX_CARD_LONG_NAME 80 From 291e727dfecf8d1cec613069a8c5156bb4b6cfa3 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 14 May 2024 15:28:41 +0200 Subject: [PATCH 017/117] ucm: doc - add Variant and Macro to the evaluation order Signed-off-by: Jaroslav Kysela --- src/ucm/ucm_confdoc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index eb12a714..908bc9d7 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -376,7 +376,9 @@ Evaluation order | Configuration block | Evaluation restart ------------------:|---------------------|-------------------- 1 | Define | No 2 | Include | Yes -3 | If | Yes +3 | Variant | Yes +4 | Macro | Yes +5 | If | Yes ### Substitutions From 5600b901b2b4c59eff7bdf507ee57b222142682e Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 4 Jun 2024 11:17:57 +0200 Subject: [PATCH 018/117] async: snd_async_del_handler - move clear signal using sigaction as last Improve the shutdown order for the asynchronous users. There may be unhandled signals, because the signal is reset before signal deactivation using fnctl (O_ASYNC). Closes: https://github.com/alsa-project/alsa-lib/issues/394 Signed-off-by: Jaroslav Kysela --- src/async.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/async.c b/src/async.c index 76c6fb9e..a003e441 100644 --- a/src/async.c +++ b/src/async.c @@ -149,12 +149,31 @@ int snd_async_add_handler(snd_async_handler_t **handler, int fd, */ int snd_async_del_handler(snd_async_handler_t *handler) { - int err = 0; - int was_empty = list_empty(&snd_async_handlers); + int err = 0, err2 = 0; + int was_empty; assert(handler); + if (handler->type != SND_ASYNC_HANDLER_GENERIC) { + if (!list_empty(&handler->hlist)) + list_del(&handler->hlist); + if (!list_empty(&handler->hlist)) + goto _glist; + switch (handler->type) { +#ifdef BUILD_PCM + case SND_ASYNC_HANDLER_PCM: + err2 = snd_pcm_async(handler->u.pcm, -1, 1); + break; +#endif + case SND_ASYNC_HANDLER_CTL: + err2 = snd_ctl_async(handler->u.ctl, -1, 1); + break; + default: + assert(0); + } + } + _glist: + was_empty = list_empty(&snd_async_handlers); list_del(&handler->glist); - if (!was_empty - && list_empty(&snd_async_handlers)) { + if (!was_empty && list_empty(&snd_async_handlers)) { err = sigaction(snd_async_signo, &previous_action, NULL); if (err < 0) { SYSERR("sigaction"); @@ -162,27 +181,8 @@ int snd_async_del_handler(snd_async_handler_t *handler) } memset(&previous_action, 0, sizeof(previous_action)); } - if (handler->type == SND_ASYNC_HANDLER_GENERIC) - goto _end; - if (!list_empty(&handler->hlist)) - list_del(&handler->hlist); - if (!list_empty(&handler->hlist)) - goto _end; - switch (handler->type) { -#ifdef BUILD_PCM - case SND_ASYNC_HANDLER_PCM: - err = snd_pcm_async(handler->u.pcm, -1, 1); - break; -#endif - case SND_ASYNC_HANDLER_CTL: - err = snd_ctl_async(handler->u.ctl, -1, 1); - break; - default: - assert(0); - } - _end: free(handler); - return err; + return err ? err : err2; } /** From b359ed813e012c38d12960c92940c418859b2806 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 10 Jun 2024 09:06:33 +0200 Subject: [PATCH 019/117] use-case.h: add DisplayPort to HDMI device description Link: https://github.com/alsa-project/alsa-ucm-conf/pull/423 Signed-off-by: Jaroslav Kysela --- include/use-case.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/use-case.h b/include/use-case.h index 297664ee..b54f2124 100644 --- a/include/use-case.h +++ b/include/use-case.h @@ -139,7 +139,7 @@ extern "C" { #define SND_USE_CASE_DEV_BLUETOOTH "Bluetooth" /**< Bluetooth Device */ #define SND_USE_CASE_DEV_EARPIECE "Earpiece" /**< Earpiece Device */ #define SND_USE_CASE_DEV_SPDIF "SPDIF" /**< SPDIF Device */ -#define SND_USE_CASE_DEV_HDMI "HDMI" /**< HDMI Device */ +#define SND_USE_CASE_DEV_HDMI "HDMI" /**< HDMI / DisplayPort Device */ #define SND_USE_CASE_DEV_USB "USB" /**< USB Device (multifunctional) */ #define SND_USE_CASE_DEV_DIRECT "Direct" /**< Direct Device (no channel remapping), (e.g. ProAudio usage) */ /* add new devices to end of list */ From 34422861f5549aee3e9df9fd8240d10b530d9abd Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 10 Jun 2024 11:18:34 +0200 Subject: [PATCH 020/117] Release v1.2.12 Signed-off-by: Jaroslav Kysela --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 3f238302..89527a01 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(alsa-lib, 1.2.11) +AC_INIT(alsa-lib, 1.2.12) AC_CONFIG_SRCDIR([src/control/control.c]) AC_CONFIG_MACRO_DIR([m4]) From 6767f623ca057e277b5478e20cdb8497dcf4c753 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Jun 2024 15:16:23 +0200 Subject: [PATCH 021/117] ump: Add missing *_set variants for snd_ump_endpoint_info and snd_ump_block_info The API functions to fill the data on snd_ump_endpoint_info and snd_ump_block_info were missing. Let's add them. They can be used to construct a virtual UMP endpoint and block. Signed-off-by: Takashi Iwai --- include/ump.h | 30 ++++- src/rawmidi/ump.c | 304 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 322 insertions(+), 12 deletions(-) diff --git a/include/ump.h b/include/ump.h index 1e5e0534..45b3ad27 100644 --- a/include/ump.h +++ b/include/ump.h @@ -77,6 +77,7 @@ size_t snd_ump_endpoint_info_sizeof(void); #define snd_ump_endpoint_info_alloca(ptr) __snd_alloca(ptr, snd_ump_endpoint_info) int snd_ump_endpoint_info_malloc(snd_ump_endpoint_info_t **info); void snd_ump_endpoint_info_free(snd_ump_endpoint_info_t *info); +void snd_ump_endpoint_info_clear(snd_ump_endpoint_info_t *info); void snd_ump_endpoint_info_copy(snd_ump_endpoint_info_t *dst, const snd_ump_endpoint_info_t *src); int snd_ump_endpoint_info_get_card(const snd_ump_endpoint_info_t *info); int snd_ump_endpoint_info_get_device(const snd_ump_endpoint_info_t *info); @@ -93,6 +94,20 @@ const char *snd_ump_endpoint_info_get_name(const snd_ump_endpoint_info_t *info); const char *snd_ump_endpoint_info_get_product_id(const snd_ump_endpoint_info_t *info); int snd_ump_endpoint_info(snd_ump_t *ump, snd_ump_endpoint_info_t *info); +void snd_ump_endpoint_info_set_card(snd_ump_endpoint_info_t *info, unsigned int card); +void snd_ump_endpoint_info_set_device(snd_ump_endpoint_info_t *info, unsigned int device); +void snd_ump_endpoint_info_set_flags(snd_ump_endpoint_info_t *info, unsigned int flags); +void snd_ump_endpoint_info_set_protocol_caps(snd_ump_endpoint_info_t *info, unsigned int caps); +void snd_ump_endpoint_info_set_protocol(snd_ump_endpoint_info_t *info, unsigned int protocols); +void snd_ump_endpoint_info_set_num_blocks(snd_ump_endpoint_info_t *info, unsigned int num_blocks); +void snd_ump_endpoint_info_set_version(snd_ump_endpoint_info_t *info, unsigned int version); +void snd_ump_endpoint_info_set_manufacturer_id(snd_ump_endpoint_info_t *info, unsigned int id); +void snd_ump_endpoint_info_set_family_id(snd_ump_endpoint_info_t *info, unsigned int id); +void snd_ump_endpoint_info_set_model_id(snd_ump_endpoint_info_t *info, unsigned int id); +void snd_ump_endpoint_info_set_sw_revision(snd_ump_endpoint_info_t *info, const unsigned char *id); +void snd_ump_endpoint_info_set_name(snd_ump_endpoint_info_t *info, const char *name); +void snd_ump_endpoint_info_set_product_id(snd_ump_endpoint_info_t *info, const char *id); + /** Bit flag for MIDI 1.0 port w/o restrict in UMP Block info flags */ #define SND_UMP_BLOCK_IS_MIDI1 (1U << 0) /** Bit flag for 31.25Kbps B/W MIDI1 port in UMP Block info flags */ @@ -118,11 +133,11 @@ size_t snd_ump_block_info_sizeof(void); #define snd_ump_block_info_alloca(ptr) __snd_alloca(ptr, snd_ump_block_info) int snd_ump_block_info_malloc(snd_ump_block_info_t **info); void snd_ump_block_info_free(snd_ump_block_info_t *info); +void snd_ump_block_info_clear(snd_ump_block_info_t *info); void snd_ump_block_info_copy(snd_ump_block_info_t *dst, const snd_ump_block_info_t *src); int snd_ump_block_info_get_card(const snd_ump_block_info_t *info); int snd_ump_block_info_get_device(const snd_ump_block_info_t *info); unsigned int snd_ump_block_info_get_block_id(const snd_ump_block_info_t *info); -void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, unsigned int id); unsigned int snd_ump_block_info_get_active(const snd_ump_block_info_t *info); unsigned int snd_ump_block_info_get_flags(const snd_ump_block_info_t *info); unsigned int snd_ump_block_info_get_direction(const snd_ump_block_info_t *info); @@ -134,6 +149,19 @@ unsigned int snd_ump_block_info_get_ui_hint(const snd_ump_block_info_t *info); const char *snd_ump_block_info_get_name(const snd_ump_block_info_t *info); int snd_ump_block_info(snd_ump_t *ump, snd_ump_block_info_t *info); +void snd_ump_block_info_set_card(snd_ump_block_info_t *info, unsigned int card); +void snd_ump_block_info_set_device(snd_ump_block_info_t *info, unsigned int device); +void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, unsigned int id); +void snd_ump_block_info_set_active(snd_ump_block_info_t *info, unsigned int active); +void snd_ump_block_info_set_flags(snd_ump_block_info_t *info, unsigned int flags); +void snd_ump_block_info_set_direction(snd_ump_block_info_t *info, unsigned int direction); +void snd_ump_block_info_set_first_group(snd_ump_block_info_t *info, unsigned int first_group); +void snd_ump_block_info_set_num_groups(snd_ump_block_info_t *info, unsigned int num_groups); +void snd_ump_block_info_set_midi_ci_version(snd_ump_block_info_t *info, unsigned int version); +void snd_ump_block_info_set_sysex8_streams(snd_ump_block_info_t *info, unsigned int streams); +void snd_ump_block_info_set_ui_hint(snd_ump_block_info_t *info, unsigned int hint); +void snd_ump_block_info_set_name(snd_ump_block_info_t *info, const char *name); + #ifdef __cplusplus } #endif diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index 39c1c4a9..6c1097a7 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -337,6 +337,17 @@ void snd_ump_endpoint_info_free(snd_ump_endpoint_info_t *info) free(info); } +/** + * \brief clears the snd_ump_endpoint_info_t structure + * \param info pointer to the snd_ump_endpoint_info_t structure to clear + * + * Zero-clear the snd_ump_endpoint_info_t object. + */ +void snd_ump_endpoint_info_clear(snd_ump_endpoint_info_t *info) +{ + memset(info, 0, sizeof(*info)); +} + /** * \brief copy one snd_ump_endpoint_info_t structure to another * \param dst destination snd_ump_endpoint_info_t structure @@ -478,6 +489,149 @@ const char *snd_ump_endpoint_info_get_product_id(const snd_ump_endpoint_info_t * return (const char *)info->product_id; } +/** + * \brief set card number of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param card the card number of the given UMP endpoint + */ +void snd_ump_endpoint_info_set_card(snd_ump_endpoint_info_t *info, + unsigned int card) +{ + info->card = card; +} + +/** + * \brief set device number of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param device the device number of the given UMP endpoint + */ +void snd_ump_endpoint_info_set_device(snd_ump_endpoint_info_t *info, + unsigned int device) +{ + info->device = device; +} + +/** + * \brief set info flags of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param flags UMP endpoint flag bits + */ +void snd_ump_endpoint_info_set_flags(snd_ump_endpoint_info_t *info, + unsigned int flags) +{ + info->flags = flags; +} + +/** + * \brief set protocol capability bits of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param caps UMP endpoint protocol capability bits + */ +void snd_ump_endpoint_info_set_protocol_caps(snd_ump_endpoint_info_t *info, + unsigned int caps) +{ + info->protocol_caps = caps; +} + +/** + * \brief set the current protocol of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param caps the UMP endpoint protocol bits + */ +void snd_ump_endpoint_info_set_protocol(snd_ump_endpoint_info_t *info, + unsigned int protocol) +{ + info->protocol = protocol; +} + +/** + * \brief set the number of UMP blocks of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param blocks the number of UMP blocks + */ +void snd_ump_endpoint_info_set_num_blocks(snd_ump_endpoint_info_t *info, + unsigned int blocks) +{ + info->num_blocks = blocks; +} + +/** + * \brief set the UMP version number of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param version the UMP version number + */ +void snd_ump_endpoint_info_set_version(snd_ump_endpoint_info_t *info, + unsigned int version) +{ + info->version = version; +} + +/** + * \brief set the UMP manufacturer ID of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param id UMP manufacturer ID + */ +void snd_ump_endpoint_info_set_manufacturer_id(snd_ump_endpoint_info_t *info, + unsigned int id) +{ + info->manufacturer_id = id; +} + +/** + * \brief set the UMP family ID of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param id UMP family ID + */ +void snd_ump_endpoint_info_set_family_id(snd_ump_endpoint_info_t *info, + unsigned int id) +{ + info->family_id = id; +} + +/** + * \brief set the UMP model ID of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param id UMP model ID + */ +void snd_ump_endpoint_info_set_model_id(snd_ump_endpoint_info_t *info, + unsigned int id) +{ + info->model_id = id; +} + +/** + * \brief set the UMP software revision of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param id UMP software revision in 4 bytes array + */ +void snd_ump_endpoint_info_set_sw_revision(snd_ump_endpoint_info_t *info, + const unsigned char *id) +{ + memcpy(info->sw_revision, id, sizeof(info->sw_revision)); +} + +/** + * \brief set the name of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param name UMP endpoint name string + */ +void snd_ump_endpoint_info_set_name(snd_ump_endpoint_info_t *info, + const char *name) +{ + snd_strlcpy((char *)info->name, name, sizeof(info->name)); +} + +/** + * \brief set the product ID string of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \param id UMP endpoint product ID string + */ +void snd_ump_endpoint_info_set_product_id(snd_ump_endpoint_info_t *info, + const char *id) +{ + snd_strlcpy((char *)info->product_id, id, sizeof(info->product_id)); +} + /** * \brief get endpoint information about UMP handle * \param ump UMP handle @@ -526,6 +680,17 @@ void snd_ump_block_info_free(snd_ump_block_info_t *info) free(info); } +/** + * \brief clears the snd_ump_block_info_t structure + * \param info pointer to the snd_ump_block_info_t structure to clear + * + * Zero-clear the snd_ump_block_info_t object. + */ +void snd_ump_block_info_clear(snd_ump_block_info_t *info) +{ + memset(info, 0, sizeof(*info)); +} + /** * \brief copy one snd_ump_block_info_t structure to another * \param dst destination snd_ump_block_info_t structure @@ -567,17 +732,6 @@ unsigned int snd_ump_block_info_get_block_id(const snd_ump_block_info_t *info) return info->block_id; } -/** - * \brief set UMP block ID for query - * \param info pointer to a snd_ump_block_info_t structure - * \param id the ID number for query - */ -void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, - unsigned int id) -{ - info->block_id = id; -} - /** * \brief get UMP block activeness * \param info pointer to a snd_ump_block_info_t structure @@ -668,6 +822,134 @@ const char *snd_ump_block_info_get_name(const snd_ump_block_info_t *info) return (const char *)info->name; } +/** + * \brief set card number to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param card the card number + */ +void snd_ump_block_info_set_card(snd_ump_block_info_t *info, unsigned int card) +{ + info->card = card; +} + +/** + * \brief set device number to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param device the device number + */ +void snd_ump_block_info_set_device(snd_ump_block_info_t *info, unsigned int device) +{ + info->device = device; +} + +/** + * \brief set UMP block ID to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param id the ID number + * + * This function is mostly used for setting the block ID to query. + */ +void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, + unsigned int id) +{ + info->block_id = id; +} + +/** + * \brief set activeness to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param active 1 if the block is active or 0 if inactive + */ +void snd_ump_block_info_set_active(snd_ump_block_info_t *info, unsigned int active) +{ + info->active = !!active; +} + +/** + * \brief set UMP block information flags to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param flags flag bits for the given UMP block + */ +void snd_ump_block_info_set_flags(snd_ump_block_info_t *info, unsigned int flags) +{ + info->flags = flags; +} + +/** + * \brief set UMP block direction to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param direction direction of UMP block (input,output,bidirectional) + */ +void snd_ump_block_info_set_direction(snd_ump_block_info_t *info, unsigned int direction) +{ + info->direction = direction; +} + +/** + * \brief set first UMP group to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param first_group the first UMP group ID belonging to the block + */ +void snd_ump_block_info_set_first_group(snd_ump_block_info_t *info, + unsigned int first_group) +{ + info->first_group = first_group; +} + +/** + * \brief set number of UMP groups to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param num_groups the number of UMP groups belonging to the block + */ +void snd_ump_block_info_set_num_groups(snd_ump_block_info_t *info, + unsigned int num_groups) +{ + info->num_groups = num_groups; +} + +/** + * \brief set MIDI-CI version number to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param version MIDI-CI version number + */ +void snd_ump_block_info_set_midi_ci_version(snd_ump_block_info_t *info, + unsigned int version) +{ + info->midi_ci_version = version; +} + +/** + * \brief set number of supported SysEx8 streams to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param streams number of supported SysEx8 streams + */ +void snd_ump_block_info_set_sysex8_streams(snd_ump_block_info_t *info, + unsigned int streams) +{ + info->sysex8_streams = streams; +} + +/** + * \brief set UI Hint to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param hint the hint bits + */ +void snd_ump_block_info_set_ui_hint(snd_ump_block_info_t *info, unsigned int hint) +{ + info->ui_hint = hint; +} + +/** + * \brief set the name string to snd_ump_block_info_t structure + * \param info pointer to a snd_ump_block_info_t structure + * \param name the name string of UMP block + */ +void snd_ump_block_info_set_name(snd_ump_block_info_t *info, + const char *name) +{ + snd_strlcpy((char *)info->name, name, sizeof(info->name)); +} + /** * \brief get UMP block information * \param ump UMP handle From 6167b8ce3e8098f3c7a0b0be7ad4b9f7e003bfc4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Jun 2024 14:35:36 +0200 Subject: [PATCH 022/117] seq: Add API helper functions for creating UMP Endpoint and Blocks For making it easer for applications to create a virtual UMP Endpoint and UMP blocks, add two API helper functions. snd_seq_create_ump_endpoint() creates (unsurprisingly) a UMP Endpoint, based on the given snd_ump_endpoint_info_t information. The number of (max) UMP groups belonging to this Endpoint has to be specified. This function sets up the Endpoint info on the sequencer client, and creates a MIDI 2.0 UMP port as well as UMP Group ports automatically. The name of the sequencer client is updated from the Endpoint name, too. After creating a UMP Endpoint, create each UMP Block via snd_seq_create_ump_block() function with a snd_ump_block_info_t info. The associated groups for each block have to be specified there. The port names and capability bits are updated accordingly after setting each block information. Signed-off-by: Takashi Iwai --- include/seqmid.h | 7 ++ include/ump.h | 6 ++ src/seq/seq.c | 6 +- src/seq/seq_local.h | 4 + src/seq/seqmid.c | 249 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 271 insertions(+), 1 deletion(-) diff --git a/include/seqmid.h b/include/seqmid.h index 4464c2d3..bf968a5b 100644 --- a/include/seqmid.h +++ b/include/seqmid.h @@ -520,6 +520,13 @@ int snd_seq_reset_pool_input(snd_seq_t *seq); ((ev)->type = SND_SEQ_EVENT_SYSEX,\ snd_seq_ev_set_variable(ev, datalen, dataptr)) +/* Helper API functions for UMP endpoint and block creations */ +int snd_seq_create_ump_endpoint(snd_seq_t *seq, + const snd_ump_endpoint_info_t *info, + unsigned int num_groups); +int snd_seq_create_ump_block(snd_seq_t *seq, int blkid, + const snd_ump_block_info_t *info); + /** \} */ #ifdef __cplusplus diff --git a/include/ump.h b/include/ump.h index 45b3ad27..01363a32 100644 --- a/include/ump.h +++ b/include/ump.h @@ -69,6 +69,9 @@ enum _snd_ump_direction { /** Bit flag for JRTS in Receive */ #define SND_UMP_EP_INFO_PROTO_JRTS_RX 0x0002 +/** Default version passed to UMP Endpoint info */ +#define SND_UMP_EP_INFO_DEFAULT_VERSION 0x0101 + size_t snd_ump_endpoint_info_sizeof(void); /** \hideinitializer * \brief allocate an invalid #snd_ump_endpoint_info_t using standard alloca @@ -125,6 +128,9 @@ enum _snd_ump_block_ui_hint { SND_UMP_BLOCK_UI_HINT_BOTH = 0x03, }; +/** Default MIDI CI version passed to UMP Block info */ +#define SND_UMP_BLOCK_INFO_DEFAULT_MIDI_CI_VERSION 0x01 + size_t snd_ump_block_info_sizeof(void); /** \hideinitializer * \brief allocate an invalid #snd_ump_block_info_t using standard alloca diff --git a/src/seq/seq.c b/src/seq/seq.c index 5eac4848..ff046814 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -1042,7 +1042,8 @@ int _snd_seq_open_lconf(snd_seq_t **seqp, const char *name, */ int snd_seq_close(snd_seq_t *seq) { - int err; + int i, err; + assert(seq); err = seq->ops->close(seq); if (seq->dl_handle) @@ -1051,6 +1052,9 @@ int snd_seq_close(snd_seq_t *seq) free(seq->ibuf); free(seq->tmpbuf); free(seq->name); + free(seq->ump_ep); + for (i = 0; i < 16; i++) + free(seq->ump_blks[i]); free(seq); return err; } diff --git a/src/seq/seq_local.h b/src/seq/seq_local.h index 46824806..26302970 100644 --- a/src/seq/seq_local.h +++ b/src/seq/seq_local.h @@ -94,6 +94,10 @@ struct _snd_seq { size_t tmpbufsize; /* size of errbuf */ size_t packet_size; /* input packet alignment size */ int midi_version; /* current protocol version */ + + unsigned int num_ump_groups; /* number of UMP groups */ + snd_ump_endpoint_info_t *ump_ep; /* optional UMP info */ + snd_ump_block_info_t *ump_blks[16]; /* optional UMP block info */ }; int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode); diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 9ec93ee8..d7eac6ca 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -493,3 +493,252 @@ int snd_seq_parse_address(snd_seq_t *seq, snd_seq_addr_t *addr, const char *arg) return 0; } +/** + * \brief create a UMP Endpoint for the given sequencer client + * \param seq sequencer handle + * \param info UMP Endpoint information to initialize + * \param num_groups max number of groups in the endpoint + * \return 0 on success or negative error code + * + * This function initializes the sequencer client to the corresponding + * MIDI 2.0 mode (either MIDI 1.0 or MIDI 2.0 protocol) depending on the + * given snd_ump_endpoint_info_t info. + * + * This function should be called right after opening a sequencer client. + * The client name is updated from the UMP Endpoint name, and a primary + * MIDI 2.0 UMP port and each UMP Group port are created. + * The application should pass each UMP block info via succeeding + * snd_seq_create_ump_block() call. + */ +int snd_seq_create_ump_endpoint(snd_seq_t *seq, + const snd_ump_endpoint_info_t *info, + unsigned int num_groups) +{ + int err, version; + unsigned int i; + snd_seq_port_info_t *pinfo; + + if (seq->ump_ep) + return -EBUSY; + + if (num_groups < 1 || num_groups > SND_UMP_MAX_GROUPS) + return -EINVAL; + + if (!(info->protocol_caps & info->protocol)) { + SNDERR("Inconsistent UMP protocol_caps and protocol\n"); + return -EINVAL; + } + + if (info->protocol & SND_UMP_EP_INFO_PROTO_MIDI2) { + version = SND_SEQ_CLIENT_UMP_MIDI_2_0; + } else if (info->protocol & SND_UMP_EP_INFO_PROTO_MIDI1) { + version = SND_SEQ_CLIENT_UMP_MIDI_1_0; + } else { + SNDERR("Invalid UMP protocol set 0x%x\n", info->protocol); + return -EINVAL; + } + + err = snd_seq_set_client_midi_version(seq, version); + if (err < 0) { + SNDERR("Failed to set to MIDI protocol 0x%x\n", version); + return err; + } + + seq->ump_ep = malloc(sizeof(*info)); + if (!seq->ump_ep) + return -ENOMEM; + + *seq->ump_ep = *info; + if (!seq->ump_ep->version) + seq->ump_ep->version = SND_UMP_EP_INFO_DEFAULT_VERSION; + + if (info->name) { + err = snd_seq_set_client_name(seq, (const char *)info->name); + if (err < 0) + goto error_free; + } + + err = snd_seq_set_ump_endpoint_info(seq, seq->ump_ep); + if (err < 0) { + SNDERR("Failed to set UMP EP info\n"); + goto error_free; + } + + snd_seq_port_info_alloca(&pinfo); + + snd_seq_port_info_set_port(pinfo, 0); + snd_seq_port_info_set_port_specified(pinfo, 1); + snd_seq_port_info_set_name(pinfo, "MIDI 2.0"); + snd_seq_port_info_set_capability(pinfo, + SNDRV_SEQ_PORT_CAP_READ | + SNDRV_SEQ_PORT_CAP_SYNC_READ | + SNDRV_SEQ_PORT_CAP_SUBS_READ | + SNDRV_SEQ_PORT_CAP_WRITE | + SNDRV_SEQ_PORT_CAP_SYNC_WRITE | + SNDRV_SEQ_PORT_CAP_SUBS_WRITE | + SNDRV_SEQ_PORT_CAP_DUPLEX); + snd_seq_port_info_set_type(pinfo, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_UMP | + SND_SEQ_PORT_TYPE_APPLICATION | + SNDRV_SEQ_PORT_TYPE_PORT); + snd_seq_port_info_set_ump_group(pinfo, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_UMP | + SND_SEQ_PORT_TYPE_APPLICATION | + SNDRV_SEQ_PORT_TYPE_PORT); + err = snd_seq_create_port(seq, pinfo); + if (err < 0) { + SNDERR("Failed to create MIDI 2.0 port\n"); + goto error_free; + } + + for (i = 0; i < num_groups; i++) { + char name[32]; + + snd_seq_port_info_set_port(pinfo, i + 1); + snd_seq_port_info_set_port_specified(pinfo, 1); + sprintf(name, "Group %d", i + 1); + snd_seq_port_info_set_capability(pinfo, 0); /* set later */ + snd_seq_port_info_set_name(pinfo, name); + snd_seq_port_info_set_ump_group(pinfo, i + 1); + err = snd_seq_create_port(seq, pinfo); + if (err < 0) { + SNDERR("Failed to create Group port %d\n", i + 1); + goto error; + } + } + + seq->num_ump_groups = num_groups; + return 0; + + error: + /* delete all ports including port 0 */ + for (i = 0; i <= num_groups; i++) + snd_seq_delete_port(seq, i); + error_free: + free(seq->ump_ep); + seq->ump_ep = NULL; + return err; +} + +/* update each port name and capability from the block list */ +static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) +{ + unsigned int i, b; + snd_seq_port_info_t *pinfo; + snd_ump_block_info_t *bp; + + snd_seq_port_info_alloca(&pinfo); + + for (i = 0; i < seq->num_ump_groups; i++) { + char blknames[64]; + char name[64]; + unsigned int caps = 0; + + blknames[0] = 0; + for (b = 0; b < ep->num_blocks; b++) { + bp = seq->ump_blks[b]; + if (!bp) + continue; + if (i < bp->first_group || + i >= bp->first_group + bp->num_groups) + continue; + switch (bp->direction) { + case SNDRV_UMP_DIR_INPUT: + caps |= SNDRV_SEQ_PORT_CAP_READ | + SNDRV_SEQ_PORT_CAP_SYNC_READ | + SNDRV_SEQ_PORT_CAP_SUBS_READ; + break; + case SNDRV_UMP_DIR_OUTPUT: + caps |= SNDRV_SEQ_PORT_CAP_WRITE | + SNDRV_SEQ_PORT_CAP_SYNC_WRITE | + SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + break; + case SNDRV_UMP_DIR_BIDIRECTION: + caps |= SNDRV_SEQ_PORT_CAP_READ | + SNDRV_SEQ_PORT_CAP_SYNC_READ | + SNDRV_SEQ_PORT_CAP_SUBS_READ | + SNDRV_SEQ_PORT_CAP_WRITE | + SNDRV_SEQ_PORT_CAP_SYNC_WRITE | + SNDRV_SEQ_PORT_CAP_SUBS_WRITE | + SNDRV_SEQ_PORT_CAP_DUPLEX; + break; + } + + if (!*bp->name) + continue; + if (*blknames) { + strlcat(blknames, ", ", sizeof(blknames)); + strlcat(blknames, (const char *)bp->name, + sizeof(blknames)); + } else { + snd_strlcpy(blknames, (const char *)bp->name, + sizeof(blknames)); + } + } + + if (!*blknames) + continue; + + snprintf(name, sizeof(name), "Group %d (%s)", i + 1, blknames); + if (snd_seq_get_port_info(seq, i + 1, pinfo) < 0) + continue; + + if (strcmp(name, snd_seq_port_info_get_name(pinfo)) || + snd_seq_port_info_get_capability(pinfo) != caps) { + snd_seq_port_info_set_name(pinfo, name); + snd_seq_port_info_set_capability(pinfo, caps); + snd_seq_set_port_info(seq, i + 1, pinfo); + } + } +} + +/** + * \brief create a UMP block for the given sequencer client + * \param seq sequencer handle + * \param blkid 0-based block id + * \param info UMP block info to initialize + * \return 0 on success or negative error code + * + * This function sets up the UMP block info of the given block id. + * The sequencer port name is updated accordingly with the associated + * block name automatically. + */ +int snd_seq_create_ump_block(snd_seq_t *seq, int blkid, + const snd_ump_block_info_t *info) +{ + snd_ump_block_info_t *bp; + snd_ump_endpoint_info_t *ep = seq->ump_ep; + int err; + + if (!ep) + return -EINVAL; + if (info->first_group >= seq->num_ump_groups || + info->first_group + info->num_groups > seq->num_ump_groups) + return -EINVAL; + if (blkid < 0 || blkid >= (int)ep->num_blocks) + return -EINVAL; + + if (seq->ump_blks[blkid]) + return -EBUSY; + seq->ump_blks[blkid] = bp = malloc(sizeof(*info)); + if (!bp) + return -ENOMEM; + *bp = *info; + + if (!bp->midi_ci_version) + bp->midi_ci_version = SND_UMP_BLOCK_INFO_DEFAULT_MIDI_CI_VERSION; + bp->active = 1; + + err = snd_seq_set_ump_block_info(seq, blkid, bp); + if (err < 0) { + SNDERR("Failed to set UMP EP info\n"); + free(bp); + seq->ump_blks[blkid] = NULL; + return err; + } + + update_group_ports(seq, ep); + return 0; +} From f784035a904c4ad19de0fbd3f531143f8f87fa06 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Jun 2024 17:22:15 +0200 Subject: [PATCH 023/117] test: Add an example program to create a virtual UMP Endpoint Provide an example program to demonstrate how to create a UMP Endpoint and Blocks, i.e. a virtual UMP device. It's a simple filtering application that just haves the incoming note on/off velocity and sends out to the output. The UMP Endpoint and Block attributes can be adjusted via command-line options. Signed-off-by: Takashi Iwai --- test/Makefile.am | 3 +- test/seq-ump-example.c | 187 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 test/seq-ump-example.c diff --git a/test/Makefile.am b/test/Makefile.am index 99c2c4ff..635fa39b 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ SUBDIRS=. lsb -check_PROGRAMS=control pcm pcm_min latency seq \ +check_PROGRAMS=control pcm pcm_min latency seq seq-ump-example \ playmidi1 timer rawmidi midiloop \ oldapi queue_timer namehint client_event_filter \ chmap audio_time user-ctl-element-set pcm-multi-thread @@ -12,6 +12,7 @@ pcm_min_LDADD=../src/libasound.la latency_LDADD=../src/libasound.la latency_LDFLAGS= -lm seq_LDADD=../src/libasound.la +seq_ump_example_LDADD=../src/libasound.la playmidi1_LDADD=../src/libasound.la timer_LDADD=../src/libasound.la rawmidi_LDADD=../src/libasound.la diff --git a/test/seq-ump-example.c b/test/seq-ump-example.c new file mode 100644 index 00000000..7f628682 --- /dev/null +++ b/test/seq-ump-example.c @@ -0,0 +1,187 @@ +// An example program to create a virtual UMP Endpoint +// +// A client simply reads each UMP packet and sends to subscribers +// while the note on/off velocity is halved + +#include +#include +#include +#include + +/* make the note on/off velocity half for MIDI1 CVM */ +static void midi1_half_note_velocity(snd_seq_ump_event_t *ev) +{ + snd_ump_msg_midi1_t *midi1 = (snd_ump_msg_midi1_t *)ev->ump; + + switch (snd_ump_msg_status(ev->ump)) { + case SND_UMP_MSG_NOTE_OFF: + case SND_UMP_MSG_NOTE_ON: + midi1->note_on.velocity >>= 1; + break; + } +} + +/* make the note on/off velocity half for MIDI2 CVM */ +static void midi2_half_note_velocity(snd_seq_ump_event_t *ev) +{ + snd_ump_msg_midi2_t *midi2 = (snd_ump_msg_midi2_t *)ev->ump; + + switch (snd_ump_msg_status(ev->ump)) { + case SND_UMP_MSG_NOTE_OFF: + case SND_UMP_MSG_NOTE_ON: + midi2->note_on.velocity >>= 1; + break; + } +} + +static void help(void) +{ + printf("seq-ump-example: Create a virtual UMP Endpoint and Blocks\n" + "\n" + "Usage: seq-ump-example [OPTIONS]\n" + "\n" + "-n,--num-blocks blocks Number of blocks (groups) to create\n" + "-m,--midi-version version MIDI protocol version (1 or 2)\n" + "-N--name UMP Endpoint name string\n" + "-P,--product name UMP Product ID string\n" + "-M,--manufacturer id UMP Manufacturer ID value (24bit)\n" + "-F,--family id UMP Family ID value (16bit)\n" + "-O,--model id UMP Model ID value (16bit)\n" + "-R,--sw-revision id UMP Software Revision ID (32bit)\n"); +} + +int main(int argc, char **argv) +{ + int midi_version = 2; + int num_blocks = 1; + const char *name = "ACMESynth"; + const char *product = "Halfmoon"; + unsigned int manufacturer = 0x123456; + unsigned int family = 0x1234; + unsigned int model = 0xabcd; + unsigned int sw_revision = 0x12345678; + snd_seq_t *seq; + snd_ump_endpoint_info_t *ep; + snd_ump_block_info_t *blk; + snd_seq_ump_event_t *ev; + int i, c, err; + unsigned char tmp[4]; + + static const struct option long_option[] = { + {"num-blocks", required_argument, 0, 'n'}, + {"midi-version", required_argument, 0, 'm'}, + {"name", required_argument, 0, 'N'}, + {"product", required_argument, 0, 'P'}, + {"manufacturer", required_argument, 0, 'M'}, + {"family", required_argument, 0, 'F'}, + {"model", required_argument, 0, 'O'}, + {"sw-revision", required_argument, 0, 'R'}, + {0, 0, 0, 0} + }; + + while ((c = getopt_long(argc, argv, "n:m:N:P:M:F:O:R:", + long_option, NULL)) >= 0) { + switch (c) { + case 'n': + num_blocks = atoi(optarg); + break; + case 'm': + midi_version = atoi(optarg); + break; + case 'N': + name = optarg; + break; + case 'P': + product = optarg; + break; + case 'M': + manufacturer = strtol(optarg, NULL, 0); + break; + case 'F': + family = strtol(optarg, NULL, 0); + break; + case 'O': + model = strtol(optarg, NULL, 0); + break; + case 'R': + sw_revision = strtol(optarg, NULL, 0); + break; + default: + help(); + return 1; + } + } + + err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0); + if (err < 0) { + fprintf(stderr, "failed to open sequencer: %d\n", err); + return 1; + } + + snd_ump_endpoint_info_alloca(&ep); + snd_ump_endpoint_info_set_name(ep, name); + snd_ump_endpoint_info_set_product_id(ep, product); + if (midi_version == 1) { + snd_ump_endpoint_info_set_protocol_caps(ep, SND_UMP_EP_INFO_PROTO_MIDI1); + snd_ump_endpoint_info_set_protocol(ep, SND_UMP_EP_INFO_PROTO_MIDI1); + } else { + snd_ump_endpoint_info_set_protocol_caps(ep, SND_UMP_EP_INFO_PROTO_MIDI2); + snd_ump_endpoint_info_set_protocol(ep, SND_UMP_EP_INFO_PROTO_MIDI2); + } + snd_ump_endpoint_info_set_num_blocks(ep, num_blocks); + snd_ump_endpoint_info_set_manufacturer_id(ep, manufacturer); + snd_ump_endpoint_info_set_family_id(ep, family); + snd_ump_endpoint_info_set_model_id(ep, model); + for (i = 0; i < 4; i++) + tmp[i] = (sw_revision >> ((3 - i) * 8)) & 0xff; + snd_ump_endpoint_info_set_sw_revision(ep, tmp); + + err = snd_seq_create_ump_endpoint(seq, ep, num_blocks); + if (err < 0) { + fprintf(stderr, "failed to set UMP EP info: %d\n", err); + return 1; + } + + snd_ump_block_info_alloca(&blk); + + for (i = 0; i < num_blocks; i++) { + char blkname[32]; + + sprintf(blkname, "Filter %d", i + 1); + snd_ump_block_info_set_name(blk, blkname); + snd_ump_block_info_set_direction(blk, SND_UMP_DIR_BIDIRECTION); + snd_ump_block_info_set_first_group(blk, i); + snd_ump_block_info_set_num_groups(blk, 1); + snd_ump_block_info_set_ui_hint(blk, SND_UMP_BLOCK_UI_HINT_BOTH); + + err = snd_seq_create_ump_block(seq, i, blk); + if (err < 0) { + fprintf(stderr, "failed to set UMP block info %d: %d\n", + i, err); + return 1; + } + } + + /* halve the incoming note-on / off velocity and pass through + * to subscribers + */ + while (snd_seq_ump_event_input(seq, &ev) >= 0) { + if (!snd_seq_ev_is_ump(ev)) + continue; + switch (snd_ump_msg_type(ev->ump)) { + case SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE: + midi1_half_note_velocity(ev); + break; + case SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE: + midi2_half_note_velocity(ev); + break; + } + + snd_seq_ev_set_subs(ev); + snd_seq_ev_set_direct(ev); + snd_seq_ump_event_output(seq, ev); + snd_seq_drain_output(seq); + } + + return 0; +} From cacc5bd5c1403a6436bd687961c5cf5fa223b0a2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Jun 2024 17:25:23 +0200 Subject: [PATCH 024/117] Add test/seq-ump-example to .gitignore Signed-off-by: Takashi Iwai --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a74b5195..947eba45 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,7 @@ test/playmidi1 test/queue_timer test/rawmidi test/seq +test/seq-ump-example test/timer test/lsb/config test/lsb/midi_event From ef38bff00ea303b32e872a459cbe5c8b24416cd4 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 21 Jun 2024 16:30:06 +0200 Subject: [PATCH 025/117] conf: fix snd_config_substitute (for src->parent) If source configuration node has a parent set, it must be always detached to avoid memory corruptions. Signed-off-by: Jaroslav Kysela --- src/conf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/conf.c b/src/conf.c index eca44c03..468d41f5 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1729,7 +1729,7 @@ static int _snd_config_save_children(snd_config_t *config, snd_output_t *out, */ int snd_config_substitute(snd_config_t *dst, snd_config_t *src) { - assert(dst && src); + assert(dst && src && src != dst); if (dst->type == SND_CONFIG_TYPE_COMPOUND) { int err = snd_config_delete_compound_members(dst); if (err < 0) @@ -1748,6 +1748,8 @@ int snd_config_substitute(snd_config_t *dst, snd_config_t *src) free(dst->id); if (dst->type == SND_CONFIG_TYPE_STRING) free(dst->u.string); + if (src->parent) /* like snd_config_remove */ + list_del(&src->list); dst->id = src->id; dst->type = src->type; dst->u = src->u; @@ -2310,7 +2312,6 @@ int snd_config_merge(snd_config_t *dst, snd_config_t *src, int override) if (override || sn->type != SND_CONFIG_TYPE_COMPOUND || dn->type != SND_CONFIG_TYPE_COMPOUND) { - snd_config_remove(sn); err = snd_config_substitute(dn, sn); if (err < 0) return err; From 6da898cf40c90ea1aaf91663dc769eb61025229d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 25 Jun 2024 10:44:41 +0200 Subject: [PATCH 026/117] ump: Fix doxygen error for snd_ump_endpoint_info_set_protocol() Fix a typo that caused a doxygen error. Fixes: 6767f623ca05 ("ump: Add missing *_set variants for snd_ump_endpoint_info and snd_ump_block_info") Signed-off-by: Takashi Iwai --- src/rawmidi/ump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index 6c1097a7..88c9a26b 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -536,7 +536,7 @@ void snd_ump_endpoint_info_set_protocol_caps(snd_ump_endpoint_info_t *info, /** * \brief set the current protocol of UMP endpoint * \param info pointer to a snd_ump_endpoint_info_t structure - * \param caps the UMP endpoint protocol bits + * \param protocol the UMP endpoint protocol bits */ void snd_ump_endpoint_info_set_protocol(snd_ump_endpoint_info_t *info, unsigned int protocol) From 3390f31664d4926cdd469a7996181903ea19d5d4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 25 Jun 2024 10:46:01 +0200 Subject: [PATCH 027/117] seq: Add description about MIDI 2.0 and UMP handling Add more description of the new ALSA sequencer features for MIDI 2.0 and UMP handling. Signed-off-by: Takashi Iwai --- src/seq/seq.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/seq/seq.c b/src/seq/seq.c index ff046814..0da1e891 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -775,6 +775,67 @@ void event_filter(snd_seq_t *seq, snd_seq_event_t *ev) } \endcode +\section MIDI 2.0 and UMP + +\subsection Extension for UMP + +The recent extension of ALSA sequencer is the support for MIDI 2.0 and +UMP (Universal MIDI Packet) handling. + +A sequencer client is opened as usual with #snd_seq_open(). +Then the application can switch to UMP mode via +#snd_seq_set_client_midi_version() (or #snd_seq_client_info_set_midi_version() +and #snd_seq_set_client_info() combo). +For running in UMP MIDI 1.0 protocol, pass #SND_SEQ_CLIENT_UMP_MIDI_1_0, +and for UMP MIDI 2.0 protocol, pass #SND_SEQ_CLIENT_UMP_MIDI_2_0, respectively. + +In either UMP mode, we use #snd_seq_ump_event_t for sequencer event records +instead of #snd_seq_event_t due to the lack of the data payload size in the +latter type. #snd_seq_ump_event_t contains the array of 32bit int as its +data payload for UMP, defined as a union of the existing data payload of +#snd_seq_event_data_t. Other than the extended data payload, all other fields +are identical with #snd_seq_event_t. + +There are corresponding API helpers for #snd_seq_ump_event_t, such as +#snd_seq_ump_event_input() and #snd_seq_ump_event_input(). +Most of macros such as #snd_seq_ev_set_dest() can be used for both +#snd_seq_event_t and #snd_seq_ump_event_t types since they are C-macro +without explicit type checks. + +When a standard MIDI event (such as note-on or channel control) is sent to a +UMP sequencer client, it's automatically translated to a UMP packet embedded +in #snd_seq_ump_event_t. Ditto about sending from a UMP sequencer client to +a legacy sequencer client; the UMP event is translated to an old-type event +like #SND_SEQ_EVENT_NOTEON type automatically, too. +The translation between UMP MIDI 1.0 and MIDI 2.0 is done in ALSA core +as well. That is, from the application POV, connections are pretty seamless, +and applications can keep running just like before, no matter whether the +target is UMP or not. + +For encoding and decoding a UMP packet data, there are structs and macros +defined in sound/ump_msg.h. + +MIDI 2.0 devices provide extra information as UMP Endpoint and UMP Function +Block information. Those information can be obtained via +#snd_seq_get_ump_endpoint_info() and #snd_seq_get_ump_block_info() from a +sequencer client. + +\subsection Creation of UMP Virtual Endpoint and Function Blocks + +For making it easier to create a virtual MIDI 2.0 device in user-space, +there are a couple of new API functions. For creating a UMP Endpoint in an +application, call snd_seq_create_ump_endpoint() with a properly filled +#snd_ump_endpoint_info data. Usually it should be called right after +#snd_seq_open() call. The call of #snd_seq_create_ump_endpoint() switches +to the corresponding MIDI protocol, and you don't need to call +#snd_seq_set_client_midi_version(). It will create a sequencer port +corresponding to UMP Endpoint (named as "MIDI 2.0") and sequencer ports +for the UMP Groups, too. + +For creating a UMP Function Block, call snd_seq_create_ump_block() with a +properly filled #snd_ump_block_info data. This will update the corresponding +sequencer ports accordingly, too. + */ #include "seq_local.h" From 8da704ef4f409f82307dc7bd54bede6961dec358 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 26 Jun 2024 11:20:18 +0200 Subject: [PATCH 028/117] seq: Correct section descriptions for UMP The usage of \section and \subsection were wrong in the previous commit, where the tags were missing. Fixes: 3390f31664d4 ("seq: Add description about MIDI 2.0 and UMP handling") Signed-off-by: Takashi Iwai --- src/seq/seq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/seq/seq.c b/src/seq/seq.c index 0da1e891..a18f67e2 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -775,9 +775,9 @@ void event_filter(snd_seq_t *seq, snd_seq_event_t *ev) } \endcode -\section MIDI 2.0 and UMP +\section seq_midi2 MIDI 2.0 and UMP -\subsection Extension for UMP +\subsection seq_midi2_extension Extension for UMP The recent extension of ALSA sequencer is the support for MIDI 2.0 and UMP (Universal MIDI Packet) handling. @@ -820,7 +820,7 @@ Block information. Those information can be obtained via #snd_seq_get_ump_endpoint_info() and #snd_seq_get_ump_block_info() from a sequencer client. -\subsection Creation of UMP Virtual Endpoint and Function Blocks +\subsection seq_midi2_virtual_ump Creation of UMP Virtual Endpoint and Function Blocks For making it easier to create a virtual MIDI 2.0 device in user-space, there are a couple of new API functions. For creating a UMP Endpoint in an From 18579adfbc0ec95058ec6a4d0e2afdd317ac0a8f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 26 Jun 2024 11:22:06 +0200 Subject: [PATCH 029/117] ump_msg.h: Fix doxygen comments Correct the lack of markers and missing comments for some unions. Fixes: 040356ecf06b ("ump: Add helpers to parse / set UMP packet data") Signed-off-by: Takashi Iwai --- include/ump_msg.h | 84 +++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/include/ump_msg.h b/include/ump_msg.h index e0264f6e..2defea6b 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -60,11 +60,11 @@ typedef struct _snd_ump_msg_midi1_paf { uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ uint8_t channel:4; /**< Channel */ - uint8_t note; /** Note (7bit) */ - uint8_t data; /** Pressure (7bit) */ + uint8_t note; /**< Note (7bit) */ + uint8_t data; /**< Pressure (7bit) */ #else - uint8_t data; /** Pressure (7bit) */ - uint8_t note; /** Note (7bit) */ + uint8_t data; /**< Pressure (7bit) */ + uint8_t note; /**< Note (7bit) */ uint8_t channel:4; /**< Channel */ uint8_t status:4; /**< Status */ uint8_t group:4; /**< UMP Group */ @@ -79,11 +79,11 @@ typedef struct _snd_ump_msg_midi1_cc { uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ uint8_t channel:4; /**< Channel */ - uint8_t index; /** Control index (7bit) */ - uint8_t data; /** Control data (7bit) */ + uint8_t index; /**< Control index (7bit) */ + uint8_t data; /**< Control data (7bit) */ #else - uint8_t data; /** Control data (7bit) */ - uint8_t index; /** Control index (7bit) */ + uint8_t data; /**< Control data (7bit) */ + uint8_t index; /**< Control index (7bit) */ uint8_t channel:4; /**< Channel */ uint8_t status:4; /**< Status */ uint8_t group:4; /**< UMP Group */ @@ -167,16 +167,16 @@ typedef struct snd_ump_msg_system { /** MIDI 1.0 UMP CVM (32bit) */ typedef union _snd_ump_msg_midi1 { - snd_ump_msg_midi1_note_t note_on; - snd_ump_msg_midi1_note_t note_off; - snd_ump_msg_midi1_paf_t poly_pressure; - snd_ump_msg_midi1_cc_t control_change; - snd_ump_msg_midi1_program_t program_change; - snd_ump_msg_midi1_caf_t channel_pressure; - snd_ump_msg_midi1_pitchbend_t pitchbend; - snd_ump_msg_system_t system; - snd_ump_msg_hdr_t hdr; - uint32_t raw; + snd_ump_msg_midi1_note_t note_on; /**< MIDI1 note-on message */ + snd_ump_msg_midi1_note_t note_off; /**< MIDI1 note-off message */ + snd_ump_msg_midi1_paf_t poly_pressure; /**< MIDI1 poly-pressure message */ + snd_ump_msg_midi1_cc_t control_change; /**< MIDI1 control-change message */ + snd_ump_msg_midi1_program_t program_change; /**< MIDI1 program-change message */ + snd_ump_msg_midi1_caf_t channel_pressure; /**< MIDI1 channel-pressure message */ + snd_ump_msg_midi1_pitchbend_t pitchbend; /**< MIDI1 pitch-bend message */ + snd_ump_msg_system_t system; /**< system message */ + snd_ump_msg_hdr_t hdr; /**< UMP header */ + uint32_t raw; /**< raw UMP packet */ } snd_ump_msg_midi1_t; /** MIDI 2.0 Note-on/off attribute type */ @@ -187,7 +187,7 @@ enum { SND_UMP_MIDI2_NOTE_ATTR_PITCH79 = 0x03, /**< Pitch 7.9 */ }; -/* MIDI 2.0 Note Off / Note On (64bit) */ +/** MIDI 2.0 Note Off / Note On (64bit) */ typedef struct _snd_ump_msg_midi2_note { #ifdef SNDRV_BIG_ENDIAN_BITFIELD uint8_t type:4; /**< UMP packet type */ @@ -371,7 +371,7 @@ typedef struct _snd_ump_msg_midi2_caf { uint8_t channel:4; /**< Channel */ uint16_t reserved; /**< Unused */ - uint32_t data; /** Data (32bit) */ + uint32_t data; /**< Data (32bit) */ #else uint16_t reserved; /**< Unused */ uint8_t channel:4; /**< Channel */ @@ -379,11 +379,11 @@ typedef struct _snd_ump_msg_midi2_caf { uint8_t group:4; /**< UMP Group */ uint8_t type:4; /**< UMP packet type */ - uint32_t data; /** Data (32bit) */ + uint32_t data; /**< Data (32bit) */ #endif } __attribute((packed)) snd_ump_msg_midi2_caf_t; -/* MIDI 2.0 Pitch Bend (64bit) */ +/** MIDI 2.0 Pitch Bend (64bit) */ typedef struct _snd_ump_msg_midi2_pitchbend { #ifdef SNDRV_BIG_ENDIAN_BITFIELD uint8_t type:4; /**< UMP packet type */ @@ -392,7 +392,7 @@ typedef struct _snd_ump_msg_midi2_pitchbend { uint8_t channel:4; /**< Channel */ uint16_t reserved; /**< Unused */ - uint32_t data; /** Data (32bit) */ + uint32_t data; /**< Data (32bit) */ #else uint16_t reserved; /**< Unused */ uint8_t channel:4; /**< Channel */ @@ -400,11 +400,11 @@ typedef struct _snd_ump_msg_midi2_pitchbend { uint8_t group:4; /**< UMP Group */ uint8_t type:4; /**< UMP packet type */ - uint32_t data; /** Data (32bit) */ + uint32_t data; /**< Data (32bit) */ #endif } __attribute((packed)) snd_ump_msg_midi2_pitchbend_t; -/* MIDI 2.0 Per-Note Pitch Bend (64bit) */ +/** MIDI 2.0 Per-Note Pitch Bend (64bit) */ typedef struct _snd_ump_msg_midi2_per_note_pitchbend { #ifdef __BIG_ENDIAN_BITFIELD uint8_t type:4; /**< UMP packet type */ @@ -430,23 +430,23 @@ typedef struct _snd_ump_msg_midi2_per_note_pitchbend { /** MIDI2 UMP packet (64bit little-endian) */ typedef union _snd_ump_msg_midi2 { - snd_ump_msg_midi2_note_t note_on; - snd_ump_msg_midi2_note_t note_off; - snd_ump_msg_midi2_paf_t poly_pressure; - snd_ump_msg_midi2_per_note_cc_t per_note_acc; - snd_ump_msg_midi2_per_note_cc_t per_note_rcc; - snd_ump_msg_midi2_per_note_mgmt_t per_note_mgmt; - snd_ump_msg_midi2_cc_t control_change; - snd_ump_msg_midi2_rpn_t rpn; - snd_ump_msg_midi2_rpn_t nrpn; - snd_ump_msg_midi2_rpn_t relative_rpn; - snd_ump_msg_midi2_rpn_t relative_nrpn; - snd_ump_msg_midi2_program_t program_change; - snd_ump_msg_midi2_caf_t channel_pressure; - snd_ump_msg_midi2_pitchbend_t pitchbend; - snd_ump_msg_midi2_per_note_pitchbend_t per_note_pitchbend; - snd_ump_msg_hdr_t hdr; - uint32_t raw[2]; + snd_ump_msg_midi2_note_t note_on; /**< MIDI2 note-on message */ + snd_ump_msg_midi2_note_t note_off; /**< MIDI2 note-off message */ + snd_ump_msg_midi2_paf_t poly_pressure; /**< MIDI2 poly-pressure message */ + snd_ump_msg_midi2_per_note_cc_t per_note_acc; /**< MIDI2 per-note ACC message */ + snd_ump_msg_midi2_per_note_cc_t per_note_rcc; /**< MIDI2 per-note RCC message */ + snd_ump_msg_midi2_per_note_mgmt_t per_note_mgmt; /**< MIDI2 per-note management message */ + snd_ump_msg_midi2_cc_t control_change; /**< MIDI2 control-change message */ + snd_ump_msg_midi2_rpn_t rpn; /**< MIDI2 RPN message */ + snd_ump_msg_midi2_rpn_t nrpn; /**< MIDI2 NRPN message */ + snd_ump_msg_midi2_rpn_t relative_rpn; /**< MIDI2 relative-RPN message */ + snd_ump_msg_midi2_rpn_t relative_nrpn; /**< MIDI2 relative-NRPN message */ + snd_ump_msg_midi2_program_t program_change; /**< MIDI2 program-change message */ + snd_ump_msg_midi2_caf_t channel_pressure; /**< MIDI2 channel-pressure message */ + snd_ump_msg_midi2_pitchbend_t pitchbend; /**< MIDI2 pitch-bend message */ + snd_ump_msg_midi2_per_note_pitchbend_t per_note_pitchbend; /**< MIDI2 per-note pitch-bend message */ + snd_ump_msg_hdr_t hdr; /**< UMP header */ + uint32_t raw[2]; /**< raw UMP packet */ } snd_ump_msg_midi2_t; /** From f8df0235979c14a47822b68622c0872fa923685e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 26 Jun 2024 11:23:34 +0200 Subject: [PATCH 030/117] ump: Add descriptions for UMP RawMidi interface Also update doxygen config accordingly. Signed-off-by: Takashi Iwai --- doc/doxygen.cfg.in | 2 ++ include/ump.h | 8 ++++++++ src/rawmidi/ump.c | 23 +++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in index 8bf29b21..bcc3a33e 100644 --- a/doc/doxygen.cfg.in +++ b/doc/doxygen.cfg.in @@ -17,6 +17,8 @@ INPUT = @top_srcdir@/doc/index.doxygen \ @top_srcdir@/include/control.h \ @top_srcdir@/include/pcm.h \ @top_srcdir@/include/rawmidi.h \ + @top_srcdir@/include/ump.h \ + @top_srcdir@/include/ump_msg.h \ @top_srcdir@/include/timer.h \ @top_srcdir@/include/hwdep.h \ @top_srcdir@/include/seq.h \ diff --git a/include/ump.h b/include/ump.h index 01363a32..1043d237 100644 --- a/include/ump.h +++ b/include/ump.h @@ -13,6 +13,12 @@ extern "C" { #endif +/** + * \defgroup RawMidi RawMidi Interface + * The RawMidi Interface. See \ref rawmidi page for more details. + * \{ + */ + /** UMP (Endpoint) RawMIDI device */ typedef struct _snd_ump snd_ump_t; /** UMP Endpoint information container */ @@ -168,6 +174,8 @@ void snd_ump_block_info_set_sysex8_streams(snd_ump_block_info_t *info, unsigned void snd_ump_block_info_set_ui_hint(snd_ump_block_info_t *info, unsigned int hint); void snd_ump_block_info_set_name(snd_ump_block_info_t *info, const char *name); +/** \} */ + #ifdef __cplusplus } #endif diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index 88c9a26b..fddb60c2 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -7,6 +7,29 @@ #include "rawmidi_local.h" #include "ump_local.h" +/*! \page rawmidi RawMidi interface + +\section rawmidi_ump UMP RawMidi Interface + +MIDI 2.0 devices have a different type of interface, communicating with +UMP (Universal MIDI Packet). For those devices, ALSA-library provides +API functions for accessing the raw UMP packet directly via the existing +RawMidi interface. + +#snd_ump_open() is the API function for opening a UMP RawMidi interface. +It works just like #snd_rawmidi_open() but for UMP devices. Similarly, +#snd_ump_close() is for closing, and there are other equivalent API functions +corresponding to the RawMidi ones. + +The new stuff for UMP is UMP Endpoint and UMP Function Blocks. The information +from Endpoint and Function Blocks can be obtained via #snd_ump_endpoint_info() +and #snd_ump_block_info() API functions. + +The objects #snd_ump_endpoint_info_t and #snd_ump_block_info_t are used for +creating a virtual UMP Endpoint and Function Blocks via ALSA sequencer, too. + +*/ + static int get_rawmidi_flags(snd_ump_t *ump) { snd_rawmidi_info_t info; From 2071eb8a442aad71e4e7ccbbdff69be804845fef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 26 Jun 2024 11:39:06 +0200 Subject: [PATCH 031/117] src/Versions.in: Add new seq / rawmidi functions for UMP Signed-off-by: Takashi Iwai --- src/Versions.in.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Versions.in.in b/src/Versions.in.in index 98f36ded..18021990 100644 --- a/src/Versions.in.in +++ b/src/Versions.in.in @@ -193,3 +193,14 @@ ALSA_1.2.10 { @SYMBOL_PREFIX@snd_seq_set_client_midi_version; @SYMBOL_PREFIX@snd_seq_set_client_ump_conversion; } ALSA_1.2.9; + +ALSA_1.2.13 { + global: + + @SYMBOL_PREFIX@snd_seq_create_ump_endpoint; + @SYMBOL_PREFIX@snd_seq_create_ump_block; + @SYMBOL_PREFIX@snd_ump_endpoint_info_clear; + @SYMBOL_PREFIX@snd_ump_endpoint_info_set_*; + @SYMBOL_PREFIX@snd_ump_block_info_clear; + @SYMBOL_PREFIX@snd_ump_block_info_set_*; +} ALSA_1.2.10; From 32e2c8d8a27f572d9738b947b0f2d81e94fbe356 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 26 Jun 2024 11:46:35 +0200 Subject: [PATCH 032/117] src/Versions.in: Add guards for sequencer and rawmidi syms Similarly like PCM and others, add the ifdef guards for new symbols for sequencer and rawmidi interfaces. Signed-off-by: Takashi Iwai --- src/Makefile.am | 2 ++ src/Versions.in.in | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/Makefile.am b/src/Makefile.am index 74a108dc..f8905343 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,6 +36,7 @@ endif if BUILD_RAWMIDI SUBDIRS += rawmidi libasound_la_LIBADD += rawmidi/librawmidi.la +VERSION_CPPFLAGS += -DHAVE_RAWMIDI_SYMS endif if BUILD_HWDEP SUBDIRS += hwdep @@ -44,6 +45,7 @@ endif if BUILD_SEQ SUBDIRS += seq libasound_la_LIBADD += seq/libseq.la +VERSION_CPPFLAGS += -DHAVE_SEQ_SYMS endif if BUILD_UCM SUBDIRS += ucm diff --git a/src/Versions.in.in b/src/Versions.in.in index 18021990..c945d6dc 100644 --- a/src/Versions.in.in +++ b/src/Versions.in.in @@ -169,10 +169,13 @@ ALSA_1.2.9 { ALSA_1.2.10 { global: +#ifdef HAVE_RAWMIDI_SYMS @SYMBOL_PREFIX@snd_ump_*; +#endif @SYMBOL_PREFIX@snd_ctl_ump_next_device; @SYMBOL_PREFIX@snd_ctl_ump_endpoint_info; @SYMBOL_PREFIX@snd_ctl_ump_block_info; +#ifdef HAVE_SEQ_SYMS @SYMBOL_PREFIX@snd_seq_ump_*; @SYMBOL_PREFIX@snd_seq_client_info_get_midi_version; @SYMBOL_PREFIX@snd_seq_client_info_get_ump_group_enabled; @@ -192,15 +195,22 @@ ALSA_1.2.10 { @SYMBOL_PREFIX@snd_seq_port_info_set_ump_group; @SYMBOL_PREFIX@snd_seq_set_client_midi_version; @SYMBOL_PREFIX@snd_seq_set_client_ump_conversion; +#endif } ALSA_1.2.9; ALSA_1.2.13 { +#if defined(HAVE_SEQ_SYMS) || defined(HAVE_RAWMIDI_SYMS) global: +#endif +#ifdef HAVE_SEQ_SYMS @SYMBOL_PREFIX@snd_seq_create_ump_endpoint; @SYMBOL_PREFIX@snd_seq_create_ump_block; +#endif +#ifdef HAVE_RAWMIDI_SYMS @SYMBOL_PREFIX@snd_ump_endpoint_info_clear; @SYMBOL_PREFIX@snd_ump_endpoint_info_set_*; @SYMBOL_PREFIX@snd_ump_block_info_clear; @SYMBOL_PREFIX@snd_ump_block_info_set_*; +#endif } ALSA_1.2.10; From 8734673c21cfa1da0d1fa2c83fb5d4328f7c1c59 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 26 Jun 2024 16:53:17 +0200 Subject: [PATCH 033/117] ump_msg: Fix the wrong snd_ump_msg_system_t argument in little-endian For the little-endian format, the parameters are stored incorrectly in snd_ump_msg_system_t type. Swap the both parameter positions to correct to the right positions. Fixes: 040356ecf06b ("ump: Add helpers to parse / set UMP packet data") Signed-off-by: Takashi Iwai --- include/ump_msg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ump_msg.h b/include/ump_msg.h index 2defea6b..068f8860 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -157,8 +157,8 @@ typedef struct snd_ump_msg_system { uint8_t parm1; /**< First parameter */ uint8_t parm2; /**< Second parameter */ #else - uint8_t parm1; /**< First parameter */ uint8_t parm2; /**< Second parameter */ + uint8_t parm1; /**< First parameter */ uint8_t status; /**< Status */ uint8_t group:4; /**< UMP Group */ uint8_t type:4; /**< UMP packet type */ From 30f8ba74c5a2a7d2fa3923422f62366268d6cce3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2024 09:44:53 +0200 Subject: [PATCH 034/117] ump_msg: Add definitions for Utility, Stream and Flex Data messages This patch provides more struct / union definitions for UMP Utility, Stream and Flex Data messages, provided in the new UMP v1.1 specification. Signed-off-by: Takashi Iwai --- include/ump_msg.h | 347 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) diff --git a/include/ump_msg.h b/include/ump_msg.h index 068f8860..c615f33a 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -449,6 +449,299 @@ typedef union _snd_ump_msg_midi2 { uint32_t raw[2]; /**< raw UMP packet */ } snd_ump_msg_midi2_t; +/** Stream Message (generic) (128bit) */ +typedef struct _snd_ump_msg_stream_gen { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint16_t type:4; /**< UMP packet type */ + uint16_t format:2; /**< Format */ + uint16_t status:10; /**< Status */ + uint16_t data1; /**< Data */ + uint32_t data2; /**< Data */ + uint32_t data3; /**< Data */ + uint32_t data4; /**< Data */ +#else + uint16_t data1; /**< Data */ + uint16_t status:10; /**< Status */ + uint16_t format:2; /**< Format */ + uint16_t type:4; /**< UMP packet type */ + uint32_t data2; /**< Data */ + uint32_t data3; /**< Data */ + uint32_t data4; /**< Data */ +#endif +} __attribute((packed)) snd_ump_msg_stream_gen_t; + +/** Stream Message (128bit) */ +typedef union _snd_ump_msg_stream { + snd_ump_msg_stream_gen_t gen; /**< Generic Stream message */ + snd_ump_msg_hdr_t hdr; /**< UMP header */ + uint32_t raw[4]; /**< raw UMP packet */ +} snd_ump_msg_stream_t; + +/** Metadata / Text Message (128bit) */ +typedef struct _snd_ump_msg_flex_data_meta { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t format:2; /**< Format */ + uint8_t addrs:2; /**< Address */ + uint8_t channel:4; /**< Channel */ + uint8_t status_bank; /**< Status Bank */ + uint8_t status; /**< Status */ + uint32_t data[3]; /**< Data (96 bits) */ +#else + uint8_t status; /**< Status */ + uint8_t status_bank; /**< Status Bank */ + uint8_t channel:4; /**< Channel */ + uint8_t addrs:2; /**< Address */ + uint8_t format:2; /**< Format */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + uint32_t data[3]; /**< Data (96 bits) */ +#endif +} __attribute((packed)) snd_ump_msg_flex_data_meta_t; + +/** Set Tempo Message (128bit) */ +typedef struct _snd_ump_msg_set_tempo { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t format:2; /**< Format */ + uint8_t addrs:2; /**< Address */ + uint8_t channel:4; /**< Channel */ + uint8_t status_bank; /**< Status Bank */ + uint8_t status; /**< Status */ + + uint32_t tempo; /**< Number of 10nsec units per quarter note */ + + uint32_t reserved[2]; /**< Unused */ +#else + uint8_t status; /**< Status */ + uint8_t status_bank; /**< Status Bank */ + uint8_t channel:4; /**< Channel */ + uint8_t addrs:2; /**< Address */ + uint8_t format:2; /**< Format */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t tempo; /**< Number of 10nsec units per quarter note */ + + uint32_t reserved[2]; /**< Unused */ +#endif +} __attribute((packed)) snd_ump_msg_set_tempo_t; + +/** Set Time Signature Message (128bit) */ +typedef struct _snd_ump_msg_set_time_sig { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t format:2; /**< Format */ + uint8_t addrs:2; /**< Address */ + uint8_t channel:4; /**< Channel */ + uint8_t status_bank; /**< Status Bank */ + uint8_t status; /**< Status */ + + uint8_t numerator; /**< Numerator */ + uint8_t denominator; /**< Denominator */ + uint8_t num_notes; /**< Number of 1/32 notes */ + uint8_t reserved1; /**< Unused */ + + uint32_t reserved[2]; /**< Unused */ +#else + uint8_t status; /**< Status */ + uint8_t status_bank; /**< Status Bank */ + uint8_t channel:4; /**< Channel */ + uint8_t addrs:2; /**< Address */ + uint8_t format:2; /**< Format */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint8_t reserved1; /**< Unused */ + uint8_t num_notes; /**< Number of 1/32 notes */ + uint8_t denominator; /**< Denominator */ + uint8_t numerator; /**< Numerator */ + + uint32_t reserved[2]; /**< Unused */ +#endif +} __attribute((packed)) snd_ump_msg_set_time_sig_t; + +/** Set Metronome Message (128bit) */ +typedef struct _snd_ump_msg_set_metronome { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t format:2; /**< Format */ + uint8_t addrs:2; /**< Address */ + uint8_t channel:4; /**< Channel */ + uint8_t status_bank; /**< Status Bank */ + uint8_t status; /**< Status */ + + uint8_t clocks_primary; /**< Num clocks per primary clock */ + uint8_t bar_accent_1; /**< Bar accent part 1 */ + uint8_t bar_accent_2; /**< Bar accent part 2 */ + uint8_t bar_accent_3; /**< Bar accent part 3 */ + + uint8_t subdivision_1; /**< Num subdivision clicks 1 */ + uint8_t subdivision_2; /**< Num subdivision clicks 1 */ + uint16_t reserved1; /**< Unused */ + + uint32_t reserved2; /**< Unused */ +#else + uint8_t status; /**< Status */ + uint8_t status_bank; /**< Status Bank */ + uint8_t channel:4; /**< Channel */ + uint8_t addrs:2; /**< Address */ + uint8_t format:2; /**< Format */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint8_t bar_accent_3; /**< Bar accent part 3 */ + uint8_t bar_accent_2; /**< Bar accent part 2 */ + uint8_t bar_accent_1; /**< Bar accent part 1 */ + uint8_t clocks_primary; /**< Num clocks per primary clock */ + + uint16_t reserved1; /**< Unused */ + uint8_t subdivision_2; /**< Num subdivision clicks 1 */ + uint8_t subdivision_1; /**< Num subdivision clicks 1 */ + + uint32_t reserved2; /**< Unused */ +#endif +} __attribute((packed)) snd_ump_msg_set_metronome_t; + +/** Set Chord Name Message (128bit) */ +typedef struct _snd_ump_msg_set_chord_name { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t format:2; /**< Format */ + uint8_t addrs:2; /**< Address */ + uint8_t channel:4; /**< Channel */ + uint8_t status_bank; /**< Status Bank */ + uint8_t status; /**< Status */ + + uint8_t tonic_sharp:4; /**< Tonic Sharps/Flats */ + uint8_t chord_tonic:4; /**< Chord Tonic Note */ + uint8_t chord_type; /**< Chord Type */ + uint8_t alter1_type:4; /**< Alteration 1 Type */ + uint8_t alter1_degree:4; /**< Alteration 1 Degree */ + uint8_t alter2_type:4; /**< Alteration 2 Type */ + uint8_t alter2_degree:4; /**< Alteration 2 Degree */ + + uint8_t alter3_type:4; /**< Alteration 3 Type */ + uint8_t alter3_degree:4; /**< Alteration 3 Degree */ + uint8_t alter4_type:4; /**< Alteration 4 Type */ + uint8_t alter4_degree:4; /**< Alteration 4 Degree */ + uint16_t reserved; /**< Unused */ + + uint8_t bass_sharp:4; /**< Bass Sharps/Flats */ + uint8_t bass_note:4; /**< Bass Note */ + uint8_t bass_type; /**< Bass Chord Type */ + uint8_t bass_alter1_type:4; /**< Bass Alteration 1 Type */ + uint8_t bass_alter1_degree:4; /**< Bass Alteration 1 Degree */ + uint8_t bass_alter2_type:4; /**< Bass Alteration 2 Type */ + uint8_t bass_alter2_degree:4; /**< Bass Alteration 2 Degree */ +#else + uint8_t status; /**< Status */ + uint8_t status_bank; /**< Status Bank */ + uint8_t channel:4; /**< Channel */ + uint8_t addrs:2; /**< Address */ + uint8_t format:2; /**< Format */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint8_t alter2_degree:4; /**< Alteration 2 Degree */ + uint8_t alter2_type:4; /**< Alteration 2 Type */ + uint8_t alter1_degree:4; /**< Alteration 1 Degree */ + uint8_t alter1_type:4; /**< Alteration 1 Type */ + uint8_t chord_type; /**< Chord Type */ + uint8_t chord_tonic:4; /**< Chord Tonic Note */ + uint8_t tonic_sharp:4; /**< Tonic Sharps/Flats */ + + uint16_t reserved; /**< Unused */ + uint8_t alter4_degree:4; /**< Alteration 4 Degree */ + uint8_t alter4_type:4; /**< Alteration 4 Type */ + uint8_t alter3_degree:4; /**< Alteration 3 Degree */ + uint8_t alter3_type:4; /**< Alteration 3 Type */ + + uint8_t bass_alter2_degree:4; /**< Bass Alteration 2 Degree */ + uint8_t bass_alter2_type:4; /**< Bass Alteration 2 Type */ + uint8_t bass_alter1_degree:4; /**< Bass Alteration 1 Degree */ + uint8_t bass_alter1_type:4; /**< Bass Alteration 1 Type */ + uint8_t bass_type; /**< Bass Chord Type */ + uint8_t bass_note:4; /**< Bass Note */ + uint8_t bass_sharp:4; /**< Bass Sharps/Flats */ +#endif +} __attribute((packed)) snd_ump_msg_set_chord_name_t; + +/** Flex Data Message (128bit) */ +typedef union _snd_ump_msg_flex_data { + snd_ump_msg_flex_data_meta_t meta; /**< Metadata */ + snd_ump_msg_flex_data_meta_t text; /**< Text data */ + snd_ump_msg_set_tempo_t set_tempo; /**< Set Tempo */ + snd_ump_msg_set_time_sig_t set_time_sig; /**< Set Time Signature */ + snd_ump_msg_set_metronome_t set_metronoe; /**< Set Metronome */ + snd_ump_msg_set_chord_name_t set_chord_name; /**< Set Chord Name */ + snd_ump_msg_hdr_t hdr; /**< UMP header */ + uint32_t raw[4]; /**< raw UMP packet */ +} snd_ump_msg_flex_data_t; + +/** Jitter Reduction Clock / Timestamp Message (32bit) */ +typedef struct _snd_ump_msg_jr_clock { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t reserved:4; /**< Unused */ + uint16_t time; /**< clock time / timestamp */ +#else + uint16_t time; /**< clock time / timestamp */ + uint8_t reserved:4; /**< Unused */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_jr_clock_t; + +/** Delta Clockstamp Ticks Per Quarter Note (DCTPQ) (32bit) */ +typedef struct _snd_ump_msg_dctpq { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t reserved:4; /**< Unused */ + uint16_t ticks; /**< number of ticks per quarter note */ +#else + uint16_t ticks; /**< number of ticks per quarter note */ + uint8_t reserved:4; /**< Unused */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_dctpq_t; + +/** Delta Clockstamp (DC) (32bit) */ +typedef struct _snd_ump_msg_dc { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint32_t type:4; /**< UMP packet type */ + uint32_t group:4; /**< UMP Group */ + uint32_t status:4; /**< Status */ + uint32_t ticks:20; /**< number of ticks since last event */ +#else + uint32_t ticks:20; /**< number of ticks since last event */ + uint32_t status:4; /**< Status */ + uint32_t group:4; /**< UMP Group */ + uint32_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_dc_t; + +/** Utility Message (32bit) */ +typedef union _snd_ump_msg_utility { + snd_ump_msg_jr_clock_t jr_clock; /**< JR Clock messages */ + snd_ump_msg_dctpq_t dctpq; /**< DCTPQ message */ + snd_ump_msg_dc_t dc; /**< DC message */ + snd_ump_msg_hdr_t hdr; /**< UMP header */ + uint32_t raw; /**< raw UMP packet */ +} snd_ump_msg_utility_t; + /** * UMP message type */ @@ -567,6 +860,60 @@ enum { SND_UMP_STREAM_MSG_FORMAT_END = 3, }; +/** UMP Flex Data Format bits */ +enum { + SND_UMP_FLEX_DATA_MSG_FORMAT_SINGLE = 0, + SND_UMP_FLEX_DATA_MSG_FORMAT_START = 1, + SND_UMP_FLEX_DATA_MSG_FORMAT_CONTINUE = 2, + SND_UMP_FLEX_DATA_MSG_FORMAT_END = 3, +}; + +/** UMP Flex Data Address bits */ +enum { + SND_UMP_FLEX_DATA_MSG_ADDR_CHANNEL = 0, + SND_UMP_FLEX_DATA_MSG_ADDR_GROUP = 1, +}; + +/** UMP Flex Data Status Bank bits */ +enum { + SND_UMP_FLEX_DATA_MSG_BANK_SETUP = 0, + SND_UMP_FLEX_DATA_MSG_BANK_METADATA = 1, + SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT = 2, +}; + +/** UMP Flex Data Status bits for Setup (Status Bank = 0) */ +enum { + SND_UMP_FLEX_DATA_MSG_STATUS_SET_TEMPO = 0x00, + SND_UMP_FLEX_DATA_MSG_STATUS_SET_TIME_SIGNATURE = 0x01, + SND_UMP_FLEX_DATA_MSG_STATUS_SET_METRONOME = 0x02, + SND_UMP_FLEX_DATA_MSG_STATUS_SET_KEY_SIGNATURE = 0x05, + SND_UMP_FLEX_DATA_MSG_STATUS_SET_CHORD_NAME = 0x06, +}; + +/** UMP Flex Data Status bits for Metadata (Status Bank = 1) */ +enum { + SND_UMP_FLEX_DATA_MSG_STATUS_PROJECT_NAME = 0x01, + SND_UMP_FLEX_DATA_MSG_STATUS_SONG_NAME = 0x02, + SND_UMP_FLEX_DATA_MSG_STATUS_MIDI_CLIP_NAME = 0x03, + SND_UMP_FLEX_DATA_MSG_STATUS_COPYRIGHT_NOTICE = 0x04, + SND_UMP_FLEX_DATA_MSG_STATUS_COMPOSER_NAME = 0x05, + SND_UMP_FLEX_DATA_MSG_STATUS_LYRICIST_NAME = 0x06, + SND_UMP_FLEX_DATA_MSG_STATUS_ARRANGER_NAME = 0x07, + SND_UMP_FLEX_DATA_MSG_STATUS_PUBLISHER_NAME = 0x08, + SND_UMP_FLEX_DATA_MSG_STATUS_PRIMARY_PERFORMER = 0x09, + SND_UMP_FLEX_DATA_MSG_STATUS_ACCOMPANY_PERFORMAER = 0x0a, + SND_UMP_FLEX_DATA_MSG_STATUS_RECORDING_DATE = 0x0b, + SND_UMP_FLEX_DATA_MSG_STATUS_RECORDING_LOCATION = 0x0c, +}; + +/** UMP Flex Data Status bits for Performance Text Events (Status Bank = 2) */ +enum { + SND_UMP_FLEX_DATA_MSG_STATUS_LYRICS = 0x01, + SND_UMP_FLEX_DATA_MSG_STATUS_LYRICS_LANGUAGE = 0x02, + SND_UMP_FLEX_DATA_MSG_STATUS_RUBY = 0x03, + SND_UMP_FLEX_DATA_MSG_STATUS_RUBY_LANGUAGE = 0x04, +}; + /** * \brief get UMP status (4bit) from 32bit UMP message header */ From ade099fab70f521ba9642778fafd51dc6017fe87 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2024 09:47:09 +0200 Subject: [PATCH 035/117] ump_msg: Drop unsuitable comments There were some leftover comments from the old code that don't fit any longer. Drop them. Signed-off-by: Takashi Iwai --- include/ump_msg.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/ump_msg.h b/include/ump_msg.h index c615f33a..19e4cb28 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -416,7 +416,6 @@ typedef struct _snd_ump_msg_midi2_per_note_pitchbend { uint32_t data; /**< Data (32bit) */ #else - /* 0 */ uint8_t reserved; /**< Unused */ uint8_t note; /**< Note (7bit) */ uint8_t channel:4; /**< Channel */ @@ -428,7 +427,7 @@ typedef struct _snd_ump_msg_midi2_per_note_pitchbend { #endif } __attribute((packed)) snd_ump_msg_midi2_per_note_pitchbend_t; -/** MIDI2 UMP packet (64bit little-endian) */ +/** MIDI2 UMP packet (64bit) */ typedef union _snd_ump_msg_midi2 { snd_ump_msg_midi2_note_t note_on; /**< MIDI2 note-on message */ snd_ump_msg_midi2_note_t note_off; /**< MIDI2 note-off message */ From 568b2ac1db491f6dac88607ef8e5ae3454357f00 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2024 09:53:20 +0200 Subject: [PATCH 036/117] ump: Add a function to provide the packet word length of a UMP type Add a helper function to return the number of words of a given UMP packet type. Used for parsing MIDI Clip File stream, for example. Signed-off-by: Takashi Iwai --- include/ump_msg.h | 1 + src/Versions.in.in | 1 + src/rawmidi/ump.c | 17 +++++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/include/ump_msg.h b/include/ump_msg.h index 19e4cb28..3d8f95ef 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -1003,6 +1003,7 @@ static inline uint8_t snd_ump_sysex_msg_length(const uint32_t *ump) int snd_ump_msg_sysex_expand(const uint32_t *ump, uint8_t *buf, size_t maxlen, size_t *filled); +int snd_ump_packet_length(unsigned int type); #ifdef __cplusplus } diff --git a/src/Versions.in.in b/src/Versions.in.in index c945d6dc..1c40d461 100644 --- a/src/Versions.in.in +++ b/src/Versions.in.in @@ -212,5 +212,6 @@ ALSA_1.2.13 { @SYMBOL_PREFIX@snd_ump_endpoint_info_set_*; @SYMBOL_PREFIX@snd_ump_block_info_clear; @SYMBOL_PREFIX@snd_ump_block_info_set_*; + @SYMBOL_PREFIX@snd_ump_packet_length; #endif } ALSA_1.2.10; diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index fddb60c2..d3676afb 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -1072,3 +1072,20 @@ int snd_ump_msg_sysex_expand(const uint32_t *ump, uint8_t *buf, size_t maxlen, return -EINVAL; } } + +/** + * \brief return the length of a UMP packet type + * \param type UMP packet type + * \return the length of the given UMP packet type in 32bit words (from 1 to 4), + * or 0 for negative inputs. + */ +int snd_ump_packet_length(unsigned int type) +{ + static int packet_length[16] = { + 1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4 + }; + + if (type > 16) + return 0; + return packet_length[type]; +} From 24c7f427339402546f17a2bb6c0624ebb85ec847 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 3 Jul 2024 15:00:20 +0200 Subject: [PATCH 037/117] seq: Add API functions to set different tempo base values MIDI2 Set Tempo message uses 10ns-based values, and we need to update the API to change the base time unit. This patch adds a few new API functions: - snd_seq_has_queue_tempo_base() returns 1 if the client supports a new tempo-base value; if 0, it's an old system and application has to use the tempo in the fixed 1us unit - the tempo base can be changed with snd_seq_queue_tempo_set_tempo_base(), provided in nsec unit; the value has to be either 10 or 1000 (or 0 as default, equivalent with 1000) The protocol version is checked and fallback to the fixed 1us base for the old clients. Signed-off-by: Takashi Iwai --- include/seq.h | 3 ++ include/sound/uapi/asequencer.h | 7 ++-- src/Versions.in.in | 3 ++ src/seq/seq.c | 58 +++++++++++++++++++++++++++++++++ src/seq/seq_hw.c | 5 +++ src/seq/seq_local.h | 1 + 6 files changed, 74 insertions(+), 3 deletions(-) diff --git a/include/seq.h b/include/seq.h index e55f5c16..3bad5dd9 100644 --- a/include/seq.h +++ b/include/seq.h @@ -506,13 +506,16 @@ unsigned int snd_seq_queue_tempo_get_tempo(const snd_seq_queue_tempo_t *info); int snd_seq_queue_tempo_get_ppq(const snd_seq_queue_tempo_t *info); unsigned int snd_seq_queue_tempo_get_skew(const snd_seq_queue_tempo_t *info); unsigned int snd_seq_queue_tempo_get_skew_base(const snd_seq_queue_tempo_t *info); +unsigned int snd_seq_queue_tempo_get_tempo_base(const snd_seq_queue_tempo_t *info); void snd_seq_queue_tempo_set_tempo(snd_seq_queue_tempo_t *info, unsigned int tempo); void snd_seq_queue_tempo_set_ppq(snd_seq_queue_tempo_t *info, int ppq); void snd_seq_queue_tempo_set_skew(snd_seq_queue_tempo_t *info, unsigned int skew); void snd_seq_queue_tempo_set_skew_base(snd_seq_queue_tempo_t *info, unsigned int base); +void snd_seq_queue_tempo_set_tempo_base(snd_seq_queue_tempo_t *info, unsigned int tempo_base); int snd_seq_get_queue_tempo(snd_seq_t *handle, int q, snd_seq_queue_tempo_t *tempo); int snd_seq_set_queue_tempo(snd_seq_t *handle, int q, snd_seq_queue_tempo_t *tempo); +int snd_seq_has_queue_tempo_base(snd_seq_t *handle); /* */ diff --git a/include/sound/uapi/asequencer.h b/include/sound/uapi/asequencer.h index b913f31d..923dfddd 100644 --- a/include/sound/uapi/asequencer.h +++ b/include/sound/uapi/asequencer.h @@ -26,7 +26,7 @@ #include /** version of the sequencer */ -#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 3) +#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 4) /** * definition of sequencer event types @@ -539,11 +539,12 @@ struct snd_seq_queue_status { /* queue tempo */ struct snd_seq_queue_tempo { int queue; /* sequencer queue */ - unsigned int tempo; /* current tempo, us/tick */ + unsigned int tempo; /* current tempo, us/tick (or different time-base below) */ int ppq; /* time resolution, ticks/quarter */ unsigned int skew_value; /* queue skew */ unsigned int skew_base; /* queue skew base */ - char reserved[24]; /* for the future */ + unsigned short tempo_base; /* tempo base in nsec unit; either 10 or 1000 */ + char reserved[22]; /* for the future */ }; diff --git a/src/Versions.in.in b/src/Versions.in.in index 1c40d461..90849277 100644 --- a/src/Versions.in.in +++ b/src/Versions.in.in @@ -206,6 +206,9 @@ ALSA_1.2.13 { #ifdef HAVE_SEQ_SYMS @SYMBOL_PREFIX@snd_seq_create_ump_endpoint; @SYMBOL_PREFIX@snd_seq_create_ump_block; + @SYMBOL_PREFIX@snd_seq_queue_tempo_get_tempo_base; + @SYMBOL_PREFIX@snd_seq_queue_tempo_set_tempo_base; + @SYMBOL_PREFIX@snd_seq_has_tempo_base; #endif #ifdef HAVE_RAWMIDI_SYMS @SYMBOL_PREFIX@snd_ump_endpoint_info_clear; diff --git a/src/seq/seq.c b/src/seq/seq.c index a18f67e2..d0ecedf0 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -490,6 +490,21 @@ the buffer is flushed by #snd_seq_drain_output() call. You can schedule the event in a certain queue so that the tempo change happens at the scheduled time, too. +The tempo is set as default in microsecond unit as defined for +Standard MIDI Format 1. But since the value in MIDI2 Set Tempo message +is based on 10-nanosecand unit, sequencer queue also allows to set up +in 10-nanosecond unit. For that, change the tempo-base value in +#snd_seq_queue_tempo_t to 10 via #snd_seq_queue_tempo_set_tempo_base() +along with the 10-nanobased tempo value. The default tempo base is 1000, +i.e. 1 microsecond. +Currently the API supports only either 0, 10 or 1000 as the tempo-base +(where 0 is treated as 1000). + +The older kernel might not support the different tempo-base, and setting a +different value from 1000 would fail. The application may heck the +availability of tempo-base change via #snd_seq_has_tempo_base() function +beforehand, and re-calculate in microsecond unit as fallback. + \subsection seq_ev_start Starting and stopping a queue To start, stop, or continue a queue, you need to send a queue-control @@ -3878,6 +3893,19 @@ unsigned int snd_seq_queue_tempo_get_skew_base(const snd_seq_queue_tempo_t *info return info->skew_base; } +/** + * \brief Get the tempo base of a queue_status container + * \param info queue_status container + * \return tempo base time in nsec unit + * + * \sa snd_seq_get_queue_tempo() + */ +unsigned int snd_seq_queue_tempo_get_tempo_base(const snd_seq_queue_tempo_t *info) +{ + assert(info); + return info->tempo_base; +} + /** * \brief Set the tempo of a queue_status container * \param info queue_status container @@ -3933,6 +3961,21 @@ void snd_seq_queue_tempo_set_skew_base(snd_seq_queue_tempo_t *info, unsigned int info->skew_base = base; } +/** + * \brief Set the tempo base of a queue_status container + * \param info queue_status container + * \param tempo_base tempo base time in nsec unit + * + * \sa snd_seq_get_queue_tempo() + */ +void snd_seq_queue_tempo_set_tempo_base(snd_seq_queue_tempo_t *info, unsigned int tempo_base) +{ + assert(info); + if (!tempo_base) + tempo_base = 1000; + info->tempo_base = tempo_base; +} + /** * \brief obtain the current tempo of the queue * \param seq sequencer handle @@ -3962,10 +4005,25 @@ int snd_seq_get_queue_tempo(snd_seq_t *seq, int q, snd_seq_queue_tempo_t * tempo int snd_seq_set_queue_tempo(snd_seq_t *seq, int q, snd_seq_queue_tempo_t * tempo) { assert(seq && tempo); + if (!seq->has_queue_tempo_base && + tempo->tempo_base && tempo->tempo_base != 1000) + return -EINVAL; tempo->queue = q; return seq->ops->set_queue_tempo(seq, tempo); } +/** + * \brief inquiry the support of tempo base change + * \param seq sequencer handle + * \return 1 if the client supports the tempo base change, 0 if not + * + * \sa snd_seq_get_queue_tempo() + */ +int snd_seq_has_queue_tempo_base(snd_seq_t *seq) +{ + assert(seq); + return seq->has_queue_tempo_base; +} /*----------------------------------------------------------------*/ diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c index eeaf26e1..e88a7b22 100644 --- a/src/seq/seq_hw.c +++ b/src/seq/seq_hw.c @@ -275,12 +275,15 @@ static int snd_seq_hw_get_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * te /*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO failed");*/ return -errno; } + if (!seq->has_queue_tempo_base) + tempo->tempo_base = 1000; return 0; } static int snd_seq_hw_set_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo) { snd_seq_hw_t *hw = seq->private_data; + if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, tempo) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO failed");*/ return -errno; @@ -587,6 +590,8 @@ int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode) seq->ops = &snd_seq_hw_ops; seq->private_data = hw; seq->packet_size = sizeof(snd_seq_event_t); + seq->has_queue_tempo_base = ver >= SNDRV_PROTOCOL_VERSION(1, 0, 4); + client = snd_seq_hw_client_id(seq); if (client < 0) { snd_seq_close(seq); diff --git a/src/seq/seq_local.h b/src/seq/seq_local.h index 26302970..00a7615a 100644 --- a/src/seq/seq_local.h +++ b/src/seq/seq_local.h @@ -94,6 +94,7 @@ struct _snd_seq { size_t tmpbufsize; /* size of errbuf */ size_t packet_size; /* input packet alignment size */ int midi_version; /* current protocol version */ + int has_queue_tempo_base; /* support queue tempo-base? */ unsigned int num_ump_groups; /* number of UMP groups */ snd_ump_endpoint_info_t *ump_ep; /* optional UMP info */ From 48101de6fa0dbff83ffbcaafc1a1b8e89806630b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Jul 2024 07:23:23 +0200 Subject: [PATCH 038/117] seq: Fix wrong FB direction at snd_seq_create_ump_block() snd_seq_create_ump_block() receives a snd_ump_block_info_t data at the creation of a FB and updates its associated sequencer port, but it handled the port direction incorrectly. The UMP / port direction means the connectivity, but the current code translated other way round. The correct translation should be that input = receiver, i.e. a writable port for applications, and output = source, a readable port for applications. This patch corrects the translation, and add more comments to the direction definition. Signed-off-by: Takashi Iwai --- include/seq.h | 4 ++-- src/seq/seqmid.c | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/seq.h b/include/seq.h index 3bad5dd9..4f03df43 100644 --- a/include/seq.h +++ b/include/seq.h @@ -250,8 +250,8 @@ typedef struct _snd_seq_port_info snd_seq_port_info_t; /** port direction */ #define SND_SEQ_PORT_DIR_UNKNOWN 0 /**< Unknown */ -#define SND_SEQ_PORT_DIR_INPUT 1 /**< Input only */ -#define SND_SEQ_PORT_DIR_OUTPUT 2 /**< Output only */ +#define SND_SEQ_PORT_DIR_INPUT 1 /**< Input only; sink, receiver */ +#define SND_SEQ_PORT_DIR_OUTPUT 2 /**< Output only; source, transmitter */ #define SND_SEQ_PORT_DIR_BIDIRECTION 3 /**< Input/output bidirectional */ /* port type */ diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index d7eac6ca..08c62d5c 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -645,16 +645,16 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) i >= bp->first_group + bp->num_groups) continue; switch (bp->direction) { - case SNDRV_UMP_DIR_INPUT: - caps |= SNDRV_SEQ_PORT_CAP_READ | - SNDRV_SEQ_PORT_CAP_SYNC_READ | - SNDRV_SEQ_PORT_CAP_SUBS_READ; - break; - case SNDRV_UMP_DIR_OUTPUT: + case SNDRV_UMP_DIR_INPUT: /* sink, receiver */ caps |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; break; + case SNDRV_UMP_DIR_OUTPUT: /* source, transmitter */ + caps |= SNDRV_SEQ_PORT_CAP_READ | + SNDRV_SEQ_PORT_CAP_SYNC_READ | + SNDRV_SEQ_PORT_CAP_SUBS_READ; + break; case SNDRV_UMP_DIR_BIDIRECTION: caps |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | From 530e2f8131802a8861178a79754bcf79e3ceaa14 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 19 Jul 2024 14:30:12 +0200 Subject: [PATCH 039/117] ump_msg: Correct a typo in snd_ump_msg_flex_data_t definition. There was a typo in snd_ump_msg_flex_data_t definition; it should be "set_metronome". Fixes: 30f8ba74c5a2 ("ump_msg: Add definitions for Utility, Stream and Flex Data messages") Signed-off-by: Takashi Iwai --- include/ump_msg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ump_msg.h b/include/ump_msg.h index 3d8f95ef..9b189144 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -677,7 +677,7 @@ typedef union _snd_ump_msg_flex_data { snd_ump_msg_flex_data_meta_t text; /**< Text data */ snd_ump_msg_set_tempo_t set_tempo; /**< Set Tempo */ snd_ump_msg_set_time_sig_t set_time_sig; /**< Set Time Signature */ - snd_ump_msg_set_metronome_t set_metronoe; /**< Set Metronome */ + snd_ump_msg_set_metronome_t set_metronome; /**< Set Metronome */ snd_ump_msg_set_chord_name_t set_chord_name; /**< Set Chord Name */ snd_ump_msg_hdr_t hdr; /**< UMP header */ uint32_t raw[4]; /**< raw UMP packet */ From 28948f2fccb389b0239819bf8effa72d57fe37af Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 28 Jul 2024 10:46:11 +0200 Subject: [PATCH 040/117] ump_msg: Add a new helper snd_ump_get_byte() For making it easier to extract a byte from the UMP packet no matter which endian is used, introduce a new helper function snd_ump_get_byte(). It'll be useful for retrieving SysEx byte or a name string. Signed-off-by: Takashi Iwai --- include/ump_msg.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/ump_msg.h b/include/ump_msg.h index 9b189144..617dbe42 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -1001,6 +1001,18 @@ static inline uint8_t snd_ump_sysex_msg_length(const uint32_t *ump) return (*ump >> 16) & 0xf; } +/** + * \brief extract one byte from a UMP packet + */ +static inline uint8_t snd_ump_get_byte(const uint32_t *ump, unsigned int offset) +{ +#ifdef SNDRV_BIG_ENDIAN + return ((const uint8_t *)ump)[offset]; +#else + return ((const uint8_t *)ump)[(offset & ~3) | (3 - (offset & 3))]; +#endif +} + int snd_ump_msg_sysex_expand(const uint32_t *ump, uint8_t *buf, size_t maxlen, size_t *filled); int snd_ump_packet_length(unsigned int type); From f090a93ea83de8e0e5ce99a3e9c5be755f8cc62c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 28 Jul 2024 10:24:18 +0200 Subject: [PATCH 041/117] ump_msg: Define types for UMP Mixed Data Set messages Mixed Data Set messages is another messages of the type 5 (SND_UMP_MSG_TYPE_EXTENDED_DATA) with the status 8 and 9. Signed-off-by: Takashi Iwai --- include/ump_msg.h | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/include/ump_msg.h b/include/ump_msg.h index 617dbe42..a64cb317 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -683,6 +683,69 @@ typedef union _snd_ump_msg_flex_data { uint32_t raw[4]; /**< raw UMP packet */ } snd_ump_msg_flex_data_t; +/** Mixed Data Set Chunk Header Message (128bit) */ +typedef struct _snd_ump_msg_mixed_data_header { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t mds_id:4; /**< Mixed Data Set ID */ + uint16_t bytes; /**< Number of valid bytes in this chunk */ + + uint16_t chunks; /**< Number of chunks in mixed data set */ + uint16_t chunk; /**< Number of this chunk */ + + uint16_t manufacturer; /**< Manufacturer ID */ + uint16_t device; /**< Device ID */ + + uint16_t sub_id_1; /**< Sub ID \# 1 */ + uint16_t sub_id_2; /**< Sub ID \# 2 */ +#else + uint16_t bytes; /**< Number of valid bytes in this chunk */ + uint8_t mds_id:4; /**< Mixed Data Set ID */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint16_t chunk; /**< Number of this chunk */ + uint16_t chunks; /**< Number of chunks in mixed data set */ + + uint16_t device; /**< Device ID */ + uint16_t manufacturer; /**< Manufacturer ID */ + + uint16_t sub_id_2; /**< Sub ID \# 2 */ + uint16_t sub_id_1; /**< Sub ID \# 1 */ +#endif +} snd_ump_msg_mixed_data_header_t; + +/** Mixed Data Set Chunk Payload Message (128bit) */ +typedef struct _snd_ump_msg_mixed_data_payload { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t mds_id:4; /**< Mixed Data Set ID */ + uint16_t payload1; /**< Payload */ + + uint32_t payloads[3]; /**< Payload */ +#else + uint16_t payload1; /**< Payload */ + uint8_t mds_id:4; /**< Mixed Data Set ID */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t payloads[3]; /**< Payload */ +#endif +} snd_ump_msg_mixed_data_payload_t; + +/** Mixed Data Set Chunk Message (128bit) */ +typedef union _snd_ump_msg_mixed_data { + snd_ump_msg_mixed_data_header_t header; /**< Header */ + snd_ump_msg_mixed_data_payload_t payload; /**< Payload */ + uint32_t raw[4]; /**< raw UMP packet */ +} snd_ump_msg_mixed_data_t; + /** Jitter Reduction Clock / Timestamp Message (32bit) */ typedef struct _snd_ump_msg_jr_clock { #ifdef SNDRV_BIG_ENDIAN_BITFIELD @@ -803,6 +866,12 @@ enum { SND_UMP_SYSEX_STATUS_END = 3, }; +/** MIDI 2.0 Mixed Data Set Status */ +enum { + SND_UMP_MIXED_DATA_SET_STATUS_HEADER = 8, + SND_UMP_MIXED_DATA_SET_STATUS_PAYLOAD = 9, +}; + /** UMP Utility Type Status (type 0x0) **/ enum { SND_UMP_UTILITY_MSG_STATUS_NOOP = 0x00, From d9694398130c2217de19165a5ac641651f837857 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 31 Jul 2024 11:14:08 +0200 Subject: [PATCH 042/117] seq: Avoid strlcat() strlcat() isn't available in every system, so better to avoid it. Rewrite the code without strlcat(). Fixes: 6167b8ce3e80 ("seq: Add API helper functions for creating UMP Endpoint and Blocks") Link: https://lore.kernel.org/0796c157-1ac3-47a3-9d54-ba86f59d64d5@linux.intel.com Signed-off-by: Takashi Iwai --- src/seq/seqmid.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 08c62d5c..b30db407 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -635,6 +635,7 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) char blknames[64]; char name[64]; unsigned int caps = 0; + int len; blknames[0] = 0; for (b = 0; b < ep->num_blocks; b++) { @@ -668,14 +669,13 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) if (!*bp->name) continue; - if (*blknames) { - strlcat(blknames, ", ", sizeof(blknames)); - strlcat(blknames, (const char *)bp->name, - sizeof(blknames)); - } else { + len = strlen(blknames); + if (len) + snprintf(blknames + len, sizeof(blknames) - len, + ", %s", bp->name); + else snd_strlcpy(blknames, (const char *)bp->name, sizeof(blknames)); - } } if (!*blknames) From ddc4c668ba642771ec4fdee70737e0c3c7ff8b46 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 7 Aug 2024 15:46:31 +0200 Subject: [PATCH 043/117] seq: Add snd_seq_{get|set}_ump_is_midi1() API functions Implement the API function calls corresponding to the new sequencer port flag bit that has been added recently to the kernel. A UMP MIDI 2.0 device allow to have an optional MIDI 1.0 port while speaking in MIDI 2.0 protocol for other UMP Groups. The new seq port flag indicates that. This is rather a minor difference, and since ALSA sequencer core covers the all conversions, usually you don't have to worry about it at all. Signed-off-by: Takashi Iwai --- include/seq.h | 2 ++ include/sound/uapi/asequencer.h | 2 ++ src/Versions.in.in | 2 ++ src/seq/seq.c | 29 +++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+) diff --git a/include/seq.h b/include/seq.h index 4f03df43..2eee95a6 100644 --- a/include/seq.h +++ b/include/seq.h @@ -318,6 +318,7 @@ int snd_seq_port_info_get_timestamp_real(const snd_seq_port_info_t *info); int snd_seq_port_info_get_timestamp_queue(const snd_seq_port_info_t *info); int snd_seq_port_info_get_direction(const snd_seq_port_info_t *info); int snd_seq_port_info_get_ump_group(const snd_seq_port_info_t *info); +int snd_seq_port_info_get_ump_is_midi1(const snd_seq_port_info_t *info); void snd_seq_port_info_set_client(snd_seq_port_info_t *info, int client); void snd_seq_port_info_set_port(snd_seq_port_info_t *info, int port); @@ -334,6 +335,7 @@ void snd_seq_port_info_set_timestamp_real(snd_seq_port_info_t *info, int realtim void snd_seq_port_info_set_timestamp_queue(snd_seq_port_info_t *info, int queue); void snd_seq_port_info_set_direction(snd_seq_port_info_t *info, int direction); void snd_seq_port_info_set_ump_group(snd_seq_port_info_t *info, int ump_group); +void snd_seq_port_info_set_ump_is_midi1(snd_seq_port_info_t *info, int is_midi1); int snd_seq_create_port(snd_seq_t *handle, snd_seq_port_info_t *info); int snd_seq_delete_port(snd_seq_t *handle, int port); diff --git a/include/sound/uapi/asequencer.h b/include/sound/uapi/asequencer.h index 923dfddd..b3e9df58 100644 --- a/include/sound/uapi/asequencer.h +++ b/include/sound/uapi/asequencer.h @@ -477,6 +477,8 @@ struct snd_seq_remove_events { #define SNDRV_SEQ_PORT_FLG_TIMESTAMP (1<<1) #define SNDRV_SEQ_PORT_FLG_TIME_REAL (1<<2) +#define SNDRV_SEQ_PORT_FLG_IS_MIDI1 (1<<3) /* Keep MIDI 1.0 protocol */ + /* port direction */ #define SNDRV_SEQ_PORT_DIR_UNKNOWN 0 #define SNDRV_SEQ_PORT_DIR_INPUT 1 diff --git a/src/Versions.in.in b/src/Versions.in.in index 90849277..298e610c 100644 --- a/src/Versions.in.in +++ b/src/Versions.in.in @@ -209,6 +209,8 @@ ALSA_1.2.13 { @SYMBOL_PREFIX@snd_seq_queue_tempo_get_tempo_base; @SYMBOL_PREFIX@snd_seq_queue_tempo_set_tempo_base; @SYMBOL_PREFIX@snd_seq_has_tempo_base; + @SYMBOL_PREFIX@snd_seq_port_info_get_ump_is_midi1; + @SYMBOL_PREFIX@snd_seq_port_info_set_ump_is_midi1; #endif #ifdef HAVE_RAWMIDI_SYMS @SYMBOL_PREFIX@snd_ump_endpoint_info_clear; diff --git a/src/seq/seq.c b/src/seq/seq.c index d0ecedf0..347ff455 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -2431,6 +2431,19 @@ int snd_seq_port_info_get_ump_group(const snd_seq_port_info_t *info) return info->ump_group; } +/** + * \brief Get the status of the optional MIDI 1.0 port in MIDI 2.0 UMP Endpoint + * \param info port_info container + * \return 1 if it's an optional MIDI 1.0 port in MIDI 2.0 UMP Endpoint + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_ump_is_midi1() + */ +int snd_seq_port_info_get_ump_is_midi1(const snd_seq_port_info_t *info) +{ + assert(info); + return !!(info->flags & SNDRV_SEQ_PORT_FLG_IS_MIDI1); +} + /** * \brief Set the client id of a port_info container * \param info port_info container @@ -2635,6 +2648,22 @@ void snd_seq_port_info_set_ump_group(snd_seq_port_info_t *info, int ump_group) info->ump_group = ump_group; } +/** + * \brief Set the optional MIDI 1.0 port in MIDI 2.0 UMP Endpoint + * \param info port_info container + * \param is_midi1 non-zero for MIDI 1.0 port in MIDI 2.0 EP + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_ump_is_midi1() + */ +void snd_seq_port_info_set_ump_is_midi1(snd_seq_port_info_t *info, int is_midi1) +{ + assert(info); + if (is_midi1) + info->flags |= SNDRV_SEQ_PORT_FLG_IS_MIDI1; + else + info->flags &= ~SNDRV_SEQ_PORT_FLG_IS_MIDI1; +} + /** * \brief create a sequencer port on the current client * \param seq sequencer handle From 769d1db1b0a213a39c7e59c0d1d724e7f45b1ac3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 7 Aug 2024 15:50:21 +0200 Subject: [PATCH 044/117] seq: Correct a typo in documentation The right function name is snd_seq_has_queue_tempo_base(). Fixes: 24c7f4273394 ("seq: Add API functions to set different tempo base values") Signed-off-by: Takashi Iwai --- src/seq/seq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/seq/seq.c b/src/seq/seq.c index 347ff455..1a40e8bb 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -502,7 +502,7 @@ Currently the API supports only either 0, 10 or 1000 as the tempo-base The older kernel might not support the different tempo-base, and setting a different value from 1000 would fail. The application may heck the -availability of tempo-base change via #snd_seq_has_tempo_base() function +availability of tempo-base change via #snd_seq_has_queue_tempo_base() function beforehand, and re-calculate in microsecond unit as fallback. \subsection seq_ev_start Starting and stopping a queue From fa673b719cca2332a6936211f0ab5e72233c4f52 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 15 Aug 2024 16:25:11 +0200 Subject: [PATCH 045/117] control: Make ump_{endpoint|block}_info calls optional Add the NULL check for ump_endpoint_info and ump_block_info calls. Those can be NULl depending on the target. Fixes: 81b0cf46d16a ("control: Add UMP Endpoint and Block info query support") Signed-off-by: Takashi Iwai --- src/control/control.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/control/control.c b/src/control/control.c index d77ab24c..e443d543 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -1293,7 +1293,10 @@ int snd_ctl_ump_next_device(snd_ctl_t *ctl, int *device) int snd_ctl_ump_endpoint_info(snd_ctl_t *ctl, snd_ump_endpoint_info_t *info) { assert(ctl && info); - return ctl->ops->ump_endpoint_info(ctl, info); + fprintf(stderr, "%s:%d\n", __func__, __LINE__); + if (ctl->ops->ump_endpoint_info) + return ctl->ops->ump_endpoint_info(ctl, info); + return -ENXIO; } /** @@ -1305,7 +1308,9 @@ int snd_ctl_ump_endpoint_info(snd_ctl_t *ctl, snd_ump_endpoint_info_t *info) int snd_ctl_ump_block_info(snd_ctl_t *ctl, snd_ump_block_info_t *info) { assert(ctl && info); - return ctl->ops->ump_block_info(ctl, info); + if (ctl->ops->ump_block_info) + return ctl->ops->ump_block_info(ctl, info); + return -ENXIO; } /** From 3e38164ee5d3a926e60bf8270aa984bf26393960 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 15 Aug 2024 16:36:18 +0200 Subject: [PATCH 046/117] test: Add an example program to inquire UMP Endpoint and Block info Provide an example program to inquire UMP Endpoint and Block info in various APIs. Signed-off-by: Takashi Iwai --- test/Makefile.am | 3 +- test/umpinfo.c | 147 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 test/umpinfo.c diff --git a/test/Makefile.am b/test/Makefile.am index 635fa39b..707063cc 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,7 +1,7 @@ SUBDIRS=. lsb check_PROGRAMS=control pcm pcm_min latency seq seq-ump-example \ - playmidi1 timer rawmidi midiloop \ + playmidi1 timer rawmidi midiloop umpinfo \ oldapi queue_timer namehint client_event_filter \ chmap audio_time user-ctl-element-set pcm-multi-thread @@ -17,6 +17,7 @@ playmidi1_LDADD=../src/libasound.la timer_LDADD=../src/libasound.la rawmidi_LDADD=../src/libasound.la midiloop_LDADD=../src/libasound.la +umpinfo_LDADD=../src/libasound.la oldapi_LDADD=../src/libasound.la queue_timer_LDADD=../src/libasound.la namehint_LDADD=../src/libasound.la diff --git a/test/umpinfo.c b/test/umpinfo.c new file mode 100644 index 00000000..92a20936 --- /dev/null +++ b/test/umpinfo.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Inquire UMP Endpoint and Block info in various APIs + +#include +#include + +enum { UNKNOWN, SEQ, RAWMIDI, CTL }; +static int mode = UNKNOWN; + +static snd_seq_t *seq; +static int client; +static snd_ump_t *ump; +static snd_ctl_t *ctl; + +static int get_ump_endpoint_info(snd_ump_endpoint_info_t *ep) +{ + switch (mode) { + case SEQ: + return snd_seq_get_ump_endpoint_info(seq, client, ep); + case RAWMIDI: + return snd_ump_endpoint_info(ump, ep); + case CTL: + return snd_ctl_ump_endpoint_info(ctl, ep); + default: + return -EINVAL; + } +} + +static int get_ump_block_info(int blk, snd_ump_block_info_t *bp) +{ + switch (mode) { + case SEQ: + return snd_seq_get_ump_block_info(seq, client, blk, bp); + case RAWMIDI: + snd_ump_block_info_set_block_id(bp, blk); + return snd_ump_block_info(ump, bp); + case CTL: + snd_ump_block_info_set_block_id(bp, blk); + return snd_ctl_ump_block_info(ctl, bp); + default: + return -EINVAL; + } +} + +static void help(void) +{ + printf("umpinfo: inquire UMP Endpoint and Block info\n" + " umpinfo -c target inquiry via control API\n" + " umpinfo -m target inquiry via UMP rawmidi API\n" + " umpinfo -s client inquiry via sequencer API\n" + " target = hw:0, etc\n" + " client = sequencer client number\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + const char *target = "hw:0"; + snd_ump_endpoint_info_t *ep; + snd_ump_block_info_t *blk; + const unsigned char *s; + int c, i, num_blks; + + while ((c = getopt(argc, argv, "s:m:c:h")) >= 0) { + switch (c) { + case 's': + mode = SEQ; + client = atoi(target); + break; + case 'm': + mode = RAWMIDI; + target = optarg; + break; + case 'c': + mode = CTL; + target = optarg; + break; + default: + help(); + break; + } + } + + switch (mode) { + case SEQ: + if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0)) { + fprintf(stderr, "failed to open sequencer\n"); + return 1; + } + break; + case RAWMIDI: + if (snd_ump_open(&ump, NULL, target, 0)) { + fprintf(stderr, "failed to open UMP rawmidi\n"); + return 1; + } + break; + case CTL: + if (snd_ctl_open(&ctl, target, 0)) { + fprintf(stderr, "failed to open control\n"); + return 1; + } + break; + default: + help(); + break; + } + + snd_ump_endpoint_info_alloca(&ep); + snd_ump_block_info_alloca(&blk); + + if (get_ump_endpoint_info(ep)) { + fprintf(stderr, "failed to get UMP EP info\n"); + return 1; + } + + printf("Name: %s\n", snd_ump_endpoint_info_get_name(ep)); + printf("Product ID: %s\n", snd_ump_endpoint_info_get_product_id(ep)); + printf("Flags: 0x%x\n", snd_ump_endpoint_info_get_flags(ep)); + printf("Protocol Caps: 0x%x\n", snd_ump_endpoint_info_get_protocol_caps(ep)); + printf("Protocol: 0x%x\n", snd_ump_endpoint_info_get_protocol(ep)); + printf("Num Blocks: %d\n", snd_ump_endpoint_info_get_num_blocks(ep)); + printf("Version: 0x%x\n", snd_ump_endpoint_info_get_version(ep)); + printf("Manufacturer ID: 0x%x\n", snd_ump_endpoint_info_get_manufacturer_id(ep)); + printf("Family ID: 0x%x\n", snd_ump_endpoint_info_get_family_id(ep)); + printf("Model ID: 0x%x\n", snd_ump_endpoint_info_get_model_id(ep)); + s = snd_ump_endpoint_info_get_sw_revision(ep); + printf("SW Revision: %02x:%02x:%02x:%02x\n", s[0], s[1], s[2], s[3]); + num_blks = snd_ump_endpoint_info_get_num_blocks(ep); + for (i = 0; i < num_blks; i++) { + if (get_ump_block_info(i, blk)) { + fprintf(stderr, "failed to get UMP Block info for %d\n", i); + continue; + } + printf("\n"); + printf("Block ID: %d\n", snd_ump_block_info_get_block_id(blk)); + printf("Name: %s\n", snd_ump_block_info_get_name(blk)); + printf("Active: %d\n", snd_ump_block_info_get_active(blk)); + printf("Flags: 0x%x\n", snd_ump_block_info_get_flags(blk)); + printf("Direction: %d\n", snd_ump_block_info_get_direction(blk)); + printf("First Group: %d\n", snd_ump_block_info_get_first_group(blk)); + printf("Num Groups: %d\n", snd_ump_block_info_get_num_groups(blk)); + printf("MIDI-CI Version: %d\n", snd_ump_block_info_get_midi_ci_version(blk)); + printf("Sysex8 Streams: %d\n", snd_ump_block_info_get_sysex8_streams(blk)); + } + + return 0; +} From f08f4aceec2036194a7a443733e15b16939075d7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 15 Aug 2024 16:43:10 +0200 Subject: [PATCH 047/117] .gitignore: Add test/umpinfo Signed-off-by: Takashi Iwai --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 947eba45..3c0791f5 100644 --- a/.gitignore +++ b/.gitignore @@ -67,5 +67,6 @@ test/rawmidi test/seq test/seq-ump-example test/timer +test/umpinfo test/lsb/config test/lsb/midi_event From 9b6dfb3eb6efd038c9ab76f790ef69639e293700 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 20 Aug 2024 09:36:27 +0200 Subject: [PATCH 048/117] ump_msg: Add missing definition for Set Key Signature Message The Set Key Signature message was missing at the time we defined for Flex Data types. Signed-off-by: Takashi Iwai --- include/ump_msg.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/include/ump_msg.h b/include/ump_msg.h index a64cb317..5f0a45ac 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -606,6 +606,39 @@ typedef struct _snd_ump_msg_set_metronome { #endif } __attribute((packed)) snd_ump_msg_set_metronome_t; +/** Set Key Signature Message (128bit) */ +typedef struct _snd_ump_msg_set_key_sig { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t format:2; /**< Format */ + uint8_t addrs:2; /**< Address */ + uint8_t channel:4; /**< Channel */ + uint8_t status_bank; /**< Status Bank */ + uint8_t status; /**< Status */ + + uint8_t sharps_flats:4; /**< Sharps/Flats */ + uint8_t tonic_note:4; /**< Tonic Note 1 */ + uint8_t reserved1[3]; /**< Unused */ + + uint32_t reserved2[2]; /**< Unused */ +#else + uint8_t status; /**< Status */ + uint8_t status_bank; /**< Status Bank */ + uint8_t channel:4; /**< Channel */ + uint8_t addrs:2; /**< Address */ + uint8_t format:2; /**< Format */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint8_t reserved1[3]; /**< Unused */ + uint8_t tonic_note:4; /**< Tonic Note */ + uint8_t sharps_flats:4; /**< Sharps/Flats */ + + uint32_t reserved2[2]; /**< Unused */ +#endif +} __attribute((packed)) snd_ump_msg_set_key_sig_t; + /** Set Chord Name Message (128bit) */ typedef struct _snd_ump_msg_set_chord_name { #ifdef SNDRV_BIG_ENDIAN_BITFIELD @@ -678,6 +711,7 @@ typedef union _snd_ump_msg_flex_data { snd_ump_msg_set_tempo_t set_tempo; /**< Set Tempo */ snd_ump_msg_set_time_sig_t set_time_sig; /**< Set Time Signature */ snd_ump_msg_set_metronome_t set_metronome; /**< Set Metronome */ + snd_ump_msg_set_key_sig_t set_key_sig; /**< Set Key Signature */ snd_ump_msg_set_chord_name_t set_chord_name; /**< Set Chord Name */ snd_ump_msg_hdr_t hdr; /**< UMP header */ uint32_t raw[4]; /**< raw UMP packet */ From b154d9145f0e17b9650e4584ddfdf14580b4e0d7 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 20 Aug 2024 15:33:45 +0200 Subject: [PATCH 049/117] pcm: implement snd_pcm_hw_params_get_sync() and obsolete snd_pcm_info_get_sync() Use the new clock source mechanism to get information about similar PCM clock sources for PCM streams. Link: https://lore.kernel.org/linux-sound/20240625172836.589380-1-perex@perex.cz/ Signed-off-by: Jaroslav Kysela --- include/pcm.h | 4 ++++ include/sound/uapi/asound.h | 3 ++- src/pcm/pcm.c | 30 ++++++++++++++++++++++++++---- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/include/pcm.h b/include/pcm.h index 102ff812..b5c67889 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -508,6 +508,9 @@ typedef union _snd_pcm_sync_id { unsigned int id32[4]; } snd_pcm_sync_id_t; +/** synchronization ID size (see snd_pcm_hw_params_get_sync) */ +#define SND_PCM_HW_PARAMS_SYNC_SIZE 16 + /** Infinite wait for snd_pcm_wait() */ #define SND_PCM_WAIT_INFINITE (-1) /** Wait for next i/o in snd_pcm_wait() */ @@ -747,6 +750,7 @@ int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params, unsigned int *rate_den); int snd_pcm_hw_params_get_sbits(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_get_fifo_size(const snd_pcm_hw_params_t *params); +const unsigned char * snd_pcm_hw_params_get_sync(const snd_pcm_hw_params_t *params); #if 0 typedef struct _snd_pcm_hw_strategy snd_pcm_hw_strategy_t; diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index f3b2b94d..012fd890 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -426,7 +426,8 @@ struct snd_pcm_hw_params { unsigned int rate_num; /* R: rate numerator */ unsigned int rate_den; /* R: rate denominator */ snd_pcm_uframes_t fifo_size; /* R: chip FIFO size in frames */ - unsigned char reserved[64]; /* reserved for future */ + unsigned char sync[16]; /* R: synchronization ID (perfect sync - one clock source) */ + unsigned char reserved[48]; /* reserved for future */ }; enum { diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 62e76e7e..f8581191 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -494,8 +494,8 @@ software parameter. There are two functions allowing link multiple streams together. In the case, the linking means that all operations are synchronized. Because the drivers cannot guarantee the synchronization (sample resolution) on hardware -lacking this feature, the #snd_pcm_info_get_sync() function -returns synchronization ID - #snd_pcm_sync_id_t, which is equal +lacking this feature, the #snd_pcm_hw_params_get_sync() function +returns 16-byte synchronization ID, which is equal for hardware synchronized streams. When the #snd_pcm_link() function is called, all operations managing the stream state for these two streams are joined. The opposite function is #snd_pcm_unlink(). @@ -3948,6 +3948,25 @@ int snd_pcm_hw_params_get_fifo_size(const snd_pcm_hw_params_t *params) return params->fifo_size; } +/** + * \brief Get hardware synchronization ID from a PCM info container + * \param params Configuration space + * \return 16-byte synchronization ID (use #SND_PCM_HW_PARAMS_SYNC_SIZE) + * + * This synchronization ID determines the similar clocks for the + * PCM stream between multiple devices (including different cards). + * "All zeros" means "not set". The contents of the ID can be used + * only for a comparison with the contents of another ID returned + * from this function. Applications should not do a comparison with + * hard-coded values, because the implementation generating such + * synchronization IDs may be changed in future. + */ +const unsigned char *snd_pcm_hw_params_get_sync(const snd_pcm_hw_params_t *params) +{ + assert(params); + return params->sync; +} + /** * \brief Fill params with a full configuration space for a PCM * \param pcm PCM handle @@ -7332,7 +7351,7 @@ unsigned int snd_pcm_info_get_subdevices_avail(const snd_pcm_info_t *obj) } /** - * \brief Get hardware synchronization ID from a PCM info container + * \brief (DEPRECATED) Get hardware synchronization ID from a PCM info container * \param obj PCM info container * \return hardware synchronization ID */ @@ -7340,9 +7359,12 @@ snd_pcm_sync_id_t snd_pcm_info_get_sync(const snd_pcm_info_t *obj) { snd_pcm_sync_id_t res; assert(obj); - memcpy(&res, &obj->sync, sizeof(res)); + bzero(&res, sizeof(res)); return res; } +#ifndef DOC_HIDDEN +link_warning(snd_pcm_info_get_sync, "Warning: snd_pcm_info_get_sync is deprecated, consider to use snd_pcm_hw_params_get_sync"); +#endif /** * \brief Set wanted device inside a PCM info container (see #snd_ctl_pcm_info) From f81236cf7e3a72a55f693a3459b6bf8dfd3dd717 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 20 Aug 2024 14:27:13 +0200 Subject: [PATCH 050/117] Sync UAPI asound.h and asequencer.h with 6.12 kernel The recent upstream kernel received a few features and extended / cleaned up the asound.h and asequencer.h uapi files: - ALSA: pcm: Introduce MSBITS subformat interface - ALSA: pcm: clarify and fix default msbits value for all formats - ALSA: pcm: reinvent the stream synchronization ID API - ALSA: timer: Introduce virtual userspace-driven timers Signed-off-by: Takashi Iwai --- include/sound/uapi/asequencer.h | 18 +------ include/sound/uapi/asound.h | 83 +++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 52 deletions(-) diff --git a/include/sound/uapi/asequencer.h b/include/sound/uapi/asequencer.h index b3e9df58..bd3c7a31 100644 --- a/include/sound/uapi/asequencer.h +++ b/include/sound/uapi/asequencer.h @@ -3,22 +3,6 @@ * Main header file for the ALSA sequencer * Copyright (c) 1998-1999 by Frank van de Pol * (c) 1998-1999 by Jaroslav Kysela - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef __SOUND_ASEQUENCER_H #define __SOUND_ASEQUENCER_H @@ -619,7 +603,7 @@ struct snd_seq_client_ump_info { int client; /* client number to inquire/set */ int type; /* type to inquire/set */ unsigned char info[512]; /* info (either UMP ep or block info) */ -} __packed; +} __attribute__((packed)); /* * IOCTL commands diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index 012fd890..533b7e46 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -3,22 +3,6 @@ * Advanced Linux Sound Architecture - ALSA - Driver * Copyright (c) 1994-2003 by Jaroslav Kysela , * Abramo Bagnara - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef __SOUND_ASOUND_H @@ -28,7 +12,7 @@ #include #include #else -#include +#include #include #endif @@ -54,8 +38,10 @@ * * ****************************************************************************/ +#define AES_IEC958_STATUS_SIZE 24 + struct snd_aes_iec958 { - unsigned char status[24]; /* AES/IEC958 channel status bits */ + unsigned char status[AES_IEC958_STATUS_SIZE]; /* AES/IEC958 channel status bits */ unsigned char subcode[147]; /* AES/IEC958 subcode bits */ unsigned char pad; /* nothing */ unsigned char dig_subframe[4]; /* AES/IEC958 subframe bits */ @@ -154,7 +140,7 @@ struct snd_hwdep_dsp_image { * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 15) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 18) typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; @@ -200,6 +186,11 @@ typedef int __bitwise snd_pcm_format_t; #define SNDRV_PCM_FORMAT_S24_BE ((snd_pcm_format_t) 7) /* low three bytes */ #define SNDRV_PCM_FORMAT_U24_LE ((snd_pcm_format_t) 8) /* low three bytes */ #define SNDRV_PCM_FORMAT_U24_BE ((snd_pcm_format_t) 9) /* low three bytes */ +/* + * For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the + * available bit count in most significant bit. It's for the case of so-called 'left-justified' or + * `right-padding` sample which has less width than 32 bit. + */ #define SNDRV_PCM_FORMAT_S32_LE ((snd_pcm_format_t) 10) #define SNDRV_PCM_FORMAT_S32_BE ((snd_pcm_format_t) 11) #define SNDRV_PCM_FORMAT_U32_LE ((snd_pcm_format_t) 12) @@ -301,7 +292,8 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME 0x02000000 /* report absolute hardware link audio time, not reset on startup */ #define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */ #define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */ - +#define SNDRV_PCM_INFO_EXPLICIT_SYNC 0x10000000 /* needs explicit sync of pointers and data */ +#define SNDRV_PCM_INFO_NO_REWINDS 0x20000000 /* hardware can only support monotonic changes of appl_ptr */ #define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ @@ -340,7 +332,7 @@ union snd_pcm_sync_id { unsigned char id[16]; unsigned short id16[8]; unsigned int id32[4]; -}; +} __attribute__((deprecated)); struct snd_pcm_info { unsigned int device; /* RO/WR (control): device number */ @@ -354,7 +346,7 @@ struct snd_pcm_info { int dev_subclass; /* SNDRV_PCM_SUBCLASS_* */ unsigned int subdevices_count; unsigned int subdevices_avail; - union snd_pcm_sync_id sync; /* hardware synchronization ID */ + unsigned char pad1[16]; /* was: hardware synchronization ID */ unsigned char reserved[64]; /* reserved for future... */ }; @@ -393,8 +385,8 @@ typedef int snd_pcm_hw_param_t; #define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) /* avoid rate resampling */ #define SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER (1<<1) /* export buffer */ #define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2) /* disable period wakeups */ -#define SNDRV_PCM_HW_PARAMS_NO_DRAIN_SILENCE (1<<3) /* suppress the silence fill - * for draining +#define SNDRV_PCM_HW_PARAMS_NO_DRAIN_SILENCE (1<<3) /* suppress drain with the filling + * of the silence samples */ struct snd_interval { @@ -422,7 +414,7 @@ struct snd_pcm_hw_params { unsigned int rmask; /* W: requested masks */ unsigned int cmask; /* R: changed masks */ unsigned int info; /* R: Info flags for returned setup */ - unsigned int msbits; /* R: used most significant bits */ + unsigned int msbits; /* R: used most significant bits (in sample bit-width) */ unsigned int rate_num; /* R: rate numerator */ unsigned int rate_den; /* R: rate denominator */ snd_pcm_uframes_t fifo_size; /* R: chip FIFO size in frames */ @@ -443,9 +435,14 @@ struct snd_pcm_sw_params { snd_pcm_uframes_t avail_min; /* min avail frames for wakeup */ snd_pcm_uframes_t xfer_align; /* obsolete: xfer size need to be a multiple */ snd_pcm_uframes_t start_threshold; /* min hw_avail frames for automatic start */ - snd_pcm_uframes_t stop_threshold; /* min avail frames for automatic stop */ - snd_pcm_uframes_t silence_threshold; /* min distance from noise for silence filling */ - snd_pcm_uframes_t silence_size; /* silence block size */ + /* + * The following two thresholds alleviate playback buffer underruns; when + * hw_avail drops below the threshold, the respective action is triggered: + */ + snd_pcm_uframes_t stop_threshold; /* - stop playback */ + snd_pcm_uframes_t silence_threshold; /* - pre-fill buffer with silence */ + snd_pcm_uframes_t silence_size; /* max size of silence pre-fill; when >= boundary, + * fill played area with silence immediately */ snd_pcm_uframes_t boundary; /* pointers wrap point */ unsigned int proto; /* protocol version */ unsigned int tstamp_type; /* timestamp type (req. proto >= 2.0.12) */ @@ -578,7 +575,8 @@ struct __snd_pcm_mmap_status64 { struct __snd_pcm_mmap_control64 { __pad_before_uframe __pad1; snd_pcm_uframes_t appl_ptr; /* RW: appl ptr (0...boundary-1) */ - __pad_before_uframe __pad2; + __pad_before_uframe __pad2; // This should be __pad_after_uframe, but binary + // backwards compatibility constraints prevent a fix. __pad_before_uframe __pad3; snd_pcm_uframes_t avail_min; /* RW: min available frames for wakeup */ @@ -760,7 +758,7 @@ struct snd_rawmidi_framing_tstamp { __u32 tv_nsec; /* nanoseconds */ __u64 tv_sec; /* seconds */ __u8 data[SNDRV_RAWMIDI_FRAMING_DATA_LENGTH]; -} __packed; +} __attribute__((packed)); struct snd_rawmidi_params { int stream; @@ -808,7 +806,7 @@ struct snd_ump_endpoint_info { unsigned char name[128]; /* endpoint name string */ unsigned char product_id[128]; /* unique product id string */ unsigned char reserved[32]; -} __packed; +} __attribute__((packed)); /* UMP direction */ #define SNDRV_UMP_DIR_INPUT 0x01 @@ -844,7 +842,7 @@ struct snd_ump_block_info { unsigned int flags; /* various info flags */ unsigned char name[128]; /* block name string */ unsigned char reserved[32]; -} __packed; +} __attribute__((packed)); #define SNDRV_RAWMIDI_IOCTL_PVERSION _IOR('W', 0x00, int) #define SNDRV_RAWMIDI_IOCTL_INFO _IOR('W', 0x01, struct snd_rawmidi_info) @@ -861,7 +859,7 @@ struct snd_ump_block_info { * Timer section - /dev/snd/timer */ -#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 7) +#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 8) enum { SNDRV_TIMER_CLASS_NONE = -1, @@ -886,6 +884,7 @@ enum { #define SNDRV_TIMER_GLOBAL_RTC 1 /* unused */ #define SNDRV_TIMER_GLOBAL_HPET 2 #define SNDRV_TIMER_GLOBAL_HRTIMER 3 +#define SNDRV_TIMER_GLOBAL_UDRIVEN 4 /* info flags */ #define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */ @@ -964,6 +963,18 @@ struct snd_timer_status { unsigned char reserved[64]; /* reserved */ }; +/* + * This structure describes the userspace-driven timer. Such timers are purely virtual, + * and can only be triggered from software (for instance, by userspace application). + */ +struct snd_timer_uinfo { + /* To pretend being a normal timer, we need to know the resolution in ns. */ + __u64 resolution; + int fd; + unsigned int id; + unsigned char reserved[16]; +}; + #define SNDRV_TIMER_IOCTL_PVERSION _IOR('T', 0x00, int) #define SNDRV_TIMER_IOCTL_NEXT_DEVICE _IOWR('T', 0x01, struct snd_timer_id) #define SNDRV_TIMER_IOCTL_TREAD_OLD _IOW('T', 0x02, int) @@ -980,6 +991,8 @@ struct snd_timer_status { #define SNDRV_TIMER_IOCTL_CONTINUE _IO('T', 0xa2) #define SNDRV_TIMER_IOCTL_PAUSE _IO('T', 0xa3) #define SNDRV_TIMER_IOCTL_TREAD64 _IOW('T', 0xa4, int) +#define SNDRV_TIMER_IOCTL_CREATE _IOWR('T', 0xa5, struct snd_timer_uinfo) +#define SNDRV_TIMER_IOCTL_TRIGGER _IO('T', 0xa6) #if __BITS_PER_LONG == 64 #define SNDRV_TIMER_IOCTL_TREAD SNDRV_TIMER_IOCTL_TREAD_OLD @@ -1065,7 +1078,7 @@ typedef int __bitwise snd_ctl_elem_iface_t; #define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1) #define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE) #define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */ -// (1 << 3) is unused. +/* (1 << 3) is unused. */ #define SNDRV_CTL_ELEM_ACCESS_TLV_READ (1<<4) /* TLV read is possible */ #define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE (1<<5) /* TLV write is possible */ #define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE (SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) @@ -1162,7 +1175,7 @@ struct snd_ctl_elem_value { struct snd_ctl_tlv { unsigned int numid; /* control element numeric identification */ unsigned int length; /* in bytes aligned to 4 */ - unsigned int tlv[0]; /* first TLV */ + unsigned int tlv[]; /* first TLV */ }; #define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int) From 645668dca2c554b43b2fbf053a7ac3699f1beae6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 20 Aug 2024 16:20:15 +0200 Subject: [PATCH 051/117] src/Versions.in: Add the new snd_pcm_hw_params_get_sync for 1.2.13 Signed-off-by: Takashi Iwai --- src/Versions.in.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Versions.in.in b/src/Versions.in.in index 298e610c..350ec3d8 100644 --- a/src/Versions.in.in +++ b/src/Versions.in.in @@ -199,10 +199,14 @@ ALSA_1.2.10 { } ALSA_1.2.9; ALSA_1.2.13 { -#if defined(HAVE_SEQ_SYMS) || defined(HAVE_RAWMIDI_SYMS) +#if defined(HAVE_PCM_SYMS) || defined(HAVE_SEQ_SYMS) || defined(HAVE_RAWMIDI_SYMS) global: #endif +#ifdef HAVE_PCM_SYMS + @SYMBOL_PREFIX@snd_pcm_hw_params_get_sync; +#endif + #ifdef HAVE_SEQ_SYMS @SYMBOL_PREFIX@snd_seq_create_ump_endpoint; @SYMBOL_PREFIX@snd_seq_create_ump_block; From 3b9f3b9431fe7adeca0bd3917f26a8877a1ee7e6 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 6 Sep 2024 09:58:47 +0200 Subject: [PATCH 052/117] Fixes: 5600b901 ("async: snd_async_del_handler - move clear signal using sigaction as last") A wrong list head is used to check if the given list with async handlers is empty. Correct this. Link: https://github.com/alsa-project/alsa-lib/issues/394 Signed-off-by: Jaroslav Kysela --- src/async.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/async.c b/src/async.c index a003e441..4117354a 100644 --- a/src/async.c +++ b/src/async.c @@ -153,9 +153,22 @@ int snd_async_del_handler(snd_async_handler_t *handler) int was_empty; assert(handler); if (handler->type != SND_ASYNC_HANDLER_GENERIC) { - if (!list_empty(&handler->hlist)) + struct list_head *alist; + switch (handler->type) { +#ifdef BUILD_PCM + case SND_ASYNC_HANDLER_PCM: + alist = &handler->u.pcm->async_handlers; + break; +#endif + case SND_ASYNC_HANDLER_CTL: + alist = &handler->u.ctl->async_handlers; + break; + default: + assert(0); + } + if (!list_empty(alist)) list_del(&handler->hlist); - if (!list_empty(&handler->hlist)) + if (!list_empty(alist)) goto _glist; switch (handler->type) { #ifdef BUILD_PCM From 2adc30e9838daf62312f32a0a93129d64b1668b3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 6 Sep 2024 10:05:56 +0200 Subject: [PATCH 053/117] pcm: dmix: Fix resume with multiple instances The fix for PCM dmix suspend/resume checks spcm->info bit of SND_PCM_INFO_RESUME for applying a workaround for drivers with the full resume support. This assumed that scpm->info is exposed from the underlying slave PCM device. The above is true for the first opened instance, but for the second opened instance, it's a copy from the saved data in shmem. And, we dropped SND_PCM_INFO_RESUME bit there to assure not to expose the full resume capability to applications. This resulted in the inconsistencies, and when the second instance is resumed at first, it misses the snd_pcm_resume() call, hence the driver doesn't react properly any longer. For addressing it, we keep SND_PCM_INFO_RESUME bit in shmptr->s.info bits as is, while dropping the bit exposed to apps in snd_pcm_direct_hw_refine() and *_hw_params() callbacks. Fixes: 6d1d620eadf3 ("pcm: dmix: resume workaround for buggy driver") Reported-and-tested-by: Chancel Liu Closes: https://lore.kernel.org/DB9PR04MB94988752ED7C43B399E0BC00E3942@DB9PR04MB9498.eurprd04.prod.outlook.com Signed-off-by: Takashi Iwai --- src/pcm/pcm_direct.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index e53e5923..17e677f6 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -1018,6 +1018,7 @@ int snd_pcm_direct_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) } dshare->timer_ticks = hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE)->max / dshare->slave_period_size; params->info = dshare->shmptr->s.info; + params->info &= ~SND_PCM_INFO_RESUME; #ifdef REFINE_DEBUG snd_output_puts(log, "DMIX REFINE (end):\n"); snd_pcm_hw_params_dump(params, log); @@ -1031,6 +1032,7 @@ int snd_pcm_direct_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) snd_pcm_direct_t *dmix = pcm->private_data; params->info = dmix->shmptr->s.info; + params->info &= ~SND_PCM_INFO_RESUME; params->rate_num = dmix->shmptr->s.rate; params->rate_den = 1; params->fifo_size = 0; @@ -1183,8 +1185,6 @@ static void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm) COPY_SLAVE(buffer_time); COPY_SLAVE(sample_bits); COPY_SLAVE(frame_bits); - - dmix->shmptr->s.info &= ~SND_PCM_INFO_RESUME; } #undef COPY_SLAVE From fc58f8fcc39c365feff4261d4923bef384fe5700 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 6 Sep 2024 10:15:46 +0200 Subject: [PATCH 054/117] pcm: dmix: Don't clear scpm->info flag spcm->info bits should be a copy of the slave PCM info as is. While we clear the unsupported SND_PCM_INFO_PAUSE bit there, it should be rather cleared only for the exposed info to apps, not spcm->info. Fixes: 982786e9ebff ("Fix bogus pause flag on dmix") Signed-off-by: Takashi Iwai --- src/pcm/pcm_direct.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 17e677f6..5b8ec08f 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -1018,7 +1018,7 @@ int snd_pcm_direct_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) } dshare->timer_ticks = hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE)->max / dshare->slave_period_size; params->info = dshare->shmptr->s.info; - params->info &= ~SND_PCM_INFO_RESUME; + params->info &= ~(SND_PCM_INFO_RESUME | SND_PCM_INFO_PAUSE); #ifdef REFINE_DEBUG snd_output_puts(log, "DMIX REFINE (end):\n"); snd_pcm_hw_params_dump(params, log); @@ -1032,7 +1032,7 @@ int snd_pcm_direct_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) snd_pcm_direct_t *dmix = pcm->private_data; params->info = dmix->shmptr->s.info; - params->info &= ~SND_PCM_INFO_RESUME; + params->info &= ~(SND_PCM_INFO_RESUME | SND_PCM_INFO_PAUSE); params->rate_num = dmix->shmptr->s.rate; params->rate_den = 1; params->fifo_size = 0; @@ -1156,8 +1156,6 @@ int snd_pcm_direct_resume(snd_pcm_t *pcm) /* copy the slave setting */ static void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm) { - spcm->info &= ~SND_PCM_INFO_PAUSE; - COPY_SLAVE(access); COPY_SLAVE(format); COPY_SLAVE(subformat); From 9ac93d1252c4ddd0043dbe7b08781320b26ad4c9 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bokowy Date: Sat, 31 Aug 2024 20:31:02 +0200 Subject: [PATCH 055/117] Fix TLV dB parser in case of used container In case when dB information does not appear as the only TLV type in the stream (it might be wrapped in a container, but the container can not have any other type), the TLV parser fails to get the dB TLV pointer. This commit fixes it by distinguishing between TLV parse error and dB information not being found in a container (-ENOENT), so the parser can iterate over all elements in the container. Also, it fixes out-of-bounds read in case of malicious TLV record. Closes: https://github.com/alsa-project/alsa-lib/pull/409 Signed-off-by: Arkadiusz Bokowy Signed-off-by: Jaroslav Kysela --- src/control/tlv.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/control/tlv.c b/src/control/tlv.c index 3a2b731d..fed46acd 100644 --- a/src/control/tlv.c +++ b/src/control/tlv.c @@ -40,6 +40,8 @@ #define int_index(size) (((size) + sizeof(int) - 1) / sizeof(int)) /* max size of a TLV entry for dB information (including compound one) */ #define MAX_TLV_RANGE_SIZE 256 +/* min length of a TLV stream to contain type and size */ +#define MIN_TLV_STREAM_LEN ((SNDRV_CTL_TLVO_LEN + 1) * sizeof(int)) #endif /** @@ -47,12 +49,12 @@ * \param tlv the TLV source * \param tlv_size the byte size of TLV source * \param db_tlvp the pointer stored the dB TLV information - * \return the byte size of dB TLV information if found in the given - * TLV source, or a negative error code. + * \return The byte size of dB TLV information if found in the given TLV + * source, -ENOENT if not found, or a negative error code in case of an error. * * This function parses the given TLV source and stores the TLV start * point if the TLV information regarding dB conversion is found. - * The stored TLV pointer can be passed to the convesion functions + * The stored TLV pointer can be passed to the conversion functions * #snd_tlv_convert_to_dB(), #snd_tlv_convert_from_dB() and * #snd_tlv_get_dB_range(). */ @@ -64,6 +66,13 @@ int snd_tlv_parse_dB_info(unsigned int *tlv, unsigned int size; int err; + /* Validate that it is possible to read the type and size + * without reading past the end of the buffer. */ + if (tlv_size < MIN_TLV_STREAM_LEN) { + SNDERR("TLV stream too short"); + return -EINVAL; + } + *db_tlvp = NULL; type = tlv[SNDRV_CTL_TLVO_TYPE]; size = tlv[SNDRV_CTL_TLVO_LEN]; @@ -79,7 +88,7 @@ int snd_tlv_parse_dB_info(unsigned int *tlv, while (size > 0) { unsigned int len; err = snd_tlv_parse_dB_info(tlv, size, db_tlvp); - if (err < 0) + if (err < 0 && err != -ENOENT) return err; /* error */ if (err > 0) return err; /* found */ @@ -114,7 +123,7 @@ int snd_tlv_parse_dB_info(unsigned int *tlv, default: break; } - return -EINVAL; /* not found */ + return -ENOENT; } /** From 513ef7ace42a8f770db935ccf822d0cb11c7e033 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 6 Sep 2024 12:53:03 +0200 Subject: [PATCH 056/117] github: use upload-artifacts@v4 Signed-off-by: Jaroslav Kysela --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b01ad81d..3e8aecc9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,7 +64,7 @@ jobs: cd alsa-lib-$(cat version) make install - name: Archive package - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: name: alsa-lib-test-package path: artifacts/ From 93d7645d841d4e2c6a84c60b99a8392b8c511eb8 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 6 Sep 2024 12:56:16 +0200 Subject: [PATCH 057/117] pcm: close - deactivate async handler before snd_pcm_drop() It reduces probablity to activate the async handler when snd_pcm_close() is called. Link: https://github.com/alsa-project/alsa-lib/issues/394 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index f8581191..d51e8abd 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -777,6 +777,10 @@ int snd_pcm_close(snd_pcm_t *pcm) { int res = 0, err; assert(pcm); + while (!list_empty(&pcm->async_handlers)) { + snd_async_handler_t *h = list_entry(pcm->async_handlers.next, snd_async_handler_t, hlist); + snd_async_del_handler(h); + } if (pcm->setup && !pcm->donot_close) { snd_pcm_drop(pcm); err = snd_pcm_hw_free(pcm); @@ -785,10 +789,6 @@ int snd_pcm_close(snd_pcm_t *pcm) } if (pcm->mmap_channels) snd_pcm_munmap(pcm); - while (!list_empty(&pcm->async_handlers)) { - snd_async_handler_t *h = list_entry(pcm->async_handlers.next, snd_async_handler_t, hlist); - snd_async_del_handler(h); - } if (pcm->ops->close) err = pcm->ops->close(pcm->op_arg); else From 073dc7577a7e282a569677312f3a708d44511421 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 19 Sep 2024 11:55:58 +0200 Subject: [PATCH 058/117] configure: bumb version to 1.2.13pre1 (for alsa-utils) Signed-off-by: Jaroslav Kysela --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 89527a01..b029a705 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(alsa-lib, 1.2.12) +AC_INIT(alsa-lib, 1.2.13pre1) AC_CONFIG_SRCDIR([src/control/control.c]) AC_CONFIG_MACRO_DIR([m4]) From a3865b2439ce686024bc83b3b9b1cd60fa75986c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 21 Oct 2024 09:31:12 +0200 Subject: [PATCH 059/117] configure: do allow to use --with-pic for static build Closes: https://github.com/alsa-project/alsa-lib/issues/411 Signed-off-by: Jaroslav Kysela --- configure.ac | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/configure.ac b/configure.ac index b029a705..e3526578 100644 --- a/configure.ac +++ b/configure.ac @@ -71,6 +71,15 @@ EOF exit 1 fi +dnl Do not use --with-pic for shared libraries (consider to use PIE) +if test "$enable_static" = "yes" -a "$pic_mode" = "yes"; then +cat < Date: Thu, 24 Oct 2024 11:08:00 +0200 Subject: [PATCH 060/117] control: remap - fix copy-n-paste in _snd_ctl_remap_open's comment Signed-off-by: Jaroslav Kysela --- src/control/control_remap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/control/control_remap.c b/src/control/control_remap.c index 81cf38e3..e0ed9748 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -1282,7 +1282,7 @@ ctl.name { * \param handlep Returns created control handle * \param name Name of control * \param root Root configuration node - * \param conf Configuration node with Route & Volume PCM description + * \param conf Configuration node with control remap description * \param mode Control handle mode * \retval zero on success otherwise a negative error code * \warning Using of this function might be dangerous in the sense From 49295a4e17777218aa630a2c48dda4ab3d282832 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 24 Oct 2024 11:08:00 +0200 Subject: [PATCH 061/117] control: remap - clarify comments and docs Fix copy-n-paste errors. Signed-off-by: Jaroslav Kysela --- src/control/control_remap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/control/control_remap.c b/src/control/control_remap.c index e0ed9748..fb027d42 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -1228,10 +1228,10 @@ child controls to one or split one control to more. \code ctl.name { - type remap # Route & Volume conversion PCM - child STR # Slave name + type remap # Remap controls + child STR # Child name # or - child { # Slave definition + child { # Child definition type STR ... } From 785fd327ada6fc1778a2bb21176cb66705eb6b33 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 12 Nov 2024 10:36:52 +0100 Subject: [PATCH 062/117] Release v1.2.13 Signed-off-by: Jaroslav Kysela --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index e3526578..1cd22a59 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(alsa-lib, 1.2.13pre1) +AC_INIT(alsa-lib, 1.2.13) AC_CONFIG_SRCDIR([src/control/control.c]) AC_CONFIG_MACRO_DIR([m4]) From 76edab4e595bd5f3f4c636cccc8d7976d3c519d6 Mon Sep 17 00:00:00 2001 From: Nicholas Vinson Date: Thu, 14 Nov 2024 07:49:53 -0500 Subject: [PATCH 063/117] src/Versions.in.in: Update *_tempo_base name Change @SYMBOL_PREFIX@snd_has_tempo_base to @SYMBOL_PREFIX@snd_has_queue_tempo_base. Starting with version 1.2.13, alsa-lib fails to link with ld.lld-19 due to "version script assignment of 'ALSA_1.2.13' to symbol 'snd_seq_has_tempo_base' failed: symbol not defined". Per commit 769d1db1b0a213a39c7e59c0d1d724e7f45b1ac3 the correct name for the symbol is @SYMBOL_PREFIX@snd_has_queue_tempo_base; therefore, update src/Vesions.in.in to match. Fixes bug #420 Fixes Gentoo bug 943399 (https://bugs.gentoo.org/943399) Closes: https://github.com/alsa-project/alsa-lib/pull/421 Signed-off-by: Nicholas Vinson Signed-off-by: Jaroslav Kysela --- src/Versions.in.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Versions.in.in b/src/Versions.in.in index 350ec3d8..7ad6a633 100644 --- a/src/Versions.in.in +++ b/src/Versions.in.in @@ -212,7 +212,7 @@ ALSA_1.2.13 { @SYMBOL_PREFIX@snd_seq_create_ump_block; @SYMBOL_PREFIX@snd_seq_queue_tempo_get_tempo_base; @SYMBOL_PREFIX@snd_seq_queue_tempo_set_tempo_base; - @SYMBOL_PREFIX@snd_seq_has_tempo_base; + @SYMBOL_PREFIX@snd_seq_has_queue_tempo_base; @SYMBOL_PREFIX@snd_seq_port_info_get_ump_is_midi1; @SYMBOL_PREFIX@snd_seq_port_info_set_ump_is_midi1; #endif From 6880219ad4ba55ae8a94a34b7a987b3369f7c96f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 24 Nov 2024 09:32:29 +0100 Subject: [PATCH 064/117] configure: Make sequencer dependent on rawmidi The sequencer feature requires rawmidi implicitly, and it became more obvious with UMP support. Add the dependency check to configure script. Signed-off-by: Takashi Iwai --- configure.ac | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure.ac b/configure.ac index 1cd22a59..69aeb978 100644 --- a/configure.ac +++ b/configure.ac @@ -483,6 +483,12 @@ fi AC_SUBST(PYTHON_LIBS) AC_SUBST(PYTHON_INCLUDES) +if test "$build_rawmidi" != "yes"; then + if test "$build_seq" = "yes"; then + AC_ERROR([Cannot enable sequencer without rawmidi]) + fi +fi + AM_CONDITIONAL([BUILD_MIXER], [test x$build_mixer = xyes]) AM_CONDITIONAL([BUILD_PCM], [test x$build_pcm = xyes]) AM_CONDITIONAL([BUILD_RAWMIDI], [test x$build_rawmidi = xyes]) From 07cee0ba05179a56764c35975d5822420d4f31f9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 24 Nov 2024 09:33:16 +0100 Subject: [PATCH 065/117] seq: include UMP headers Some applications seem including alsa/seqmid.h individually, and this got broken with the update of alsa-lib because now we have dependencies to UMP stuff. Include the necessary UMP headers internally. Also, add the inclusion of rawmidi.h in ump.h for similar reasons. Link: https://bugzilla.suse.com/show_bug.cgi?id=1233682 Signed-off-by: Takashi Iwai --- include/seq.h | 2 ++ include/seq_event.h | 2 ++ include/ump.h | 2 ++ 3 files changed, 6 insertions(+) diff --git a/include/seq.h b/include/seq.h index 2eee95a6..5082ad0a 100644 --- a/include/seq.h +++ b/include/seq.h @@ -29,6 +29,8 @@ #ifndef __ALSA_SEQ_H #define __ALSA_SEQ_H +#include "ump.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/include/seq_event.h b/include/seq_event.h index 9ca384ee..0b59202f 100644 --- a/include/seq_event.h +++ b/include/seq_event.h @@ -28,6 +28,8 @@ #ifndef __ALSA_SEQ_EVENT_H #define __ALSA_SEQ_EVENT_H +#include "ump_msg.h" + /** * \defgroup SeqEvents Sequencer Event Definitions * Sequencer Event Definitions diff --git a/include/ump.h b/include/ump.h index 1043d237..06c86a5e 100644 --- a/include/ump.h +++ b/include/ump.h @@ -9,6 +9,8 @@ #ifndef __ALSA_UMP_H #define __ALSA_UMP_H +#include "rawmidi.h" + #ifdef __cplusplus extern "C" { #endif From 76d2d285c10b1e368b4bf9494cf9270ae46197cb Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 25 Nov 2024 15:11:27 +0100 Subject: [PATCH 066/117] rawmidi: ump - fix snd_ump_block_info_get_block_id double version The changes for 1.2.13 in Versions.in.in file matches also old 1.2.10 function snd_ump_block_info_get_block_id: 1 Removed function: [D] 'function void snd_ump_block_info_set_block_id(snd_ump_block_info_t*, unsigned int)' {snd_ump_block_info_set_block_id@@ALSA_1.2.10} Add 1.2.10 symbol back, but keep 1.2.13 symbol as default. Closes: https://github.com/alsa-project/alsa-lib/issues/422 Signed-off-by: Jaroslav Kysela --- src/rawmidi/ump.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index d3676afb..b1246c33 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -750,11 +750,20 @@ int snd_ump_block_info_get_device(const snd_ump_block_info_t *info) * \param info pointer to a snd_ump_block_info_t structure * \return ID number of the given UMP block */ +#ifndef DOXYGEN +EXPORT_SYMBOL unsigned int INTERNAL(snd_ump_block_info_get_block_id)(const snd_ump_block_info_t *info) +#else unsigned int snd_ump_block_info_get_block_id(const snd_ump_block_info_t *info) +#endif { return info->block_id; } +#ifndef DOC_HIDDEN +use_symbol_version(__snd_ump_block_info_get_block_id, snd_ump_block_info_get_block_id, ALSA_1.2.10); +use_default_symbol_version(__snd_ump_block_info_get_block_id, snd_ump_block_info_get_block_id, ALSA_1.2.13); +#endif /* DOC_HIDDEN */ + /** * \brief get UMP block activeness * \param info pointer to a snd_ump_block_info_t structure From 352cbc5eb94a271a9c3c0ff5bf1742232a69e0d0 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 25 Nov 2024 16:17:30 +0100 Subject: [PATCH 067/117] rawmidi: ump - fix snd_ump_block_info_set_block_id double version Fix mistake snd_ump_block_info_get_block_id / snd_ump_block_info_set_block_id . Fixes: 76d2d285 ("rawmidi: ump - fix snd_ump_block_info_get_block_id double version") Link: https://github.com/alsa-project/alsa-lib/issues/422 Signed-off-by: Jaroslav Kysela --- src/rawmidi/ump.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index b1246c33..6807e877 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -750,20 +750,11 @@ int snd_ump_block_info_get_device(const snd_ump_block_info_t *info) * \param info pointer to a snd_ump_block_info_t structure * \return ID number of the given UMP block */ -#ifndef DOXYGEN -EXPORT_SYMBOL unsigned int INTERNAL(snd_ump_block_info_get_block_id)(const snd_ump_block_info_t *info) -#else unsigned int snd_ump_block_info_get_block_id(const snd_ump_block_info_t *info) -#endif { return info->block_id; } -#ifndef DOC_HIDDEN -use_symbol_version(__snd_ump_block_info_get_block_id, snd_ump_block_info_get_block_id, ALSA_1.2.10); -use_default_symbol_version(__snd_ump_block_info_get_block_id, snd_ump_block_info_get_block_id, ALSA_1.2.13); -#endif /* DOC_HIDDEN */ - /** * \brief get UMP block activeness * \param info pointer to a snd_ump_block_info_t structure @@ -881,12 +872,22 @@ void snd_ump_block_info_set_device(snd_ump_block_info_t *info, unsigned int devi * * This function is mostly used for setting the block ID to query. */ +#ifndef DOXYGEN +EXPORT_SYMBOL void INTERNAL(snd_ump_block_info_set_block_id)(snd_ump_block_info_t *info, + unsigned int id) +#else void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, unsigned int id) +#endif { info->block_id = id; } +#ifndef DOC_HIDDEN +use_symbol_version(__snd_ump_block_info_set_block_id, snd_ump_block_info_set_block_id, ALSA_1.2.10); +use_default_symbol_version(__snd_ump_block_info_set_block_id, snd_ump_block_info_set_block_id, ALSA_1.2.13); +#endif /* DOC_HIDDEN */ + /** * \brief set activeness to snd_ump_block_info_t structure * \param info pointer to a snd_ump_block_info_t structure From 3fe7d9a49b82fbaf1d805e318239d2f49aad329f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 13 Jan 2025 15:03:46 +0100 Subject: [PATCH 068/117] Sync UAPI asound.h with 6.14 kernel There is a small update of rawmidi API for supporting the UMP tied device info and inactive flag. Signed-off-by: Takashi Iwai --- include/sound/uapi/asound.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index 533b7e46..1f91d994 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -708,7 +708,7 @@ enum { * Raw MIDI section - /dev/snd/midi?? */ -#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 4) +#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 5) enum { SNDRV_RAWMIDI_STREAM_OUTPUT = 0, @@ -720,6 +720,9 @@ enum { #define SNDRV_RAWMIDI_INFO_INPUT 0x00000002 #define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004 #define SNDRV_RAWMIDI_INFO_UMP 0x00000008 +#define SNDRV_RAWMIDI_INFO_STREAM_INACTIVE 0x00000010 + +#define SNDRV_RAWMIDI_DEVICE_UNKNOWN 0 struct snd_rawmidi_info { unsigned int device; /* RO/WR (control): device number */ @@ -732,7 +735,8 @@ struct snd_rawmidi_info { unsigned char subname[32]; /* name of active or selected subdevice */ unsigned int subdevices_count; unsigned int subdevices_avail; - unsigned char reserved[64]; /* reserved for future use */ + int tied_device; /* R: tied rawmidi device (UMP/legacy) */ + unsigned char reserved[60]; /* reserved for future use */ }; #define SNDRV_RAWMIDI_MODE_FRAMING_MASK (7<<0) From e1cf4d3f6841d54e046f2a1b4a6151c8041cc2c2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 13 Jan 2025 15:07:23 +0100 Subject: [PATCH 069/117] Sync UAPI asequencer.h with 6.14 kernel There are the new event types for UMP EP and FB notifications as well as the new struct for them. Signed-off-by: Takashi Iwai --- include/sound/uapi/asequencer.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/include/sound/uapi/asequencer.h b/include/sound/uapi/asequencer.h index bd3c7a31..9682795b 100644 --- a/include/sound/uapi/asequencer.h +++ b/include/sound/uapi/asequencer.h @@ -10,7 +10,7 @@ #include /** version of the sequencer */ -#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 4) +#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 5) /** * definition of sequencer event types @@ -92,6 +92,9 @@ #define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED 66 /* ports connected */ #define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED 67 /* ports disconnected */ +#define SNDRV_SEQ_EVENT_UMP_EP_CHANGE 68 /* UMP EP info has changed */ +#define SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE 69 /* UMP block info has changed */ + /* 70-89: synthesizer events - obsoleted */ /** user-defined events with fixed length @@ -253,6 +256,12 @@ struct snd_seq_ev_quote { struct snd_seq_event *event; /* quoted event */ } __attribute__((packed)); + /* UMP info change notify */ +struct snd_seq_ev_ump_notify { + unsigned char client; /* Client number */ + unsigned char block; /* Block number (optional) */ +}; + union snd_seq_event_data { /* event data... */ struct snd_seq_ev_note note; struct snd_seq_ev_ctrl control; @@ -265,6 +274,7 @@ union snd_seq_event_data { /* event data... */ struct snd_seq_connect connect; struct snd_seq_result result; struct snd_seq_ev_quote quote; + struct snd_seq_ev_ump_notify ump_notify; }; /* sequencer event */ From 137eca7720be69f0ef46afb6d30332b21d89b8b5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 13 Jan 2025 15:24:11 +0100 Subject: [PATCH 070/117] rawmidi: Extensions for tied device and substream inactive flag This is the enhancements of rawmidi API for the new feature added in 6.14 kernel: the indication of a tied device and the inactive flag for the selected substream. The new function is added for obtaining the tied device, snd_rawmidi_info_get_tied_device(). And the new bit flag is defined for indicating the inactive substream, SNDRV_RAWMIDI_INFO_STREAM_INACTIVE, which is exposed via snd_rawmidi_info_get_flags(). Signed-off-by: Takashi Iwai --- include/rawmidi.h | 2 ++ src/rawmidi/rawmidi.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/rawmidi.h b/include/rawmidi.h index 2630d1e6..af734b21 100644 --- a/include/rawmidi.h +++ b/include/rawmidi.h @@ -95,6 +95,7 @@ typedef enum _snd_rawmidi_read_mode { /** rawmidi info bit flags */ #define SND_RAWMIDI_INFO_UMP 0x00000008 /* rawmidi is UMP */ +#define SNDRV_RAWMIDI_INFO_STREAM_INACTIVE 0x00000010 /* the selected substream is inactive */ int snd_rawmidi_open(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi, const char *name, int mode); @@ -124,6 +125,7 @@ const char *snd_rawmidi_info_get_name(const snd_rawmidi_info_t *obj); const char *snd_rawmidi_info_get_subdevice_name(const snd_rawmidi_info_t *obj); unsigned int snd_rawmidi_info_get_subdevices_count(const snd_rawmidi_info_t *obj); unsigned int snd_rawmidi_info_get_subdevices_avail(const snd_rawmidi_info_t *obj); +int snd_rawmidi_info_get_tied_device(const snd_rawmidi_info_t *obj); void snd_rawmidi_info_set_device(snd_rawmidi_info_t *obj, unsigned int val); void snd_rawmidi_info_set_subdevice(snd_rawmidi_info_t *obj, unsigned int val); void snd_rawmidi_info_set_stream(snd_rawmidi_info_t *obj, snd_rawmidi_stream_t val); diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index c4b45fa2..9bb6d744 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -639,6 +639,22 @@ unsigned int snd_rawmidi_info_get_subdevices_avail(const snd_rawmidi_info_t *inf return info->subdevices_avail; } +/** + * \brief get the tied device number for the given rawmidi device + * \param info pointer to a snd_rawmidi_info_t structure + * \return the device number for the tied device, or -1 if untied / unknown. + * + * This function is useful for UMP rawmidi devices where each of them may + * have the mirroring legacy rawmidi device. Those are shown as "tied". + */ +int snd_rawmidi_info_get_tied_device(const snd_rawmidi_info_t *info) +{ + assert(info); + if (info->tied_device > 0) + return info->tied_device - 1; + return -1; +} + /** * \brief set rawmidi device number * \param info pointer to a snd_rawmidi_info_t structure From cdd5a9fa104af4eea204088a3c610156cb50d0e2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 13 Jan 2025 15:27:36 +0100 Subject: [PATCH 071/117] rawmidi: Make rawmidi flag bits doxygen-style comments We forgot to put the markers in rawmidi info bit flags. Signed-off-by: Takashi Iwai --- include/rawmidi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rawmidi.h b/include/rawmidi.h index af734b21..713258f2 100644 --- a/include/rawmidi.h +++ b/include/rawmidi.h @@ -94,8 +94,8 @@ typedef enum _snd_rawmidi_read_mode { } snd_rawmidi_read_mode_t; /** rawmidi info bit flags */ -#define SND_RAWMIDI_INFO_UMP 0x00000008 /* rawmidi is UMP */ -#define SNDRV_RAWMIDI_INFO_STREAM_INACTIVE 0x00000010 /* the selected substream is inactive */ +#define SND_RAWMIDI_INFO_UMP 0x00000008 /**< rawmidi is UMP */ +#define SNDRV_RAWMIDI_INFO_STREAM_INACTIVE 0x00000010 /**< the selected substream is inactive */ int snd_rawmidi_open(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi, const char *name, int mode); From 35d2efefa95d28e0cd963a06e051f8125823f077 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 13 Jan 2025 15:34:49 +0100 Subject: [PATCH 072/117] seq: Define new events for UMP EP/FB change notifications Two new sequencer event types are added for notifications of UMP info changes: SND_SEQ_EVENT_UMP_EP_CHANGE (68) and SND_SEQ_EVENT_UMP_BLOCK_CHANGE (69). Signed-off-by: Takashi Iwai --- include/local.h | 1 + include/seq_event.h | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/local.h b/include/local.h index 512e4455..2b38ce64 100644 --- a/include/local.h +++ b/include/local.h @@ -208,6 +208,7 @@ #define snd_seq_result sndrv_seq_result #define snd_seq_queue_skew sndrv_seq_queue_skew #define snd_seq_ev_queue_control sndrv_seq_ev_queue_control +#define snd_seq_ev_ump_notify sndrv_seq_ev_ump_notify #define snd_seq_client_t sndrv_seq_client_t #define snd_seq_client_type_t sndrv_seq_client_type_t diff --git a/include/seq_event.h b/include/seq_event.h index 0b59202f..da542b10 100644 --- a/include/seq_event.h +++ b/include/seq_event.h @@ -135,6 +135,11 @@ enum snd_seq_event_type { /** Ports disconnected; event data type = #snd_seq_connect_t */ SND_SEQ_EVENT_PORT_UNSUBSCRIBED, + /** UMP Endpoint info has changed; event data type = #snd_seq_ev_ump_notify_t */ + SND_SEQ_EVENT_UMP_EP_CHANGE, + /** UMP Block info has changed; event data type = #snd_seq_ev_ump_notify_t */ + SND_SEQ_EVENT_UMP_BLOCK_CHANGE, + /** user-defined event; event data type = any (fixed size) */ SND_SEQ_EVENT_USR0 = 90, /** user-defined event; event data type = any (fixed size) */ @@ -294,6 +299,12 @@ typedef struct snd_seq_ev_queue_control { } param; /**< data value union */ } snd_seq_ev_queue_control_t; +/** UMP info change notify */ +typedef struct snd_seq_ev_ump_notify { + unsigned char client; /**< Client number */ + unsigned char block; /**< Block number (optional) */ +} snd_seq_ev_ump_notify_t; + /** Sequencer event data */ typedef union snd_seq_event_data { snd_seq_ev_note_t note; /**< note information */ @@ -306,6 +317,7 @@ typedef union snd_seq_event_data { snd_seq_addr_t addr; /**< address */ snd_seq_connect_t connect; /**< connect information */ snd_seq_result_t result; /**< operation result code */ + snd_seq_ev_ump_notify_t ump_notify; /**< UMP info change notification */ } snd_seq_event_data_t; /** Sequencer event */ From ea8972c83b020d92e1a9f0a5c12eaee159bf6c63 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 2 Feb 2025 13:46:12 +0100 Subject: [PATCH 073/117] include: prefer alsa/asoundlib.h for apps, dependency cleanups Fixes several issues with header files: - prefer alsa/asoundlib.h file for the alsa-lib core functionalities (use #warning to inform current and future developers, do the job) - include alsa/asoundlib.h in headers for external plugins by default - pcm_external.h: dependencies cleanup - as benefit, the parsers in IDEs should get all information for individial header files (see PR#435) This change was mainly tergetted to fix errors caused by wrong include order (like for endianness detection, missing typedefs etc.). Closes: https://github.com/alsa-project/alsa-lib/issues/431 Link: https://github.com/alsa-project/alsa-lib/pull/435 Signed-off-by: Jaroslav Kysela --- configure.ac | 2 ++ include/Makefile.am | 17 +++++++++-------- include/asoundef.h | 6 ++++++ include/asoundlib-head.h | 2 ++ include/conf.h | 5 +++++ include/control.h | 6 ++++++ include/control_external.h | 5 ++++- include/error.h | 6 ++++++ include/global.h | 9 ++++++--- include/hwdep.h | 6 ++++++ include/input.h | 6 ++++++ include/local.h | 4 ++++ include/mixer.h | 6 ++++++ include/output.h | 8 ++++++-- include/pcm.h | 8 ++++++-- include/pcm_external.h | 4 +++- include/pcm_extplug.h | 5 +++++ include/pcm_ioplug.h | 5 +++++ include/pcm_old.h | 5 +++++ include/pcm_rate.h | 4 ++++ include/rawmidi.h | 6 ++++++ include/seq.h | 8 ++++++-- include/seq_event.h | 8 ++++++-- include/seq_midi_event.h | 6 ++++++ include/seqmid.h | 6 ++++++ include/timer.h | 6 ++++++ include/ump.h | 8 ++++++-- include/ump_msg.h | 8 ++++++-- 28 files changed, 150 insertions(+), 25 deletions(-) diff --git a/configure.ac b/configure.ac index 69aeb978..75c65530 100644 --- a/configure.ac +++ b/configure.ac @@ -634,6 +634,7 @@ AM_CONDITIONAL([BUILD_PCM_PLUGIN_DSNOOP], [test x$build_pcm_dsnoop = xyes]) AM_CONDITIONAL([BUILD_PCM_PLUGIN_ASYM], [test x$build_pcm_asym = xyes]) AM_CONDITIONAL([BUILD_PCM_PLUGIN_IEC958], [test x$build_pcm_iec958 = xyes]) AM_CONDITIONAL([BUILD_PCM_PLUGIN_SOFTVOL], [test x$build_pcm_softvol = xyes]) +AM_CONDITIONAL([BUILD_PCM_PLUGIN_EXTERNAL], [test x$build_pcm_extplug = xyes -o x$build_pcm_ioplug = xyes]) AM_CONDITIONAL([BUILD_PCM_PLUGIN_EXTPLUG], [test x$build_pcm_extplug = xyes]) AM_CONDITIONAL([BUILD_PCM_PLUGIN_IOPLUG], [test x$build_pcm_ioplug = xyes]) AM_CONDITIONAL([BUILD_PCM_PLUGIN_MMAP_EMUL], [test x$build_pcm_mmap_emul = xyes]) @@ -826,6 +827,7 @@ test "$build_pcm" = "yes" && echo "#include " >> include/asoundlib test "$build_hwdep" = "yes" && echo "#include " >> include/asoundlib.h echo "#include " >> include/asoundlib.h test "$build_mixer" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h diff --git a/include/Makefile.am b/include/Makefile.am index 16d68b51..c1885e1d 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -22,15 +22,16 @@ endif if BUILD_PCM_PLUGIN_RATE alsainclude_HEADERS += pcm_rate.h endif -if BUILD_PCM_PLUGIN_EXTPLUG -alsainclude_HEADERS += pcm_external.h pcm_extplug.h -endif -if BUILD_PCM_PLUGIN_IOPLUG -if !BUILD_PCM_PLUGIN_EXTPLUG -alsainclude_HEADERS += pcm_external.h -endif -alsainclude_HEADERS += pcm_ioplug.h +if BUILD_PCM_PLUGIN_EXTERNAL +# FIXME: pcm_external.h includes both pcm_extplug.h and pcm_ioplug.h +alsainclude_HEADERS += pcm_external.h pcm_extplug.h pcm_ioplug.h endif +#if BUILD_PCM_PLUGIN_EXTPLUG +#alsainclude_HEADERS += pcm_extplug.h +#endif +#if BUILD_PCM_PLUGIN_IOPLUG +#alsainclude_HEADERS += pcm_ioplug.h +#endif endif if BUILD_RAWMIDI diff --git a/include/asoundef.h b/include/asoundef.h index 261db40b..5e158b35 100644 --- a/include/asoundef.h +++ b/include/asoundef.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_ASOUNDEF_H #define __ALSA_ASOUNDEF_H diff --git a/include/asoundlib-head.h b/include/asoundlib-head.h index 7cfa440d..382a0a58 100644 --- a/include/asoundlib-head.h +++ b/include/asoundlib-head.h @@ -38,3 +38,5 @@ #include #include #include +#include +#include diff --git a/include/conf.h b/include/conf.h index 09da0e9a..327659c9 100644 --- a/include/conf.h +++ b/include/conf.h @@ -25,6 +25,11 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#include +#endif + #ifndef __ALSA_CONF_H #define __ALSA_CONF_H diff --git a/include/control.h b/include/control.h index e7541d56..b206fbb0 100644 --- a/include/control.h +++ b/include/control.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_CONTROL_H #define __ALSA_CONTROL_H diff --git a/include/control_external.h b/include/control_external.h index 488fa6e2..2baaea26 100644 --- a/include/control_external.h +++ b/include/control_external.h @@ -23,10 +23,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ + #ifndef __ALSA_CONTROL_EXTERNAL_H #define __ALSA_CONTROL_EXTERNAL_H -#include "control.h" +#ifndef __ASOUNDLIB_LOCAL +#include +#endif #ifdef __cplusplus extern "C" { diff --git a/include/error.h b/include/error.h index 7239db85..8a2a9abc 100644 --- a/include/error.h +++ b/include/error.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_ERROR_H #define __ALSA_ERROR_H diff --git a/include/global.h b/include/global.h index 3ecaeee8..84a40557 100644 --- a/include/global.h +++ b/include/global.h @@ -25,12 +25,15 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_GLOBAL_H_ #define __ALSA_GLOBAL_H_ -/* for timeval and timespec */ -#include - #ifdef __cplusplus extern "C" { #endif diff --git a/include/hwdep.h b/include/hwdep.h index 46ed1f78..174aa899 100644 --- a/include/hwdep.h +++ b/include/hwdep.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_HWDEP_H #define __ALSA_HWDEP_H diff --git a/include/input.h b/include/input.h index f84954a9..80fc8624 100644 --- a/include/input.h +++ b/include/input.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_INPUT_H #define __ALSA_INPUT_H diff --git a/include/local.h b/include/local.h index 2b38ce64..55f252df 100644 --- a/include/local.h +++ b/include/local.h @@ -30,6 +30,9 @@ #include #include #include +#include +/* for timeval and timespec */ +#include #ifdef HAVE_ENDIAN_H #include #elif defined(HAVE_SYS_ENDIAN_H) @@ -187,6 +190,7 @@ #include "hwdep.h" #include "control.h" #include "mixer.h" +#include "ump_msg.h" #include "seq_event.h" #include "seq.h" diff --git a/include/mixer.h b/include/mixer.h index 735dfdd8..93dd70cd 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_MIXER_H #define __ALSA_MIXER_H diff --git a/include/output.h b/include/output.h index 5e16420b..14089c05 100644 --- a/include/output.h +++ b/include/output.h @@ -25,11 +25,15 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_OUTPUT_H #define __ALSA_OUTPUT_H -#include - #ifdef __cplusplus extern "C" { #endif diff --git a/include/pcm.h b/include/pcm.h index b5c67889..3fa9203e 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -26,6 +26,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_PCM_H #define __ALSA_PCM_H @@ -33,8 +39,6 @@ extern "C" { #endif -#include - /** * \defgroup PCM PCM Interface * See the \ref pcm page for more details. diff --git a/include/pcm_external.h b/include/pcm_external.h index a6005b34..27e53fc0 100644 --- a/include/pcm_external.h +++ b/include/pcm_external.h @@ -26,7 +26,9 @@ #ifndef __ALSA_PCM_EXTERNAL_H #define __ALSA_PCM_EXTERNAL_H -#include "pcm.h" +#ifndef __ASOUNDLIB_LOCAL +#include +#endif #ifdef __cplusplus extern "C" { diff --git a/include/pcm_extplug.h b/include/pcm_extplug.h index e5c02d4d..09d7c7cb 100644 --- a/include/pcm_extplug.h +++ b/include/pcm_extplug.h @@ -28,6 +28,11 @@ * */ +#ifndef __ALSA_PCM_EXTERNAL_H +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_PCM_EXTPLUG_H #define __ALSA_PCM_EXTPLUG_H diff --git a/include/pcm_ioplug.h b/include/pcm_ioplug.h index 81ac8613..457b3e8b 100644 --- a/include/pcm_ioplug.h +++ b/include/pcm_ioplug.h @@ -28,6 +28,11 @@ * */ +#ifndef __ALSA_PCM_EXTERNAL_H +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_PCM_IOPLUG_H #define __ALSA_PCM_IOPLUG_H diff --git a/include/pcm_old.h b/include/pcm_old.h index a9f5308f..3ff5f82c 100644 --- a/include/pcm_old.h +++ b/include/pcm_old.h @@ -1,3 +1,8 @@ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#error "use #include , should not be used directly" +#endif + /* * Old ALSA 0.9.x API */ diff --git a/include/pcm_rate.h b/include/pcm_rate.h index 48473ed4..0a914d86 100644 --- a/include/pcm_rate.h +++ b/include/pcm_rate.h @@ -31,6 +31,10 @@ #ifndef __ALSA_PCM_RATE_H #define __ALSA_PCM_RATE_H +#ifndef __ASOUNDLIB_LOCAL +#include +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/include/rawmidi.h b/include/rawmidi.h index 713258f2..a144f659 100644 --- a/include/rawmidi.h +++ b/include/rawmidi.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_RAWMIDI_H #define __ALSA_RAWMIDI_H diff --git a/include/seq.h b/include/seq.h index 5082ad0a..17bcfa53 100644 --- a/include/seq.h +++ b/include/seq.h @@ -26,11 +26,15 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_SEQ_H #define __ALSA_SEQ_H -#include "ump.h" - #ifdef __cplusplus extern "C" { #endif diff --git a/include/seq_event.h b/include/seq_event.h index da542b10..b27ce36c 100644 --- a/include/seq_event.h +++ b/include/seq_event.h @@ -25,11 +25,15 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_SEQ_EVENT_H #define __ALSA_SEQ_EVENT_H -#include "ump_msg.h" - /** * \defgroup SeqEvents Sequencer Event Definitions * Sequencer Event Definitions diff --git a/include/seq_midi_event.h b/include/seq_midi_event.h index 4cfb15c4..6fbbf400 100644 --- a/include/seq_midi_event.h +++ b/include/seq_midi_event.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_SEQ_MIDI_EVENT_H #define __ALSA_SEQ_MIDI_EVENT_H diff --git a/include/seqmid.h b/include/seqmid.h index bf968a5b..6d22692f 100644 --- a/include/seqmid.h +++ b/include/seqmid.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_SEQMID_H #define __ALSA_SEQMID_H diff --git a/include/timer.h b/include/timer.h index dc5ca69e..09283ce0 100644 --- a/include/timer.h +++ b/include/timer.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_TIMER_H #define __ALSA_TIMER_H diff --git a/include/ump.h b/include/ump.h index 06c86a5e..d19ab557 100644 --- a/include/ump.h +++ b/include/ump.h @@ -6,11 +6,15 @@ * API library for ALSA rawmidi/UMP interface */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_UMP_H #define __ALSA_UMP_H -#include "rawmidi.h" - #ifdef __cplusplus extern "C" { #endif diff --git a/include/ump_msg.h b/include/ump_msg.h index 5f0a45ac..f846ffe9 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -6,11 +6,15 @@ * API library for ALSA rawmidi/UMP interface */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_UMP_MSG_H #define __ALSA_UMP_MSG_H -#include - #ifdef __cplusplus extern "C" { #endif From a7de48692f7d52443b8a29b470206c868c2c6987 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 2 Feb 2025 18:51:04 +0100 Subject: [PATCH 074/117] include/ump_msg.h: Fix endianness detection The SNDRV_BIG_ENDIAN_BITFIELD define is only available in the alsa-lib's internal build process (include/local.h). Signed-off-by: Jaroslav Kysela --- include/ump_msg.h | 60 +++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/include/ump_msg.h b/include/ump_msg.h index f846ffe9..f61cee04 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -19,9 +19,13 @@ extern "C" { #endif +#if __BYTE_ORDER != __LITTLE_ENDIAN && __BYTE_ORDER != __BIG_ENDIAN +#error "Endianness check failed!" +#endif + /** general UMP packet header in 32bit word */ typedef struct _snd_ump_msg_hdr { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -40,7 +44,7 @@ typedef struct _snd_ump_msg_hdr { /** MIDI 1.0 Note Off / Note On (32bit) */ typedef struct _snd_ump_msg_midi1_note { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -59,7 +63,7 @@ typedef struct _snd_ump_msg_midi1_note { /** MIDI 1.0 Poly Pressure (32bit) */ typedef struct _snd_ump_msg_midi1_paf { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -78,7 +82,7 @@ typedef struct _snd_ump_msg_midi1_paf { /** MIDI 1.0 Control Change (32bit) */ typedef struct _snd_ump_msg_midi1_cc { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -97,7 +101,7 @@ typedef struct _snd_ump_msg_midi1_cc { /** MIDI 1.0 Program Change (32bit) */ typedef struct _snd_ump_msg_midi1_program { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -116,7 +120,7 @@ typedef struct _snd_ump_msg_midi1_program { /** MIDI 1.0 Channel Pressure (32bit) */ typedef struct _snd_ump_msg_midi1_caf { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -135,7 +139,7 @@ typedef struct _snd_ump_msg_midi1_caf { /** MIDI 1.0 Pitch Bend (32bit) */ typedef struct _snd_ump_msg_midi1_pitchbend { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -154,7 +158,7 @@ typedef struct _snd_ump_msg_midi1_pitchbend { /** System Common and Real Time messages (32bit); no channel field */ typedef struct snd_ump_msg_system { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status; /**< Status */ @@ -193,7 +197,7 @@ enum { /** MIDI 2.0 Note Off / Note On (64bit) */ typedef struct _snd_ump_msg_midi2_note { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -218,7 +222,7 @@ typedef struct _snd_ump_msg_midi2_note { /** MIDI 2.0 Poly Pressure (64bit) */ typedef struct _snd_ump_msg_midi2_paf { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -241,7 +245,7 @@ typedef struct _snd_ump_msg_midi2_paf { /** MIDI 2.0 Per-Note Controller (64bit) */ typedef struct _snd_ump_msg_midi2_per_note_cc { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -270,7 +274,7 @@ enum { /** MIDI 2.0 Per-Note Management (64bit) */ typedef struct _snd_ump_msg_midi2_per_note_mgmt { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -293,7 +297,7 @@ typedef struct _snd_ump_msg_midi2_per_note_mgmt { /** MIDI 2.0 Control Change (64bit) */ typedef struct _snd_ump_msg_midi2_cc { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -316,7 +320,7 @@ typedef struct _snd_ump_msg_midi2_cc { /** MIDI 2.0 Registered Controller (RPN) / Assignable Controller (NRPN) (64bit) */ typedef struct _snd_ump_msg_midi2_rpn { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -339,7 +343,7 @@ typedef struct _snd_ump_msg_midi2_rpn { /** MIDI 2.0 Program Change (64bit) */ typedef struct _snd_ump_msg_midi2_program { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -368,7 +372,7 @@ typedef struct _snd_ump_msg_midi2_program { /** MIDI 2.0 Channel Pressure (64bit) */ typedef struct _snd_ump_msg_midi2_caf { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -389,7 +393,7 @@ typedef struct _snd_ump_msg_midi2_caf { /** MIDI 2.0 Pitch Bend (64bit) */ typedef struct _snd_ump_msg_midi2_pitchbend { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -454,7 +458,7 @@ typedef union _snd_ump_msg_midi2 { /** Stream Message (generic) (128bit) */ typedef struct _snd_ump_msg_stream_gen { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint16_t type:4; /**< UMP packet type */ uint16_t format:2; /**< Format */ uint16_t status:10; /**< Status */ @@ -482,7 +486,7 @@ typedef union _snd_ump_msg_stream { /** Metadata / Text Message (128bit) */ typedef struct _snd_ump_msg_flex_data_meta { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t format:2; /**< Format */ @@ -505,7 +509,7 @@ typedef struct _snd_ump_msg_flex_data_meta { /** Set Tempo Message (128bit) */ typedef struct _snd_ump_msg_set_tempo { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t format:2; /**< Format */ @@ -534,7 +538,7 @@ typedef struct _snd_ump_msg_set_tempo { /** Set Time Signature Message (128bit) */ typedef struct _snd_ump_msg_set_time_sig { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t format:2; /**< Format */ @@ -569,7 +573,7 @@ typedef struct _snd_ump_msg_set_time_sig { /** Set Metronome Message (128bit) */ typedef struct _snd_ump_msg_set_metronome { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t format:2; /**< Format */ @@ -612,7 +616,7 @@ typedef struct _snd_ump_msg_set_metronome { /** Set Key Signature Message (128bit) */ typedef struct _snd_ump_msg_set_key_sig { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t format:2; /**< Format */ @@ -645,7 +649,7 @@ typedef struct _snd_ump_msg_set_key_sig { /** Set Chord Name Message (128bit) */ typedef struct _snd_ump_msg_set_chord_name { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t format:2; /**< Format */ @@ -786,7 +790,7 @@ typedef union _snd_ump_msg_mixed_data { /** Jitter Reduction Clock / Timestamp Message (32bit) */ typedef struct _snd_ump_msg_jr_clock { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -803,7 +807,7 @@ typedef struct _snd_ump_msg_jr_clock { /** Delta Clockstamp Ticks Per Quarter Note (DCTPQ) (32bit) */ typedef struct _snd_ump_msg_dctpq { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint8_t type:4; /**< UMP packet type */ uint8_t group:4; /**< UMP Group */ uint8_t status:4; /**< Status */ @@ -820,7 +824,7 @@ typedef struct _snd_ump_msg_dctpq { /** Delta Clockstamp (DC) (32bit) */ typedef struct _snd_ump_msg_dc { -#ifdef SNDRV_BIG_ENDIAN_BITFIELD +#if __BYTE_ORDER == __BIG_ENDIAN uint32_t type:4; /**< UMP packet type */ uint32_t group:4; /**< UMP Group */ uint32_t status:4; /**< Status */ @@ -1113,7 +1117,7 @@ static inline uint8_t snd_ump_sysex_msg_length(const uint32_t *ump) */ static inline uint8_t snd_ump_get_byte(const uint32_t *ump, unsigned int offset) { -#ifdef SNDRV_BIG_ENDIAN +#if __BYTE_ORDER == __BIG_ENDIAN return ((const uint8_t *)ump)[offset]; #else return ((const uint8_t *)ump)[(offset & ~3) | (3 - (offset & 3))]; From c8bc54a9cae3d5080dc7a298aee573b10f2bbf62 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 2 Feb 2025 19:18:25 +0100 Subject: [PATCH 075/117] test/playmidi1: fix compilation caused by conflict between midifile.h and ump_msg.h It's a fast fix. The better way is to fix midifile.h or remote this example (we have already some Closes: https://github.com/alsa-project/alsa-lib/issues/436 Signed-off-by: Jaroslav Kysela --- test/playmidi1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/playmidi1.c b/test/playmidi1.c index 831e9578..286aaa86 100644 --- a/test/playmidi1.c +++ b/test/playmidi1.c @@ -45,10 +45,11 @@ #include #include +#include "../include/asoundlib.h" + #include "midifile.h" /* SMF library header */ #include "midifile.c" /* SMF library code */ -#include "../include/asoundlib.h" /* send the real-time time stamps (instead of midi ticks) to the ALSA sequencer */ static int use_realtime = 0; From 1101c397aae8ee17677afc0c87b1b46210f110c9 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 2 Feb 2025 21:51:21 +0100 Subject: [PATCH 076/117] doc: fix permissions Doxygen create also subdirectories. Make sure that files in those directories have required permissions. Signed-off-by: Jaroslav Kysela --- doc/Makefile.am | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index e087f424..77174cf3 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -9,14 +9,16 @@ doc: doxygen doxygen.cfg doc-pack: doc - -chmod a+r $(top_srcdir)/doc/doxygen/html/* - -chmod a-w $(top_srcdir)/doc/doxygen/html/* + -chmod -R a+r $(top_srcdir)/doc/doxygen/html/* + -chmod -R a-w $(top_srcdir)/doc/doxygen/html/* if ! test -z "$(AMTAR)"; then \ $(AMTAR) --create --directory=$(top_srcdir)/doc/doxygen/html --verbose --file=- . | bzip2 -c -9 > $(top_srcdir)/../alsa-lib-doc.tar.bz2 ; \ else \ $(TAR) --create --directory=$(top_srcdir)/doc/doxygen/html --verbose --file=- . | bzip2 -c -9 > $(top_srcdir)/../alsa-lib-doc.tar.bz2 ; \ fi + -chmod -R u+w $(top_srcdir)/doc/doxygen/html/* rm -rf $(top_srcdir)/doc/doxygen/html/* doc-clean: + -chmod -R u+w $(top_srcdir)/doc/doxygen/html/* rm -rf $(top_srcdir)/doc/doxygen/html/* From 995e279081958cd292fe8515075b21df7cac85ae Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 2 Feb 2025 22:00:54 +0100 Subject: [PATCH 077/117] include: pcm extplug/ioplug: fix internal include The header files are directly included when the alsa-lib is building. Do not show warning. Fixes: ea8972c8 ("include: prefer alsa/asoundlib.h for apps, dependency cleanups") Signed-off-by: Jaroslav Kysela --- include/pcm_extplug.h | 2 +- include/pcm_ioplug.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pcm_extplug.h b/include/pcm_extplug.h index 09d7c7cb..1294a59e 100644 --- a/include/pcm_extplug.h +++ b/include/pcm_extplug.h @@ -28,7 +28,7 @@ * */ -#ifndef __ALSA_PCM_EXTERNAL_H +#if !defined(__ALSA_PCM_EXTERNAL_H) && !defined(ALSA_LIBRARY_BUILD) #warning "use #include , should not be used directly" #include #endif diff --git a/include/pcm_ioplug.h b/include/pcm_ioplug.h index 457b3e8b..722dc9d7 100644 --- a/include/pcm_ioplug.h +++ b/include/pcm_ioplug.h @@ -28,7 +28,7 @@ * */ -#ifndef __ALSA_PCM_EXTERNAL_H +#if !defined(__ALSA_PCM_EXTERNAL_H) && !defined(ALSA_LIBRARY_BUILD) #warning "use #include , should not be used directly" #include #endif From 0bb35d4980c32dc4b434ca65d8288bb9199936e5 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 2 Feb 2025 22:26:39 +0100 Subject: [PATCH 078/117] seq: seqmid - fix info->name is always true error Replate pointer check to the zero string check as it was the intention. Signed-off-by: Jaroslav Kysela --- src/seq/seqmid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index b30db407..17733ca6 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -552,7 +552,7 @@ int snd_seq_create_ump_endpoint(snd_seq_t *seq, if (!seq->ump_ep->version) seq->ump_ep->version = SND_UMP_EP_INFO_DEFAULT_VERSION; - if (info->name) { + if (info->name[0]) { err = snd_seq_set_client_name(seq, (const char *)info->name); if (err < 0) goto error_free; From 305168fd22b4ed430b562d669da1d22c93f09d87 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 2 Feb 2025 22:27:29 +0100 Subject: [PATCH 079/117] seq: seq.c - fix calloc arguments The usage was inverted. The first argument is count of elements, the second one is size of one element. Signed-off-by: Jaroslav Kysela --- src/seq/seq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/seq/seq.c b/src/seq/seq.c index 1a40e8bb..d3af93f5 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -1356,7 +1356,7 @@ int snd_seq_set_input_buffer_size(snd_seq_t *seq, size_t size) if (size != seq->ibufsize) { char *newbuf; /* use ump event size for avoiding reallocation at switching */ - newbuf = calloc(sizeof(snd_seq_ump_event_t), size); + newbuf = calloc(size, sizeof(snd_seq_ump_event_t)); if (newbuf == NULL) return -ENOMEM; free(seq->ibuf); From e550dcdd905d78db48571225807cccabc7d7baaa Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 3 Feb 2025 11:54:06 +0100 Subject: [PATCH 080/117] utils: add missing alsa-topology.pc.in to EXTRA_DIST Signed-off-by: Jaroslav Kysela --- utils/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/Makefile.am b/utils/Makefile.am index 94a71053..792ff1cc 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -2,7 +2,7 @@ if INSTALL_M4 aclocaldir=$(datadir)/aclocal aclocal_DATA=alsa.m4 endif -EXTRA_DIST=alsa.m4 buildrpm alsa.pc.in +EXTRA_DIST=alsa.m4 buildrpm alsa.pc.in alsa-topology.pc.in alsapkgconfdir = @ALSA_PKGCONF_DIR@ pkgconfigdir = $(alsapkgconfdir) From df8f1cc1ec9d9ee15be5e2c23ad25b9389fd8766 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 17 Feb 2025 12:06:09 +0100 Subject: [PATCH 081/117] seq: Fix bogus return of snd_seq_client_info_get_ump_conversion() snd_seq_client_info_get_ump_conversion() should have returned the proper bit of group_filter bit field, but it just did return midi_version field -- a stupid copy & paste error. Let's fix it. Fixes: 2aefb5c41cc0 ("seq: Add UMP support") Signed-off-by: Takashi Iwai --- src/seq/seq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/seq/seq.c b/src/seq/seq.c index d3af93f5..38cb1afd 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -1870,7 +1870,7 @@ int snd_seq_client_info_get_ump_groupless_enabled(const snd_seq_client_info_t *i int snd_seq_client_info_get_ump_conversion(const snd_seq_client_info_t *info) { assert(info); - return info->midi_version; + return !(info->group_filter & SNDRV_SEQ_FILTER_NO_CONVERT); } /** From 0c7086777aa4e6076a8277b3918c5243daf1c56a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 1 Mar 2025 11:00:15 +0100 Subject: [PATCH 082/117] seq: Fix typo of the group number in snd_seq_create_ump_endpoint() The group number of UMP Endpoint client created by snd_seq_create_ump_endpoint() is wrongly set due to a copy&paste error. Fixes: 6167b8ce3e80 ("seq: Add API helper functions for creating UMP Endpoint and Blocks") Closes: https://github.com/alsa-project/alsa-lib/issues/440 Signed-off-by: Takashi Iwai --- src/seq/seqmid.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 17733ca6..28c345de 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -582,11 +582,7 @@ int snd_seq_create_ump_endpoint(snd_seq_t *seq, SNDRV_SEQ_PORT_TYPE_MIDI_UMP | SND_SEQ_PORT_TYPE_APPLICATION | SNDRV_SEQ_PORT_TYPE_PORT); - snd_seq_port_info_set_ump_group(pinfo, - SND_SEQ_PORT_TYPE_MIDI_GENERIC | - SNDRV_SEQ_PORT_TYPE_MIDI_UMP | - SND_SEQ_PORT_TYPE_APPLICATION | - SNDRV_SEQ_PORT_TYPE_PORT); + snd_seq_port_info_set_ump_group(pinfo, 0); err = snd_seq_create_port(seq, pinfo); if (err < 0) { SNDERR("Failed to create MIDI 2.0 port\n"); From 7fbd47ce797939862025ef424865570dcdc2565b Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 13 Mar 2025 17:46:58 +0100 Subject: [PATCH 083/117] ucm: add sys-card substitution It may be useful to check additional sysfs parameters like USB descriptors to determine the exact hardware capabilities. Introduce 'sys-card' substitution and 'sys' substitution to allow data fetching from given range. Also, add conversion to hexadecimal format when the source file has binary contents. Example - fetch bytes from positions 0x10..0x15 (6 bytes): Define.Bytes1 "${sys-card:[type=hex,pos=0x10,size=6]device/../descriptors}" Example - fetch one byte from position 0x22: Define.Bytes2 "${sys-card:[type=hex,pos=0x22]device/../descriptors}" Replace type=hex or omit this variable settings to work with ASCII characters. Link: https://github.com/alsa-project/alsa-ucm-conf/issues/444 Signed-off-by: Jaroslav Kysela --- src/ucm/ucm_local.h | 2 +- src/ucm/ucm_subs.c | 158 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 153 insertions(+), 7 deletions(-) diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index 464d1154..b274277d 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -40,7 +40,7 @@ #include #include "use-case.h" -#define SYNTAX_VERSION_MAX 7 +#define SYNTAX_VERSION_MAX 8 #define MAX_CARD_SHORT_NAME 32 #define MAX_CARD_LONG_NAME 80 diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c index 2b010338..d1dc5819 100644 --- a/src/ucm/ucm_subs.c +++ b/src/ucm/ucm_subs.c @@ -30,6 +30,8 @@ #include #include +static unsigned char _hex_table[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + static char *rval_open_name(snd_use_case_mgr_t *uc_mgr) { const char *name; @@ -504,20 +506,117 @@ static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *i return NULL; } -static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id) +#define RANGE_TYPE_ASCII 0 +#define RANGE_TYPE_HEX 1 + +static int parse_position(snd_config_t *config, const char *name, ssize_t *pos, bool optional) +{ + snd_config_t *d; + const char *s; + long v; + + if (snd_config_search(config, name, &d)) { + if (optional) { + *pos = -1; + return 0; + } + uc_error("Unable to find field '%s'", name); + return -1; + } + if (!snd_config_get_integer(d, &v)) + goto fin; + if (snd_config_get_string(d, &s)) + return -1; + if (safe_strtol(s, &v)) { + uc_error("Unable to parse position '%s'", s); + return -1; + } +fin: + *pos = v; + return 0; +} + +static int parse_range(const char *cfg, int *type, ssize_t *pos, ssize_t *size) +{ + snd_config_t *config, *d; + int err, retval = 0; + const char *s; + + err = snd_config_load_string(&config, cfg, 0); + if (err < 0) { + uc_error("The range arguments '%s' are invalid", cfg); + return -1; + } + if (snd_config_search(config, "type", &d)) { + *type = RANGE_TYPE_ASCII; + } else { + if (snd_config_get_string(d, &s)) + goto null; + if (strcasecmp(s, "ascii") == 0) { + *type = RANGE_TYPE_ASCII; + } else if (strcasecmp(s, "hex") == 0) { + *type = RANGE_TYPE_HEX; + } else { + uc_error("Unknown range type '%s'", s); + } + } + *pos = 0; + *size = -1; + if (parse_position(config, "pos", pos, false) || + parse_position(config, "size", size, true)) { + retval = -1; + goto null; + } + + if (*size <= 0) + *size = 1; + if (*pos < 0) { + uc_error("Invalid start position"); + retval = -1; + goto null; + } + +null: + snd_config_delete(config); + return retval; +} + +static char *rval_sysfs_main(snd_use_case_mgr_t *uc_mgr, const char *top_path, const char *id) { char path[PATH_MAX], link[PATH_MAX + 1]; struct stat64 sb; - ssize_t len; - const char *e; - int fd; + ssize_t len, range_start = -1, range_size = -1; + const char *e, *s; + int fd, type = RANGE_TYPE_ASCII; e = uc_mgr_sysfs_root(); if (e == NULL) return NULL; + if (id[0] == '[') { + if (uc_mgr->conf_format < 8) { + uc_error("Sysfs ranges are supported in v8+ syntax"); + return NULL; + } + s = strchr(id, ']'); + if (s == NULL) + return NULL; + len = s - id - 1; + if ((size_t)(len - 1) > sizeof(link) - 1) + return NULL; + strncpy(link, id + 1, len); + link[len] = '\0'; + if (parse_range(link, &type, &range_start, &range_size)) { + uc_error("sysfs: cannot parse hex range '%s'", link); + return NULL; + } + id = s + 1; + } if (id[0] == '/') id++; - snprintf(path, sizeof(path), "%s/%s", e, id); + if (top_path) + snprintf(path, sizeof(path), "%s/%s/%s", e, top_path, id); + else + snprintf(path, sizeof(path), "%s/%s", e, id); if (lstat64(path, &sb) != 0) return NULL; if (S_ISLNK(sb.st_mode)) { @@ -542,18 +641,64 @@ static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char uc_error("sysfs open failed for '%s' (%d)", path, errno); return NULL; } - len = read(fd, path, sizeof(path)-1); + len = sizeof(path) - 1; + if (range_start > 0 && lseek(fd, range_start, SEEK_SET) != range_start) { + uc_error("sysfs seek failed (%d)", errno); + close(fd); + return NULL; + } + if (range_size > 0) { + if (range_size > len) { + uc_error("sysfs EOB for '%s'", path); + close(fd); + return NULL; + } else { + len = range_size; + } + } + len = read(fd, path, len); close(fd); if (len < 0) { uc_error("sysfs unable to read value '%s' (%d)", path, errno); return NULL; } + if (type == RANGE_TYPE_HEX && range_start >= 0) { + char *m = malloc(len * 2 + 1); + ssize_t idx; + if (m == NULL) + return NULL; + for (idx = 0; idx < len; idx++) { + m[(idx * 2) + 0] = _hex_table[((unsigned char)path[idx]) >> 4]; + m[(idx * 2) + 1] = _hex_table[((unsigned char)path[idx]) & 0x0f]; + } + m[len * 2] = '\0'; + return m; + } while (len > 0 && path[len-1] == '\n') len--; path[len] = '\0'; return strdup(path); } +static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr, const char *id) +{ + return rval_sysfs_main(uc_mgr, NULL, id); +} + +static char *rval_sysfs_card(snd_use_case_mgr_t *uc_mgr, const char *id) +{ + char top_path[32], *s; + + if (uc_mgr->conf_format < 8) { + uc_error("sys-card is supported in v8+ syntax"); + return NULL; + } + s = get_card_number(uc_mgr_get_master_ctl(uc_mgr)); + snprintf(top_path, sizeof(top_path), "class/sound/card%s", s); + free(s); + return rval_sysfs_main(uc_mgr, top_path, id); +} + static char *rval_var(snd_use_case_mgr_t *uc_mgr, const char *id) { const char *v; @@ -751,6 +896,7 @@ __std: MATCH_VARIABLE(value, "${CardComponents}", rval_card_components, true); MATCH_VARIABLE2(value, "${env:", rval_env, false); MATCH_VARIABLE2(value, "${sys:", rval_sysfs, false); + MATCH_VARIABLE2(value, "${sys-card:", rval_sysfs_card, false); MATCH_VARIABLE2(value, "${var:", rval_var, true); MATCH_VARIABLE2(value, "${eval:", rval_eval, false); MATCH_VARIABLE2(value, "${find-card:", rval_card_lookup, false); From 12f6790910de53f76dc26a51c09e26f0f7be56fe Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 19 Mar 2025 11:54:46 +0100 Subject: [PATCH 084/117] ucm: add @@LibraryVersion and @@SyntaxVersion variables It may be useful to check the current syntax version (and maybe library version) when new features are added. Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 5 +++++ src/ucm/ucm_confdoc.h | 28 +++++++++++++++------------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index 3a3c9c15..f1849aa2 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -1502,6 +1502,11 @@ const char *parse_open_variables(snd_use_case_mgr_t *uc_mgr, const char *name) skip: snd_config_delete(cfg); + + uc_mgr_set_variable(uc_mgr, "@@LibraryVersion", SND_LIB_VERSION_STR); + snprintf(vname, sizeof(vname), "%d", SYNTAX_VERSION_MAX); + uc_mgr_set_variable(uc_mgr, "@@SyntaxVersion", vname); + return end + 3; } diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index 908bc9d7..5002a2dc 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -386,25 +386,27 @@ Evaluation order | Configuration block | Evaluation restart The dynamic tree identifiers and assigned values in the configuration tree are substituted. The substitutes strings are in the table bellow. -Substituted string | Value ----------------------|--------------------- -${OpenName} | Original UCM card name (passed to snd_use_case_mgr_open()) -${ConfLibDir} | Library top-level configuration directory (e.g. /usr/share/alsa) -${ConfTopDir} | Top-level UCM configuration directory (e.g. /usr/share/alsa/ucm2) -${ConfDir} | Card's UCM configuration directory (e.g. /usr/share/alsa/ucm2/conf.d/USB-Audio) -${ConfName} | Configuration name (e.g. USB-Audio.conf) -${CardNumber} | Real ALSA card number (or empty string for the virtual UCM card) -${CardId} | ALSA card identifier (see snd_ctl_card_info_get_id()) -${CardDriver} | ALSA card driver (see snd_ctl_card_info_get_driver()) -${CardName} | ALSA card name (see snd_ctl_card_info_get_name()) -${CardLongName} | ALSA card long name (see snd_ctl_card_info_get_longname()) -${CardComponents} | ALSA card components (see snd_ctl_card_info_get_components()) +Substituted string | Value +-----------------------|--------------------- +${OpenName} | Original UCM card name (passed to snd_use_case_mgr_open()) +${ConfLibDir} | Library top-level configuration directory (e.g. /usr/share/alsa) +${ConfTopDir} | Top-level UCM configuration directory (e.g. /usr/share/alsa/ucm2) +${ConfDir} | Card's UCM configuration directory (e.g. /usr/share/alsa/ucm2/conf.d/USB-Audio) +${ConfName} | Configuration name (e.g. USB-Audio.conf) +${CardNumber} | Real ALSA card number (or empty string for the virtual UCM card) +${CardId} | ALSA card identifier (see snd_ctl_card_info_get_id()) +${CardDriver} | ALSA card driver (see snd_ctl_card_info_get_driver()) +${CardName} | ALSA card name (see snd_ctl_card_info_get_name()) +${CardLongName} | ALSA card long name (see snd_ctl_card_info_get_longname()) +${CardComponents} | ALSA card components (see snd_ctl_card_info_get_components()) ${env:\} | Environment variable \ ${sys:\} | Contents of sysfs file \ ${var:\} | UCM parser variable (set using a _Define_ block) ${eval:\} | Evaluate expression like *($var+2)/3* [**Syntax 5**] ${find-card:\} | Find a card - see _Find card substitution_ section ${find-device:\} | Find a device - see _Find device substitution_ section +@@LibraryVersion | e.g. "1.2.14" [**Syntax 8**] +@@SyntaxVersion | e.g. "8" [**Syntax 8**] #### Special whole string substitution From 8f6fef8b1a7d6bebc7ebbd610bbf9dc04c919a2b Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 19 Mar 2025 12:05:26 +0100 Subject: [PATCH 085/117] ucm: enhance documentation (sys-card + ranges + more) Signed-off-by: Jaroslav Kysela --- src/ucm/ucm_confdoc.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index 5002a2dc..bbfbd92f 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -401,6 +401,7 @@ ${CardLongName} | ALSA card long name (see snd_ctl_card_info_get_longname ${CardComponents} | ALSA card components (see snd_ctl_card_info_get_components()) ${env:\} | Environment variable \ ${sys:\} | Contents of sysfs file \ +${sys-card:\} | Contents of sysfs file in /sys/class/sound/card? tree [**Syntax 8**] ${var:\} | UCM parser variable (set using a _Define_ block) ${eval:\} | Evaluate expression like *($var+2)/3* [**Syntax 5**] ${find-card:\} | Find a card - see _Find card substitution_ section @@ -408,6 +409,32 @@ ${find-device:\} | Find a device - see _Find device substitution_ section @@LibraryVersion | e.g. "1.2.14" [**Syntax 8**] @@SyntaxVersion | e.g. "8" [**Syntax 8**] +General note: If two dollars '$$' instead one dolar '$' are used for the +substitution identification, the error is ignored (e.g. file does not +exists in sysfs tree). + +Note for *var* substitution: If the first characters is minus ('-') the +empty string is substituted when the variable is not defined. + +Note for *sys* and *sys-card* substitutions: since syntax 8, there is +also extension to fetch data from given range with the optional conversion +to hexadecimal format when the source file has binary contents. + +Example - fetch bytes from positions 0x10..0x15 (6 bytes): + +~~~{.html} +Define.Bytes1 "${sys-card:[type=hex,pos=0x10,size=6]device/../descriptors}" +~~~ + +Example - fetch one byte from position 0x22: + +~~~{.html} +Define.Bytes2 "${sys-card:[type=hex,pos=0x22]device/../descriptors}" +~~~ + +Replace *type=hex* with *type=ascii* or omit this variable settings to work with ASCII characters. + + #### Special whole string substitution Substituted string | Value From e9e3c01ff751b4828a916cb1e74515b320759a0c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 19 Mar 2025 12:20:25 +0100 Subject: [PATCH 086/117] ucm: format @@SyntaxVersion to 4 digits It is better for regex matching. Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 2 +- src/ucm/ucm_confdoc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index f1849aa2..5a86b9cd 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -1504,7 +1504,7 @@ skip: snd_config_delete(cfg); uc_mgr_set_variable(uc_mgr, "@@LibraryVersion", SND_LIB_VERSION_STR); - snprintf(vname, sizeof(vname), "%d", SYNTAX_VERSION_MAX); + snprintf(vname, sizeof(vname), "%04d", SYNTAX_VERSION_MAX); uc_mgr_set_variable(uc_mgr, "@@SyntaxVersion", vname); return end + 3; diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index bbfbd92f..025148f9 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -407,7 +407,7 @@ ${eval:\} | Evaluate expression like *($var+2)/3* [**Syntax 5**] ${find-card:\} | Find a card - see _Find card substitution_ section ${find-device:\} | Find a device - see _Find device substitution_ section @@LibraryVersion | e.g. "1.2.14" [**Syntax 8**] -@@SyntaxVersion | e.g. "8" [**Syntax 8**] +@@SyntaxVersion | e.g. "0008" (decimal base - no hex) [**Syntax 8**] General note: If two dollars '$$' instead one dolar '$' are used for the substitution identification, the error is ignored (e.g. file does not From 6855cb838d0a69183e1db6429bc9c14265b68240 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 19 Mar 2025 17:55:33 +0100 Subject: [PATCH 087/117] control: remap - add possibility to remap multiple source channels For UCM (ASoC), there is a requirement to remap two stereo controls to one stereo control (amplifiers). Link: https://github.com/alsa-project/alsa-ucm-conf/pull/525 Signed-off-by: Jaroslav Kysela --- src/control/control_remap.c | 115 ++++++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 18 deletions(-) diff --git a/src/control/control_remap.c b/src/control/control_remap.c index fb027d42..e577ab91 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -2,11 +2,11 @@ * \file control/control_remap.c * \brief CTL Remap Plugin Interface * \author Jaroslav Kysela - * \date 2021 + * \date 2021-2025 */ /* * Control - Remap Controls - * Copyright (c) 2021 by Jaroslav Kysela + * Copyright (c) 2021-2025 by Jaroslav Kysela * * * This library is free software; you can redistribute it and/or modify @@ -76,6 +76,7 @@ typedef struct { snd_ctl_elem_id_t id_child; size_t channel_map_items; size_t channel_map_alloc; + unsigned int src_channels; long *channel_map; } *controls; unsigned int event_mask; @@ -481,7 +482,7 @@ static int remap_map_elem_read(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *cont snd_ctl_map_t *map; struct snd_ctl_map_ctl *mctl; snd_ctl_elem_value_t control2; - size_t item, index; + size_t item, index, src_index; int err; map = remap_find_map_id(priv, &control->id); @@ -501,19 +502,36 @@ static int remap_map_elem_read(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *cont if (map->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || map->type == SNDRV_CTL_ELEM_TYPE_INTEGER) { for (index = 0; index < mctl->channel_map_items; index++) { - long src = mctl->channel_map[index]; + long base_idx = mctl->src_channels * index; + long src = mctl->channel_map[base_idx]; if ((unsigned long)src < ARRAY_SIZE(control->value.integer.value)) control->value.integer.value[index] = control2.value.integer.value[src]; + for (src_index = 1; src_index < mctl->src_channels; src_index++) { + src = mctl->channel_map[base_idx + src_index]; + if ((unsigned long)src < ARRAY_SIZE(control->value.integer.value)) + if (control2.value.integer.value[src] < control->value.integer.value[index]) + control->value.integer.value[index] = control2.value.integer.value[src]; + } } } else if (map->type == SNDRV_CTL_ELEM_TYPE_INTEGER64) { for (index = 0; index < mctl->channel_map_items; index++) { - long src = mctl->channel_map[index]; + long base_idx = mctl->src_channels * index; + long src = mctl->channel_map[base_idx]; if ((unsigned long)src < ARRAY_SIZE(control->value.integer64.value)) control->value.integer64.value[index] = control2.value.integer64.value[src]; + for (src_index = 1; src_index < mctl->src_channels; src_index++) { + src = mctl->channel_map[base_idx + src_index]; + if ((unsigned long)src < ARRAY_SIZE(control->value.integer64.value)) + if (control2.value.integer64.value[src] < control->value.integer64.value[index]) + control->value.integer64.value[index] = control2.value.integer64.value[src]; + } } } else if (map->type == SNDRV_CTL_ELEM_TYPE_BYTES) { + /* does it make sense to merge bytes? */ + if (mctl->src_channels > 1) + return -EINVAL; for (index = 0; index < mctl->channel_map_items; index++) { - long src = mctl->channel_map[index]; + long src = mctl->channel_map[mctl->src_channels]; if ((unsigned long)src < ARRAY_SIZE(control->value.bytes.data)) control->value.bytes.data[index] = control2.value.bytes.data[src]; } @@ -544,7 +562,7 @@ static int remap_map_elem_write(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *con snd_ctl_map_t *map; struct snd_ctl_map_ctl *mctl; snd_ctl_elem_value_t control2; - size_t item, index; + size_t item, index, src_index; int err, changes; map = remap_find_map_id(priv, &control->id); @@ -564,21 +582,40 @@ static int remap_map_elem_write(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *con if (map->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || map->type == SNDRV_CTL_ELEM_TYPE_INTEGER) { for (index = 0; index < mctl->channel_map_items; index++) { - long dst = mctl->channel_map[index]; + long base_idx = mctl->src_channels * index; + long dst = mctl->channel_map[base_idx]; if ((unsigned long)dst < ARRAY_SIZE(control->value.integer.value)) { changes |= control2.value.integer.value[dst] != control->value.integer.value[index]; control2.value.integer.value[dst] = control->value.integer.value[index]; } + for (src_index = 1; src_index < mctl->src_channels; src_index++) { + dst = mctl->channel_map[base_idx + src_index]; + if ((unsigned long)dst < ARRAY_SIZE(control->value.integer.value)) { + changes |= control2.value.integer.value[dst] != control->value.integer.value[index]; + control2.value.integer.value[dst] = control->value.integer.value[index]; + } + } } } else if (map->type == SNDRV_CTL_ELEM_TYPE_INTEGER64) { for (index = 0; index < mctl->channel_map_items; index++) { - long dst = mctl->channel_map[index]; + long base_idx = mctl->src_channels * index; + long dst = mctl->channel_map[base_idx]; if ((unsigned long)dst < ARRAY_SIZE(control->value.integer64.value)) { changes |= control2.value.integer64.value[dst] != control->value.integer64.value[index]; control2.value.integer64.value[dst] = control->value.integer64.value[index]; } + for (src_index = 1; src_index < mctl->src_channels; src_index++) { + dst = mctl->channel_map[base_idx + src_index]; + if ((unsigned long)dst < ARRAY_SIZE(control->value.integer.value)) { + changes |= control2.value.integer64.value[dst] != control->value.integer64.value[index]; + control2.value.integer64.value[dst] = control->value.integer64.value[index]; + } + } } } else if (map->type == SNDRV_CTL_ELEM_TYPE_BYTES) { + /* does it make sense to merge bytes? */ + if (mctl->src_channels > 1) + return -EINVAL; for (index = 0; index < mctl->channel_map_items; index++) { long dst = mctl->channel_map[index]; if ((unsigned long)dst < ARRAY_SIZE(control->value.bytes.data)) { @@ -1017,43 +1054,79 @@ static int add_ctl_to_map(snd_ctl_map_t *map, struct snd_ctl_map_ctl **_mctl, sn return 0; } -static int add_chn_to_map(struct snd_ctl_map_ctl *mctl, long idx, long val) +static int add_chn_to_map(struct snd_ctl_map_ctl *mctl, long idx, long src_idx, long val) { size_t off; long *map; + if (src_idx >= mctl->src_channels) { + SNDERR("Wrong channel mapping (extra source channel?)"); + return -EINVAL; + } if (mctl->channel_map_alloc <= (size_t)idx) { - map = realloc(mctl->channel_map, (idx + 4) * sizeof(*map)); + map = realloc(mctl->channel_map, mctl->src_channels * (idx + 4) * sizeof(*map)); if (map == NULL) return -ENOMEM; mctl->channel_map = map; off = mctl->channel_map_alloc; mctl->channel_map_alloc = idx + 4; - for ( ; off < mctl->channel_map_alloc; off++) + for ( ; off < mctl->src_channels * mctl->channel_map_alloc; off++) map[off] = -1; } if ((size_t)idx >= mctl->channel_map_items) mctl->channel_map_items = idx + 1; - mctl->channel_map[idx] = val; + mctl->channel_map[mctl->src_channels * idx + src_idx] = val; + return 0; +} + +static int add_chn_to_map_array(struct snd_ctl_map_ctl *mctl, const char *dst_id, snd_config_t *conf) +{ + snd_config_iterator_t i, next; + long src_idx = 0; + int err; + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + long idx = -1, chn = -1; + if (safe_strtol(dst_id, &idx) || snd_config_get_integer(n, &chn)) { + SNDERR("Wrong channel mapping (%ld -> %ld)", idx, chn); + return -EINVAL; + } + err = add_chn_to_map(mctl, idx, src_idx, chn); + if (err < 0) + return err; + src_idx++; + } return 0; } static int parse_map_vindex(struct snd_ctl_map_ctl *mctl, snd_config_t *conf) { snd_config_iterator_t i, next; + snd_config_t *n; int err; snd_config_for_each(i, next, conf) { - snd_config_t *n = snd_config_iterator_entry(i); + n = snd_config_iterator_entry(i); + err = snd_config_is_array(n); + if (err > 0 && (unsigned int)err > mctl->src_channels) + mctl->src_channels = err; /* count of array items */ + } + snd_config_for_each(i, next, conf) { + n = snd_config_iterator_entry(i); long idx = -1, chn = -1; const char *id; if (snd_config_get_id(n, &id) < 0) continue; - if (safe_strtol(id, &idx) || snd_config_get_integer(n, &chn)) { - SNDERR("Wrong channel mapping (%ld -> %ld)", idx, chn); - return -EINVAL; + if (snd_config_is_array(n) > 0) { + err = add_chn_to_map_array(mctl, id, n); + } else { + if (safe_strtol(id, &idx) || snd_config_get_integer(n, &chn)) { + SNDERR("Wrong channel mapping (%ld -> %ld)", idx, chn); + return -EINVAL; + } + err = add_chn_to_map(mctl, idx, 0, chn); } - err = add_chn_to_map(mctl, idx, chn); if (err < 0) return err; } @@ -1066,6 +1139,7 @@ static int parse_map_config(struct snd_ctl_map_ctl *mctl, snd_config_t *conf) snd_config_iterator_t i, next; int err; + mctl->src_channels = 1; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); const char *id; @@ -1264,6 +1338,11 @@ ctl.name { vindex.0 1 # stereo to mono (second channel) } } + # join two stereo to one stereo (minimum value is returned for read operation) + CREATE_ID4_STR { + SRC_ID5_STR.vindex.0 [ 0 1 ] # source channels 0+1 to merged channel 0 + SRC_ID6_STR.vindex.1 [ 0 1 ] # source channels 0+1 to merged channel 1 + } } } \endcode From 1b9a073e6a4e56a0faf75188966968621abd0786 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 20 Mar 2025 21:55:23 +0100 Subject: [PATCH 088/117] seq: add more checks to snd_seq_hw_set_client_info for older kernels The snd_seq_set_client_midi_version() should fail for older kernel when applications are trying to configure new midi versions. BugLink: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/4621 Signed-off-by: Jaroslav Kysela --- src/seq/seq_hw.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c index e88a7b22..5e9b8953 100644 --- a/src/seq/seq_hw.c +++ b/src/seq/seq_hw.c @@ -125,6 +125,15 @@ static int snd_seq_hw_set_client_info(snd_seq_t *seq, snd_seq_client_info_t * in { snd_seq_hw_t *hw = seq->private_data; + /* added fields are not checked on older kernels */ + if (SNDRV_PROTOCOL_VERSION(1, 0, 3) > hw->version) { + if (info->midi_version > 0) + return -EINVAL; + if (info->filter & SNDRV_SEQ_FILTER_NO_CONVERT) + return -EINVAL; + if (info->group_filter != 0) + return -EINVAL; + } if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_CLIENT_INFO failed");*/ return -errno; From fb285b366f8955c4c446779c81e3a5b3ecdf640d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 21 Mar 2025 09:42:35 +0100 Subject: [PATCH 089/117] seq: shuffle calloc arguments in snd_seq_hw_open (gcc warning) Signed-off-by: Jaroslav Kysela --- src/seq/seq_hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c index 5e9b8953..fe9d9bc7 100644 --- a/src/seq/seq_hw.c +++ b/src/seq/seq_hw.c @@ -579,7 +579,7 @@ int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode) } } if (streams & SND_SEQ_OPEN_INPUT) { - seq->ibuf = (char *) calloc(sizeof(snd_seq_ump_event_t), seq->ibufsize = SND_SEQ_IBUF_SIZE); + seq->ibuf = (char *) calloc(seq->ibufsize = SND_SEQ_IBUF_SIZE, sizeof(snd_seq_ump_event_t)); if (!seq->ibuf) { free(seq->obuf); free(hw); From 42b8f1299f503ec4cea41058c331366b04c0ccd3 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 20 Mar 2025 12:44:28 +0100 Subject: [PATCH 090/117] control: remap - separate event handling from map (preparation for sync) Signed-off-by: Jaroslav Kysela --- src/control/control_remap.c | 89 ++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/src/control/control_remap.c b/src/control/control_remap.c index e577ab91..eaef390b 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -79,26 +79,36 @@ typedef struct { unsigned int src_channels; long *channel_map; } *controls; - unsigned int event_mask; } snd_ctl_map_t; +typedef struct { + snd_ctl_elem_id_t *id; + unsigned int numid_app; + unsigned int event_mask; +} snd_ctl_map_event_t; + typedef struct { snd_ctl_t *child; int numid_remap_active; unsigned int numid_app_last; + size_t numid_items; size_t numid_alloc; snd_ctl_numid_t *numid; snd_ctl_numid_t numid_temp; + size_t remap_items; size_t remap_alloc; snd_ctl_remap_id_t *remap; + size_t map_items; size_t map_alloc; snd_ctl_map_t *map; - size_t map_read_queue_head; - size_t map_read_queue_tail; - snd_ctl_map_t **map_read_queue; + + size_t event_items; + size_t event_queue_head; + size_t event_queue_tail; + snd_ctl_map_event_t *event_queue; } snd_ctl_remap_t; #endif @@ -303,7 +313,7 @@ static void remap_free(snd_ctl_remap_t *priv) free(map->controls[idx2].channel_map); free(map->controls); } - free(priv->map_read_queue); + free(priv->event_queue); free(priv->map); free(priv->remap); free(priv->numid); @@ -831,19 +841,45 @@ static void _next_ptr(size_t *ptr, size_t count) *ptr = (*ptr + 1) % count; } +static void event_add(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id, + unsigned int numid_app, unsigned int event_mask) +{ + snd_ctl_map_event_t *map_event; + int found = 0; + size_t head; + + for (head = priv->event_queue_head; + head != priv->event_queue_tail; + _next_ptr(&head, priv->event_items)) + if (priv->event_queue[head].numid_app == numid_app) { + found = 1; + priv->event_queue[head].event_mask |= event_mask; + break; + } + debug_id(id, "%s marking for read (already %d)\n", __func__, found); + if (found) + return; + map_event = &priv->event_queue[priv->event_queue_tail]; + map_event->id = id; + map_event->numid_app = numid_app; + map_event->event_mask = event_mask; + _next_ptr(&priv->event_queue_tail, priv->event_items); +} + static void remap_event_for_all_map_controls(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id, unsigned int event_mask) { - size_t count, index, head; + size_t count, index; snd_ctl_map_t *map; struct snd_ctl_map_ctl *mctl; - int found; + int changed = 0; if (event_mask == SNDRV_CTL_EVENT_MASK_REMOVE) event_mask = SNDRV_CTL_EVENT_MASK_INFO; map = priv->map; for (count = priv->map_items; count > 0; count--, map++) { + changed = 0; for (index = 0; index < map->controls_items; index++) { mctl = &map->controls[index]; if (mctl->id_child.numid == 0) { @@ -854,21 +890,10 @@ static void remap_event_for_all_map_controls(snd_ctl_remap_t *priv, if (id->numid != mctl->id_child.numid) continue; debug_id(&map->map_id, "%s found (all)\n", __func__); - map->event_mask |= event_mask; - found = 0; - for (head = priv->map_read_queue_head; - head != priv->map_read_queue_tail; - _next_ptr(&head, priv->map_items)) - if (priv->map_read_queue[head] == map) { - found = 1; - break; - } - if (found) - continue; - debug_id(&map->map_id, "%s marking for read\n", __func__); - priv->map_read_queue[priv->map_read_queue_tail] = map; - _next_ptr(&priv->map_read_queue_tail, priv->map_items); + changed = 1; } + if (changed) + event_add(priv, &map->map_id, map->map_id.numid, event_mask); } } @@ -877,18 +902,18 @@ static int snd_ctl_remap_read(snd_ctl_t *ctl, snd_ctl_event_t *event) snd_ctl_remap_t *priv = ctl->private_data; snd_ctl_remap_id_t *rid; snd_ctl_numid_t *numid; - snd_ctl_map_t *map; + snd_ctl_map_event_t *map_event; int err; - if (priv->map_read_queue_head != priv->map_read_queue_tail) { - map = priv->map_read_queue[priv->map_read_queue_head]; - _next_ptr(&priv->map_read_queue_head, priv->map_items); + if (priv->event_queue_head != priv->event_queue_tail) { + map_event = &priv->event_queue[priv->event_queue_head]; + _next_ptr(&priv->event_queue_head, priv->event_items); memset(event, 0, sizeof(*event)); event->type = SNDRV_CTL_EVENT_ELEM; - event->data.elem.mask = map->event_mask; - event->data.elem.id = map->map_id; - map->event_mask = 0; - debug_id(&map->map_id, "%s queue read\n", __func__); + event->data.elem.mask = map_event->event_mask; + event->data.elem.id = *map_event->id; + event->data.elem.id.numid = map_event->numid_app; + debug_id(&event->data.elem.id, "%s queue read\n", __func__); return 1; } err = snd_ctl_read(priv->child, event); @@ -951,6 +976,7 @@ static int add_to_remap(snd_ctl_remap_t *priv, { snd_ctl_remap_id_t *rid; + if (priv->remap_alloc == priv->remap_items) { rid = realloc(priv->remap, (priv->remap_alloc + 16) * sizeof(*rid)); if (rid == NULL) @@ -1266,8 +1292,9 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema return 0; } - priv->map_read_queue = calloc(priv->map_items, sizeof(priv->map_read_queue[0])); - if (priv->map_read_queue == NULL) { + priv->event_items = priv->map_items; + priv->event_queue = calloc(priv->event_items, sizeof(priv->event_queue[0])); + if (priv->event_queue == NULL) { result = -ENOMEM; goto _err; } From d5f19bcabcb0e256f0ed663e351d41a1538a43a0 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 20 Mar 2025 11:24:32 +0100 Subject: [PATCH 091/117] control: remap - add sync feature For UCM, it may be required to sync multiple controls. The logic is really simple - last write to any control in the group wins. Link: https://github.com/alsa-project/alsa-ucm-conf/pull/410 Signed-off-by: Jaroslav Kysela --- src/control/control_remap.c | 263 ++++++++++++++++++++++++++++++++++-- 1 file changed, 255 insertions(+), 8 deletions(-) diff --git a/src/control/control_remap.c b/src/control/control_remap.c index eaef390b..05c0c88a 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -87,6 +87,11 @@ typedef struct { unsigned int event_mask; } snd_ctl_map_event_t; +typedef struct { + size_t control_items; + snd_ctl_elem_id_t *control_ids; +} snd_ctl_sync_t; + typedef struct { snd_ctl_t *child; int numid_remap_active; @@ -105,6 +110,10 @@ typedef struct { size_t map_alloc; snd_ctl_map_t *map; + size_t sync_items; + size_t sync_alloc; + snd_ctl_sync_t *sync; + size_t event_items; size_t event_queue_head; size_t event_queue_tail; @@ -183,6 +192,23 @@ static snd_ctl_numid_t *remap_find_numid_child(snd_ctl_remap_t *priv, unsigned i return remap_numid_child_new(priv, numid_child); } +static void remap_forget_numid_child(snd_ctl_remap_t *priv, unsigned int numid_child) +{ + snd_ctl_numid_t *numid; + size_t index; + + if (!priv->numid_remap_active) + return; + numid = priv->numid; + for (index = 0; index < priv->numid_items; index++) { + if (numid[index].numid_child != numid_child) + continue; + memcpy(&priv->numid[index], &priv->numid[index + 1], + (priv->numid_items - 1 - index) * sizeof(*numid)); + priv->numid_items++; + } +} + static snd_ctl_remap_id_t *remap_find_id_child(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id) { size_t count; @@ -302,11 +328,63 @@ static int remap_id_to_app(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id, snd_ctl return err; } +static snd_ctl_sync_t *remap_find_sync_numid(snd_ctl_remap_t *priv, unsigned int numid) +{ + size_t count, index2; + snd_ctl_sync_t *sync; + + if (numid == 0) + return NULL; + sync = priv->sync; + for (count = priv->sync_items; count > 0; count--, sync++) + for (index2 = 0; index2 < sync->control_items; index2++) + if (numid == sync->control_ids[index2].numid) + return sync; + return NULL; +} + +static snd_ctl_sync_t *remap_find_sync_id(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id) +{ + size_t count, index2; + snd_ctl_sync_t *sync; + + if (id->numid > 0) + return remap_find_sync_numid(priv, id->numid); + sync = priv->sync; + for (count = priv->sync_items; count > 0; count--, sync++) + for (index2 = 0; index2 < sync->control_items; index2++) + if (snd_ctl_elem_id_compare_set(id, &sync->control_ids[index2]) == 0) + return sync; + return NULL; +} + +static void remap_update_sync_id(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id) +{ + size_t count, index2; + snd_ctl_sync_t *sync; + + if (id->numid == 0) + return; + sync = priv->sync; + for (count = priv->sync_items; count > 0; count--, sync++) + for (index2 = 0; index2 < sync->control_items; index2++) { + if (snd_ctl_elem_id_compare_set(id, &sync->control_ids[index2]) == 0) { + sync->control_ids[index2].numid = id->numid; + break; + } + } +} + static void remap_free(snd_ctl_remap_t *priv) { size_t idx1, idx2; + snd_ctl_sync_t *sync; snd_ctl_map_t *map; + for (idx1 = 0; idx1 < priv->sync_items; idx1++) { + sync = &priv->sync[idx1]; + free(sync->control_ids); + } for (idx1 = 0; idx1 < priv->map_items; idx1++) { map = &priv->map[idx1]; for (idx2 = 0; idx2 < map->controls_items; idx2++) @@ -314,6 +392,7 @@ static void remap_free(snd_ctl_remap_t *priv) free(map->controls); } free(priv->event_queue); + free(priv->sync); free(priv->map); free(priv->remap); free(priv->numid); @@ -484,6 +563,8 @@ static int snd_ctl_remap_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info) if (err < 0) return err; err = snd_ctl_elem_info(priv->child, info); + if (err >= 0 && priv->sync_items > 0) + remap_update_sync_id(priv, &info->id); return remap_id_to_app(priv, &info->id, rid, err); } @@ -644,6 +725,29 @@ static int remap_map_elem_write(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *con return 0; } +static int remap_sync_elem_write(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *control) +{ + snd_ctl_sync_t *sync; + snd_ctl_elem_value_t control2; + size_t item; + int err; + + sync = remap_find_sync_id(priv, &control->id); + if (sync == NULL) + return -EREMAPNOTFOUND; + debug_id(&control->id, "%s\n", __func__); + control2 = *control; + for (item = 0; item < sync->control_items; item++) { + control2.id = sync->control_ids[item]; + debug_id(&control2.id, "%s sync[%zd]\n", __func__, item); + /* TODO: it's a blind write - no checks if the values are in range for all controls */ + err = snd_ctl_elem_write(priv->child, &control2); + if (err < 0) + return err; + } + return remap_id_to_app(priv, &control->id, NULL, 0); +} + static int snd_ctl_remap_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *control) { snd_ctl_remap_t *priv = ctl->private_data; @@ -652,6 +756,9 @@ static int snd_ctl_remap_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *contro debug_id(&control->id, "%s\n", __func__); err = remap_map_elem_write(priv, control); + if (err != -EREMAPNOTFOUND) + return err; + err = remap_sync_elem_write(priv, control); if (err != -EREMAPNOTFOUND) return err; err = remap_id_to_child(priv, &control->id, &rid); @@ -897,12 +1004,55 @@ static void remap_event_for_all_map_controls(snd_ctl_remap_t *priv, } } +static void remap_event_for_all_sync_controls(snd_ctl_remap_t *priv, + snd_ctl_elem_id_t *id, + unsigned int event_mask) +{ + size_t count, index; + snd_ctl_sync_t *sync; + snd_ctl_numid_t *numid; + snd_ctl_elem_id_t *sid; + int changed = 0; + + if (event_mask == SNDRV_CTL_EVENT_MASK_REMOVE) + return; + sync = priv->sync; + for (count = priv->sync_items; count > 0; count--, sync++) { + changed = 0; + for (index = 0; index < sync->control_items; index++) { + sid = &sync->control_ids[index]; + if (sid->numid == 0) { + if (snd_ctl_elem_id_compare_set(id, sid)) + continue; + sid->numid = id->numid; + } + if (id->numid != sid->numid) + continue; + debug_id(sid, "%s found (all)\n", __func__); + changed = 1; + break; + } + if (!changed) + continue; + for (index = 0; index < sync->control_items; index++) { + sid = &sync->control_ids[index]; + /* skip double updates */ + if (sid->numid == id->numid) + continue; + numid = remap_find_numid_child(priv, sid->numid); + if (numid) + event_add(priv, sid, numid->numid_app, event_mask); + } + } +} + static int snd_ctl_remap_read(snd_ctl_t *ctl, snd_ctl_event_t *event) { snd_ctl_remap_t *priv = ctl->private_data; snd_ctl_remap_id_t *rid; snd_ctl_numid_t *numid; snd_ctl_map_event_t *map_event; + unsigned int numid_child; int err; if (priv->event_queue_head != priv->event_queue_tail) { @@ -923,11 +1073,13 @@ static int snd_ctl_remap_read(snd_ctl_t *ctl, snd_ctl_event_t *event) (event->data.elem.mask & (SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO | SNDRV_CTL_EVENT_MASK_ADD | SNDRV_CTL_EVENT_MASK_TLV)) != 0) { debug_id(&event->data.elem.id, "%s event mask 0x%x\n", __func__, event->data.elem.mask); + numid_child = event->data.elem.id.numid; remap_event_for_all_map_controls(priv, &event->data.elem.id, event->data.elem.mask); + remap_event_for_all_sync_controls(priv, &event->data.elem.id, event->data.elem.mask); rid = remap_find_id_child(priv, &event->data.elem.id); if (rid) { if (rid->id_child.numid == 0) { - numid = remap_find_numid_child(priv, event->data.elem.id.numid); + numid = remap_find_numid_child(priv, numid_child); if (numid == NULL) return -EIO; rid->id_child.numid = numid->numid_child; @@ -935,11 +1087,13 @@ static int snd_ctl_remap_read(snd_ctl_t *ctl, snd_ctl_event_t *event) } event->data.elem.id = rid->id_app; } else { - numid = remap_find_numid_child(priv, event->data.elem.id.numid); + numid = remap_find_numid_child(priv, numid_child); if (numid == NULL) return -EIO; event->data.elem.id.numid = numid->numid_app; } + if (event->data.elem.mask == SNDRV_CTL_EVENT_MASK_REMOVE) + remap_forget_numid_child(priv, numid_child); } return err; } @@ -1240,12 +1394,84 @@ static int parse_map(snd_ctl_remap_t *priv, snd_config_t *conf) return 0; } +static snd_ctl_sync_t *alloc_sync(snd_ctl_remap_t *priv) +{ + snd_ctl_sync_t *sync; + + if (priv->sync_alloc == priv->sync_items) { + sync = realloc(priv->sync, (priv->sync_alloc + 16) * sizeof(*sync)); + if (sync == NULL) + return NULL; + memset(sync + priv->sync_alloc, 0, sizeof(*sync) * 16); + priv->sync_alloc += 16; + priv->sync = sync; + } + return &priv->sync[priv->sync_items++]; +} + +static int parse_sync1(snd_ctl_remap_t *priv, unsigned int count, snd_config_t *conf) +{ + snd_config_iterator_t i, next; + snd_ctl_elem_id_t *eid; + snd_ctl_sync_t *sync; + const char *str; + int err, index = 0; + + sync = alloc_sync(priv); + if (sync == NULL) + return -ENOMEM; + sync->control_ids = calloc(count, sizeof(sync->control_ids[0])); + if (sync->control_ids == NULL) + return -ENOMEM; + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + if (snd_config_get_string(n, &str) < 0) { + SNDERR("strings are expected in sync array"); + return -EINVAL; + } + eid = &sync->control_ids[index]; + snd_ctl_elem_id_clear(eid); + err = snd_ctl_ascii_elem_id_parse(eid, str); + if (err < 0) { + SNDERR("unable to parse control id '%s'!", str); + return -EINVAL; + } + sync->control_items++; + index++; + } + + return 0; +} + +static int parse_sync(snd_ctl_remap_t *priv, snd_config_t *conf) +{ + snd_config_iterator_t i, next; + int count, err; + + if (conf == NULL) + return 0; + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + count = snd_config_is_array(n); + if (count <= 0) { + SNDERR("Array is expected for sync!"); + return -EINVAL; + } + err = parse_sync1(priv, count, n); + if (err < 0) + return err; + } + + return 0; +} + /** - * \brief Creates a new remap & map control handle + * \brief Creates a new remap/map/sync control handle * \param handlep Returns created control handle * \param name Name of control device * \param remap Remap configuration * \param map Map configuration + * \param sync Sync configuration * \param child child configuration root * \param mode Control handle mode * \retval zero on success otherwise a negative error code @@ -1254,14 +1480,15 @@ static int parse_map(snd_ctl_remap_t *priv, snd_config_t *conf) * changed in future. */ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *remap, - snd_config_t *map, snd_ctl_t *child, int mode) + snd_config_t *map, snd_config_t *sync, snd_ctl_t *child, int mode) { snd_ctl_remap_t *priv; snd_ctl_t *ctl; + size_t index; int result, err; /* no-op, remove the plugin */ - if (!remap && !map) + if (!remap && !map && !sync) goto _noop; priv = calloc(1, sizeof(*priv)); @@ -1280,8 +1507,14 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema goto _err; } + err = parse_sync(priv, sync); + if (err < 0) { + result = err; + goto _err; + } + /* no-op check, remove the plugin */ - if (priv->map_items == 0 && priv->remap_items == 0) { + if (priv->map_items == 0 && priv->remap_items == 0 && priv->sync_items == 0) { remap_free(priv); _noop: free(child->name); @@ -1293,13 +1526,15 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema } priv->event_items = priv->map_items; + for (index = 0; index < priv->sync_items; index++) + priv->event_items += priv->sync[index].control_items; priv->event_queue = calloc(priv->event_items, sizeof(priv->event_queue[0])); if (priv->event_queue == NULL) { result = -ENOMEM; goto _err; } - priv->numid_remap_active = priv->map_items > 0; + priv->numid_remap_active = priv->map_items > 0 || priv->sync_items; priv->child = child; err = snd_ctl_new(&ctl, SND_CTL_TYPE_REMAP, name, mode); @@ -1371,6 +1606,13 @@ ctl.name { SRC_ID6_STR.vindex.1 [ 0 1 ] # source channels 0+1 to merged channel 1 } } + sync { + # synchronize multiple controls without any translations + sample_group_1 [ + SYNC_ID1_STR + SYNC_ID2_STR + ] + } } \endcode @@ -1401,6 +1643,7 @@ int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd snd_config_t *child = NULL; snd_config_t *remap = NULL; snd_config_t *map = NULL; + snd_config_t *sync = NULL; snd_ctl_t *cctl; int err; @@ -1419,6 +1662,10 @@ int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd map = n; continue; } + if (strcmp(id, "sync") == 0) { + sync = n; + continue; + } if (strcmp(id, "child") == 0) { child = n; continue; @@ -1433,7 +1680,7 @@ int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd err = _snd_ctl_open_child(&cctl, root, child, mode, conf); if (err < 0) return err; - err = snd_ctl_remap_open(handlep, name, remap, map, cctl, mode); + err = snd_ctl_remap_open(handlep, name, remap, map, sync, cctl, mode); if (err < 0) snd_ctl_close(cctl); return err; From 70f4c95df58fad9fbf91179ce31011d86b679475 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 21 Mar 2025 11:26:28 +0100 Subject: [PATCH 092/117] ucm: remove @@LibraryVersion and @@SyntaxVersion variables It seems that version checking is more complicated: Syntax is one-way settlement from the configuration files. It cannot be conditional. The library version string is hard to check with regex. Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 5 ----- src/ucm/ucm_confdoc.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index 5a86b9cd..3a3c9c15 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -1502,11 +1502,6 @@ const char *parse_open_variables(snd_use_case_mgr_t *uc_mgr, const char *name) skip: snd_config_delete(cfg); - - uc_mgr_set_variable(uc_mgr, "@@LibraryVersion", SND_LIB_VERSION_STR); - snprintf(vname, sizeof(vname), "%04d", SYNTAX_VERSION_MAX); - uc_mgr_set_variable(uc_mgr, "@@SyntaxVersion", vname); - return end + 3; } diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index 025148f9..b8baeb5e 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -406,8 +406,6 @@ ${var:\} | UCM parser variable (set using a _Define_ block) ${eval:\} | Evaluate expression like *($var+2)/3* [**Syntax 5**] ${find-card:\} | Find a card - see _Find card substitution_ section ${find-device:\} | Find a device - see _Find device substitution_ section -@@LibraryVersion | e.g. "1.2.14" [**Syntax 8**] -@@SyntaxVersion | e.g. "0008" (decimal base - no hex) [**Syntax 8**] General note: If two dollars '$$' instead one dolar '$' are used for the substitution identification, the error is ignored (e.g. file does not From d8300e5cb794ea014e7d9d233d61af7a0cabc459 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 21 Mar 2025 11:41:09 +0100 Subject: [PATCH 093/117] ucm: add '${LibCaps}' substitution It is a preparation for future checking of alsa-lib's extensions. Signed-off-by: Jaroslav Kysela --- src/ucm/ucm_confdoc.h | 5 +++++ src/ucm/ucm_subs.c | 14 +++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index b8baeb5e..e6c0a091 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -388,6 +388,7 @@ substituted. The substitutes strings are in the table bellow. Substituted string | Value -----------------------|--------------------- +${LibCaps} | Library capabilities (string like '*a*b*c*') [**Syntax 8**] ${OpenName} | Original UCM card name (passed to snd_use_case_mgr_open()) ${ConfLibDir} | Library top-level configuration directory (e.g. /usr/share/alsa) ${ConfTopDir} | Top-level UCM configuration directory (e.g. /usr/share/alsa/ucm2) @@ -433,6 +434,10 @@ Define.Bytes2 "${sys-card:[type=hex,pos=0x22]device/../descriptors}" Replace *type=hex* with *type=ascii* or omit this variable settings to work with ASCII characters. +#### Library capabilities + +None at the moment. The list will grow after *Syntax 8* (library 1.2.14). + #### Special whole string substitution Substituted string | Value diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c index d1dc5819..a3d9c392 100644 --- a/src/ucm/ucm_subs.c +++ b/src/ucm/ucm_subs.c @@ -20,7 +20,7 @@ * transition sequences, multiple client access and user defined use * cases was kindly sponsored by Wolfson Microelectronics PLC. * - * Copyright (C) 2019 Red Hat Inc. + * Copyright (C) 2019-2025 Red Hat Inc. * Authors: Jaroslav Kysela */ @@ -30,8 +30,19 @@ #include #include +/* capabilities string like "*a*b*c" will be used to check library extensions */ +/* use Needle like "*a*" or regex expression for a match */ +#define LIB_CAPS_STRING "*" "*" + static unsigned char _hex_table[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; +static char *rval_lib_caps(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED) +{ + if (uc_mgr->conf_format < 8) + return NULL; + return strdup(LIB_CAPS_STRING); +} + static char *rval_open_name(snd_use_case_mgr_t *uc_mgr) { const char *name; @@ -883,6 +894,7 @@ __std: goto __std; } fcn2 = NULL; + MATCH_VARIABLE(value, "${LibCaps}", rval_lib_caps, false); MATCH_VARIABLE(value, "${OpenName}", rval_open_name, false); MATCH_VARIABLE(value, "${ConfLibDir}", rval_conf_libdir, false); MATCH_VARIABLE(value, "${ConfTopDir}", rval_conf_topdir, false); From e088d08c76169e39f9ce19b2f11aacc95eb68a40 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 24 Mar 2025 11:49:31 +0100 Subject: [PATCH 094/117] control: remap - improve sync feature It may be useful to deactivate the sync mechanism for some configurations. Create a new virtual boolean control for this. Link: https://github.com/alsa-project/alsa-ucm-conf/pull/410 Signed-off-by: Jaroslav Kysela --- src/control/control_remap.c | 169 +++++++++++++++++++++++++++++++++--- 1 file changed, 158 insertions(+), 11 deletions(-) diff --git a/src/control/control_remap.c b/src/control/control_remap.c index 05c0c88a..226b3c0b 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -90,6 +91,8 @@ typedef struct { typedef struct { size_t control_items; snd_ctl_elem_id_t *control_ids; + snd_ctl_elem_id_t switch_id; + bool switch_state; } snd_ctl_sync_t; typedef struct { @@ -113,6 +116,7 @@ typedef struct { size_t sync_items; size_t sync_alloc; snd_ctl_sync_t *sync; + size_t sync_switch_items; size_t event_items; size_t event_queue_head; @@ -375,6 +379,37 @@ static void remap_update_sync_id(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id) } } +static snd_ctl_sync_t *remap_find_sync_switch_numid(snd_ctl_remap_t *priv, unsigned int numid) +{ + size_t count; + snd_ctl_sync_t *sync; + + if (numid == 0) + return NULL; + sync = priv->sync; + for (count = priv->sync_items; count > 0; count--, sync++) + if (numid == sync->switch_id.numid) + return sync; + return NULL; +} + +static snd_ctl_sync_t *remap_find_sync_switch_id(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id) +{ + size_t count; + snd_ctl_sync_t *sync; + + if (id->numid > 0) + return remap_find_sync_switch_numid(priv, id->numid); + sync = priv->sync; + for (count = priv->sync_items; count > 0; count--, sync++) { + if (sync->switch_id.numid == 0) + continue; + if (snd_ctl_elem_id_compare_set(id, &sync->switch_id) == 0) + return sync; + } + return NULL; +} + static void remap_free(snd_ctl_remap_t *priv) { size_t idx1, idx2; @@ -437,7 +472,6 @@ static int snd_ctl_remap_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list) snd_ctl_elem_id_t *id; snd_ctl_remap_id_t *rid; snd_ctl_numid_t *numid; - snd_ctl_map_t *map; unsigned int index; size_t index2; int err; @@ -457,18 +491,25 @@ static int snd_ctl_remap_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list) return -EIO; id->numid = numid->numid_app; } - if (list->offset >= list->count + priv->map_items) + if (list->offset >= list->count + priv->map_items + priv->sync_switch_items) return 0; index2 = 0; if (list->offset > list->count) index2 = list->offset - list->count; for ( ; index < list->space && index2 < priv->map_items; index2++, index++) { - id = &list->pids[index]; - map = &priv->map[index2]; - *id = map->map_id; + snd_ctl_map_t *map = &priv->map[index2]; + list->pids[index] = map->map_id; list->used++; } - list->count += priv->map_items; + if (index2 >= priv->map_items) { + index2 -= priv->map_items; + for ( ; index < list->space && index2 < priv->sync_switch_items; index2++, index++) { + snd_ctl_sync_t *sync = &priv->sync[index2]; + list->pids[index] = sync->switch_id; + list->used++; + } + } + list->count += priv->map_items + priv->sync_switch_items; return 0; } @@ -549,6 +590,21 @@ static int remap_map_elem_info(snd_ctl_remap_t *priv, snd_ctl_elem_info_t *info) return 0; } +static int remap_sync_elem_info(snd_ctl_remap_t *priv, snd_ctl_elem_info_t *info) +{ + snd_ctl_sync_t *sync; + + sync = remap_find_sync_switch_id(priv, &info->id); + if (!sync) + return -EREMAPNOTFOUND; + snd_ctl_elem_info_clear(info); + info->id = sync->switch_id; + info->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + info->access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE; + info->count = 1; + return 0; +} + static int snd_ctl_remap_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info) { snd_ctl_remap_t *priv = ctl->private_data; @@ -557,6 +613,9 @@ static int snd_ctl_remap_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info) debug_id(&info->id, "%s\n", __func__); err = remap_map_elem_info(priv, info); + if (err != -EREMAPNOTFOUND) + return err; + err = remap_sync_elem_info(priv, info); if (err != -EREMAPNOTFOUND) return err; err = remap_id_to_child(priv, &info->id, &rid); @@ -631,6 +690,17 @@ static int remap_map_elem_read(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *cont return 0; } +static int remap_sync_elem_read(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *control) +{ + snd_ctl_sync_t *sync; + + sync = remap_find_sync_switch_id(priv, &control->id); + if (!sync) + return -EREMAPNOTFOUND; + control->value.integer.value[0] = sync->switch_state ? 1 : 0; + return 0; +} + static int snd_ctl_remap_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *control) { snd_ctl_remap_t *priv = ctl->private_data; @@ -639,6 +709,9 @@ static int snd_ctl_remap_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *control debug_id(&control->id, "%s\n", __func__); err = remap_map_elem_read(priv, control); + if (err != -EREMAPNOTFOUND) + return err; + err = remap_sync_elem_read(priv, control); if (err != -EREMAPNOTFOUND) return err; err = remap_id_to_child(priv, &control->id, &rid); @@ -732,9 +805,17 @@ static int remap_sync_elem_write(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *co size_t item; int err; + sync = remap_find_sync_switch_id(priv, &control->id); + if (sync) { + err = sync->switch_state != control->value.integer.value[0]; + sync->switch_state = control->value.integer.value[0] != 0; + return err; + } sync = remap_find_sync_id(priv, &control->id); if (sync == NULL) return -EREMAPNOTFOUND; + if (sync->switch_state == false) + return -EREMAPNOTFOUND; debug_id(&control->id, "%s\n", __func__); control2 = *control; for (item = 0; item < sync->control_items; item++) { @@ -1420,6 +1501,7 @@ static int parse_sync1(snd_ctl_remap_t *priv, unsigned int count, snd_config_t * sync = alloc_sync(priv); if (sync == NULL) return -ENOMEM; + sync->switch_state = true; sync->control_ids = calloc(count, sizeof(sync->control_ids[0])); if (sync->control_ids == NULL) return -ENOMEM; @@ -1443,6 +1525,58 @@ static int parse_sync1(snd_ctl_remap_t *priv, unsigned int count, snd_config_t * return 0; } +static int parse_sync_compound(snd_ctl_remap_t *priv, snd_config_t *conf) +{ + snd_config_iterator_t i, next; + snd_ctl_elem_id_t eid; + snd_ctl_numid_t *numid; + bool eid_found = false; + bool controls_found = false; + int count, err; + + snd_ctl_elem_id_clear(&eid); + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id, *str; + if (snd_config_get_id(n, &id) < 0) + continue; + if (strcmp(id, "switch") == 0) { + if (snd_config_get_string(n, &str) < 0) { + SNDERR("String is expected for switch"); + return -EINVAL; + } + err = snd_ctl_ascii_elem_id_parse(&eid, str); + if (err < 0) { + SNDERR("unable to parse id '%s'!", str); + return -EINVAL; + } + eid_found = true; + } + if (strcmp(id, "controls") == 0) { + count = snd_config_is_array(n); + if (count <= 0) { + SNDERR("Array is expected for sync!"); + return -EINVAL; + } + err = parse_sync1(priv, count, n); + if (err < 0) + return err; + controls_found = true; + } + } + + if (!controls_found || !eid_found) + return 0; + + numid = remap_numid_new(priv, 0, ++priv->numid_app_last); + if (numid == NULL) + return -ENOMEM; + eid.numid = numid->numid_app; + priv->sync[priv->sync_items - 1].switch_id = eid; + priv->sync_switch_items++; + return 0; +} + static int parse_sync(snd_ctl_remap_t *priv, snd_config_t *conf) { snd_config_iterator_t i, next; @@ -1452,12 +1586,16 @@ static int parse_sync(snd_ctl_remap_t *priv, snd_config_t *conf) return 0; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); - count = snd_config_is_array(n); - if (count <= 0) { - SNDERR("Array is expected for sync!"); - return -EINVAL; + if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) { + err = parse_sync_compound(priv, n); + } else { + count = snd_config_is_array(n); + if (count <= 0) { + SNDERR("Array is expected for sync!"); + return -EINVAL; + } + err = parse_sync1(priv, count, n); } - err = parse_sync1(priv, count, n); if (err < 0) return err; } @@ -1612,6 +1750,15 @@ ctl.name { SYNC_ID1_STR SYNC_ID2_STR ] + # synchronize multiple controls without any translations + # add functionality on/off switch + sample_group_2 { + switch SYNC_SWITCH_ID + controls [ + SYNC_ID3_STR + SYNC_ID4_STR + ] + } } } \endcode From 95d0274b6015afa943762f51ff15bf6781707288 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 1 Apr 2025 07:59:40 +0200 Subject: [PATCH 095/117] ALSA: seq: Add missing UMP EP cap bit at snd_seq_create_ump_endpoint() In snd_seq_create_ump_endpoint(), it was forgotten to give the UMP Endpoint capability bit (SND_SEQ_PORT_CAP_UMP_ENDPOINT) to the port 0 ("UMP 2.0"). This resulted in port 0 being a normal port for the non-existing group. Fixes: 6167b8ce3e80 ("seq: Add API helper functions for creating UMP Endpoint and Blocks") Closes: https://github.com/alsa-project/alsa-lib/issues/447 Signed-off-by: Takashi Iwai --- src/seq/seqmid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 28c345de..154e6677 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -570,6 +570,7 @@ int snd_seq_create_ump_endpoint(snd_seq_t *seq, snd_seq_port_info_set_port_specified(pinfo, 1); snd_seq_port_info_set_name(pinfo, "MIDI 2.0"); snd_seq_port_info_set_capability(pinfo, + SND_SEQ_PORT_CAP_UMP_ENDPOINT | SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ | From 90eb3c1e960bbb69ba1911a82f13a4c0099cf1d9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 1 Apr 2025 08:02:51 +0200 Subject: [PATCH 096/117] ALSA: seq: Use SND_* instead of SNDRV_* The values SNDRV_XXX are used incorrectly in some code where they should have been SND_XXX, due to copy&paste from the kernel code. Practically seen there are no difference, and the code still works fine, but those should be corrected for consistency. Fixes: 6167b8ce3e80 ("seq: Add API helper functions for creating UMP Endpoint and Blocks") Signed-off-by: Takashi Iwai --- src/seq/seqmid.c | 50 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 154e6677..10637f8b 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -571,18 +571,18 @@ int snd_seq_create_ump_endpoint(snd_seq_t *seq, snd_seq_port_info_set_name(pinfo, "MIDI 2.0"); snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_UMP_ENDPOINT | - SNDRV_SEQ_PORT_CAP_READ | - SNDRV_SEQ_PORT_CAP_SYNC_READ | - SNDRV_SEQ_PORT_CAP_SUBS_READ | - SNDRV_SEQ_PORT_CAP_WRITE | - SNDRV_SEQ_PORT_CAP_SYNC_WRITE | - SNDRV_SEQ_PORT_CAP_SUBS_WRITE | - SNDRV_SEQ_PORT_CAP_DUPLEX); + SND_SEQ_PORT_CAP_READ | + SND_SEQ_PORT_CAP_SYNC_READ | + SND_SEQ_PORT_CAP_SUBS_READ | + SND_SEQ_PORT_CAP_WRITE | + SND_SEQ_PORT_CAP_SYNC_WRITE | + SND_SEQ_PORT_CAP_SUBS_WRITE | + SND_SEQ_PORT_CAP_DUPLEX); snd_seq_port_info_set_type(pinfo, SND_SEQ_PORT_TYPE_MIDI_GENERIC | - SNDRV_SEQ_PORT_TYPE_MIDI_UMP | + SND_SEQ_PORT_TYPE_MIDI_UMP | SND_SEQ_PORT_TYPE_APPLICATION | - SNDRV_SEQ_PORT_TYPE_PORT); + SND_SEQ_PORT_TYPE_PORT); snd_seq_port_info_set_ump_group(pinfo, 0); err = snd_seq_create_port(seq, pinfo); if (err < 0) { @@ -643,24 +643,24 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) i >= bp->first_group + bp->num_groups) continue; switch (bp->direction) { - case SNDRV_UMP_DIR_INPUT: /* sink, receiver */ - caps |= SNDRV_SEQ_PORT_CAP_WRITE | - SNDRV_SEQ_PORT_CAP_SYNC_WRITE | - SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + case SND_UMP_DIR_INPUT: /* sink, receiver */ + caps |= SND_SEQ_PORT_CAP_WRITE | + SND_SEQ_PORT_CAP_SYNC_WRITE | + SND_SEQ_PORT_CAP_SUBS_WRITE; break; - case SNDRV_UMP_DIR_OUTPUT: /* source, transmitter */ - caps |= SNDRV_SEQ_PORT_CAP_READ | - SNDRV_SEQ_PORT_CAP_SYNC_READ | - SNDRV_SEQ_PORT_CAP_SUBS_READ; + case SND_UMP_DIR_OUTPUT: /* source, transmitter */ + caps |= SND_SEQ_PORT_CAP_READ | + SND_SEQ_PORT_CAP_SYNC_READ | + SND_SEQ_PORT_CAP_SUBS_READ; break; - case SNDRV_UMP_DIR_BIDIRECTION: - caps |= SNDRV_SEQ_PORT_CAP_READ | - SNDRV_SEQ_PORT_CAP_SYNC_READ | - SNDRV_SEQ_PORT_CAP_SUBS_READ | - SNDRV_SEQ_PORT_CAP_WRITE | - SNDRV_SEQ_PORT_CAP_SYNC_WRITE | - SNDRV_SEQ_PORT_CAP_SUBS_WRITE | - SNDRV_SEQ_PORT_CAP_DUPLEX; + case SND_UMP_DIR_BIDIRECTION: + caps |= SND_SEQ_PORT_CAP_READ | + SND_SEQ_PORT_CAP_SYNC_READ | + SND_SEQ_PORT_CAP_SUBS_READ | + SND_SEQ_PORT_CAP_WRITE | + SND_SEQ_PORT_CAP_SYNC_WRITE | + SND_SEQ_PORT_CAP_SUBS_WRITE | + SND_SEQ_PORT_CAP_DUPLEX; break; } From 0d2acc20841fc4c8615c8b41a91d161647c1387d Mon Sep 17 00:00:00 2001 From: Felix Wolfsteller Date: Sat, 29 Mar 2025 23:40:31 +0100 Subject: [PATCH 097/117] pcm: fix minor typos in doc Closes: https://github.com/alsa-project/alsa-lib/pull/446 Signed-off-by: Felix Wolfsteller Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index d51e8abd..da339ace 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -3098,7 +3098,7 @@ snd_pcm_sframes_t snd_pcm_avail(snd_pcm_t *pcm) * \param delayp Total I/O latency in frames * \return zero on success otherwise a negative error code * - * The avail and delay values retuned are in sync. + * The avail and delay values returned are in sync. * * The function is thread-safe when built with the proper option. */ @@ -8753,7 +8753,7 @@ _snd_pcm_parse_config_chmaps(snd_config_t *conf) * -EPIPE (overrun or underrun) and -ESTRPIPE (stream is suspended) * error codes trying to prepare given stream for next I/O. * - * Note that this function returs the original error code when it is not + * Note that this function returns the original error code when it is not * handled inside this function (for example -EAGAIN is returned back). */ int snd_pcm_recover(snd_pcm_t *pcm, int err, int silent) From 647c001321e0348cbf51234ec9dde0ebd36d05af Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 2 Apr 2025 09:56:23 -0400 Subject: [PATCH 098/117] Delete alsalisp code Install of the alsalisp binary has been disabled since 2006 (in commit 8d382ccd), and building of it was disabled by default in 2018 (in commit 32ceab21), so it is reasonable to assume that nobody is using it. Use within the alsa-lib project is limited to an aliases file that looks like it is intended as an example, plus some very small .alisp files associated with the SiS SI7018 PCI sound card which has not been manufactured in years. These too have not been installed since 2018 when commit 32ceab21 disabled building of the alsalisp binary. In preparing this change, I searched the Github issue tracker for "lisp", "alisp" and "alsalisp", and found no complaints about the above changes. I also did a Github code search for projects that might be including the `alisp.h` header and found none. Therefore I think this code can be safely deleted and nobody is likely to object. Closes: https://github.com/alsa-project/alsa-lib/pull/448 Signed-off-by: Simon Howard Signed-off-by: Jaroslav Kysela --- .gitignore | 1 - Makefile.am | 5 - alsalisp/Makefile.am | 8 - alsalisp/alsalisp.c | 110 - alsalisp/hctl.lisp | 91 - alsalisp/hello.lisp | 26 - alsalisp/itest.lisp | 1 - alsalisp/test.lisp | 382 --- configure.ac | 9 +- gitcompile | 9 - include/Makefile.am | 4 - include/alisp.h | 55 - include/error.h | 1 - src/Makefile.am | 10 - src/Versions.in.in | 10 - src/alisp/Makefile.am | 11 - src/alisp/alisp.c | 3495 ----------------------- src/alisp/alisp_local.h | 151 - src/alisp/alisp_snd.c | 936 ------ src/conf/Makefile.am | 3 - src/conf/cards/Makefile.am | 18 +- src/conf/cards/SI7018/sndoc-mixer.alisp | 11 - src/conf/cards/SI7018/sndop-mixer.alisp | 11 - src/conf/cards/aliases.alisp | 29 - src/conf/sndo-mixer.alisp | 115 - 25 files changed, 3 insertions(+), 5499 deletions(-) delete mode 100644 alsalisp/Makefile.am delete mode 100644 alsalisp/alsalisp.c delete mode 100644 alsalisp/hctl.lisp delete mode 100644 alsalisp/hello.lisp delete mode 100644 alsalisp/itest.lisp delete mode 100644 alsalisp/test.lisp delete mode 100644 include/alisp.h delete mode 100644 src/alisp/Makefile.am delete mode 100644 src/alisp/alisp.c delete mode 100644 src/alisp/alisp_local.h delete mode 100644 src/alisp/alisp_snd.c delete mode 100644 src/conf/cards/SI7018/sndoc-mixer.alisp delete mode 100644 src/conf/cards/SI7018/sndop-mixer.alisp delete mode 100644 src/conf/cards/aliases.alisp delete mode 100644 src/conf/sndo-mixer.alisp diff --git a/.gitignore b/.gitignore index 3c0791f5..80d3f8b7 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,6 @@ include/version.h include/alsa include/asoundlib.h utils/alsa-lib.spec -alsalisp/alsalisp aserver/aserver m4/libtool.m4 m4/ltoptions.m4 diff --git a/Makefile.am b/Makefile.am index ff4c963a..5bcef999 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,11 +10,6 @@ endif if BUILD_PCM_PLUGIN_SHM SUBDIRS += aserver endif -if BUILD_MIXER -if BUILD_ALISP -SUBDIRS += alsalisp -endif -endif SUBDIRS += test utils EXTRA_DIST=README.md ChangeLog INSTALL TODO NOTES configure gitcompile libtool \ depcomp version MEMORY-LEAK m4/attributes.m4 diff --git a/alsalisp/Makefile.am b/alsalisp/Makefile.am deleted file mode 100644 index 8e3e0159..00000000 --- a/alsalisp/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -noinst_PROGRAMS = alsalisp - -alsalisp_SOURCES = alsalisp.c -alsalisp_LDADD = ../src/libasound.la - -all: alsalisp - -AM_CPPFLAGS=-I$(top_srcdir)/include -I$(top_srcdir)/src/alisp diff --git a/alsalisp/alsalisp.c b/alsalisp/alsalisp.c deleted file mode 100644 index d1e1bce2..00000000 --- a/alsalisp/alsalisp.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * ALSA lisp implementation - * Copyright (c) 2003 by Jaroslav Kysela - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include - -#include "asoundlib.h" -#include "alisp.h" - -static int verbose = 0; -static int warning = 0; -static int debug = 0; - -static void interpret_filename(const char *file) -{ - struct alisp_cfg cfg; - snd_input_t *in; - snd_output_t *out; - int err; - - memset(&cfg, 0, sizeof(cfg)); - if (file != NULL && strcmp(file, "-") != 0) { - if ((err = snd_input_stdio_open(&in, file, "r")) < 0) { - fprintf(stderr, "unable to open filename '%s' (%s)\n", file, snd_strerror(err)); - return; - } - } else { - if ((err = snd_input_stdio_attach(&in, stdin, 0)) < 0) { - fprintf(stderr, "unable to attach stdin '%s' (%s)\n", file, snd_strerror(err)); - return; - } - } - if (snd_output_stdio_attach(&out, stdout, 0) < 0) { - snd_input_close(in); - fprintf(stderr, "unable to attach stdout (%s)\n", strerror(errno)); - return; - } - cfg.verbose = verbose; - cfg.warning = warning; - cfg.debug = debug; - cfg.in = in; - cfg.out = cfg.eout = cfg.vout = cfg.wout = cfg.dout = out; - err = alsa_lisp(&cfg, NULL); - if (err < 0) - fprintf(stderr, "alsa lisp returned error %i (%s)\n", err, strerror(err)); - else if (verbose) - printf("file %s passed ok via alsa lisp interpreter\n", file); - snd_output_close(out); - snd_input_close(in); -} - -static void usage(void) -{ - fprintf(stderr, "usage: alsalisp [-vdw] [file...]\n"); - exit(1); -} - -int main(int argc, char **argv) -{ - int c; - - while ((c = getopt(argc, argv, "vdw")) != -1) { - switch (c) { - case 'v': - verbose = 1; - break; - case 'd': - debug = 1; - break; - case 'w': - warning = 1; - break; - case '?': - default: - usage(); - /* NOTREACHED */ - } - } - argc -= optind; - argv += optind; - - if (argc < 1) - interpret_filename(NULL); - else - while (*argv) - interpret_filename(*argv++); - - return 0; -} diff --git a/alsalisp/hctl.lisp b/alsalisp/hctl.lisp deleted file mode 100644 index 504050f6..00000000 --- a/alsalisp/hctl.lisp +++ /dev/null @@ -1,91 +0,0 @@ -(setq card (Acall 'card_next -1)) -(setq card (Aresult card)) -(while (>= card 0) - (progn - (princ "found card: " card "\n") - (princ " name : " (Aresult (Acall 'card_get_name card)) "\n") - (princ " longname: " (Aresult (Acall 'card_get_longname card)) "\n") - (setq card (Acall 'card_next card)) - (setq card (Aresult card)) - ) -) -(unsetq card) - -(princ "card_get_index test (SI7018): " (Acall 'card_get_index "SI7018") "\n") -(princ "card_get_index test (ABCD): " (Acall 'card_get_index "ABCD") "\n") - -(setq hctl (Acall 'hctl_open 'default nil)) -(if (= (Aerror hctl) 0) - (progn - (princ "open success: " hctl "\n") - (setq hctl (Ahandle hctl)) - (princ "open hctl: " hctl "\n") - (setq hctl (Acall 'hctl_close hctl)) - (if (= hctl 0) - (princ "close success\n") - (princ "close failed: " hctl "\n") - ) - ) - (progn - (princ "open failed: " hctl "\n") - ) -) -(unsetq hctl) - -(setq ctl (Acall 'ctl_open 'default nil)) -(if (= (Aerror ctl) 0) - (progn - (princ "ctl open success: " ctl "\n") - (setq ctl (Ahandle ctl)) - (setq info (Aresult (Acall 'ctl_card_info ctl))) - (princ "ctl card info: " info "\n") - (princ "ctl card info (mixername): " (cdr (assq "mixername" info)) "\n") - (unsetq info) - (setq hctl (Acall 'hctl_open_ctl ctl)) - (if (= (Aerror hctl) 0) - (progn - (princ "hctl open success: " hctl "\n") - (setq hctl (Ahandle hctl)) - (princ "open hctl: " hctl "\n") - (princ "load hctl: " (Acall 'hctl_load hctl) "\n") - (princ "first : " (Acall 'hctl_first_elem hctl) "\n") - (princ "last : " (Acall 'hctl_last_elem hctl) "\n") - (princ "next (first): " (Acall 'hctl_elem_next (Acall 'hctl_first_elem hctl)) "\n") - (princ "prev (last) : " (Acall 'hctl_elem_prev (Acall 'hctl_last_elem hctl)) "\n") - (setq elem (Acall 'hctl_first_elem hctl)) - (while elem - (progn - (setq info (Acall 'hctl_elem_info elem)) - (princ info "\n") - (setq value (Acall 'hctl_elem_read elem)) - (princ value "\n") - (when (equal (cdr (assq "name" (car (cdr (assq "id" (Aresult info)))))) "Master Playback Volume") - (princ "write Master: " (Acall 'hctl_elem_write elem (20 20)) "\n") - ) - (unsetq info value) - (gc) - (setq elem (Acall 'hctl_elem_next elem)) - ) - ) - (unsetq elem) - (setq hctl (Acall 'hctl_close hctl)) - (if (= hctl 0) - (princ "hctl close success\n") - (princ "hctl close failed: " hctl "\n") - ) - ) - (progn - (princ "hctl open failed: " hctl "\n") - (Acall 'ctl_close ctl) - ) - ) - (unsetq hctl) - ) - (progn - (princ "ctl open failed: " ctl "\n") - ) -) -(unsetq ctl) - -(&stat-memory) -(&dump-memory "memory.dump") diff --git a/alsalisp/hello.lisp b/alsalisp/hello.lisp deleted file mode 100644 index f04fc381..00000000 --- a/alsalisp/hello.lisp +++ /dev/null @@ -1,26 +0,0 @@ -(princ "Hello ALSA world\n") -(princ "One " 1 "\n") -(princ "Two " (+ 1 1) "\n") - -(defun myprinc (o) (progn (princ o))) -(myprinc "Printed via myprinc function!\n") -(unsetq myprinc) - -(defun printnum (from to) (while (<= from to) (princ " " from) (setq from (+ from 1)))) -(princ "Numbers 1-10: ") (printnum 1 10) (princ "\n") -(unsetq printnum) - -(defun factorial (n) (if (> n 1) (* n (factorial (- n 1))) 1)) -(princ "Factorial of 10: " (factorial 10) "\n") -(princ "Float test 1.1 + 1.35 = " (+ 1.1 1.35) "\n") -(princ "Factorial of 10.0: " (factorial 10.0) "\n") -(princ "Factorial of 20.0: " (factorial 20.0) "\n") -(unsetq factorial) - -(setq alist '((one . first) (two . second) (three . third))) -(princ "alist = " alist "\n") -(princ "alist assoc one = " (assoc 'one alist) "\n") -(princ "alist rassoc third = " (rassoc 'third alist) "\n") -(unsetq alist) - -(&stat-memory) diff --git a/alsalisp/itest.lisp b/alsalisp/itest.lisp deleted file mode 100644 index decd9ae7..00000000 --- a/alsalisp/itest.lisp +++ /dev/null @@ -1 +0,0 @@ -(princ "itest.lisp file included!\n") diff --git a/alsalisp/test.lisp b/alsalisp/test.lisp deleted file mode 100644 index 5e3820f4..00000000 --- a/alsalisp/test.lisp +++ /dev/null @@ -1,382 +0,0 @@ -; -; Test code for all basic alsa lisp commands. -; The test is indended to find memory leaks. -; -; Copyright (c) 2003 Jaroslav Kysela -; License: GPL v2 (http://www.gnu.org/licenses/gpl.html) -; - -; -; Basic commands -; - -(!=) (&check-memory) -(!= 0) (&check-memory) -(!= 0 1) (&check-memory) -(!= 1 1) (&check-memory) -(!= 0 1 2) (&check-memory) -(!= 'aaaa 'bbbb) (&check-memory) - -(%) (&check-memory) -(% 11) (&check-memory) -(% 11 5) (&check-memory) -(% 11.5 5.1) (&check-memory) -(% 11.5 5.1 2.2) (&check-memory) -(% 'aaaa 'bbbb) (&check-memory) - -(&check-memory) (&check-memory) -(&check-memory "abcd") (&check-memory) -(&dump-memory "-") (&check-memory) -(&dump-memory) (&check-memory) -(&dump-objects "-") (&check-memory) -(&dump-objects) (&check-memory) -(&stat-memory) (&check-memory) -(&stat-memory "abcd") (&check-memory) - -(*) (&check-memory) -(* 1) (&check-memory) -(* 1 2) (&check-memory) -(* 1.1 2.2) (&check-memory) -(* 1.1 2.2 3.3) (&check-memory) -(* 'aaaa) (&check-memory) - -(+) (&check-memory) -(+ 1) (&check-memory) -(+ 1 2) (&check-memory) -(+ 1.1 2.2) (&check-memory) -(+ 1.1 2.2 3.3) (&check-memory) -(+ 'aaaa) (&check-memory) -(+ 'aaaa 'bbbb) (&check-memory) -(+ "aaaa") (&check-memory) -(+ "aaaa" "bbbb") (&check-memory) -(+ "aaaa" "bbbb" "cccc") (&check-memory) - -(-) (&check-memory) -(- 1) (&check-memory) -(- 1 2) (&check-memory) -(- 1.1 2.2) (&check-memory) -(- 1.1 2.2 3.3) (&check-memory) -(- 'aaaa) (&check-memory) -(- 'aaaa 'bbbb) (&check-memory) - -(/) (&check-memory) -(/ 1) (&check-memory) -(/ 1 2) (&check-memory) -(/ 1.1 2.2) (&check-memory) -(/ 1.1 2.2 3.3) (&check-memory) -(/ 'aaaa) (&check-memory) -(/ 'aaaa 'bbbb) (&check-memory) - -(<) (&check-memory) -(< 0) (&check-memory) -(< 0 1) (&check-memory) -(< 1 0) (&check-memory) -(< 0 1 2) (&check-memory) - -(<=) (&check-memory) -(<= 0) (&check-memory) -(<= 0 1) (&check-memory) -(<= 1 0) (&check-memory) -(<= 0 1 2) (&check-memory) - -(=) (&check-memory) -(= 0) (&check-memory) -(= 0 1) (&check-memory) -(= 1 1) (&check-memory) -(= 0 1 2) (&check-memory) - -(>) (&check-memory) -(> 0) (&check-memory) -(> 0 1) (&check-memory) -(> 1 0) (&check-memory) -(> 0 1 2) (&check-memory) - -(>= 0) (&check-memory) -(>= 0 1) (&check-memory) -(>= 1 0) (&check-memory) -(>= 0 1 2) (&check-memory) - -(and) (&check-memory) -(and 0) (&check-memory) -(and 1) (&check-memory) -(and 0 0 0) (&check-memory) - -(quote a) (&check-memory) - -(assoc) (&check-memory) -(assoc 'one) (&check-memory) -(assoc 'one '((one . first))) (&check-memory) -(assoc 'one '((two . second))) (&check-memory) -(assoc 'one '((one . first) (two . second))) (&check-memory) - -(assq) (&check-memory) -(assq 'one) (&check-memory) -(assq "one" '(("one" . "first"))) (&check-memory) -(assq "one" '(("two" . "second"))) (&check-memory) -(assq "one" '(("one" . "first") ("two" . "second"))) (&check-memory) - -(atom) (&check-memory) -(atom 'one) (&check-memory) -(atom "one") (&check-memory) -(atom "one" 'two) (&check-memory) - -(funcall) (&check-memory) - -(car) (&check-memory) -(car '(one . two)) (&check-memory) - -(cdr) (&check-memory) -(cdr '(one . two)) (&check-memory) - -(concat) (&check-memory) -(concat 'aaaa) (&check-memory) -(concat 'aaaa 'bbbb) (&check-memory) -(concat "aaaa") (&check-memory) -(concat "aaaa" "bbbb") (&check-memory) -(concat "aaaa" "bbbb" "cccc") (&check-memory) - -(cond) (&check-memory) -(cond 0) (&check-memory) -(cond 0 1) (&check-memory) -(cond 0 1 2) (&check-memory) -(cond 0 1 2 3) (&check-memory) -(cond (0 'a) (1 'b) (0 'd)) (&check-memory) -(cond 1) (&check-memory) -(cond 1 1) (&check-memory) -(cond 1 1 2) (&check-memory) -(cond 1 1 2 3) (&check-memory) - -(cons) (&check-memory) -(cons "a") (&check-memory) -(cons "a" "b") (&check-memory) -(cons "a" "b" "c") (&check-memory) - -(eq) (&check-memory) -(eq 1) (&check-memory) -(eq 0 0) (&check-memory) -(eq "a" "b") (&check-memory) -(eq "a" "b" "c") (&check-memory) - -(equal) (&check-memory) -(equal 1) (&check-memory) -(equal 0 0) (&check-memory) -(equal "a" "b") (&check-memory) -(equal "a" "b" "c") (&check-memory) - -(exfun) (&check-memory) -(exfun 'abcd) (&check-memory) -(exfun 'abcd 'ijkl) (&check-memory) - -(format) (&check-memory) -(format 1) (&check-memory) -(format 'a) (&check-memory) -(format "a" "b" "c") (&check-memory) -(format "1.2") (&check-memory) -(format "%c" 43) (&check-memory) -(format "%d" 12) (&check-memory) -(format "%i" 12) (&check-memory) -(format "%f" 12.1) (&check-memory) -(format "%s" "abcd") (&check-memory) -(format "%s %i %i" "abcd" 1 2) (&check-memory) - -(garbage-collect) (&check-memory) -(gc) (&check-memory) - -(if) (&check-memory) -(if t) (&check-memory) -(if t 'a) (&check-memory) -(if t 'a 'b) (&check-memory) -(if nil) (&check-memory) -(if nil 'a) (&check-memory) -(if nil 'a 'b) (&check-memory) - -(include "itest.lisp") (&check-memory) - -(list) (&check-memory) -(list "a") (&check-memory) -(list "a" "b") (&check-memory) -(list "a" "b" "c") (&check-memory) - -(not) (&check-memory) -(not 0) (&check-memory) -(not nil) (&check-memory) -(not t) (&check-memory) -(not 'a) (&check-memory) -(not 'a 'b 'c 'd) (&check-memory) - -(nth) (&check-memory) -(nth 2) (&check-memory) -(nth 2 nil) (&check-memory) -(nth 2 '(('one 'two 'three))) (&check-memory) - -(null) (&check-memory) -(null 0) (&check-memory) -(null nil) (&check-memory) -(null t) (&check-memory) -(null 'a) (&check-memory) -(null 'a 'b 'c 'd) (&check-memory) - -(or) (&check-memory) -(or 0) (&check-memory) -(or 1) (&check-memory) -(or 0 0 0) (&check-memory) - -(path) (&check-memory) -(path 0) (&check-memory) -(path 1) (&check-memory) -(path 0 0 0) (&check-memory) -(path "data") (&check-memory) - -(princ) (&check-memory) -(princ "\nabcd\n") (&check-memory) -(princ "a" "b" "c\n") (&check-memory) - -(prog1) (&check-memory) -(prog1 1) (&check-memory) -(prog1 1 2 3 4) (&check-memory) - -(prog2) (&check-memory) -(prog2 1) (&check-memory) -(prog2 1 2 3 4) (&check-memory) - -(progn) (&check-memory) -(progn 1) (&check-memory) -(progn 1 2 3 4) (&check-memory) - -(quote) (&check-memory) -(quote a) (&check-memory) - -(rassoc) (&check-memory) -(rassoc 'first) (&check-memory) -(rassoc 'first '((one . first))) (&check-memory) -(rassoc 'first '((two . second))) (&check-memory) -(rassoc 'first '((one . first) (two . second))) (&check-memory) - -(rassq) (&check-memory) -(rassq "first") (&check-memory) -(rassq "first" '(("one" . "first"))) (&check-memory) -(rassq "first" '(("two" . "second"))) (&check-memory) -(rassq "first" '(("one" . "first") ("two" . "second"))) (&check-memory) - -(set) (&check-memory) -(set "a") (unset "a") (&check-memory) -(set "a" 1) (unset "a") (&check-memory) -(set a 1) (unset a) (&check-memory) -(set "a" 1 2) (unset "a") (&check-memory) - -(setf) (&check-memory) -(setf a) (unsetf a) (&check-memory) -(setf a 1) (unsetf a) (&check-memory) -(setf a 1 2) (unsetf a) (&check-memory) - -(setq) (&check-memory) -(setq a) (unsetq a) (&check-memory) -(setq a 1) (unsetq a) (&check-memory) -(setq a 1 2) (unsetq a) (&check-memory) - -(string-equal) (&check-memory) -(string-equal 1) (&check-memory) -(string-equal "a") (&check-memory) -(string-equal "a" "a") (&check-memory) -(string-equal "a" "b") (&check-memory) -(string-equal "a" "b" "c") (&check-memory) - -(string-to-integer) (&check-memory) -(string-to-integer 1) (&check-memory) -(string-to-integer 1.5) (&check-memory) -(string-to-integer "a") (&check-memory) -(string-to-integer "a" "a") (&check-memory) -(string-to-integer "a" "b") (&check-memory) -(string-to-integer "a" "b" "c") (&check-memory) - -(string-to-float) (&check-memory) -(string-to-float 1) (&check-memory) -(string-to-float 1.5) (&check-memory) -(string-to-float "a") (&check-memory) -(string-to-float "a" "a") (&check-memory) -(string-to-float "a" "b") (&check-memory) -(string-to-float "a" "b" "c") (&check-memory) - -(string=) (&check-memory) -(string= 1) (&check-memory) -(string= "a") (&check-memory) -(string= "a" "a") (&check-memory) -(string= "a" "b") (&check-memory) -(string= "a" "b" "c") (&check-memory) - -(unless) (&check-memory) -(unless 1) (&check-memory) -(unless 0 1 2) (&check-memory) -(unless t 2 3 4) (&check-memory) -(unless nil 2 3 4) (&check-memory) - -(unset) (&check-memory) -(unset "a") (&check-memory) - -(unsetf) (&check-memory) -(unsetf a) (&check-memory) -(unsetf a b) (&check-memory) - -(unsetq) (&check-memory) -(unsetq a) (&check-memory) -(unsetq a b) (&check-memory) - -(when) (&check-memory) -(when 0) (&check-memory) -(when 0 1) (&check-memory) -(when t 1) (&check-memory) -(when nil 1) (&check-memory) - -(while) (&check-memory) -(while nil) (&check-memory) -(while nil 1) (&check-memory) -(while nil 1 2 3 4) (&check-memory) - -; -; more complex command sequences -; - -(setq abcd "abcd") -(unsetq abcd) -(&check-memory) - -(setq abcd (("abcd" . "efgh") ("1234" . "5678"))) -(unsetq abcd) -(&check-memory) - -(defun myfun () (princ "a\n")) -(exfun 'myfun) -(unsetq myfun) -(&check-memory) - -(defun myfun () (princ "a\n")) -(funcall 'myfun) -(funcall 'myfun 'aaaaa) -(unsetq myfun) -(&check-memory) - -(defun myfun (o) (princ o "a\n")) -(funcall 'myfun) -(funcall 'myfun 'aaaaa) -(unsetq myfun) -(&check-memory) - -(defun myfun (o p) (princ o p "\n")) -(funcall 'myfun) -(funcall 'myfun 'aaaaa) -(funcall 'myfun 'aaaaa 'bbbbb) -(unsetq myfun) -(&check-memory) - -(defun printnum (from to) (while (<= from to) (princ " " from) (setq from (+ from 1)))) -(princ "Numbers 1-10:") (printnum 1 10) (princ "\n") -(unsetq printnum) - -; -; game over -; - -(princ "*********************\n") -(princ "OK, all tests passed!\n") -(princ "*********************\n") -(&stat-memory) diff --git a/configure.ac b/configure.ac index 75c65530..094bd11b 100644 --- a/configure.ac +++ b/configure.ac @@ -419,10 +419,6 @@ AC_ARG_ENABLE(ucm, AC_ARG_ENABLE(topology, AS_HELP_STRING([--disable-topology], [disable the DSP topology component]), [build_topology="$enableval"], [build_topology="yes"]) -AC_ARG_ENABLE(alisp, - AS_HELP_STRING([--enable-alisp], [enable the alisp component]), - [build_alisp="$enableval"], [build_alisp="no"]) -test "$softfloat" = "yes" && build_alisp="no" AC_ARG_ENABLE(old-symbols, AS_HELP_STRING([--disable-old-symbols], [disable old obsoleted symbols]), [keep_old_symbols="$enableval"], [keep_old_symbols="yes"]) @@ -496,7 +492,6 @@ AM_CONDITIONAL([BUILD_HWDEP], [test x$build_hwdep = xyes]) AM_CONDITIONAL([BUILD_SEQ], [test x$build_seq = xyes]) AM_CONDITIONAL([BUILD_UCM], [test x$build_ucm = xyes]) AM_CONDITIONAL([BUILD_TOPOLOGY], [test x$build_topology = xyes]) -AM_CONDITIONAL([BUILD_ALISP], [test x$build_alisp = xyes]) AM_CONDITIONAL([BUILD_MIXER_MODULES], [test x$build_mixer_modules = xyes]) AM_CONDITIONAL([BUILD_MIXER_PYMODULES], [test x$build_mixer_pymodules = xyes]) @@ -773,13 +768,13 @@ AC_CONFIG_FILES(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \ src/pcm/Makefile src/pcm/scopes/Makefile \ src/rawmidi/Makefile src/timer/Makefile \ src/hwdep/Makefile src/seq/Makefile src/ucm/Makefile \ - src/alisp/Makefile src/topology/Makefile \ + src/topology/Makefile \ src/conf/Makefile \ src/conf/cards/Makefile \ src/conf/ctl/Makefile \ src/conf/pcm/Makefile \ modules/Makefile modules/mixer/Makefile modules/mixer/simple/Makefile \ - alsalisp/Makefile aserver/Makefile \ + aserver/Makefile \ test/Makefile test/lsb/Makefile \ utils/Makefile utils/alsa-lib.spec utils/alsa.pc utils/alsa-topology.pc) diff --git a/gitcompile b/gitcompile index c70448f7..783d0fd3 100755 --- a/gitcompile +++ b/gitcompile @@ -5,7 +5,6 @@ set -e bits32= cbits32= modules= -alisp= lto= if [ $# -ne 0 ]; then endloop= @@ -20,10 +19,6 @@ if [ $# -ne 0 ]; then modules=yes echo "Forced mixer modules build..." shift ;; - alisp) - alisp=yes - echo "Forced alisp code build..." - shift ;; python2) python2=yes echo "Forced python2 interpreter build..." @@ -71,10 +66,6 @@ if [ "$modules" = "yes" ]; then args="$args --enable-mixer-pymods" fi -if [ "$alisp" = "yes" ]; then - args="$args --enable-alisp" -fi - if [ "$python2" = "yes" ]; then args="$args --enable-python2" fi diff --git a/include/Makefile.am b/include/Makefile.am index c1885e1d..bd8a4b97 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -58,10 +58,6 @@ if BUILD_TOPOLOGY alsainclude_HEADERS += topology.h endif -if BUILD_ALISP -alsainclude_HEADERS += alisp.h -endif - noinst_HEADERS = alsa sys.h search.h list.h aserver.h local.h alsa-symbols.h \ asoundlib-head.h asoundlib-tail.h bswap.h type_compat.h diff --git a/include/alisp.h b/include/alisp.h deleted file mode 100644 index 11d7adf4..00000000 --- a/include/alisp.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * ALSA lisp implementation - * Copyright (c) 2003 by Jaroslav Kysela - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -struct alisp_cfg { - int verbose: 1, - warning: 1, - debug: 1; - snd_input_t *in; /* program code */ - snd_output_t *out; /* program output */ - snd_output_t *eout; /* error output */ - snd_output_t *vout; /* verbose output */ - snd_output_t *wout; /* warning output */ - snd_output_t *dout; /* debug output */ -}; - -struct alisp_instance; -struct alisp_object; -struct alisp_seq_iterator; - -struct alisp_cfg *alsa_lisp_default_cfg(snd_input_t *input); -void alsa_lisp_default_cfg_free(struct alisp_cfg *cfg); -int alsa_lisp(struct alisp_cfg *cfg, struct alisp_instance **instance); -void alsa_lisp_free(struct alisp_instance *instance); -int alsa_lisp_function(struct alisp_instance *instance, struct alisp_seq_iterator **result, - const char *id, const char *args, ...) -#ifndef DOC_HIDDEN - __attribute__ ((format (printf, 4, 5))) -#endif - ; -void alsa_lisp_result_free(struct alisp_instance *instance, - struct alisp_seq_iterator *result); -int alsa_lisp_seq_first(struct alisp_instance *instance, const char *id, - struct alisp_seq_iterator **seq); -int alsa_lisp_seq_next(struct alisp_seq_iterator **seq); -int alsa_lisp_seq_count(struct alisp_seq_iterator *seq); -int alsa_lisp_seq_integer(struct alisp_seq_iterator *seq, long *val); -int alsa_lisp_seq_pointer(struct alisp_seq_iterator *seq, const char *ptr_id, void **ptr); diff --git a/include/error.h b/include/error.h index 8a2a9abc..13f59d55 100644 --- a/include/error.h +++ b/include/error.h @@ -46,7 +46,6 @@ extern "C" { #define SND_ERROR_BEGIN 500000 /**< Lower boundary of sound error codes. */ #define SND_ERROR_INCOMPATIBLE_VERSION (SND_ERROR_BEGIN+0) /**< Kernel/library protocols are not compatible. */ -#define SND_ERROR_ALISP_NIL (SND_ERROR_BEGIN+1) /**< Lisp encountered an error during acall. */ const char *snd_strerror(int errnum); diff --git a/src/Makefile.am b/src/Makefile.am index f8905343..679c626c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -51,13 +51,6 @@ if BUILD_UCM SUBDIRS += ucm libasound_la_LIBADD += ucm/libucm.la endif -if BUILD_ALISP -if VERSIONED_SYMBOLS -VERSION_CPPFLAGS += -DHAVE_ALISP_SYMS -endif -SUBDIRS += alisp -libasound_la_LIBADD += alisp/libalisp.la -endif SUBDIRS += conf libasound_la_LIBADD += @ALSA_DEPLIBS@ @@ -102,7 +95,4 @@ topology/libtopology.la: instr/libinstr.la: $(MAKE) -C instr libinstr.la -alisp/libalisp.la: - $(MAKE) -C alisp libalisp.la - AM_CPPFLAGS=-I$(top_srcdir)/include diff --git a/src/Versions.in.in b/src/Versions.in.in index 7ad6a633..4aa2e13b 100644 --- a/src/Versions.in.in +++ b/src/Versions.in.in @@ -129,19 +129,9 @@ ALSA_0.9.3 { } ALSA_0.9.0; ALSA_0.9.5 { -#ifdef HAVE_ALISP_SYMS - global: - - @SYMBOL_PREFIX@alsa_lisp; -#endif } ALSA_0.9.3; ALSA_0.9.7 { -#ifdef HAVE_ALISP_SYMS - global: - - @SYMBOL_PREFIX@alsa_lisp_*; -#endif } ALSA_0.9.5; ALSA_1.1.6 { diff --git a/src/alisp/Makefile.am b/src/alisp/Makefile.am deleted file mode 100644 index 1234e111..00000000 --- a/src/alisp/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ -EXTRA_LTLIBRARIES = libalisp.la - -EXTRA_DIST = alisp_snd.c - -libalisp_la_SOURCES = alisp.c - -noinst_HEADERS = alisp_local.h - -all: libalisp.la - -AM_CPPFLAGS=-I$(top_srcdir)/include diff --git a/src/alisp/alisp.c b/src/alisp/alisp.c deleted file mode 100644 index bb841119..00000000 --- a/src/alisp/alisp.c +++ /dev/null @@ -1,3495 +0,0 @@ -/* - * ALSA lisp implementation - * Copyright (c) 2003 by Jaroslav Kysela - * - * Based on work of Sandro Sigala (slisp-1.2) - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define alisp_seq_iterator alisp_object - -#include "local.h" -#include "alisp.h" -#include "alisp_local.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - - -struct alisp_object alsa_lisp_nil; -struct alisp_object alsa_lisp_t; - -/* parser prototypes */ -static struct alisp_object * parse_object(struct alisp_instance *instance, int havetoken); -static void princ_cons(snd_output_t *out, struct alisp_object * p); -static void princ_object(snd_output_t *out, struct alisp_object * p); -static struct alisp_object * eval(struct alisp_instance *instance, struct alisp_object * p); - -/* functions */ -static struct alisp_object *F_eval(struct alisp_instance *instance, struct alisp_object *); -static struct alisp_object *F_progn(struct alisp_instance *instance, struct alisp_object *); -static struct alisp_object *F_funcall(struct alisp_instance *instance, struct alisp_object *); - -/* others */ -static int alisp_include_file(struct alisp_instance *instance, const char *filename); - -/* - * object handling - */ - -static int get_string_hash(const char *s) -{ - int val = 0; - if (s == NULL) - return val; - while (*s) - val += *s++; - return val & ALISP_OBJ_PAIR_HASH_MASK; -} - -static void nomem(void) -{ - SNDERR("alisp: no enough memory"); -} - -static void lisp_verbose(struct alisp_instance *instance, const char *fmt, ...) -{ - va_list ap; - - if (!instance->verbose) - return; - va_start(ap, fmt); - snd_output_printf(instance->vout, "alisp: "); - snd_output_vprintf(instance->vout, fmt, ap); - snd_output_putc(instance->vout, '\n'); - va_end(ap); -} - -static void lisp_error(struct alisp_instance *instance, const char *fmt, ...) -{ - va_list ap; - - if (!instance->warning) - return; - va_start(ap, fmt); - snd_output_printf(instance->eout, "alisp error: "); - snd_output_vprintf(instance->eout, fmt, ap); - snd_output_putc(instance->eout, '\n'); - va_end(ap); -} - -static void lisp_warn(struct alisp_instance *instance, const char *fmt, ...) -{ - va_list ap; - - if (!instance->warning) - return; - va_start(ap, fmt); - snd_output_printf(instance->wout, "alisp warning: "); - snd_output_vprintf(instance->wout, fmt, ap); - snd_output_putc(instance->wout, '\n'); - va_end(ap); -} - -static void lisp_debug(struct alisp_instance *instance, const char *fmt, ...) -{ - va_list ap; - - if (!instance->debug) - return; - va_start(ap, fmt); - snd_output_printf(instance->dout, "alisp debug: "); - snd_output_vprintf(instance->dout, fmt, ap); - snd_output_putc(instance->dout, '\n'); - va_end(ap); -} - -static struct alisp_object * new_object(struct alisp_instance *instance, int type) -{ - struct alisp_object * p; - - if (list_empty(&instance->free_objs_list)) { - p = (struct alisp_object *)malloc(sizeof(struct alisp_object)); - if (p == NULL) { - nomem(); - return NULL; - } - lisp_debug(instance, "allocating cons %p", p); - } else { - p = (struct alisp_object *)instance->free_objs_list.next; - list_del(&p->list); - instance->free_objs--; - lisp_debug(instance, "recycling cons %p", p); - } - - instance->used_objs++; - - alisp_set_type(p, type); - alisp_set_refs(p, 1); - if (type == ALISP_OBJ_CONS) { - p->value.c.car = &alsa_lisp_nil; - p->value.c.cdr = &alsa_lisp_nil; - list_add(&p->list, &instance->used_objs_list[0][ALISP_OBJ_CONS]); - } - - if (instance->used_objs + instance->free_objs > instance->max_objs) - instance->max_objs = instance->used_objs + instance->free_objs; - - return p; -} - -static void free_object(struct alisp_object * p) -{ - switch (alisp_get_type(p)) { - case ALISP_OBJ_STRING: - case ALISP_OBJ_IDENTIFIER: - free(p->value.s); - alisp_set_type(p, ALISP_OBJ_INTEGER); - break; - default: - break; - } -} - -static void delete_object(struct alisp_instance *instance, struct alisp_object * p) -{ - if (p == NULL || p == &alsa_lisp_nil || p == &alsa_lisp_t) - return; - if (alisp_compare_type(p, ALISP_OBJ_NIL) || - alisp_compare_type(p, ALISP_OBJ_T)) - return; - assert(alisp_get_refs(p) > 0); - lisp_debug(instance, "delete cons %p (type = %i, refs = %i) (s = '%s')", p, alisp_get_type(p), alisp_get_refs(p), - alisp_compare_type(p, ALISP_OBJ_STRING) || - alisp_compare_type(p, ALISP_OBJ_IDENTIFIER) ? p->value.s : "???"); - if (alisp_dec_refs(p)) - return; - list_del(&p->list); - instance->used_objs--; - free_object(p); - if (instance->free_objs >= ALISP_FREE_OBJ_POOL) { - lisp_debug(instance, "freed cons %p", p); - free(p); - return; - } - lisp_debug(instance, "moved cons %p to free list", p); - list_add(&p->list, &instance->free_objs_list); - instance->free_objs++; -} - -static void delete_tree(struct alisp_instance *instance, struct alisp_object * p) -{ - if (p == NULL) - return; - if (alisp_compare_type(p, ALISP_OBJ_CONS)) { - delete_tree(instance, p->value.c.car); - delete_tree(instance, p->value.c.cdr); - } - delete_object(instance, p); -} - -static struct alisp_object * incref_object(struct alisp_instance *instance ATTRIBUTE_UNUSED, struct alisp_object * p) -{ - if (p == NULL || p == &alsa_lisp_nil || p == &alsa_lisp_t) - return p; - if (alisp_get_refs(p) == ALISP_MAX_REFS) { - assert(0); - fprintf(stderr, "OOPS: alsa lisp: incref fatal error\n"); - exit(EXIT_FAILURE); - } - alisp_inc_refs(p); - return p; -} - -static struct alisp_object * incref_tree(struct alisp_instance *instance, struct alisp_object * p) -{ - if (p == NULL) - return NULL; - if (alisp_compare_type(p, ALISP_OBJ_CONS)) { - incref_tree(instance, p->value.c.car); - incref_tree(instance, p->value.c.cdr); - } - return incref_object(instance, p); -} - -/* Function not used yet. Leave it commented out until we actually use it to - * avoid compiler complaints */ -#if 0 -static struct alisp_object * incref_tree_explicit(struct alisp_instance *instance, struct alisp_object * p, struct alisp_object * e) -{ - if (p == NULL) - return NULL; - if (alisp_compare_type(p, ALISP_OBJ_CONS)) { - if (e == p) { - incref_tree(instance, p->value.c.car); - incref_tree(instance, p->value.c.cdr); - } else { - incref_tree_explicit(instance, p->value.c.car, e); - incref_tree_explicit(instance, p->value.c.cdr, e); - } - } - if (e == p) - return incref_object(instance, p); - return p; -} -#endif - -static void free_objects(struct alisp_instance *instance) -{ - struct list_head *pos, *pos1; - struct alisp_object * p; - struct alisp_object_pair * pair; - int i, j; - - for (i = 0; i < ALISP_OBJ_PAIR_HASH_SIZE; i++) { - list_for_each_safe(pos, pos1, &instance->setobjs_list[i]) { - pair = list_entry(pos, struct alisp_object_pair, list); - lisp_debug(instance, "freeing pair: '%s' -> %p", pair->name, pair->value); - delete_tree(instance, pair->value); - free((void *)pair->name); - free(pair); - } - } - for (i = 0; i < ALISP_OBJ_PAIR_HASH_SIZE; i++) - for (j = 0; j <= ALISP_OBJ_LAST_SEARCH; j++) { - list_for_each_safe(pos, pos1, &instance->used_objs_list[i][j]) { - p = list_entry(pos, struct alisp_object, list); - lisp_warn(instance, "object %p is still referenced %i times!", p, alisp_get_refs(p)); -#if 0 - snd_output_printf(instance->wout, ">>>> "); - princ_object(instance->wout, p); - snd_output_printf(instance->wout, " <<<<\n"); -#endif - if (alisp_get_refs(p) > 0) - alisp_set_refs(p, 1); - delete_object(instance, p); - } - } - list_for_each_safe(pos, pos1, &instance->free_objs_list) { - p = list_entry(pos, struct alisp_object, list); - list_del(&p->list); - free(p); - lisp_debug(instance, "freed (all) cons %p", p); - } -} - -static struct alisp_object * search_object_identifier(struct alisp_instance *instance, const char *s) -{ - struct list_head * pos; - struct alisp_object * p; - - list_for_each(pos, &instance->used_objs_list[get_string_hash(s)][ALISP_OBJ_IDENTIFIER]) { - p = list_entry(pos, struct alisp_object, list); - if (alisp_get_refs(p) > ALISP_MAX_REFS_LIMIT) - continue; - if (!strcmp(p->value.s, s)) - return incref_object(instance, p); - } - - return NULL; -} - -static struct alisp_object * search_object_string(struct alisp_instance *instance, const char *s) -{ - struct list_head * pos; - struct alisp_object * p; - - list_for_each(pos, &instance->used_objs_list[get_string_hash(s)][ALISP_OBJ_STRING]) { - p = list_entry(pos, struct alisp_object, list); - if (!strcmp(p->value.s, s)) { - if (alisp_get_refs(p) > ALISP_MAX_REFS_LIMIT) - continue; - return incref_object(instance, p); - } - } - - return NULL; -} - -static struct alisp_object * search_object_integer(struct alisp_instance *instance, long in) -{ - struct list_head * pos; - struct alisp_object * p; - - list_for_each(pos, &instance->used_objs_list[in & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_INTEGER]) { - p = list_entry(pos, struct alisp_object, list); - if (p->value.i == in) { - if (alisp_get_refs(p) > ALISP_MAX_REFS_LIMIT) - continue; - return incref_object(instance, p); - } - } - - return NULL; -} - -static struct alisp_object * search_object_float(struct alisp_instance *instance, double in) -{ - struct list_head * pos; - struct alisp_object * p; - - list_for_each(pos, &instance->used_objs_list[(long)in & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_FLOAT]) { - p = list_entry(pos, struct alisp_object, list); - if (p->value.i == in) { - if (alisp_get_refs(p) > ALISP_MAX_REFS_LIMIT) - continue; - return incref_object(instance, p); - } - } - - return NULL; -} - -static struct alisp_object * search_object_pointer(struct alisp_instance *instance, const void *ptr) -{ - struct list_head * pos; - struct alisp_object * p; - - list_for_each(pos, &instance->used_objs_list[(long)ptr & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_POINTER]) { - p = list_entry(pos, struct alisp_object, list); - if (p->value.ptr == ptr) { - if (alisp_get_refs(p) > ALISP_MAX_REFS_LIMIT) - continue; - return incref_object(instance, p); - } - } - - return NULL; -} - -static struct alisp_object * new_integer(struct alisp_instance *instance, long value) -{ - struct alisp_object * obj; - - obj = search_object_integer(instance, value); - if (obj != NULL) - return obj; - obj = new_object(instance, ALISP_OBJ_INTEGER); - if (obj) { - list_add(&obj->list, &instance->used_objs_list[value & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_INTEGER]); - obj->value.i = value; - } - return obj; -} - -static struct alisp_object * new_float(struct alisp_instance *instance, double value) -{ - struct alisp_object * obj; - - obj = search_object_float(instance, value); - if (obj != NULL) - return obj; - obj = new_object(instance, ALISP_OBJ_FLOAT); - if (obj) { - list_add(&obj->list, &instance->used_objs_list[(long)value & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_FLOAT]); - obj->value.f = value; - } - return obj; -} - -static struct alisp_object * new_string(struct alisp_instance *instance, const char *str) -{ - struct alisp_object * obj; - - obj = search_object_string(instance, str); - if (obj != NULL) - return obj; - obj = new_object(instance, ALISP_OBJ_STRING); - if (obj) - list_add(&obj->list, &instance->used_objs_list[get_string_hash(str)][ALISP_OBJ_STRING]); - if (obj && (obj->value.s = strdup(str)) == NULL) { - delete_object(instance, obj); - nomem(); - return NULL; - } - return obj; -} - -static struct alisp_object * new_identifier(struct alisp_instance *instance, const char *id) -{ - struct alisp_object * obj; - - obj = search_object_identifier(instance, id); - if (obj != NULL) - return obj; - obj = new_object(instance, ALISP_OBJ_IDENTIFIER); - if (obj) - list_add(&obj->list, &instance->used_objs_list[get_string_hash(id)][ALISP_OBJ_IDENTIFIER]); - if (obj && (obj->value.s = strdup(id)) == NULL) { - delete_object(instance, obj); - nomem(); - return NULL; - } - return obj; -} - -static struct alisp_object * new_pointer(struct alisp_instance *instance, const void *ptr) -{ - struct alisp_object * obj; - - obj = search_object_pointer(instance, ptr); - if (obj != NULL) - return obj; - obj = new_object(instance, ALISP_OBJ_POINTER); - if (obj) { - list_add(&obj->list, &instance->used_objs_list[(long)ptr & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_POINTER]); - obj->value.ptr = ptr; - } - return obj; -} - -static struct alisp_object * new_cons_pointer(struct alisp_instance * instance, const char *ptr_id, void *ptr) -{ - struct alisp_object * lexpr; - - if (ptr == NULL) - return &alsa_lisp_nil; - lexpr = new_object(instance, ALISP_OBJ_CONS); - if (lexpr == NULL) - return NULL; - lexpr->value.c.car = new_string(instance, ptr_id); - if (lexpr->value.c.car == NULL) - goto __end; - lexpr->value.c.cdr = new_pointer(instance, ptr); - if (lexpr->value.c.cdr == NULL) { - delete_object(instance, lexpr->value.c.car); - __end: - delete_object(instance, lexpr); - return NULL; - } - return lexpr; -} - -void alsa_lisp_init_objects(void) __attribute__ ((constructor)); - -void alsa_lisp_init_objects(void) -{ - memset(&alsa_lisp_nil, 0, sizeof(alsa_lisp_nil)); - alisp_set_type(&alsa_lisp_nil, ALISP_OBJ_NIL); - INIT_LIST_HEAD(&alsa_lisp_nil.list); - memset(&alsa_lisp_t, 0, sizeof(alsa_lisp_t)); - alisp_set_type(&alsa_lisp_t, ALISP_OBJ_T); - INIT_LIST_HEAD(&alsa_lisp_t.list); -} - -/* - * lexer - */ - -static int xgetc(struct alisp_instance *instance) -{ - instance->charno++; - if (instance->lex_bufp > instance->lex_buf) - return *--(instance->lex_bufp); - return snd_input_getc(instance->in); -} - -static inline void xungetc(struct alisp_instance *instance, int c) -{ - *(instance->lex_bufp)++ = c; - instance->charno--; -} - -static int init_lex(struct alisp_instance *instance) -{ - instance->charno = instance->lineno = 1; - instance->token_buffer_max = 10; - if ((instance->token_buffer = (char *)malloc(instance->token_buffer_max)) == NULL) { - nomem(); - return -ENOMEM; - } - instance->lex_bufp = instance->lex_buf; - return 0; -} - -static void done_lex(struct alisp_instance *instance) -{ - free(instance->token_buffer); -} - -static char * extend_buf(struct alisp_instance *instance, char *p) -{ - int off = p - instance->token_buffer; - - instance->token_buffer_max += 10; - instance->token_buffer = (char *)realloc(instance->token_buffer, instance->token_buffer_max); - if (instance->token_buffer == NULL) { - nomem(); - return NULL; - } - - return instance->token_buffer + off; -} - -static int gettoken(struct alisp_instance *instance) -{ - char *p; - int c; - - for (;;) { - c = xgetc(instance); - switch (c) { - case '\n': - ++instance->lineno; - break; - - case ' ': case '\f': case '\t': case '\v': case '\r': - break; - - case ';': - /* Comment: ";".*"\n" */ - while ((c = xgetc(instance)) != '\n' && c != EOF) - ; - if (c != EOF) - ++instance->lineno; - break; - - case '?': - /* Character: "?". */ - c = xgetc(instance); - sprintf(instance->token_buffer, "%d", c); - return instance->thistoken = ALISP_INTEGER; - - case '-': - /* Minus sign: "-". */ - c = xgetc(instance); - if (!isdigit(c)) { - xungetc(instance, c); - c = '-'; - goto got_id; - } - xungetc(instance, c); - c = '-'; - /* FALLTRHU */ - - case '0': - case '1': case '2': case '3': - case '4': case '5': case '6': - case '7': case '8': case '9': - /* Integer: [0-9]+ */ - p = instance->token_buffer; - instance->thistoken = ALISP_INTEGER; - do { - __ok: - if (p - instance->token_buffer >= instance->token_buffer_max - 1) { - p = extend_buf(instance, p); - if (p == NULL) - return instance->thistoken = EOF; - } - *p++ = c; - c = xgetc(instance); - if (c == '.' && instance->thistoken == ALISP_INTEGER) { - c = xgetc(instance); - xungetc(instance, c); - if (isdigit(c)) { - instance->thistoken = ALISP_FLOAT; - c = '.'; - goto __ok; - } else { - c = '.'; - } - } else if (c == 'e' && instance->thistoken == ALISP_FLOAT) { - c = xgetc(instance); - if (isdigit(c)) { - instance->thistoken = ALISP_FLOATE; - goto __ok; - } - } - } while (isdigit(c)); - xungetc(instance, c); - *p = '\0'; - return instance->thistoken; - - got_id: - case '!': case '_': case '+': case '*': case '/': case '%': - case '<': case '>': case '=': case '&': - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': - case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': - case 's': case 't': case 'u': case 'v': case 'w': case 'x': - case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': - case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': - case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': - case 'Y': case 'Z': - /* Identifier: [!-/+*%<>=&a-zA-Z_][-/+*%<>=&a-zA-Z_0-9]* */ - p = instance->token_buffer; - do { - if (p - instance->token_buffer >= instance->token_buffer_max - 1) { - p = extend_buf(instance, p); - if (p == NULL) - return instance->thistoken = EOF; - } - *p++ = c; - c = xgetc(instance); - } while (isalnum(c) || strchr("!_-+*/%<>=&", c) != NULL); - xungetc(instance, c); - *p = '\0'; - return instance->thistoken = ALISP_IDENTIFIER; - - case '"': - /* String: "\""([^"]|"\\".)*"\"" */ - p = instance->token_buffer; - while ((c = xgetc(instance)) != '"' && c != EOF) { - if (p - instance->token_buffer >= instance->token_buffer_max - 1) { - p = extend_buf(instance, p); - if (p == NULL) - return instance->thistoken = EOF; - } - if (c == '\\') { - c = xgetc(instance); - switch (c) { - case '\n': ++instance->lineno; break; - case 'a': *p++ = '\a'; break; - case 'b': *p++ = '\b'; break; - case 'f': *p++ = '\f'; break; - case 'n': *p++ = '\n'; break; - case 'r': *p++ = '\r'; break; - case 't': *p++ = '\t'; break; - case 'v': *p++ = '\v'; break; - default: *p++ = c; - } - } else { - if (c == '\n') - ++instance->lineno; - *p++ = c; - } - } - *p = '\0'; - return instance->thistoken = ALISP_STRING; - - default: - return instance->thistoken = c; - } - } -} - -/* - * parser - */ - -static struct alisp_object * parse_form(struct alisp_instance *instance) -{ - int thistoken; - struct alisp_object * p, * first = NULL, * prev = NULL; - - while ((thistoken = gettoken(instance)) != ')' && thistoken != EOF) { - /* - * Parse a dotted pair notation. - */ - if (thistoken == '.') { - gettoken(instance); - if (prev == NULL) { - lisp_error(instance, "unexpected '.'"); - __err: - delete_tree(instance, first); - return NULL; - } - prev->value.c.cdr = parse_object(instance, 1); - if (prev->value.c.cdr == NULL) - goto __err; - if ((thistoken = gettoken(instance)) != ')') { - lisp_error(instance, "expected ')'"); - goto __err; - } - break; - } - - p = new_object(instance, ALISP_OBJ_CONS); - if (p == NULL) - goto __err; - - if (first == NULL) - first = p; - if (prev != NULL) - prev->value.c.cdr = p; - - p->value.c.car = parse_object(instance, 1); - if (p->value.c.car == NULL) - goto __err; - - prev = p; - } - - if (first == NULL) - return &alsa_lisp_nil; - else - return first; -} - -static struct alisp_object * quote_object(struct alisp_instance *instance, struct alisp_object * obj) -{ - struct alisp_object * p; - - if (obj == NULL) - goto __end1; - - p = new_object(instance, ALISP_OBJ_CONS); - if (p == NULL) - goto __end1; - - p->value.c.car = new_identifier(instance, "quote"); - if (p->value.c.car == NULL) - goto __end; - p->value.c.cdr = new_object(instance, ALISP_OBJ_CONS); - if (p->value.c.cdr == NULL) { - delete_object(instance, p->value.c.car); - __end: - delete_object(instance, p); - __end1: - delete_tree(instance, obj); - return NULL; - } - - p->value.c.cdr->value.c.car = obj; - return p; -} - -static inline struct alisp_object * parse_quote(struct alisp_instance *instance) -{ - return quote_object(instance, parse_object(instance, 0)); -} - -static struct alisp_object * parse_object(struct alisp_instance *instance, int havetoken) -{ - int thistoken; - struct alisp_object * p = NULL; - - if (!havetoken) - thistoken = gettoken(instance); - else - thistoken = instance->thistoken; - - switch (thistoken) { - case EOF: - break; - case '(': - p = parse_form(instance); - break; - case '\'': - p = parse_quote(instance); - break; - case ALISP_IDENTIFIER: - if (!strcmp(instance->token_buffer, "t")) - p = &alsa_lisp_t; - else if (!strcmp(instance->token_buffer, "nil")) - p = &alsa_lisp_nil; - else { - p = new_identifier(instance, instance->token_buffer); - } - break; - case ALISP_INTEGER: { - p = new_integer(instance, atol(instance->token_buffer)); - break; - } - case ALISP_FLOAT: - case ALISP_FLOATE: { - p = new_float(instance, atof(instance->token_buffer)); - break; - } - case ALISP_STRING: - p = new_string(instance, instance->token_buffer); - break; - default: - lisp_warn(instance, "%d:%d: unexpected character `%c'", instance->lineno, instance->charno, thistoken); - break; - } - - return p; -} - -/* - * object manipulation - */ - -static struct alisp_object_pair * set_object_direct(struct alisp_instance *instance, struct alisp_object * name, struct alisp_object * value) -{ - struct alisp_object_pair *p; - const char *id; - - id = name->value.s; - p = (struct alisp_object_pair *)malloc(sizeof(struct alisp_object_pair)); - if (p == NULL) { - nomem(); - return NULL; - } - p->name = strdup(id); - if (p->name == NULL) { - delete_tree(instance, value); - free(p); - return NULL; - } - list_add(&p->list, &instance->setobjs_list[get_string_hash(id)]); - p->value = value; - return p; -} - -static int check_set_object(struct alisp_instance * instance, struct alisp_object * name) -{ - if (name == &alsa_lisp_nil) { - lisp_warn(instance, "setting the value of a nil object"); - return 0; - } - if (name == &alsa_lisp_t) { - lisp_warn(instance, "setting the value of a t object"); - return 0; - } - if (!alisp_compare_type(name, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(name, ALISP_OBJ_STRING)) { - lisp_warn(instance, "setting the value of an object with non-indentifier"); - return 0; - } - return 1; -} - -static struct alisp_object_pair * set_object(struct alisp_instance *instance, struct alisp_object * name, struct alisp_object * value) -{ - struct list_head *pos; - struct alisp_object_pair *p; - const char *id; - - if (name == NULL || value == NULL) - return NULL; - - id = name->value.s; - - list_for_each(pos, &instance->setobjs_list[get_string_hash(id)]) { - p = list_entry(pos, struct alisp_object_pair, list); - if (!strcmp(p->name, id)) { - delete_tree(instance, p->value); - p->value = value; - return p; - } - } - - p = (struct alisp_object_pair *)malloc(sizeof(struct alisp_object_pair)); - if (p == NULL) { - nomem(); - return NULL; - } - p->name = strdup(id); - if (p->name == NULL) { - delete_tree(instance, value); - free(p); - return NULL; - } - list_add(&p->list, &instance->setobjs_list[get_string_hash(id)]); - p->value = value; - return p; -} - -static struct alisp_object * unset_object(struct alisp_instance *instance, struct alisp_object * name) -{ - struct list_head *pos; - struct alisp_object *res; - struct alisp_object_pair *p; - const char *id; - - if (!alisp_compare_type(name, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(name, ALISP_OBJ_STRING)) { - lisp_warn(instance, "unset object with a non-indentifier"); - return &alsa_lisp_nil; - } - id = name->value.s; - - list_for_each(pos, &instance->setobjs_list[get_string_hash(id)]) { - p = list_entry(pos, struct alisp_object_pair, list); - if (!strcmp(p->name, id)) { - list_del(&p->list); - res = p->value; - free((void *)p->name); - free(p); - return res; - } - } - - return &alsa_lisp_nil; -} - -static struct alisp_object * get_object1(struct alisp_instance *instance, const char *id) -{ - struct alisp_object_pair *p; - struct list_head *pos; - - list_for_each(pos, &instance->setobjs_list[get_string_hash(id)]) { - p = list_entry(pos, struct alisp_object_pair, list); - if (!strcmp(p->name, id)) - return p->value; - } - - return &alsa_lisp_nil; -} - -static struct alisp_object * get_object(struct alisp_instance *instance, struct alisp_object * name) -{ - if (!alisp_compare_type(name, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(name, ALISP_OBJ_STRING)) { - delete_tree(instance, name); - return &alsa_lisp_nil; - } - return get_object1(instance, name->value.s); -} - -static struct alisp_object * replace_object(struct alisp_instance *instance, struct alisp_object * name, struct alisp_object * onew) -{ - struct alisp_object_pair *p; - struct alisp_object *r; - struct list_head *pos; - const char *id; - - if (!alisp_compare_type(name, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(name, ALISP_OBJ_STRING)) { - delete_tree(instance, name); - return &alsa_lisp_nil; - } - id = name->value.s; - list_for_each(pos, &instance->setobjs_list[get_string_hash(id)]) { - p = list_entry(pos, struct alisp_object_pair, list); - if (!strcmp(p->name, id)) { - r = p->value; - p->value = onew; - return r; - } - } - - return NULL; -} - -static void dump_objects(struct alisp_instance *instance, const char *fname) -{ - struct alisp_object_pair *p; - snd_output_t *out; - struct list_head *pos; - int i, err; - - if (!strcmp(fname, "-")) - err = snd_output_stdio_attach(&out, stdout, 0); - else - err = snd_output_stdio_open(&out, fname, "w+"); - if (err < 0) { - SNDERR("alisp: cannot open file '%s' for writing (%s)", fname, snd_strerror(errno)); - return; - } - - for (i = 0; i < ALISP_OBJ_PAIR_HASH_SIZE; i++) { - list_for_each(pos, &instance->setobjs_list[i]) { - p = list_entry(pos, struct alisp_object_pair, list); - if (alisp_compare_type(p->value, ALISP_OBJ_CONS) && - alisp_compare_type(p->value->value.c.car, ALISP_OBJ_IDENTIFIER) && - !strcmp(p->value->value.c.car->value.s, "lambda")) { - snd_output_printf(out, "(defun %s ", p->name); - princ_cons(out, p->value->value.c.cdr); - snd_output_printf(out, ")\n"); - continue; - } - snd_output_printf(out, "(setq %s '", p->name); - princ_object(out, p->value); - snd_output_printf(out, ")\n"); - } - } - snd_output_close(out); -} - -static const char *obj_type_str(struct alisp_object * p) -{ - switch (alisp_get_type(p)) { - case ALISP_OBJ_NIL: return "nil"; - case ALISP_OBJ_T: return "t"; - case ALISP_OBJ_INTEGER: return "integer"; - case ALISP_OBJ_FLOAT: return "float"; - case ALISP_OBJ_IDENTIFIER: return "identifier"; - case ALISP_OBJ_STRING: return "string"; - case ALISP_OBJ_POINTER: return "pointer"; - case ALISP_OBJ_CONS: return "cons"; - default: assert(0); - } -} - -static void print_obj_lists(struct alisp_instance *instance, snd_output_t *out) -{ - struct list_head *pos; - struct alisp_object * p; - int i, j; - - snd_output_printf(out, "** used objects\n"); - for (i = 0; i < ALISP_OBJ_PAIR_HASH_SIZE; i++) - for (j = 0; j <= ALISP_OBJ_LAST_SEARCH; j++) - list_for_each(pos, &instance->used_objs_list[i][j]) { - p = list_entry(pos, struct alisp_object, list); - snd_output_printf(out, "** %p (%s) (", p, obj_type_str(p)); - if (!alisp_compare_type(p, ALISP_OBJ_CONS)) - princ_object(out, p); - else - snd_output_printf(out, "cons"); - snd_output_printf(out, ") refs=%i\n", alisp_get_refs(p)); - } - snd_output_printf(out, "** free objects\n"); - list_for_each(pos, &instance->free_objs_list) { - p = list_entry(pos, struct alisp_object, list); - snd_output_printf(out, "** %p\n", p); - } -} - -static void dump_obj_lists(struct alisp_instance *instance, const char *fname) -{ - snd_output_t *out; - int err; - - if (!strcmp(fname, "-")) - err = snd_output_stdio_attach(&out, stdout, 0); - else - err = snd_output_stdio_open(&out, fname, "w+"); - if (err < 0) { - SNDERR("alisp: cannot open file '%s' for writing (%s)", fname, snd_strerror(errno)); - return; - } - - print_obj_lists(instance, out); - - snd_output_close(out); -} - -/* - * functions - */ - -static int count_list(struct alisp_object * p) -{ - int i = 0; - - while (p != &alsa_lisp_nil && alisp_compare_type(p, ALISP_OBJ_CONS)) { - p = p->value.c.cdr; - ++i; - } - - return i; -} - -static inline struct alisp_object * car(struct alisp_object * p) -{ - if (alisp_compare_type(p, ALISP_OBJ_CONS)) - return p->value.c.car; - - return &alsa_lisp_nil; -} - -static inline struct alisp_object * cdr(struct alisp_object * p) -{ - if (alisp_compare_type(p, ALISP_OBJ_CONS)) - return p->value.c.cdr; - - return &alsa_lisp_nil; -} - -/* - * Syntax: (car expr) - */ -static struct alisp_object * F_car(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object *p1 = car(args), *p2; - delete_tree(instance, cdr(args)); - delete_object(instance, args); - p1 = eval(instance, p1); - delete_tree(instance, cdr(p1)); - p2 = car(p1); - delete_object(instance, p1); - return p2; -} - -/* - * Syntax: (cdr expr) - */ -static struct alisp_object * F_cdr(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object *p1 = car(args), *p2; - delete_tree(instance, cdr(args)); - delete_object(instance, args); - p1 = eval(instance, p1); - delete_tree(instance, car(p1)); - p2 = cdr(p1); - delete_object(instance, p1); - return p2; -} - -/* - * Syntax: (+ expr...) - */ -static struct alisp_object * F_add(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * n; - long v = 0; - double f = 0; - int type = ALISP_OBJ_INTEGER; - - p1 = eval(instance, car(p)); - for (;;) { - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - if (type == ALISP_OBJ_FLOAT) - f += p1->value.i; - else - v += p1->value.i; - } else if (alisp_compare_type(p1, ALISP_OBJ_FLOAT)) { - f += p1->value.f + v; - v = 0; - type = ALISP_OBJ_FLOAT; - } else { - lisp_warn(instance, "sum with a non integer or float operand"); - } - delete_tree(instance, p1); - p = cdr(n = p); - delete_object(instance, n); - if (p == &alsa_lisp_nil) - break; - p1 = eval(instance, car(p)); - } - if (type == ALISP_OBJ_INTEGER) { - return new_integer(instance, v); - } else { - return new_float(instance, f); - } -} - -/* - * Syntax: (concat expr...) - */ -static struct alisp_object * F_concat(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * n; - char *str = NULL, *str1; - - p1 = eval(instance, car(p)); - for (;;) { - if (alisp_compare_type(p1, ALISP_OBJ_STRING)) { - str1 = realloc(str, (str ? strlen(str) : 0) + strlen(p1->value.s) + 1); - if (str1 == NULL) { - nomem(); - free(str); - return NULL; - } - if (str == NULL) - strcpy(str1, p1->value.s); - else - strcat(str1, p1->value.s); - str = str1; - } else { - lisp_warn(instance, "concat with a non string or identifier operand"); - } - delete_tree(instance, p1); - p = cdr(n = p); - delete_object(instance, n); - if (p == &alsa_lisp_nil) - break; - p1 = eval(instance, car(p)); - } - if (str) { - p = new_string(instance, str); - free(str); - } else { - p = &alsa_lisp_nil; - } - return p; -} - -/* - * Syntax: (- expr...) - */ -static struct alisp_object * F_sub(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * n; - long v = 0; - double f = 0; - int type = ALISP_OBJ_INTEGER; - - do { - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - if (p == args && cdr(p) != &alsa_lisp_nil) { - v = p1->value.i; - } else { - if (type == ALISP_OBJ_FLOAT) - f -= p1->value.i; - else - v -= p1->value.i; - } - } else if (alisp_compare_type(p1, ALISP_OBJ_FLOAT)) { - if (type == ALISP_OBJ_INTEGER) { - f = v; - type = ALISP_OBJ_FLOAT; - } - if (p == args && cdr(p) != &alsa_lisp_nil) - f = p1->value.f; - else { - f -= p1->value.f; - } - } else - lisp_warn(instance, "difference with a non integer or float operand"); - delete_tree(instance, p1); - n = cdr(p); - delete_object(instance, p); - p = n; - } while (p != &alsa_lisp_nil); - - if (type == ALISP_OBJ_INTEGER) { - return new_integer(instance, v); - } else { - return new_float(instance, f); - } -} - -/* - * Syntax: (* expr...) - */ -static struct alisp_object * F_mul(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * n; - long v = 1; - double f = 1; - int type = ALISP_OBJ_INTEGER; - - do { - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - if (type == ALISP_OBJ_FLOAT) - f *= p1->value.i; - else - v *= p1->value.i; - } else if (alisp_compare_type(p1, ALISP_OBJ_FLOAT)) { - f *= p1->value.f * v; v = 1; - type = ALISP_OBJ_FLOAT; - } else { - lisp_warn(instance, "product with a non integer or float operand"); - } - delete_tree(instance, p1); - n = cdr(p); - delete_object(instance, p); - p = n; - } while (p != &alsa_lisp_nil); - - if (type == ALISP_OBJ_INTEGER) { - return new_integer(instance, v); - } else { - return new_float(instance, f); - } -} - -/* - * Syntax: (/ expr...) - */ -static struct alisp_object * F_div(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * n; - long v = 0; - double f = 0; - int type = ALISP_OBJ_INTEGER; - - do { - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - if (p == args && cdr(p) != &alsa_lisp_nil) { - v = p1->value.i; - } else { - if (p1->value.i == 0) { - lisp_warn(instance, "division by zero"); - v = 0; - f = 0; - break; - } else { - if (type == ALISP_OBJ_FLOAT) - f /= p1->value.i; - else - v /= p1->value.i; - } - } - } else if (alisp_compare_type(p1, ALISP_OBJ_FLOAT)) { - if (type == ALISP_OBJ_INTEGER) { - f = v; - type = ALISP_OBJ_FLOAT; - } - if (p == args && cdr(p) != &alsa_lisp_nil) { - f = p1->value.f; - } else { - if (p1->value.f == 0) { - lisp_warn(instance, "division by zero"); - f = 0; - break; - } else { - f /= p1->value.i; - } - } - } else - lisp_warn(instance, "quotient with a non integer or float operand"); - delete_tree(instance, p1); - n = cdr(p); - delete_object(instance, p); - p = n; - } while (p != &alsa_lisp_nil); - - if (type == ALISP_OBJ_INTEGER) { - return new_integer(instance, v); - } else { - return new_float(instance, f); - } -} - -/* - * Syntax: (% expr1 expr2) - */ -static struct alisp_object * F_mod(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * p3; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p2->value.i == 0) { - lisp_warn(instance, "module by zero"); - p3 = new_integer(instance, 0); - } else { - p3 = new_integer(instance, p1->value.i % p2->value.i); - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - f1 = fmod(f1, f2); - if (f1 == EDOM) { - lisp_warn(instance, "module by zero"); - p3 = new_float(instance, 0); - } else { - p3 = new_float(instance, f1); - } - } else { - lisp_warn(instance, "module with a non integer or float operand"); - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return p3; -} - -/* - * Syntax: (< expr1 expr2) - */ -static struct alisp_object * F_lt(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p1->value.i < p2->value.i) { - __true: - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - if (f1 < f2) - goto __true; - } else { - lisp_warn(instance, "comparison with a non integer or float operand"); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (> expr1 expr2) - */ -static struct alisp_object * F_gt(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p1->value.i > p2->value.i) { - __true: - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - if (f1 > f2) - goto __true; - } else { - lisp_warn(instance, "comparison with a non integer or float operand"); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (<= expr1 expr2) - */ -static struct alisp_object * F_le(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p1->value.i <= p2->value.i) { - __true: - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - if (f1 <= f2) - goto __true; - } else { - lisp_warn(instance, "comparison with a non integer or float operand"); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (>= expr1 expr2) - */ -static struct alisp_object * F_ge(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p1->value.i >= p2->value.i) { - __true: - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - if (f1 >= f2) - goto __true; - } else { - lisp_warn(instance, "comparison with a non integer or float operand"); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (= expr1 expr2) - */ -static struct alisp_object * F_numeq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p1->value.i == p2->value.i) { - __true: - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - if (f1 == f2) - goto __true; - } else { - lisp_warn(instance, "comparison with a non integer or float operand"); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (!= expr1 expr2) - */ -static struct alisp_object * F_numneq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p; - - p = F_numeq(instance, args); - if (p == &alsa_lisp_nil) - return &alsa_lisp_t; - return &alsa_lisp_nil; -} - -/* - * Syntax: (exfun name) - * Test, if a function exists - */ -static struct alisp_object * F_exfun(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - p2 = get_object(instance, p1); - if (p2 == &alsa_lisp_nil) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - p2 = car(p2); - if (alisp_compare_type(p2, ALISP_OBJ_IDENTIFIER) && - !strcmp(p2->value.s, "lambda")) { - delete_tree(instance, p1); - return &alsa_lisp_t; - } - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -static void princ_string(snd_output_t *out, char *s) -{ - char *p; - - snd_output_putc(out, '"'); - for (p = s; *p != '\0'; ++p) - switch (*p) { - case '\a': snd_output_putc(out, '\\'); snd_output_putc(out, 'a'); break; - case '\b': snd_output_putc(out, '\\'); snd_output_putc(out, 'b'); break; - case '\f': snd_output_putc(out, '\\'); snd_output_putc(out, 'f'); break; - case '\n': snd_output_putc(out, '\\'); snd_output_putc(out, 'n'); break; - case '\r': snd_output_putc(out, '\\'); snd_output_putc(out, 'r'); break; - case '\t': snd_output_putc(out, '\\'); snd_output_putc(out, 't'); break; - case '\v': snd_output_putc(out, '\\'); snd_output_putc(out, 'v'); break; - case '"': snd_output_putc(out, '\\'); snd_output_putc(out, '"'); break; - default: snd_output_putc(out, *p); - } - snd_output_putc(out, '"'); -} - -static void princ_cons(snd_output_t *out, struct alisp_object * p) -{ - do { - princ_object(out, p->value.c.car); - p = p->value.c.cdr; - if (p != &alsa_lisp_nil) { - snd_output_putc(out, ' '); - if (!alisp_compare_type(p, ALISP_OBJ_CONS)) { - snd_output_printf(out, ". "); - princ_object(out, p); - } - } - } while (p != &alsa_lisp_nil && alisp_compare_type(p, ALISP_OBJ_CONS)); -} - -static void princ_object(snd_output_t *out, struct alisp_object * p) -{ - switch (alisp_get_type(p)) { - case ALISP_OBJ_NIL: - snd_output_printf(out, "nil"); - break; - case ALISP_OBJ_T: - snd_output_putc(out, 't'); - break; - case ALISP_OBJ_IDENTIFIER: - snd_output_printf(out, "%s", p->value.s); - break; - case ALISP_OBJ_STRING: - princ_string(out, p->value.s); - break; - case ALISP_OBJ_INTEGER: - snd_output_printf(out, "%ld", p->value.i); - break; - case ALISP_OBJ_FLOAT: - snd_output_printf(out, "%f", p->value.f); - break; - case ALISP_OBJ_POINTER: - snd_output_printf(out, "<%p>", p->value.ptr); - break; - case ALISP_OBJ_CONS: - snd_output_putc(out, '('); - princ_cons(out, p); - snd_output_putc(out, ')'); - } -} - -/* - * Syntax: (princ expr...) - */ -static struct alisp_object * F_princ(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1 = NULL, * n; - - do { - if (p1) - delete_tree(instance, p1); - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_STRING)) - snd_output_printf(instance->out, "%s", p1->value.s); - else - princ_object(instance->out, p1); - n = cdr(p); - delete_object(instance, p); - p = n; - } while (p != &alsa_lisp_nil); - - return p1; -} - -/* - * Syntax: (atom expr) - */ -static struct alisp_object * F_atom(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p; - - p = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (p == NULL) - return NULL; - - switch (alisp_get_type(p)) { - case ALISP_OBJ_T: - case ALISP_OBJ_NIL: - case ALISP_OBJ_INTEGER: - case ALISP_OBJ_FLOAT: - case ALISP_OBJ_STRING: - case ALISP_OBJ_IDENTIFIER: - case ALISP_OBJ_POINTER: - delete_tree(instance, p); - return &alsa_lisp_t; - default: - break; - } - - delete_tree(instance, p); - return &alsa_lisp_nil; -} - -/* - * Syntax: (cons expr1 expr2) - */ -static struct alisp_object * F_cons(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p; - - p = new_object(instance, ALISP_OBJ_CONS); - if (p) { - p->value.c.car = eval(instance, car(args)); - p->value.c.cdr = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - } else { - delete_tree(instance, args); - } - - return p; -} - -/* - * Syntax: (list expr1...) - */ -static struct alisp_object * F_list(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * first = NULL, * prev = NULL, * p1; - - if (p == &alsa_lisp_nil) - return &alsa_lisp_nil; - - do { - p1 = new_object(instance, ALISP_OBJ_CONS); - if (p1 == NULL) { - delete_tree(instance, p); - delete_tree(instance, first); - return NULL; - } - p1->value.c.car = eval(instance, car(p)); - if (p1->value.c.car == NULL) { - delete_tree(instance, first); - delete_tree(instance, cdr(p)); - delete_object(instance, p); - return NULL; - } - if (first == NULL) - first = p1; - if (prev != NULL) - prev->value.c.cdr = p1; - prev = p1; - p = cdr(p1 = p); - delete_object(instance, p1); - } while (p != &alsa_lisp_nil); - - return first; -} - -static inline int eq(struct alisp_object * p1, struct alisp_object * p2) -{ - return p1 == p2; -} - -static int equal(struct alisp_object * p1, struct alisp_object * p2) -{ - int type1, type2; - - if (eq(p1, p2)) - return 1; - - type1 = alisp_get_type(p1); - type2 = alisp_get_type(p2); - - if (type1 == ALISP_OBJ_CONS || type2 == ALISP_OBJ_CONS) - return 0; - - if (type1 == type2) { - switch (type1) { - case ALISP_OBJ_STRING: - return !strcmp(p1->value.s, p2->value.s); - case ALISP_OBJ_INTEGER: - return p1->value.i == p2->value.i; - case ALISP_OBJ_FLOAT: - return p1->value.i == p2->value.i; - } - } - - return 0; -} - -/* - * Syntax: (eq expr1 expr2) - */ -static struct alisp_object * F_eq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (eq(p1, p2)) { - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (equal expr1 expr2) - */ -static struct alisp_object * F_equal(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (equal(p1, p2)) { - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (quote expr) - */ -static struct alisp_object * F_quote(struct alisp_instance *instance ATTRIBUTE_UNUSED, struct alisp_object * args) -{ - struct alisp_object *p = car(args); - - delete_tree(instance, cdr(args)); - delete_object(instance, args); - return p; -} - -/* - * Syntax: (and expr...) - */ -static struct alisp_object * F_and(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1 = NULL, * n; - - do { - if (p1) - delete_tree(instance, p1); - p1 = eval(instance, car(p)); - if (p1 == &alsa_lisp_nil) { - delete_tree(instance, p1); - delete_tree(instance, cdr(p)); - delete_object(instance, p); - return &alsa_lisp_nil; - } - p = cdr(n = p); - delete_object(instance, n); - } while (p != &alsa_lisp_nil); - - return p1; -} - -/* - * Syntax: (or expr...) - */ -static struct alisp_object * F_or(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1 = NULL, * n; - - do { - if (p1) - delete_tree(instance, p1); - p1 = eval(instance, car(p)); - if (p1 != &alsa_lisp_nil) { - delete_tree(instance, cdr(p)); - delete_object(instance, p); - return p1; - } - p = cdr(n = p); - delete_object(instance, n); - } while (p != &alsa_lisp_nil); - - return &alsa_lisp_nil; -} - -/* - * Syntax: (not expr) - * Syntax: (null expr) - */ -static struct alisp_object * F_not(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = eval(instance, car(args)); - - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (p != &alsa_lisp_nil) { - delete_tree(instance, p); - return &alsa_lisp_nil; - } - - delete_tree(instance, p); - return &alsa_lisp_t; -} - -/* - * Syntax: (cond (expr1 [expr2])...) - */ -static struct alisp_object * F_cond(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * p2, * p3; - - do { - p1 = car(p); - if ((p2 = eval(instance, car(p1))) != &alsa_lisp_nil) { - p3 = cdr(p1); - delete_object(instance, p1); - delete_tree(instance, cdr(p)); - delete_object(instance, p); - if (p3 != &alsa_lisp_nil) { - delete_tree(instance, p2); - return F_progn(instance, p3); - } else { - delete_tree(instance, p3); - return p2; - } - } else { - delete_tree(instance, p2); - delete_tree(instance, cdr(p1)); - delete_object(instance, p1); - } - p = cdr(p2 = p); - delete_object(instance, p2); - } while (p != &alsa_lisp_nil); - - return &alsa_lisp_nil; -} - -/* - * Syntax: (if expr then-expr else-expr...) - */ -static struct alisp_object * F_if(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * p3; - - p1 = car(args); - p2 = car(cdr(args)); - p3 = cdr(cdr(args)); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - p1 = eval(instance, p1); - if (p1 != &alsa_lisp_nil) { - delete_tree(instance, p1); - delete_tree(instance, p3); - return eval(instance, p2); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return F_progn(instance, p3); -} - -/* - * Syntax: (when expr then-expr...) - */ -static struct alisp_object * F_when(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = car(args); - p2 = cdr(args); - delete_object(instance, args); - if ((p1 = eval(instance, p1)) != &alsa_lisp_nil) { - delete_tree(instance, p1); - return F_progn(instance, p2); - } else { - delete_tree(instance, p1); - delete_tree(instance, p2); - } - - return &alsa_lisp_nil; -} - -/* - * Syntax: (unless expr else-expr...) - */ -static struct alisp_object * F_unless(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = car(args); - p2 = cdr(args); - delete_object(instance, args); - if ((p1 = eval(instance, p1)) == &alsa_lisp_nil) { - return F_progn(instance, p2); - } else { - delete_tree(instance, p1); - delete_tree(instance, p2); - } - - return &alsa_lisp_nil; -} - -/* - * Syntax: (while expr exprs...) - */ -static struct alisp_object * F_while(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * p3; - - p1 = car(args); - p2 = cdr(args); - - delete_object(instance, args); - while (1) { - incref_tree(instance, p1); - if ((p3 = eval(instance, p1)) == &alsa_lisp_nil) - break; - delete_tree(instance, p3); - incref_tree(instance, p2); - delete_tree(instance, F_progn(instance, p2)); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (progn expr...) - */ -static struct alisp_object * F_progn(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1 = NULL, * n; - - do { - if (p1) - delete_tree(instance, p1); - p1 = eval(instance, car(p)); - n = cdr(p); - delete_object(instance, p); - p = n; - } while (p != &alsa_lisp_nil); - - return p1; -} - -/* - * Syntax: (prog1 expr...) - */ -static struct alisp_object * F_prog1(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * first = NULL, * p1; - - do { - p1 = eval(instance, car(p)); - if (first == NULL) - first = p1; - else - delete_tree(instance, p1); - p1 = cdr(p); - delete_object(instance, p); - p = p1; - } while (p != &alsa_lisp_nil); - - if (first == NULL) - first = &alsa_lisp_nil; - - return first; -} - -/* - * Syntax: (prog2 expr...) - */ -static struct alisp_object * F_prog2(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * second = NULL, * p1; - int i = 0; - - do { - ++i; - p1 = eval(instance, car(p)); - if (i == 2) - second = p1; - else - delete_tree(instance, p1); - p1 = cdr(p); - delete_object(instance, p); - p = p1; - } while (p != &alsa_lisp_nil); - - if (second == NULL) - second = &alsa_lisp_nil; - - return second; -} - -/* - * Syntax: (set name value) - */ -static struct alisp_object * F_set(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1 = eval(instance, car(args)), - * p2 = eval(instance, car(cdr(args))); - - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - if (!check_set_object(instance, p1)) { - delete_tree(instance, p2); - p2 = &alsa_lisp_nil; - } else { - if (set_object(instance, p1, p2) == NULL) { - delete_tree(instance, p1); - delete_tree(instance, p2); - return NULL; - } - } - delete_tree(instance, p1); - return incref_tree(instance, p2); -} - -/* - * Syntax: (unset name) - */ -static struct alisp_object * F_unset(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1 = eval(instance, car(args)); - - delete_tree(instance, unset_object(instance, p1)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - return p1; -} - -/* - * Syntax: (setq name value...) - * Syntax: (setf name value...) - * `name' is not evalled - */ -static struct alisp_object * F_setq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * p2 = NULL, *n; - - do { - p1 = car(p); - p2 = eval(instance, car(cdr(p))); - n = cdr(cdr(p)); - delete_object(instance, cdr(p)); - delete_object(instance, p); - if (!check_set_object(instance, p1)) { - delete_tree(instance, p2); - p2 = &alsa_lisp_nil; - } else { - if (set_object(instance, p1, p2) == NULL) { - delete_tree(instance, p1); - delete_tree(instance, p2); - return NULL; - } - } - delete_tree(instance, p1); - p = n; - } while (p != &alsa_lisp_nil); - - return incref_tree(instance, p2); -} - -/* - * Syntax: (unsetq name...) - * Syntax: (unsetf name...) - * `name' is not evalled - */ -static struct alisp_object * F_unsetq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1 = NULL, * n; - - do { - if (p1) - delete_tree(instance, p1); - p1 = unset_object(instance, car(p)); - delete_tree(instance, car(p)); - p = cdr(n = p); - delete_object(instance, n); - } while (p != &alsa_lisp_nil); - - return p1; -} - -/* - * Syntax: (defun name arglist expr...) - * `name' is not evalled - * `arglist' is not evalled - */ -static struct alisp_object * F_defun(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1 = car(args), - * p2 = car(cdr(args)), - * p3 = cdr(cdr(args)); - struct alisp_object * lexpr; - - lexpr = new_object(instance, ALISP_OBJ_CONS); - if (lexpr) { - lexpr->value.c.car = new_identifier(instance, "lambda"); - if (lexpr->value.c.car == NULL) { - delete_object(instance, lexpr); - delete_tree(instance, args); - return NULL; - } - if ((lexpr->value.c.cdr = new_object(instance, ALISP_OBJ_CONS)) == NULL) { - delete_object(instance, lexpr->value.c.car); - delete_object(instance, lexpr); - delete_tree(instance, args); - return NULL; - } - lexpr->value.c.cdr->value.c.car = p2; - lexpr->value.c.cdr->value.c.cdr = p3; - delete_object(instance, cdr(args)); - delete_object(instance, args); - if (set_object(instance, p1, lexpr) == NULL) { - delete_tree(instance, p1); - delete_tree(instance, lexpr); - return NULL; - } - delete_tree(instance, p1); - } else { - delete_tree(instance, args); - } - return &alsa_lisp_nil; -} - -static struct alisp_object * eval_func(struct alisp_instance *instance, struct alisp_object * p, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * p3, * p4; - struct alisp_object ** eval_objs, ** save_objs; - int i; - - p1 = car(p); - if (alisp_compare_type(p1, ALISP_OBJ_IDENTIFIER) && - !strcmp(p1->value.s, "lambda")) { - p2 = car(cdr(p)); - p3 = args; - - if ((i = count_list(p2)) != count_list(p3)) { - lisp_warn(instance, "wrong number of parameters"); - goto _delete; - } - - eval_objs = malloc(2 * i * sizeof(struct alisp_object *)); - if (eval_objs == NULL) { - nomem(); - goto _delete; - } - save_objs = eval_objs + i; - - /* - * Save the new variable values. - */ - i = 0; - while (p3 != &alsa_lisp_nil) { - eval_objs[i++] = eval(instance, car(p3)); - p3 = cdr(p4 = p3); - delete_object(instance, p4); - } - - /* - * Save the old variable values and set the new ones. - */ - i = 0; - while (p2 != &alsa_lisp_nil) { - p3 = car(p2); - save_objs[i] = replace_object(instance, p3, eval_objs[i]); - if (save_objs[i] == NULL && - set_object_direct(instance, p3, eval_objs[i]) == NULL) { - p4 = NULL; - goto _end; - } - p2 = cdr(p2); - ++i; - } - - p4 = F_progn(instance, cdr(incref_tree(instance, p3 = cdr(p)))); - - /* - * Restore the old variable values. - */ - p2 = car(p3); - delete_object(instance, p3); - i = 0; - while (p2 != &alsa_lisp_nil) { - p3 = car(p2); - if (save_objs[i] == NULL) { - p3 = unset_object(instance, p3); - } else { - p3 = replace_object(instance, p3, save_objs[i]); - } - i++; - delete_tree(instance, p3); - delete_tree(instance, car(p2)); - p2 = cdr(p3 = p2); - delete_object(instance, p3); - } - - _end: - free(eval_objs); - - return p4; - } else { - _delete: - delete_tree(instance, args); - } - return &alsa_lisp_nil; -} - -struct alisp_object * F_gc(struct alisp_instance *instance ATTRIBUTE_UNUSED, struct alisp_object * args ATTRIBUTE_UNUSED) -{ - /* improved: no more traditional gc */ - return &alsa_lisp_t; -} - -/* - * Syntax: (path what) - * what is string ('data') - */ -struct alisp_object * F_path(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (!alisp_compare_type(p1, ALISP_OBJ_STRING)) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - if (!strcmp(p1->value.s, "data")) { - delete_tree(instance, p1); - return new_string(instance, snd_config_topdir()); - } - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -/* - * Syntax: (include filename...) - */ -struct alisp_object * F_include(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1; - int res = -ENOENT; - - do { - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_STRING)) - res = alisp_include_file(instance, p1->value.s); - delete_tree(instance, p1); - p = cdr(p1 = p); - delete_object(instance, p1); - } while (p != &alsa_lisp_nil); - - return new_integer(instance, res); -} - -/* - * Syntax: (string-to-integer value) - * 'value' can be integer or float type - */ -struct alisp_object * F_string_to_integer(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = eval(instance, car(args)), * p1; - - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (alisp_compare_type(p, ALISP_OBJ_INTEGER)) - return p; - if (alisp_compare_type(p, ALISP_OBJ_FLOAT)) { - p1 = new_integer(instance, floor(p->value.f)); - } else { - lisp_warn(instance, "expected an integer or float for integer conversion"); - p1 = &alsa_lisp_nil; - } - delete_tree(instance, p); - return p1; -} - -/* - * Syntax: (string-to-float value) - * 'value' can be integer or float type - */ -struct alisp_object * F_string_to_float(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = eval(instance, car(args)), * p1; - - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (alisp_compare_type(p, ALISP_OBJ_FLOAT)) - return p; - if (alisp_compare_type(p, ALISP_OBJ_INTEGER)) { - p1 = new_float(instance, p->value.i); - } else { - lisp_warn(instance, "expected an integer or float for integer conversion"); - p1 = &alsa_lisp_nil; - } - delete_tree(instance, p); - return p1; -} - -static int append_to_string(char **s, int *len, char *from, int size) -{ - if (*len == 0) { - *s = malloc(*len = size + 1); - if (*s == NULL) { - nomem(); - return -ENOMEM; - } - memcpy(*s, from, size); - } else { - *len += size; - *s = realloc(*s, *len); - if (*s == NULL) { - nomem(); - return -ENOMEM; - } - memcpy(*s + strlen(*s), from, size); - } - (*s)[*len - 1] = '\0'; - return 0; -} - -static int format_parse_char(struct alisp_instance *instance, char **s, int *len, struct alisp_object *p) -{ - char b; - - if (!alisp_compare_type(p, ALISP_OBJ_INTEGER)) { - lisp_warn(instance, "format: expected integer\n"); - return 0; - } - b = p->value.i; - return append_to_string(s, len, &b, 1); -} - -static int format_parse_integer(struct alisp_instance *instance, char **s, int *len, struct alisp_object *p) -{ - int res; - char *s1; - - if (!alisp_compare_type(p, ALISP_OBJ_INTEGER) && - !alisp_compare_type(p, ALISP_OBJ_FLOAT)) { - lisp_warn(instance, "format: expected integer or float\n"); - return 0; - } - s1 = malloc(64); - if (s1 == NULL) { - nomem(); - return -ENOMEM; - } - sprintf(s1, "%li", alisp_compare_type(p, ALISP_OBJ_FLOAT) ? (long)floor(p->value.f) : p->value.i); - res = append_to_string(s, len, s1, strlen(s1)); - free(s1); - return res; -} - -static int format_parse_float(struct alisp_instance *instance, char **s, int *len, struct alisp_object *p) -{ - int res; - char *s1; - - if (!alisp_compare_type(p, ALISP_OBJ_INTEGER) && - !alisp_compare_type(p, ALISP_OBJ_FLOAT)) { - lisp_warn(instance, "format: expected integer or float\n"); - return 0; - } - s1 = malloc(64); - if (s1 == NULL) { - nomem(); - return -ENOMEM; - } - sprintf(s1, "%f", alisp_compare_type(p, ALISP_OBJ_FLOAT) ? p->value.f : (double)p->value.i); - res = append_to_string(s, len, s1, strlen(s1)); - free(s1); - return res; -} - -static int format_parse_string(struct alisp_instance *instance, char **s, int *len, struct alisp_object *p) -{ - if (!alisp_compare_type(p, ALISP_OBJ_STRING)) { - lisp_warn(instance, "format: expected string\n"); - return 0; - } - return append_to_string(s, len, p->value.s, strlen(p->value.s)); -} - -/* - * Syntax: (format format value...) - * 'format' is C-like format string - */ -struct alisp_object * F_format(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = eval(instance, car(args)), * p1 = cdr(args), * n; - char *s, *s1, *s2; - int len; - - delete_object(instance, args); - if (!alisp_compare_type(p, ALISP_OBJ_STRING)) { - delete_tree(instance, p1); - delete_tree(instance, p); - lisp_warn(instance, "format: expected an format string"); - return &alsa_lisp_nil; - } - s = p->value.s; - s1 = NULL; - len = 0; - n = eval(instance, car(p1)); - do { - while (1) { - s2 = s; - while (*s2 && *s2 != '%') - s2++; - if (s2 != s) { - if (append_to_string(&s1, &len, s, s2 - s) < 0) { - __error: - delete_tree(instance, n); - delete_tree(instance, cdr(p1)); - delete_object(instance, p1); - delete_tree(instance, p); - return NULL; - } - } - if (*s2 == '%') - s2++; - switch (*s2) { - case '%': - if (append_to_string(&s1, &len, s2, 1) < 0) - goto __error; - s = s2 + 1; - break; - case 'c': - if (format_parse_char(instance, &s1, &len, n) < 0) - goto __error; - s = s2 + 1; - goto __next; - case 'd': - case 'i': - if (format_parse_integer(instance, &s1, &len, n) < 0) - goto __error; - s = s2 + 1; - goto __next; - case 'f': - if (format_parse_float(instance, &s1, &len, n) < 0) - goto __error; - s = s2 + 1; - goto __next; - case 's': - if (format_parse_string(instance, &s1, &len, n) < 0) - goto __error; - s = s2 + 1; - goto __next; - case '\0': - goto __end; - default: - lisp_warn(instance, "unknown format char '%c'", *s2); - s = s2 + 1; - goto __next; - } - } - __next: - delete_tree(instance, n); - p1 = cdr(n = p1); - delete_object(instance, n); - n = eval(instance, car(p1)); - } while (*s); - __end: - delete_tree(instance, n); - delete_tree(instance, cdr(p1)); - delete_object(instance, p1); - delete_tree(instance, p); - if (len > 0) { - p1 = new_string(instance, s1); - free(s1); - } else { - p1 = &alsa_lisp_nil; - } - return p1; -} - -/* - * Syntax: (compare-strings str1 start1 end1 str2 start2 end2 /opt-case-insensitive) - * 'str1' is first compared string - * 'start1' is first char (0..) - * 'end1' is last char (0..) - * 'str2' is second compared string - * 'start2' is first char (0..) - * 'end2' is last char (0..) - * /opt-case-insensitive true - case insensitive match - */ -struct alisp_object * F_compare_strings(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1 = args, * n, * p[7]; - char *s1, *s2; - int start1, end1, start2, end2; - - for (start1 = 0; start1 < 7; start1++) { - p[start1] = eval(instance, car(p1)); - p1 = cdr(n = p1); - delete_object(instance, n); - } - delete_tree(instance, p1); - if (alisp_compare_type(p[0], ALISP_OBJ_STRING)) { - lisp_warn(instance, "compare-strings: first argument must be string\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - if (alisp_compare_type(p[1], ALISP_OBJ_INTEGER)) { - lisp_warn(instance, "compare-strings: second argument must be integer\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - if (alisp_compare_type(p[2], ALISP_OBJ_INTEGER)) { - lisp_warn(instance, "compare-strings: third argument must be integer\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - if (alisp_compare_type(p[3], ALISP_OBJ_STRING)) { - lisp_warn(instance, "compare-strings: fifth argument must be string\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - if (!alisp_compare_type(p[4], ALISP_OBJ_NIL) && - !alisp_compare_type(p[4], ALISP_OBJ_INTEGER)) { - lisp_warn(instance, "compare-strings: fourth argument must be integer\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - if (!alisp_compare_type(p[5], ALISP_OBJ_NIL) && - !alisp_compare_type(p[5], ALISP_OBJ_INTEGER)) { - lisp_warn(instance, "compare-strings: sixth argument must be integer\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - s1 = p[0]->value.s; - start1 = p[1]->value.i; - end1 = p[2]->value.i; - s2 = p[3]->value.s; - start2 = alisp_compare_type(p[4], ALISP_OBJ_NIL) ? 0 : p[4]->value.i; - end2 = alisp_compare_type(p[5], ALISP_OBJ_NIL) ? start2 + (end1 - start1) : p[5]->value.i; - if (start1 < 0 || start2 < 0 || end1 < 0 || end2 < 0 || - start1 >= (int)strlen(s1) || start2 >= (int)strlen(s2) || - (end1 - start1) != (end2 - start2)) { - p1 = &alsa_lisp_nil; - goto __err; - } - if (p[6] != &alsa_lisp_nil) { - while (start1 < end1) { - if (s1[start1] == '\0' || - s2[start2] == '\0' || - tolower(s1[start1]) != tolower(s2[start2])) { - p1 = &alsa_lisp_nil; - goto __err; - } - start1++; - start2++; - } - } else { - while (start1 < end1) { - if (s1[start1] == '\0' || - s2[start2] == '\0' || - s1[start1] != s2[start2]) { - p1 = &alsa_lisp_nil; - goto __err; - } - start1++; - start2++; - } - } - p1 = &alsa_lisp_t; - - __err: - for (start1 = 0; start1 < 7; start1++) - delete_tree(instance, p[start1]); - return p1; -} - -/* - * Syntax: (assoc key alist) - */ -struct alisp_object * F_assoc(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * n; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - do { - if (eq(p1, car(car(p2)))) { - n = car(p2); - delete_tree(instance, p1); - delete_tree(instance, cdr(p2)); - delete_object(instance, p2); - return n; - } - delete_tree(instance, car(p2)); - p2 = cdr(n = p2); - delete_object(instance, n); - } while (p2 != &alsa_lisp_nil); - - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -/* - * Syntax: (rassoc value alist) - */ -struct alisp_object * F_rassoc(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, *p2, * n; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - do { - if (eq(p1, cdr(car(p2)))) { - n = car(p2); - delete_tree(instance, p1); - delete_tree(instance, cdr(p2)); - delete_object(instance, p2); - return n; - } - delete_tree(instance, car(p2)); - p2 = cdr(n = p2); - delete_object(instance, n); - } while (p2 != &alsa_lisp_nil); - - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -/* - * Syntax: (assq key alist) - */ -struct alisp_object * F_assq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * n; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - do { - if (equal(p1, car(car(p2)))) { - n = car(p2); - delete_tree(instance, p1); - delete_tree(instance, cdr(p2)); - delete_object(instance, p2); - return n; - } - delete_tree(instance, car(p2)); - p2 = cdr(n = p2); - delete_object(instance, n); - } while (p2 != &alsa_lisp_nil); - - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -/* - * Syntax: (nth index alist) - */ -struct alisp_object * F_nth(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * n; - long idx; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (!alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; - } - if (!alisp_compare_type(p2, ALISP_OBJ_CONS)) { - delete_object(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; - } - idx = p1->value.i; - delete_object(instance, p1); - while (idx-- > 0) { - delete_tree(instance, car(p2)); - p2 = cdr(n = p2); - delete_object(instance, n); - } - n = car(p2); - delete_tree(instance, cdr(p2)); - delete_object(instance, p2); - return n; -} - -/* - * Syntax: (rassq value alist) - */ -struct alisp_object * F_rassq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * n; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - do { - if (equal(p1, cdr(car(p2)))) { - n = car(p2); - delete_tree(instance, p1); - delete_tree(instance, cdr(p2)); - delete_object(instance, p2); - return n; - } - delete_tree(instance, car(p2)); - p2 = cdr(n = p2); - delete_object(instance, n); - } while (p2 != &alsa_lisp_nil); - - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -static struct alisp_object * F_dump_memory(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = car(args); - - if (p != &alsa_lisp_nil && cdr(args) == &alsa_lisp_nil && - alisp_compare_type(p, ALISP_OBJ_STRING)) { - if (strlen(p->value.s) > 0) { - dump_objects(instance, p->value.s); - delete_tree(instance, args); - return &alsa_lisp_t; - } else - lisp_warn(instance, "expected filename"); - } else - lisp_warn(instance, "wrong number of parameters (expected string)"); - - delete_tree(instance, args); - return &alsa_lisp_nil; -} - -static struct alisp_object * F_stat_memory(struct alisp_instance *instance, struct alisp_object * args) -{ - snd_output_printf(instance->out, "*** Memory stats\n"); - snd_output_printf(instance->out, " used_objs = %li, free_objs = %li, max_objs = %li, obj_size = %i (total bytes = %li, max bytes = %li)\n", - instance->used_objs, - instance->free_objs, - instance->max_objs, - (int)sizeof(struct alisp_object), - (long)((instance->used_objs + instance->free_objs) * sizeof(struct alisp_object)), - (long)(instance->max_objs * sizeof(struct alisp_object))); - delete_tree(instance, args); - return &alsa_lisp_nil; -} - -static struct alisp_object * F_check_memory(struct alisp_instance *instance, struct alisp_object * args) -{ - delete_tree(instance, args); - if (instance->used_objs > 0) { - fprintf(stderr, "!!!alsa lisp - check memory failed!!!\n"); - F_stat_memory(instance, &alsa_lisp_nil); - exit(EXIT_FAILURE); - } - return &alsa_lisp_t; -} - -static struct alisp_object * F_dump_objects(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = car(args); - - if (p != &alsa_lisp_nil && cdr(args) == &alsa_lisp_nil && - alisp_compare_type(p, ALISP_OBJ_STRING)) { - if (strlen(p->value.s) > 0) { - dump_obj_lists(instance, p->value.s); - delete_tree(instance, args); - return &alsa_lisp_t; - } else - lisp_warn(instance, "expected filename"); - } else - lisp_warn(instance, "wrong number of parameters (expected string)"); - - delete_tree(instance, args); - return &alsa_lisp_nil; -} - -struct intrinsic { - const char *name; - struct alisp_object * (*func)(struct alisp_instance *instance, struct alisp_object * args); -}; - -static const struct intrinsic intrinsics[] = { - { "!=", F_numneq }, - { "%", F_mod }, - { "&check-memory", F_check_memory }, - { "&dump-memory", F_dump_memory }, - { "&dump-objects", F_dump_objects }, - { "&stat-memory", F_stat_memory }, - { "*", F_mul }, - { "+", F_add }, - { "-", F_sub }, - { "/", F_div }, - { "<", F_lt }, - { "<=", F_le }, - { "=", F_numeq }, - { ">", F_gt }, - { ">=", F_ge }, - { "and", F_and }, - { "assoc", F_assoc }, - { "assq", F_assq }, - { "atom", F_atom }, - { "car", F_car }, - { "cdr", F_cdr }, - { "compare-strings", F_compare_strings }, - { "concat", F_concat }, - { "cond", F_cond }, - { "cons", F_cons }, - { "defun", F_defun }, - { "eq", F_eq }, - { "equal", F_equal }, - { "eval", F_eval }, - { "exfun", F_exfun }, - { "format", F_format }, - { "funcall", F_funcall }, - { "garbage-collect", F_gc }, - { "gc", F_gc }, - { "if", F_if }, - { "include", F_include }, - { "list", F_list }, - { "not", F_not }, - { "nth", F_nth }, - { "null", F_not }, - { "or", F_or }, - { "path", F_path }, - { "princ", F_princ }, - { "prog1", F_prog1 }, - { "prog2", F_prog2 }, - { "progn", F_progn }, - { "quote", F_quote }, - { "rassoc", F_rassoc }, - { "rassq", F_rassq }, - { "set", F_set }, - { "setf", F_setq }, - { "setq", F_setq }, - { "string-equal", F_equal }, - { "string-to-float", F_string_to_float }, - { "string-to-integer", F_string_to_integer }, - { "string-to-number", F_string_to_float }, - { "string=", F_equal }, - { "unless", F_unless }, - { "unset", F_unset }, - { "unsetf", F_unsetq }, - { "unsetq", F_unsetq }, - { "when", F_when }, - { "while", F_while }, -}; - -#include "alisp_snd.c" - -static int compar(const void *p1, const void *p2) -{ - return strcmp(((struct intrinsic *)p1)->name, - ((struct intrinsic *)p2)->name); -} - -static inline struct alisp_object * eval_cons1(struct alisp_instance *instance, struct alisp_object * p1, struct alisp_object * p2) -{ - struct alisp_object * p3; - struct intrinsic key, *item; - - key.name = p1->value.s; - - if ((item = bsearch(&key, intrinsics, - sizeof intrinsics / sizeof intrinsics[0], - sizeof intrinsics[0], compar)) != NULL) { - delete_object(instance, p1); - return item->func(instance, p2); - } - - if ((item = bsearch(&key, snd_intrinsics, - sizeof snd_intrinsics / sizeof snd_intrinsics[0], - sizeof snd_intrinsics[0], compar)) != NULL) { - delete_object(instance, p1); - return item->func(instance, p2); - } - - if ((p3 = get_object(instance, p1)) != &alsa_lisp_nil) { - delete_object(instance, p1); - return eval_func(instance, p3, p2); - } else { - lisp_warn(instance, "function `%s' is undefined", p1->value.s); - delete_object(instance, p1); - delete_tree(instance, p2); - } - - return &alsa_lisp_nil; -} - -/* - * Syntax: (funcall function args...) - */ -static struct alisp_object * F_funcall(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = eval(instance, car(args)), * p1; - - if (!alisp_compare_type(p, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(p, ALISP_OBJ_STRING)) { - lisp_warn(instance, "expected an function name"); - delete_tree(instance, p); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - return &alsa_lisp_nil; - } - p1 = cdr(args); - delete_object(instance, args); - return eval_cons1(instance, p, p1); -} - -static inline struct alisp_object * eval_cons(struct alisp_instance *instance, struct alisp_object * p) -{ - struct alisp_object * p1 = car(p), * p2; - - if (p1 != &alsa_lisp_nil && alisp_compare_type(p1, ALISP_OBJ_IDENTIFIER)) { - if (!strcmp(p1->value.s, "lambda")) - return p; - - p2 = cdr(p); - delete_object(instance, p); - return eval_cons1(instance, p1, p2); - } else { - delete_tree(instance, p); - } - - return &alsa_lisp_nil; -} - -static struct alisp_object * eval(struct alisp_instance *instance, struct alisp_object * p) -{ - switch (alisp_get_type(p)) { - case ALISP_OBJ_IDENTIFIER: { - struct alisp_object *r = incref_tree(instance, get_object(instance, p)); - delete_object(instance, p); - return r; - } - case ALISP_OBJ_INTEGER: - case ALISP_OBJ_FLOAT: - case ALISP_OBJ_STRING: - case ALISP_OBJ_POINTER: - return p; - case ALISP_OBJ_CONS: - return eval_cons(instance, p); - default: - break; - } - - return p; -} - -static struct alisp_object * F_eval(struct alisp_instance *instance, struct alisp_object * args) -{ - return eval(instance, eval(instance, car(args))); -} - -/* - * main routine - */ - -static int alisp_include_file(struct alisp_instance *instance, const char *filename) -{ - snd_input_t *old_in; - struct alisp_object *p, *p1; - char *name; - int retval = 0, err; - - err = snd_user_file(filename, &name); - if (err < 0) - return err; - old_in = instance->in; - err = snd_input_stdio_open(&instance->in, name, "r"); - if (err < 0) { - retval = err; - goto _err; - } - if (instance->verbose) - lisp_verbose(instance, "** include filename '%s'", name); - - for (;;) { - if ((p = parse_object(instance, 0)) == NULL) - break; - if (instance->verbose) { - lisp_verbose(instance, "** code"); - princ_object(instance->vout, p); - snd_output_putc(instance->vout, '\n'); - } - p1 = eval(instance, p); - if (p1 == NULL) { - retval = -ENOMEM; - break; - } - if (instance->verbose) { - lisp_verbose(instance, "** result"); - princ_object(instance->vout, p1); - snd_output_putc(instance->vout, '\n'); - } - delete_tree(instance, p1); - if (instance->debug) { - lisp_debug(instance, "** objects after operation"); - print_obj_lists(instance, instance->dout); - } - } - - snd_input_close(instance->in); - _err: - free(name); - instance->in = old_in; - return retval; -} - -int alsa_lisp(struct alisp_cfg *cfg, struct alisp_instance **_instance) -{ - struct alisp_instance *instance; - struct alisp_object *p, *p1; - int i, j, retval = 0; - - instance = (struct alisp_instance *)calloc(1, sizeof(struct alisp_instance)); - if (instance == NULL) { - nomem(); - return -ENOMEM; - } - instance->verbose = cfg->verbose && cfg->vout; - instance->warning = cfg->warning && cfg->wout; - instance->debug = cfg->debug && cfg->dout; - instance->in = cfg->in; - instance->out = cfg->out; - instance->vout = cfg->vout; - instance->eout = cfg->eout; - instance->wout = cfg->wout; - instance->dout = cfg->dout; - INIT_LIST_HEAD(&instance->free_objs_list); - for (i = 0; i < ALISP_OBJ_PAIR_HASH_SIZE; i++) { - for (j = 0; j <= ALISP_OBJ_LAST_SEARCH; j++) - INIT_LIST_HEAD(&instance->used_objs_list[i][j]); - INIT_LIST_HEAD(&instance->setobjs_list[i]); - } - - init_lex(instance); - - for (;;) { - if ((p = parse_object(instance, 0)) == NULL) - break; - if (instance->verbose) { - lisp_verbose(instance, "** code"); - princ_object(instance->vout, p); - snd_output_putc(instance->vout, '\n'); - } - p1 = eval(instance, p); - if (p1 == NULL) { - retval = -ENOMEM; - break; - } - if (instance->verbose) { - lisp_verbose(instance, "** result"); - princ_object(instance->vout, p1); - snd_output_putc(instance->vout, '\n'); - } - delete_tree(instance, p1); - if (instance->debug) { - lisp_debug(instance, "** objects after operation"); - print_obj_lists(instance, instance->dout); - } - } - - if (_instance) - *_instance = instance; - else - alsa_lisp_free(instance); - - return retval; -} - -void alsa_lisp_free(struct alisp_instance *instance) -{ - if (instance == NULL) - return; - done_lex(instance); - free_objects(instance); - free(instance); -} - -struct alisp_cfg *alsa_lisp_default_cfg(snd_input_t *input) -{ - snd_output_t *output, *eoutput; - struct alisp_cfg *cfg; - int err; - - err = snd_output_stdio_attach(&output, stdout, 0); - if (err < 0) - return NULL; - err = snd_output_stdio_attach(&eoutput, stderr, 0); - if (err < 0) { - snd_output_close(output); - return NULL; - } - cfg = calloc(1, sizeof(struct alisp_cfg)); - if (cfg == NULL) { - snd_output_close(eoutput); - snd_output_close(output); - return NULL; - } - cfg->out = output; - cfg->wout = eoutput; - cfg->eout = eoutput; - cfg->dout = eoutput; - cfg->in = input; - return cfg; -} - -void alsa_lisp_default_cfg_free(struct alisp_cfg *cfg) -{ - snd_input_close(cfg->in); - snd_output_close(cfg->out); - snd_output_close(cfg->dout); - free(cfg); -} - -int alsa_lisp_function(struct alisp_instance *instance, struct alisp_seq_iterator **result, - const char *id, const char *args, ...) -{ - int err = 0; - struct alisp_object *aargs = NULL, *obj, *res; - - if (args && *args != 'n') { - va_list ap; - struct alisp_object *p; - p = NULL; - va_start(ap, args); - while (*args) { - if (*args++ != '%') { - err = -EINVAL; - break; - } - if (*args == '\0') { - err = -EINVAL; - break; - } - obj = NULL; - err = 0; - switch (*args++) { - case 's': - obj = new_string(instance, va_arg(ap, char *)); - break; - case 'i': - obj = new_integer(instance, va_arg(ap, int)); - break; - case 'l': - obj = new_integer(instance, va_arg(ap, long)); - break; - case 'f': - case 'd': - obj = new_integer(instance, va_arg(ap, double)); - break; - case 'p': { - char _ptrid[24]; - char *ptrid = _ptrid; - while (*args && *args != '%') - *ptrid++ = *args++; - *ptrid = 0; - if (ptrid == _ptrid) { - err = -EINVAL; - break; - } - obj = new_cons_pointer(instance, _ptrid, va_arg(ap, void *)); - obj = quote_object(instance, obj); - break; - } - default: - err = -EINVAL; - break; - } - if (err < 0) - goto __args_end; - if (obj == NULL) { - err = -ENOMEM; - goto __args_end; - } - if (p == NULL) { - p = aargs = new_object(instance, ALISP_OBJ_CONS); - } else { - p->value.c.cdr = new_object(instance, ALISP_OBJ_CONS); - p = p->value.c.cdr; - } - if (p == NULL) { - err = -ENOMEM; - goto __args_end; - } - p->value.c.car = obj; - } - __args_end: - va_end(ap); - if (err < 0) - return err; -#if 0 - snd_output_printf(instance->wout, ">>>"); - princ_object(instance->wout, aargs); - snd_output_printf(instance->wout, "<<<\n"); -#endif - } - - err = -ENOENT; - if (aargs == NULL) - aargs = &alsa_lisp_nil; - if ((obj = get_object1(instance, id)) != &alsa_lisp_nil) { - res = eval_func(instance, obj, aargs); - err = 0; - } else { - struct intrinsic key, *item; - key.name = id; - if ((item = bsearch(&key, intrinsics, - sizeof intrinsics / sizeof intrinsics[0], - sizeof intrinsics[0], compar)) != NULL) { - res = item->func(instance, aargs); - err = 0; - } else if ((item = bsearch(&key, snd_intrinsics, - sizeof snd_intrinsics / sizeof snd_intrinsics[0], - sizeof snd_intrinsics[0], compar)) != NULL) { - res = item->func(instance, aargs); - err = 0; - } else { - res = &alsa_lisp_nil; - } - } - if (res == NULL) - err = -ENOMEM; - if (err == 0 && result) { - *result = res; - } else { - delete_tree(instance, res); - } - - return 0; -} - -void alsa_lisp_result_free(struct alisp_instance *instance, - struct alisp_seq_iterator *result) -{ - delete_tree(instance, result); -} - -int alsa_lisp_seq_first(struct alisp_instance *instance, const char *id, - struct alisp_seq_iterator **seq) -{ - struct alisp_object * p1; - - p1 = get_object1(instance, id); - if (p1 == NULL) - return -ENOMEM; - *seq = p1; - return 0; -} - -int alsa_lisp_seq_next(struct alisp_seq_iterator **seq) -{ - struct alisp_object * p1 = *seq; - - p1 = cdr(p1); - if (p1 == &alsa_lisp_nil) - return -ENOENT; - *seq = p1; - return 0; -} - -int alsa_lisp_seq_count(struct alisp_seq_iterator *seq) -{ - int count = 0; - - while (seq != &alsa_lisp_nil) { - count++; - seq = cdr(seq); - } - return count; -} - -int alsa_lisp_seq_integer(struct alisp_seq_iterator *seq, long *val) -{ - if (alisp_compare_type(seq, ALISP_OBJ_CONS)) - seq = seq->value.c.cdr; - if (alisp_compare_type(seq, ALISP_OBJ_INTEGER)) - *val = seq->value.i; - else - return -EINVAL; - return 0; -} - -int alsa_lisp_seq_pointer(struct alisp_seq_iterator *seq, const char *ptr_id, void **ptr) -{ - struct alisp_object * p2; - - if (alisp_compare_type(seq, ALISP_OBJ_CONS) && - alisp_compare_type(seq->value.c.car, ALISP_OBJ_CONS)) - seq = seq->value.c.car; - if (alisp_compare_type(seq, ALISP_OBJ_CONS)) { - p2 = seq->value.c.car; - if (!alisp_compare_type(p2, ALISP_OBJ_STRING)) - return -EINVAL; - if (strcmp(p2->value.s, ptr_id)) - return -EINVAL; - p2 = seq->value.c.cdr; - if (!alisp_compare_type(p2, ALISP_OBJ_POINTER)) - return -EINVAL; - *ptr = (void *)seq->value.ptr; - } else - return -EINVAL; - return 0; -} diff --git a/src/alisp/alisp_local.h b/src/alisp/alisp_local.h deleted file mode 100644 index af638843..00000000 --- a/src/alisp/alisp_local.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * ALSA lisp implementation - * Copyright (c) 2003 by Jaroslav Kysela - * - * Based on work of Sandro Sigala (slisp-1.2) - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "list.h" - -enum alisp_tokens { - ALISP_IDENTIFIER, - ALISP_INTEGER, - ALISP_FLOAT, - ALISP_FLOATE, - ALISP_STRING -}; - -enum alisp_objects { - ALISP_OBJ_INTEGER, - ALISP_OBJ_FLOAT, - ALISP_OBJ_IDENTIFIER, - ALISP_OBJ_STRING, - ALISP_OBJ_POINTER, - ALISP_OBJ_CONS, - ALISP_OBJ_LAST_SEARCH = ALISP_OBJ_CONS, - ALISP_OBJ_NIL, - ALISP_OBJ_T, -}; - -struct alisp_object; - -#define ALISP_TYPE_MASK 0xf0000000 -#define ALISP_TYPE_SHIFT 28 -#define ALISP_REFS_MASK 0x0fffffff -#define ALISP_REFS_SHIFT 0 -#define ALISP_MAX_REFS (ALISP_REFS_MASK>>ALISP_REFS_SHIFT) -#define ALISP_MAX_REFS_LIMIT ((ALISP_MAX_REFS + 1) / 2) - -struct alisp_object { - struct list_head list; - unsigned int type_refs; /* type and count of references */ - union { - char *s; - long i; - double f; - const void *ptr; - struct { - struct alisp_object *car; - struct alisp_object *cdr; - } c; - } value; -}; - -static inline enum alisp_objects alisp_get_type(struct alisp_object *p) -{ - return (p->type_refs >> ALISP_TYPE_SHIFT); -} - -static inline void alisp_set_type(struct alisp_object *p, enum alisp_objects type) -{ - p->type_refs &= ~ALISP_TYPE_MASK; - p->type_refs |= (unsigned int)type << ALISP_TYPE_SHIFT; -} - -static inline int alisp_compare_type(struct alisp_object *p, enum alisp_objects type) -{ - return ((unsigned int)type << ALISP_TYPE_SHIFT) == - (p->type_refs & ALISP_TYPE_MASK); -} - -static inline void alisp_set_refs(struct alisp_object *p, unsigned int refs) -{ - p->type_refs &= ~ALISP_REFS_MASK; - p->type_refs |= refs & ALISP_REFS_MASK; -} - -static inline unsigned int alisp_get_refs(struct alisp_object *p) -{ - return p->type_refs & ALISP_REFS_MASK; -} - -static inline unsigned int alisp_inc_refs(struct alisp_object *p) -{ - unsigned r = alisp_get_refs(p) + 1; - alisp_set_refs(p, r); - return r; -} - -static inline unsigned int alisp_dec_refs(struct alisp_object *p) -{ - unsigned r = alisp_get_refs(p) - 1; - alisp_set_refs(p, r); - return r; -} - -struct alisp_object_pair { - struct list_head list; - const char *name; - struct alisp_object *value; -}; - -#define ALISP_LEX_BUF_MAX 16 -#define ALISP_OBJ_PAIR_HASH_SHIFT 4 -#define ALISP_OBJ_PAIR_HASH_SIZE (1< - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "../control/control_local.h" - -struct acall_table { - const char *name; - struct alisp_object * (*func) (struct alisp_instance *instance, struct acall_table * item, struct alisp_object * args); - void * xfunc; - const char *prefix; -}; - -/* - * helper functions - */ - -static inline int get_integer(struct alisp_object * obj) -{ - if (alisp_compare_type(obj, ALISP_OBJ_INTEGER)) - return obj->value.i; - return 0; -} - -static inline const void *get_pointer(struct alisp_object * obj) -{ - if (alisp_compare_type(obj, ALISP_OBJ_POINTER)) - return obj->value.ptr; - return NULL; -} - -static const char *get_string(struct alisp_object * obj, const char * deflt) -{ - if (obj == &alsa_lisp_t) - return "true"; - if (alisp_compare_type(obj, ALISP_OBJ_STRING) || - alisp_compare_type(obj, ALISP_OBJ_IDENTIFIER)) - return obj->value.s; - return deflt; -} - -struct flags { - const char *key; - unsigned int mask; -}; - -static unsigned int get_flags(struct alisp_instance * instance, - struct alisp_object * obj, - const struct flags * flags, - unsigned int deflt) -{ - const char *key; - int invert; - unsigned int result; - const struct flags *ptr; - struct alisp_object *n; - - if (obj == &alsa_lisp_nil) - return deflt; - result = deflt; - do { - key = get_string(obj, NULL); - if (key) { - invert = key[0] == '!'; - key += invert; - ptr = flags; - while (ptr->key) { - if (!strcmp(ptr->key, key)) { - if (invert) - result &= ~ptr->mask; - else - result |= ptr->mask; - break; - } - ptr++; - } - } - delete_tree(instance, car(obj)); - obj = cdr(n = obj); - delete_object(instance, n); - } while (obj != &alsa_lisp_nil); - return result; -} - -static const void *get_ptr(struct alisp_instance * instance, - struct alisp_object * obj, - const char *_ptr_id) -{ - const char *ptr_id; - const void *ptr; - - ptr_id = get_string(car(obj), NULL); - if (ptr_id == NULL) { - delete_tree(instance, obj); - return NULL; - } - if (strcmp(ptr_id, _ptr_id)) { - delete_tree(instance, obj); - return NULL; - } - ptr = get_pointer(cdr(obj)); - delete_tree(instance, obj); - return ptr; -} - -static struct alisp_object * new_lexpr(struct alisp_instance * instance, int err) -{ - struct alisp_object * lexpr; - - lexpr = new_object(instance, ALISP_OBJ_CONS); - if (lexpr == NULL) - return NULL; - lexpr->value.c.car = new_integer(instance, err); - if (lexpr->value.c.car == NULL) { - delete_object(instance, lexpr); - return NULL; - } - lexpr->value.c.cdr = new_object(instance, ALISP_OBJ_CONS); - if (lexpr->value.c.cdr == NULL) { - delete_object(instance, lexpr->value.c.car); - delete_object(instance, lexpr); - return NULL; - } - return lexpr; -} - -static struct alisp_object * add_cons(struct alisp_instance * instance, - struct alisp_object *lexpr, - int cdr, const char *id, - struct alisp_object *obj) -{ - struct alisp_object * p1, * p2; - - if (lexpr == NULL || obj == NULL) { - delete_tree(instance, obj); - return NULL; - } - if (cdr) { - p1 = lexpr->value.c.cdr = new_object(instance, ALISP_OBJ_CONS); - } else { - p1 = lexpr->value.c.car = new_object(instance, ALISP_OBJ_CONS); - } - lexpr = p1; - if (p1 == NULL) { - delete_tree(instance, obj); - return NULL; - } - p1->value.c.car = new_object(instance, ALISP_OBJ_CONS); - if ((p2 = p1->value.c.car) == NULL) - goto __err; - p2->value.c.car = new_string(instance, id); - if (p2->value.c.car == NULL) { - __err: - if (cdr) - lexpr->value.c.cdr = NULL; - else - lexpr->value.c.car = NULL; - delete_tree(instance, p1); - delete_tree(instance, obj); - return NULL; - } - p2->value.c.cdr = obj; - return lexpr; -} - -static struct alisp_object * add_cons2(struct alisp_instance * instance, - struct alisp_object *lexpr, - int cdr, struct alisp_object *obj) -{ - struct alisp_object * p1; - - if (lexpr == NULL || obj == NULL) { - delete_tree(instance, obj); - return NULL; - } - if (cdr) { - p1 = lexpr->value.c.cdr = new_object(instance, ALISP_OBJ_CONS); - } else { - p1 = lexpr->value.c.car = new_object(instance, ALISP_OBJ_CONS); - } - lexpr = p1; - if (p1 == NULL) { - delete_tree(instance, obj); - return NULL; - } - p1->value.c.car = obj; - return lexpr; -} - -static struct alisp_object * new_result1(struct alisp_instance * instance, - int err, const char *ptr_id, void *ptr) -{ - struct alisp_object * lexpr, * p1; - - if (err < 0) - ptr = NULL; - lexpr = new_object(instance, ALISP_OBJ_CONS); - if (lexpr == NULL) - return NULL; - lexpr->value.c.car = new_integer(instance, err); - if (lexpr->value.c.car == NULL) { - delete_object(instance, lexpr); - return NULL; - } - p1 = add_cons(instance, lexpr, 1, ptr_id, new_pointer(instance, ptr)); - if (p1 == NULL) { - delete_object(instance, lexpr); - return NULL; - } - return lexpr; -} - -static struct alisp_object * new_result2(struct alisp_instance * instance, - int err, int val) -{ - struct alisp_object * lexpr, * p1; - - if (err < 0) - val = 0; - lexpr = new_lexpr(instance, err); - if (lexpr == NULL) - return NULL; - p1 = lexpr->value.c.cdr; - p1->value.c.car = new_integer(instance, val); - if (p1->value.c.car == NULL) { - delete_object(instance, lexpr); - return NULL; - } - return lexpr; -} - -static struct alisp_object * new_result3(struct alisp_instance * instance, - int err, const char *str) -{ - struct alisp_object * lexpr, * p1; - - if (err < 0) - str = ""; - lexpr = new_lexpr(instance, err); - if (lexpr == NULL) - return NULL; - p1 = lexpr->value.c.cdr; - p1->value.c.car = new_string(instance, str); - if (p1->value.c.car == NULL) { - delete_object(instance, lexpr); - return NULL; - } - return lexpr; -} - -/* - * macros - */ - -/* - * HCTL functions - */ - -typedef int (*snd_int_pp_strp_int_t)(void **rctl, const char *name, int mode); -typedef int (*snd_int_pp_p_t)(void **rctl, void *handle); -typedef int (*snd_int_p_t)(void *rctl); -typedef char * (*snd_str_p_t)(void *rctl); -typedef int (*snd_int_intp_t)(int *val); -typedef int (*snd_int_str_t)(const char *str); -typedef int (*snd_int_int_strp_t)(int val, char **str); -typedef void *(*snd_p_p_t)(void *handle); - -static struct alisp_object * FA_int_pp_strp_int(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - const char *name; - int err, mode; - void *handle; - struct alisp_object *p1, *p2; - static const struct flags flags[] = { - { "nonblock", SND_CTL_NONBLOCK }, - { "async", SND_CTL_ASYNC }, - { "readonly", SND_CTL_READONLY }, - { NULL, 0 } - }; - - name = get_string(p1 = eval(instance, car(args)), NULL); - if (name == NULL) - return &alsa_lisp_nil; - mode = get_flags(instance, p2 = eval(instance, car(cdr(args))), flags, 0); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - delete_tree(instance, p2); - err = ((snd_int_pp_strp_int_t)item->xfunc)(&handle, name, mode); - delete_tree(instance, p1); - return new_result1(instance, err, item->prefix, handle); -} - -static struct alisp_object * FA_int_pp_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - int err; - void *handle; - const char *prefix1; - struct alisp_object *p1; - - if (item->xfunc == &snd_hctl_open_ctl) - prefix1 = "ctl"; - else { - delete_tree(instance, args); - return &alsa_lisp_nil; - } - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (void *)get_ptr(instance, p1, prefix1); - if (handle == NULL) - return &alsa_lisp_nil; - err = ((snd_int_pp_p_t)item->xfunc)(&handle, handle); - return new_result1(instance, err, item->prefix, handle); -} - -static struct alisp_object * FA_p_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - void *handle; - const char *prefix1; - struct alisp_object * p1; - - if (item->xfunc == &snd_hctl_first_elem || - item->xfunc == &snd_hctl_last_elem || - item->xfunc == &snd_hctl_elem_next || - item->xfunc == &snd_hctl_elem_prev) - prefix1 = "hctl_elem"; - else if (item->xfunc == &snd_hctl_ctl) - prefix1 = "ctl"; - else { - delete_tree(instance, args); - return &alsa_lisp_nil; - } - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (void *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - handle = ((snd_p_p_t)item->xfunc)(handle); - return new_cons_pointer(instance, prefix1, handle); -} - -static struct alisp_object * FA_int_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - void *handle; - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (void *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - return new_integer(instance, ((snd_int_p_t)item->xfunc)(handle)); -} - -static struct alisp_object * FA_str_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - void *handle; - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (void *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - return new_string(instance, ((snd_str_p_t)item->xfunc)(handle)); -} - -static struct alisp_object * FA_int_intp(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - int val, err; - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (!alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - val = p1->value.i; - delete_tree(instance, p1); - err = ((snd_int_intp_t)item->xfunc)(&val); - return new_result2(instance, err, val); -} - -static struct alisp_object * FA_int_str(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - int err; - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (!alisp_compare_type(p1, ALISP_OBJ_STRING) && - !alisp_compare_type(p1, ALISP_OBJ_IDENTIFIER)) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - err = ((snd_int_str_t)item->xfunc)(p1->value.s); - delete_tree(instance, p1); - return new_integer(instance, err); -} - -static struct alisp_object * FA_int_int_strp(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - int err; - char *str; - long val; - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (!alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - val = p1->value.i; - delete_tree(instance, p1); - err = ((snd_int_int_strp_t)item->xfunc)(val, &str); - return new_result3(instance, err, str); -} - -static struct alisp_object * FA_card_info(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_ctl_t *handle; - struct alisp_object * lexpr, * p1; - snd_ctl_card_info_t info = {0}; - int err; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (snd_ctl_t *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - err = snd_ctl_card_info(handle, &info); - lexpr = new_lexpr(instance, err); - if (err < 0) - return lexpr; - p1 = add_cons(instance, lexpr->value.c.cdr, 0, "id", new_string(instance, snd_ctl_card_info_get_id(&info))); - p1 = add_cons(instance, p1, 1, "driver", new_string(instance, snd_ctl_card_info_get_driver(&info))); - p1 = add_cons(instance, p1, 1, "name", new_string(instance, snd_ctl_card_info_get_name(&info))); - p1 = add_cons(instance, p1, 1, "longname", new_string(instance, snd_ctl_card_info_get_longname(&info))); - p1 = add_cons(instance, p1, 1, "mixername", new_string(instance, snd_ctl_card_info_get_mixername(&info))); - p1 = add_cons(instance, p1, 1, "components", new_string(instance, snd_ctl_card_info_get_components(&info))); - if (p1 == NULL) { - delete_tree(instance, lexpr); - return NULL; - } - return lexpr; -} - -static struct alisp_object * create_ctl_elem_id(struct alisp_instance * instance, snd_ctl_elem_id_t * id, struct alisp_object * cons) -{ - cons = add_cons(instance, cons, 0, "numid", new_integer(instance, snd_ctl_elem_id_get_numid(id))); - cons = add_cons(instance, cons, 1, "iface", new_string(instance, snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(id)))); - cons = add_cons(instance, cons, 1, "dev", new_integer(instance, snd_ctl_elem_id_get_device(id))); - cons = add_cons(instance, cons, 1, "subdev", new_integer(instance, snd_ctl_elem_id_get_subdevice(id))); - cons = add_cons(instance, cons, 1, "name", new_string(instance, snd_ctl_elem_id_get_name(id))); - cons = add_cons(instance, cons, 1, "index", new_integer(instance, snd_ctl_elem_id_get_index(id))); - return cons; -} - -static int parse_ctl_elem_id(struct alisp_instance * instance, - struct alisp_object * cons, - snd_ctl_elem_id_t * id) -{ - struct alisp_object *p1; - const char *xid; - - if (cons == NULL) - return -ENOMEM; - snd_ctl_elem_id_clear(id); - id->numid = 0; - do { - p1 = car(cons); - if (alisp_compare_type(p1, ALISP_OBJ_CONS)) { - xid = get_string(p1->value.c.car, NULL); - if (xid == NULL) { - /* noop */ - } else if (!strcmp(xid, "numid")) { - snd_ctl_elem_id_set_numid(id, get_integer(p1->value.c.cdr)); - } else if (!strcmp(xid, "iface")) { - snd_ctl_elem_id_set_interface(id, snd_config_get_ctl_iface_ascii(get_string(p1->value.c.cdr, "0"))); - } else if (!strcmp(xid, "dev")) { - snd_ctl_elem_id_set_device(id, get_integer(p1->value.c.cdr)); - } else if (!strcmp(xid, "subdev")) { - snd_ctl_elem_id_set_subdevice(id, get_integer(p1->value.c.cdr)); - } else if (!strcmp(xid, "name")) { - snd_ctl_elem_id_set_name(id, get_string(p1->value.c.cdr, "?")); - } else if (!strcmp(xid, "index")) { - snd_ctl_elem_id_set_index(id, get_integer(p1->value.c.cdr)); - } - } - delete_tree(instance, p1); - cons = cdr(p1 = cons); - delete_object(instance, p1); - } while (cons != &alsa_lisp_nil); - return 0; -} - -static struct alisp_object * FA_hctl_find_elem(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_hctl_t *handle; - snd_ctl_elem_id_t id = {0}; - struct alisp_object *p1; - - handle = (snd_hctl_t *)get_ptr(instance, car(args), item->prefix); - if (handle == NULL) { - delete_tree(instance, cdr(args)); - delete_object(instance, args); - return &alsa_lisp_nil; - } - p1 = car(cdr(args)); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - if (parse_ctl_elem_id(instance, eval(instance, p1), &id) < 0) - return &alsa_lisp_nil; - return new_cons_pointer(instance, "hctl_elem", snd_hctl_find_elem(handle, &id)); -} - -static struct alisp_object * FA_hctl_elem_info(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_hctl_elem_t *handle; - struct alisp_object * lexpr, * p1, * p2; - snd_ctl_elem_info_t info = {0}; - snd_ctl_elem_id_t id = {0}; - snd_ctl_elem_type_t type; - int err; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (snd_hctl_elem_t *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - err = snd_hctl_elem_info(handle, &info); - lexpr = new_lexpr(instance, err); - if (err < 0) - return lexpr; - type = snd_ctl_elem_info_get_type(&info); - p1 = add_cons(instance, lexpr->value.c.cdr, 0, "id", p2 = new_object(instance, ALISP_OBJ_CONS)); - snd_ctl_elem_info_get_id(&info, &id); - if (create_ctl_elem_id(instance, &id, p2) == NULL) { - delete_tree(instance, lexpr); - return NULL; - } - p1 = add_cons(instance, p1, 1, "type", new_string(instance, snd_ctl_elem_type_name(type))); - p1 = add_cons(instance, p1, 1, "readable", new_integer(instance, snd_ctl_elem_info_is_readable(&info))); - p1 = add_cons(instance, p1, 1, "writable", new_integer(instance, snd_ctl_elem_info_is_writable(&info))); - p1 = add_cons(instance, p1, 1, "volatile", new_integer(instance, snd_ctl_elem_info_is_volatile(&info))); - p1 = add_cons(instance, p1, 1, "inactive", new_integer(instance, snd_ctl_elem_info_is_inactive(&info))); - p1 = add_cons(instance, p1, 1, "locked", new_integer(instance, snd_ctl_elem_info_is_locked(&info))); - p1 = add_cons(instance, p1, 1, "isowner", new_integer(instance, snd_ctl_elem_info_is_owner(&info))); - p1 = add_cons(instance, p1, 1, "owner", new_integer(instance, snd_ctl_elem_info_get_owner(&info))); - p1 = add_cons(instance, p1, 1, "count", new_integer(instance, snd_ctl_elem_info_get_count(&info))); - err = INTERNAL(snd_ctl_elem_info_get_dimensions)(&info); - if (err > 0) { - int idx; - p1 = add_cons(instance, p1, 1, "dimensions", p2 = new_object(instance, ALISP_OBJ_CONS)); - for (idx = 0; idx < err; idx++) - p2 = add_cons2(instance, p2, idx > 0, new_integer(instance, INTERNAL(snd_ctl_elem_info_get_dimension)(&info, idx))); - } - switch (type) { - case SND_CTL_ELEM_TYPE_ENUMERATED: { - unsigned int items, item; - items = snd_ctl_elem_info_get_items(&info); - p1 = add_cons(instance, p1, 1, "items", p2 = new_object(instance, ALISP_OBJ_CONS)); - for (item = 0; item < items; item++) { - snd_ctl_elem_info_set_item(&info, item); - err = snd_hctl_elem_info(handle, &info); - if (err < 0) { - p2 = add_cons2(instance, p2, item, &alsa_lisp_nil); - } else { - p2 = add_cons2(instance, p2, item, new_string(instance, snd_ctl_elem_info_get_item_name(&info))); - } - } - break; - } - case SND_CTL_ELEM_TYPE_INTEGER: - p1 = add_cons(instance, p1, 1, "min", new_integer(instance, snd_ctl_elem_info_get_min(&info))); - p1 = add_cons(instance, p1, 1, "max", new_integer(instance, snd_ctl_elem_info_get_max(&info))); - p1 = add_cons(instance, p1, 1, "step", new_integer(instance, snd_ctl_elem_info_get_step(&info))); - break; - case SND_CTL_ELEM_TYPE_INTEGER64: - p1 = add_cons(instance, p1, 1, "min64", new_float(instance, snd_ctl_elem_info_get_min64(&info))); - p1 = add_cons(instance, p1, 1, "max64", new_float(instance, snd_ctl_elem_info_get_max64(&info))); - p1 = add_cons(instance, p1, 1, "step64", new_float(instance, snd_ctl_elem_info_get_step64(&info))); - break; - default: - break; - } - if (p1 == NULL) { - delete_tree(instance, lexpr); - return NULL; - } - return lexpr; -} - -static struct alisp_object * FA_hctl_elem_read(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_hctl_elem_t *handle; - struct alisp_object * lexpr, * p1 = NULL, * obj; - snd_ctl_elem_info_t info = {0}; - snd_ctl_elem_value_t value = {0}; - snd_ctl_elem_type_t type; - unsigned int idx, count; - int err; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (snd_hctl_elem_t *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - err = snd_hctl_elem_info(handle, &info); - if (err >= 0) - err = snd_hctl_elem_read(handle, &value); - lexpr = new_lexpr(instance, err); - if (err < 0) - return lexpr; - type = snd_ctl_elem_info_get_type(&info); - count = snd_ctl_elem_info_get_count(&info); - if (type == SND_CTL_ELEM_TYPE_IEC958) { - count = sizeof(snd_aes_iec958_t); - type = SND_CTL_ELEM_TYPE_BYTES; - } - for (idx = 0; idx < count; idx++) { - switch (type) { - case SND_CTL_ELEM_TYPE_BOOLEAN: - obj = new_integer(instance, snd_ctl_elem_value_get_boolean(&value, idx)); - break; - case SND_CTL_ELEM_TYPE_INTEGER: - obj = new_integer(instance, snd_ctl_elem_value_get_integer(&value, idx)); - break; - case SND_CTL_ELEM_TYPE_INTEGER64: - obj = new_integer(instance, snd_ctl_elem_value_get_integer64(&value, idx)); - break; - case SND_CTL_ELEM_TYPE_ENUMERATED: - obj = new_integer(instance, snd_ctl_elem_value_get_enumerated(&value, idx)); - break; - case SND_CTL_ELEM_TYPE_BYTES: - obj = new_integer(instance, snd_ctl_elem_value_get_byte(&value, idx)); - break; - default: - obj = NULL; - break; - } - if (idx == 0) { - p1 = add_cons2(instance, lexpr->value.c.cdr, 0, obj); - } else { - p1 = add_cons2(instance, p1, 1, obj); - } - } - if (p1 == NULL) { - delete_tree(instance, lexpr); - return &alsa_lisp_nil; - } - return lexpr; -} - -static struct alisp_object * FA_hctl_elem_write(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_hctl_elem_t *handle; - struct alisp_object * p1 = NULL, * obj; - snd_ctl_elem_info_t info = {0}; - snd_ctl_elem_value_t value = {0}; - snd_ctl_elem_type_t type; - unsigned int idx, count; - int err; - - p1 = car(cdr(args)); - obj = eval(instance, car(args)); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - handle = (snd_hctl_elem_t *)get_ptr(instance, obj, item->prefix); - if (handle == NULL) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - err = snd_hctl_elem_info(handle, &info); - if (err < 0) { - delete_tree(instance, p1); - return new_integer(instance, err); - } - type = snd_ctl_elem_info_get_type(&info); - count = snd_ctl_elem_info_get_count(&info); - if (type == SND_CTL_ELEM_TYPE_IEC958) { - count = sizeof(snd_aes_iec958_t); - type = SND_CTL_ELEM_TYPE_BYTES; - } - idx = -1; - do { - if (++idx >= count) { - delete_tree(instance, p1); - break; - } - obj = car(p1); - switch (type) { - case SND_CTL_ELEM_TYPE_BOOLEAN: - snd_ctl_elem_value_set_boolean(&value, idx, get_integer(obj)); - break; - case SND_CTL_ELEM_TYPE_INTEGER: - snd_ctl_elem_value_set_integer(&value, idx, get_integer(obj)); - break; - case SND_CTL_ELEM_TYPE_INTEGER64: - snd_ctl_elem_value_set_integer64(&value, idx, get_integer(obj)); - break; - case SND_CTL_ELEM_TYPE_ENUMERATED: - snd_ctl_elem_value_set_enumerated(&value, idx, get_integer(obj)); - break; - case SND_CTL_ELEM_TYPE_BYTES: - snd_ctl_elem_value_set_byte(&value, idx, get_integer(obj)); - break; - default: - break; - } - delete_tree(instance, obj); - p1 = cdr(obj = p1); - delete_object(instance, obj); - } while (p1 != &alsa_lisp_nil); - err = snd_hctl_elem_write(handle, &value); - return new_integer(instance, err); -} - -static struct alisp_object * FA_pcm_info(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_pcm_t *handle; - struct alisp_object * lexpr, * p1; - snd_pcm_info_t info = {0}; - int err; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (snd_pcm_t *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - err = snd_pcm_info(handle, &info); - lexpr = new_lexpr(instance, err); - if (err < 0) - return lexpr; - p1 = add_cons(instance, lexpr->value.c.cdr, 0, "card", new_integer(instance, snd_pcm_info_get_card(&info))); - p1 = add_cons(instance, p1, 1, "device", new_integer(instance, snd_pcm_info_get_device(&info))); - p1 = add_cons(instance, p1, 1, "subdevice", new_integer(instance, snd_pcm_info_get_subdevice(&info))); - p1 = add_cons(instance, p1, 1, "id", new_string(instance, snd_pcm_info_get_id(&info))); - p1 = add_cons(instance, p1, 1, "name", new_string(instance, snd_pcm_info_get_name(&info))); - p1 = add_cons(instance, p1, 1, "subdevice_name", new_string(instance, snd_pcm_info_get_subdevice_name(&info))); - p1 = add_cons(instance, p1, 1, "class", new_integer(instance, snd_pcm_info_get_class(&info))); - p1 = add_cons(instance, p1, 1, "subclass", new_integer(instance, snd_pcm_info_get_subclass(&info))); - p1 = add_cons(instance, p1, 1, "subdevices_count", new_integer(instance, snd_pcm_info_get_subdevices_count(&info))); - p1 = add_cons(instance, p1, 1, "subdevices_avail", new_integer(instance, snd_pcm_info_get_subdevices_avail(&info))); - //p1 = add_cons(instance, p1, 1, "sync", new_string(instance, snd_pcm_info_get_sync(&info))); - return lexpr; -} - -/* - * main code - */ - -static const struct acall_table acall_table[] = { - { "card_get_index", &FA_int_str, (void *)snd_card_get_index, NULL }, - { "card_get_longname", &FA_int_int_strp, (void *)snd_card_get_longname, NULL }, - { "card_get_name", &FA_int_int_strp, (void *)snd_card_get_name, NULL }, - { "card_next", &FA_int_intp, (void *)&snd_card_next, NULL }, - { "ctl_card_info", &FA_card_info, NULL, "ctl" }, - { "ctl_close", &FA_int_p, (void *)&snd_ctl_close, "ctl" }, - { "ctl_open", &FA_int_pp_strp_int, (void *)&snd_ctl_open, "ctl" }, - { "hctl_close", &FA_int_p, (void *)&snd_hctl_close, "hctl" }, - { "hctl_ctl", &FA_p_p, (void *)&snd_hctl_ctl, "hctl" }, - { "hctl_elem_info", &FA_hctl_elem_info, (void *)&snd_hctl_elem_info, "hctl_elem" }, - { "hctl_elem_next", &FA_p_p, (void *)&snd_hctl_elem_next, "hctl_elem" }, - { "hctl_elem_prev", &FA_p_p, (void *)&snd_hctl_elem_prev, "hctl_elem" }, - { "hctl_elem_read", &FA_hctl_elem_read, (void *)&snd_hctl_elem_read, "hctl_elem" }, - { "hctl_elem_write", &FA_hctl_elem_write, (void *)&snd_hctl_elem_write, "hctl_elem" }, - { "hctl_find_elem", &FA_hctl_find_elem, (void *)&snd_hctl_find_elem, "hctl" }, - { "hctl_first_elem", &FA_p_p, (void *)&snd_hctl_first_elem, "hctl" }, - { "hctl_free", &FA_int_p, (void *)&snd_hctl_free, "hctl" }, - { "hctl_last_elem", &FA_p_p, (void *)&snd_hctl_last_elem, "hctl" }, - { "hctl_load", &FA_int_p, (void *)&snd_hctl_load, "hctl" }, - { "hctl_open", &FA_int_pp_strp_int, (void *)&snd_hctl_open, "hctl" }, - { "hctl_open_ctl", &FA_int_pp_p, (void *)&snd_hctl_open_ctl, "hctl" }, - { "pcm_info", &FA_pcm_info, NULL, "pcm" }, - { "pcm_name", &FA_str_p, (void *)&snd_pcm_name, "pcm" }, -}; - -static int acall_compar(const void *p1, const void *p2) -{ - return strcmp(((struct acall_table *)p1)->name, - ((struct acall_table *)p2)->name); -} - -static struct alisp_object * F_acall(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, *p2; - struct acall_table key, *item; - - p1 = eval(instance, car(args)); - p2 = cdr(args); - delete_object(instance, args); - if (!alisp_compare_type(p1, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(p1, ALISP_OBJ_STRING)) { - delete_tree(instance, p2); - return &alsa_lisp_nil; - } - key.name = p1->value.s; - if ((item = bsearch(&key, acall_table, - sizeof acall_table / sizeof acall_table[0], - sizeof acall_table[0], acall_compar)) != NULL) { - delete_tree(instance, p1); - return item->func(instance, item, p2); - } - delete_tree(instance, p1); - delete_tree(instance, p2); - lisp_warn(instance, "acall function %s' is undefined", p1->value.s); - return &alsa_lisp_nil; -} - -static struct alisp_object * F_ahandle(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object *p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - args = car(cdr(p1)); - delete_tree(instance, cdr(cdr(p1))); - delete_object(instance, cdr(p1)); - delete_tree(instance, car(p1)); - delete_object(instance, p1); - return args; -} - -static struct alisp_object * F_aerror(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object *p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - args = car(p1); - if (args == &alsa_lisp_nil) { - delete_tree(instance, p1); - return new_integer(instance, SND_ERROR_ALISP_NIL); - } else { - delete_tree(instance, cdr(p1)); - delete_object(instance, p1); - } - return args; -} - -static int common_error(snd_output_t **rout, struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1; - snd_output_t *out; - int err; - - err = snd_output_buffer_open(&out); - if (err < 0) { - delete_tree(instance, args); - return err; - } - - do { - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_STRING)) - snd_output_printf(out, "%s", p1->value.s); - else - princ_object(out, p1); - delete_tree(instance, p1); - p = cdr(p1 = p); - delete_object(instance, p1); - } while (p != &alsa_lisp_nil); - - *rout = out; - return 0; -} - -static struct alisp_object * F_snderr(struct alisp_instance *instance, struct alisp_object * args) -{ - snd_output_t *out; - char *str; - - if (common_error(&out, instance, args) < 0) - return &alsa_lisp_nil; - snd_output_buffer_string(out, &str); - SNDERR(str); - snd_output_close(out); - return &alsa_lisp_t; -} - -static struct alisp_object * F_syserr(struct alisp_instance *instance, struct alisp_object * args) -{ - snd_output_t *out; - char *str; - - if (common_error(&out, instance, args) < 0) - return &alsa_lisp_nil; - snd_output_buffer_string(out, &str); - SYSERR(str); - snd_output_close(out); - return &alsa_lisp_t; -} - -static const struct intrinsic snd_intrinsics[] = { - { "Acall", F_acall }, - { "Aerror", F_aerror }, - { "Ahandle", F_ahandle }, - { "Aresult", F_ahandle }, - { "Asnderr", F_snderr }, - { "Asyserr", F_syserr } -}; diff --git a/src/conf/Makefile.am b/src/conf/Makefile.am index e7bcbbe0..dc59d7d1 100644 --- a/src/conf/Makefile.am +++ b/src/conf/Makefile.am @@ -1,9 +1,6 @@ SUBDIRS=cards ctl pcm cfg_files = alsa.conf -if BUILD_ALISP -cfg_files += sndo-mixer.alisp -endif if BUILD_MODULES if BUILD_MIXER_MODULES cfg_files += smixer.conf diff --git a/src/conf/cards/Makefile.am b/src/conf/cards/Makefile.am index 00999f01..f387cf41 100644 --- a/src/conf/cards/Makefile.am +++ b/src/conf/cards/Makefile.am @@ -60,22 +60,6 @@ cfg_files = aliases.conf \ VXPocket.conf \ VXPocket440.conf -if BUILD_ALISP -cfg_files += aliases.alisp -endif - alsa_DATA = $(cfg_files) -if BUILD_ALISP -SI7018dir = $(alsaconfigdir)/cards/SI7018 -SI7018_files = \ - SI7018/sndoc-mixer.alisp \ - SI7018/sndop-mixer.alisp -SI7018_DATA = $(SI7018_files) -else -SI7018_files= -endif - -EXTRA_DIST = \ - $(cfg_files) \ - $(SI7018_files) +EXTRA_DIST = $(cfg_files) diff --git a/src/conf/cards/SI7018/sndoc-mixer.alisp b/src/conf/cards/SI7018/sndoc-mixer.alisp deleted file mode 100644 index ade1ea3f..00000000 --- a/src/conf/cards/SI7018/sndoc-mixer.alisp +++ /dev/null @@ -1,11 +0,0 @@ -; -; SiS SI7018 mixer abstract layer -; -; Copyright (c) 2003 Jaroslav Kysela -; License: GPL v2 (http://www.gnu.org/licenses/gpl.html) -; - -(defun sndoc_mixer_open (hctl pcm) - (princ "sndoc_mixer_open: hctl=" hctl " pcm=" pcm "\n") - 0 -) diff --git a/src/conf/cards/SI7018/sndop-mixer.alisp b/src/conf/cards/SI7018/sndop-mixer.alisp deleted file mode 100644 index 285e2898..00000000 --- a/src/conf/cards/SI7018/sndop-mixer.alisp +++ /dev/null @@ -1,11 +0,0 @@ -; -; SiS SI7018 mixer abstract layer -; -; Copyright (c) 2003 Jaroslav Kysela -; License: GPL v2 (http://www.gnu.org/licenses/gpl.html) -; - -(defun sndop_mixer_open (hctl pcm) - (princ "sndop_mixer_open: hctl=" hctl " pcm=" pcm "\n") - 0 -) diff --git a/src/conf/cards/aliases.alisp b/src/conf/cards/aliases.alisp deleted file mode 100644 index 1661caa3..00000000 --- a/src/conf/cards/aliases.alisp +++ /dev/null @@ -1,29 +0,0 @@ -(setq snd_card_aliases_array - ( - ("YMF724" . "YMF744") - ("YMF724F" . "YMF744") - ("YMF740" . "YMF744") - ("YMF740C" . "YMF744") - ("YMF754" . "YMF744") - ("CMIPCI" . "CMI8338") - ("CMI8738" . "CMI8338") - ("CMI8738-MC4" . "CMI8738-MC6") - ("E-mu APS" . "EMU10K1") - ("GUS Max" . "GUS") - ("GUS ACE" . "GUS") - ("GUS Extreme" . "GUS") - ("AMD InterWave" . "GUS") - ("Dynasonic 3-D" . "GUS") - ("InterWave STB" . "GUS") - ) -) - -(defun snd_card_alias (cardname) - (setq r (assq cardname snd_card_aliases_array)) - (setq r (if (null r) cardname r)) - (unsetq r) -) - -(defun snd_card_alias_unset () - (unsetq snd_card_aliases_array snd_card_alias) -) diff --git a/src/conf/sndo-mixer.alisp b/src/conf/sndo-mixer.alisp deleted file mode 100644 index c8b03f06..00000000 --- a/src/conf/sndo-mixer.alisp +++ /dev/null @@ -1,115 +0,0 @@ -; -; Toplevel configuration for the ALSA Ordinary Mixer Interface -; -; Copyright (c) 2003 Jaroslav Kysela -; License: GPL v2 (http://www.gnu.org/licenses/gpl.html) -; - -(defun sndo_include (hctl stream) - (setq info (Acall "ctl_card_info" (Acall "hctl_ctl" hctl))) - (if (= (Aerror info) 0) - (progn - (setq info (Aresult info)) - (setq driver (cdr (assq "driver" (unsetq info)))) - (setq file (concat (path "data") "/alsa/cards/" (snd_card_alias driver) "/sndo" stream "-mixer.alisp")) - (setq r (include file)) - (when (= r -2) (Asyserr "unable to find file " file)) - ) - (setq r (Aerror info)) - ) - (unsetq info driver file r) -) - -(defun sndo_mixer_open_fcn (hctl stream pcm) - (setq fcn (concat "sndo" stream "_mixer_open")) - (setq r (if (exfun fcn) (funcall fcn hctl pcm) 0)) - (when (= r 0) - (setq hctls (if hctls (cons hctls (cons hctl)) hctl)) - ) - (unsetq fcn r) -) - -(defun sndo_mixer_open_hctl (name stream pcm) - (setq hctl (Acall "hctl_open" name nil)) - (setq r (Aerror hctl)) - (when (= r 0) - (setq hctl (Aresult hctl)) - (setq r (sndo_include hctl stream)) - (if (= r 0) - (setq r (sndo_mixer_open_fcn hctl stream pcm)) - (Acall "hctl_close" hctl) - ) - ) - (unsetq hctl r) -) - -(defun sndo_mixer_open_virtual (name stream pcm) - (setq file (concat (path "data") "/alsa/virtual/" name "/sndo" stream "-mixer.alisp")) - (setq r (include file)) - (when (= r -2) (Asyserr "unable to find file " file)) - (when (= r 0) (setq r (sndo_mixer_open_fcn nil stream pcm))) - (unsetq file r) -) - -(defun sndo_mixer_open1 (name stream) - (if (compare-strings name 0 2 "hw:" 0 2) - (sndo_mixer_open_hctl name stream nil) - (sndo_mixer_open_virtual name stream nil) - ) -) - -(defun sndo_mixer_open (pname cname) - (setq r (sndo_mixer_open1 pname "p")) - (when (= r 0) (setq r (sndo_mixer_open1 cname "c"))) - (when (!= r 0) (sndo_mixer_close)) - (unsetq sndo_mixer_open - sndo_mixer_open_pcm sndo_mixer_open_pcm1 - sndo_mixer_open_virtual sndo_mixer_open_fcn - sndo_include r) -) - -(defun sndo_mixer_open_pcm1 (pcm stream) - (setq info (Acall "pcm_info" pcm)) - (setq r (Aerror info)) - (when (= r 0) - (setq info (Aresult info)) - (setq card (cdr (assq "card" info))) - (setq r - (if (< card 0) - (sndo_mixer_open_virtual (Acall "pcm_name" pcm) stream pcm) - (sndo_mixer_open_hctl (format "hw:%i" card) stream pcm) - ) - ) - ) - (unsetq info card r) -) - -(defun sndo_mixer_open_pcm (ppcm cpcm) - (setq r (sndo_mixer_open_pcm1 ppcm "p")) - (when (= r 0) (setq r (sndo_mixer_open_pcm1 cpcm "c"))) - (when (!= r 0) (sndo_mixer_close)) - (unsetq sndo_mixer_open - sndo_mixer_open_pcm sndo_mixer_open_pcm1 - sndo_mixer_open_virtual sndo_mixer_open_fcn - sndo_include r) -) - -(defun sndo_mixer_close1 (hctl stream) - (when hctl - (progn - (setq fcn (concat "sndo" stream "_mixer_close")) - (when (exfun fcn) (funcall fcn hctl)) - (unsetq fcn) - (Acall "hctl_close" hctl) - ) - ) -) - -(defun sndo_mixer_close nil - (sndo_mixer_close1 (nth 1 hctls) "c") - (sndo_mixer_close1 (nth 0 hctls) "p") - (snd_card_alias_unset) - (unsetq hctls) -) - -(include (concat (path "data") "/alsa/cards/aliases.alisp")) From a4e47461eca1decd61fbab42690a55358d77804a Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 7 Apr 2025 18:45:42 +0200 Subject: [PATCH 099/117] seq: update_group_ports - rewrite blknames update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve the warning bellow and use more optimized calls. seqmid.c: In function ‘update_group_ports’: seqmid.c:672:45: warning: ‘%s’ directive output may be truncated writing up to 127 bytes into a region of size 61 [-Wformat-truncation=] 672 | ", %s", bp->name); | ^~ seqmid.c:671:33: note: ‘snprintf’ output between 3 and 130 bytes into a destination of size 63 671 | snprintf(blknames + len, sizeof(blknames) - len, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 672 | ", %s", bp->name); | ~~~~~~~~~~~~~~~~~ Signed-off-by: Jaroslav Kysela --- src/seq/seqmid.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 10637f8b..542d92e3 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -632,7 +632,6 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) char blknames[64]; char name[64]; unsigned int caps = 0; - int len; blknames[0] = 0; for (b = 0; b < ep->num_blocks; b++) { @@ -664,15 +663,11 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) break; } - if (!*bp->name) + if (bp->name[0] == '\0') continue; - len = strlen(blknames); - if (len) - snprintf(blknames + len, sizeof(blknames) - len, - ", %s", bp->name); - else - snd_strlcpy(blknames, (const char *)bp->name, - sizeof(blknames)); + if (blknames[0]) + snd_strlcpy(blknames, ", ", sizeof(blknames)); + snd_strlcpy(blknames, (const char *)bp->name, sizeof(blknames)); } if (!*blknames) From 7e01443ecc2c887e943d4e88070665a24f1d2218 Mon Sep 17 00:00:00 2001 From: Kevin Groeneveld Date: Fri, 4 Apr 2025 15:59:59 -0400 Subject: [PATCH 100/117] pcm: hw: do not reset tstamp_type in SND_PCM_APPEND mode When the first client of plugins such as dshare open the hw device they set a default tstamp_type in snd_pcm_direct_initialize_slave based on tstamp_type from the config file. But when subsequent clients open the same plugin the snd_pcm_hw_open_fd function clobbers this default. Closes: https://github.com/alsa-project/alsa-lib/pull/450 Signed-off-by: Kevin Groeneveld Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_hw.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index f3fbcb6e..c99b186a 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -1665,26 +1665,28 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd, } } + if (!(mode & SND_PCM_APPEND)) { #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - if (SNDRV_PROTOCOL_VERSION(2, 0, 9) <= ver) { - struct timespec timespec; - if (clock_gettime(CLOCK_MONOTONIC, ×pec) == 0) { - int on = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; - if (ioctl(fd, SNDRV_PCM_IOCTL_TTSTAMP, &on) < 0) { + if (SNDRV_PROTOCOL_VERSION(2, 0, 9) <= ver) { + struct timespec timespec; + if (clock_gettime(CLOCK_MONOTONIC, ×pec) == 0) { + int on = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; + if (ioctl(fd, SNDRV_PCM_IOCTL_TTSTAMP, &on) < 0) { + ret = -errno; + SNDMSG("TTSTAMP failed"); + return ret; + } + tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC; + } + } else +#endif + if (SNDRV_PROTOCOL_VERSION(2, 0, 5) <= ver) { + int on = 1; + if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) { ret = -errno; - SNDMSG("TTSTAMP failed"); + SNDMSG("TSTAMP failed"); return ret; } - tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC; - } - } else -#endif - if (SNDRV_PROTOCOL_VERSION(2, 0, 5) <= ver) { - int on = 1; - if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) { - ret = -errno; - SNDMSG("TSTAMP failed"); - return ret; } } From 15f2b276121b0acbdfdf32035f721c8e58a9114e Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 7 Apr 2025 18:52:17 +0200 Subject: [PATCH 101/117] pcm: hw: fix default timestamp type for O_APPPEND Fixes: 7e01443e ("pcm: hw: do not reset tstamp_type in SND_PCM_APPEND mode") Link: https://github.com/alsa-project/alsa-lib/pull/450 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_hw.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index c99b186a..ab5e45ee 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -1689,6 +1689,12 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd, } } } +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + else { + /* the first stream already sets this */ + tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC; + } +#endif hw = calloc(1, sizeof(snd_pcm_hw_t)); if (!hw) { From 8b75db9676999bfbd3c4ab59dac3c4b7a253dad2 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 8 Apr 2025 11:18:16 +0200 Subject: [PATCH 102/117] pcm: hw: do not reset tstamp_type in SND_PCM_APPEND mode (#2) This is basically redo of all changed and add appropriate conditions to disable only ioctl calls, but preserve tstamp_type assignments. Fixes: 15f2b276 ("pcm: hw: fix default timestamp type for O_APPPEND") Fixes: 7e01443e ("pcm: hw: do not reset tstamp_type in SND_PCM_APPEND mode") Link: https://github.com/alsa-project/alsa-lib/pull/450 Suggested-by: Signed-off-by: Kevin Groeneveld Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_hw.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index ab5e45ee..833cad02 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -1665,36 +1665,30 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd, } } - if (!(mode & SND_PCM_APPEND)) { #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - if (SNDRV_PROTOCOL_VERSION(2, 0, 9) <= ver) { - struct timespec timespec; - if (clock_gettime(CLOCK_MONOTONIC, ×pec) == 0) { + if (SNDRV_PROTOCOL_VERSION(2, 0, 9) <= ver) { + struct timespec timespec; + if (clock_gettime(CLOCK_MONOTONIC, ×pec) == 0) { + if (!(mode & SND_PCM_APPEND)) { int on = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; if (ioctl(fd, SNDRV_PCM_IOCTL_TTSTAMP, &on) < 0) { ret = -errno; SNDMSG("TTSTAMP failed"); return ret; } - tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC; } - } else + tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC; + } + } else #endif - if (SNDRV_PROTOCOL_VERSION(2, 0, 5) <= ver) { - int on = 1; - if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) { - ret = -errno; - SNDMSG("TSTAMP failed"); - return ret; - } + if (SNDRV_PROTOCOL_VERSION(2, 0, 5) <= ver && !(mode & SND_PCM_APPEND)) { + int on = 1; + if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) { + ret = -errno; + SNDMSG("TSTAMP failed"); + return ret; } } -#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - else { - /* the first stream already sets this */ - tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC; - } -#endif hw = calloc(1, sizeof(snd_pcm_hw_t)); if (!hw) { From 6073f53051cc8e9ddb835c787a70b504d4f1ec36 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 8 Apr 2025 16:27:42 +0200 Subject: [PATCH 103/117] rawmidi: ump - fix snd_ump_block_info_set_block_id double version #2 Define two different symbols with similar functionality. Fix for: CC ump.lo /tmp/ccZKscpB.s: Assembler messages: /tmp/ccZKscpB.s:18: Error: multiple versions [`snd_ump_block_info_set_block_id@@ALSA_1.2.13'|`snd_ump_block_info_set_block_id@ALSA_1.2.10'] for symbol `__snd_ump_block_info_set_block_id' make[3]: *** [Makefile:392: ump.lo] Error 1 Fixes: 352cbc5e ("rawmidi: ump - fix snd_ump_block_info_set_block_id double version") Link: https://github.com/alsa-project/alsa-lib/issues/422 Signed-off-by: Jaroslav Kysela --- src/rawmidi/ump.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index 6807e877..3ff5fc59 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -883,8 +883,16 @@ void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, info->block_id = id; } +#ifndef DOXYGEN +EXPORT_SYMBOL void INTERNAL(snd_ump_block_info_set_block_id_old) + (snd_ump_block_info_t *info, unsigned int id) +{ + return INTERNAL(snd_ump_block_info_set_block_id)(info, id); +} +#endif + #ifndef DOC_HIDDEN -use_symbol_version(__snd_ump_block_info_set_block_id, snd_ump_block_info_set_block_id, ALSA_1.2.10); +use_symbol_version(__snd_ump_block_info_set_block_id_old, snd_ump_block_info_set_block_id, ALSA_1.2.10); use_default_symbol_version(__snd_ump_block_info_set_block_id, snd_ump_block_info_set_block_id, ALSA_1.2.13); #endif /* DOC_HIDDEN */ From e51cba0973d97d8a4c3d2d5896b203d8c3628c89 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 14 Apr 2025 09:44:40 +0200 Subject: [PATCH 104/117] ucm: do not bump syntax version to 8 The new code for syntax 8 is not widely tested. Postpone it for next alsa-lib release. Signed-off-by: Jaroslav Kysela --- src/ucm/ucm_local.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index b274277d..464d1154 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -40,7 +40,7 @@ #include #include "use-case.h" -#define SYNTAX_VERSION_MAX 8 +#define SYNTAX_VERSION_MAX 7 #define MAX_CARD_SHORT_NAME 32 #define MAX_CARD_LONG_NAME 80 From 8291d2c6014e460d9b85a75bebc9a78ffb446a41 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 14 Apr 2025 18:34:45 +0200 Subject: [PATCH 105/117] add snd_strlcat() function Signed-off-by: Jaroslav Kysela --- include/local.h | 1 + src/error.c | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/include/local.h b/include/local.h index 55f252df..2498cd26 100644 --- a/include/local.h +++ b/include/local.h @@ -267,6 +267,7 @@ int _snd_safe_strtod(const char *str, double *val); int snd_send_fd(int sock, void *data, size_t len, int fd); int snd_receive_fd(int sock, void *data, size_t len, int *fd); size_t snd_strlcpy(char *dst, const char *src, size_t size); +size_t snd_strlcat(char *dst, const char *src, size_t size); /* * error messages diff --git a/src/error.c b/src/error.c index c06af7c7..8db7556b 100644 --- a/src/error.c +++ b/src/error.c @@ -191,11 +191,38 @@ snd_lib_error_handler_t snd_err_msg = snd_err_msg_default; */ size_t snd_strlcpy(char *dst, const char *src, size_t size) { - size_t ret = strlen(src); - if (size) { - size_t len = ret >= size ? size - 1 : ret; - memcpy(dst, src, len); - dst[len] = '\0'; - } - return ret; + size_t ret = strlen(src); + if (size) { + size_t len = ret >= size ? size - 1 : ret; + memcpy(dst, src, len); + dst[len] = '\0'; + } + return ret; +} + +/** + * \brief Append a C-string into a sized buffer + * \param dst Where to append the string to + * \param src Where to copy the string from + * \param size Size of destination buffer + * \retval The total string length (no trimming) + * + * The result is always a valid NUL-terminated string that fits + * in the buffer (unless, of course, the buffer size is zero). + * It does not pad out the result. + */ +size_t snd_strlcat(char *dst, const char *src, size_t size) +{ + size_t dst_len = strlen(dst); + size_t len = strlen(src); + size_t ret = dst_len + len; + if (dst_len < size) { + dst += dst_len; + size -= dst_len; + if (len >= size) + len = size - 1; + memcpy(dst, src, len); + dst[len] = '\0'; + } + return ret; } From c748afe123b3e9fdd020853d508b33beff80afa5 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 14 Apr 2025 18:36:45 +0200 Subject: [PATCH 106/117] seq: seqmid - use correct snd_strlcat instead snd_strlcpy in update_group_ports() Fixes: a4e47461 ("seq: update_group_ports - rewrite blknames update") Suggested-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- src/seq/seqmid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 542d92e3..edffa675 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -666,8 +666,8 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep) if (bp->name[0] == '\0') continue; if (blknames[0]) - snd_strlcpy(blknames, ", ", sizeof(blknames)); - snd_strlcpy(blknames, (const char *)bp->name, sizeof(blknames)); + snd_strlcat(blknames, ", ", sizeof(blknames)); + snd_strlcat(blknames, (const char *)bp->name, sizeof(blknames)); } if (!*blknames) From 81d70a27136840fa8fb1bec6eb8e04fc1475a380 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 14 Apr 2025 18:42:04 +0200 Subject: [PATCH 107/117] Release v1.2.14 Signed-off-by: Jaroslav Kysela --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 094bd11b..2a808fa8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(alsa-lib, 1.2.13) +AC_INIT(alsa-lib, 1.2.14) AC_CONFIG_SRCDIR([src/control/control.c]) AC_CONFIG_MACRO_DIR([m4]) From 8ab0228f5143282fef7359a79a457f5814177b2b Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 24 Apr 2025 13:25:30 +0200 Subject: [PATCH 108/117] Revert "ucm: do not bump syntax version to 8" This reverts commit e51cba0973d97d8a4c3d2d5896b203d8c3628c89. Signed-off-by: Jaroslav Kysela --- src/ucm/ucm_local.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index 464d1154..b274277d 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -40,7 +40,7 @@ #include #include "use-case.h" -#define SYNTAX_VERSION_MAX 7 +#define SYNTAX_VERSION_MAX 8 #define MAX_CARD_SHORT_NAME 32 #define MAX_CARD_LONG_NAME 80 From 368ea9af82afdc27fe0798b846c205b315b36eff Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 24 Apr 2025 15:16:12 +0200 Subject: [PATCH 109/117] github: fix Fedora workflow (awk package dependency) Signed-off-by: Jaroslav Kysela --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3e8aecc9..0de93aa4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: - name: Prepare environment run: | dnf -y upgrade - dnf -y install @development-tools libtool bzip2 + dnf -y install @development-tools libtool bzip2 awk - name: Checkout uses: actions/checkout@v4 with: From 8df60992b7d23da6209e3262f5f65b986d409e4a Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 9 May 2025 09:55:00 +0200 Subject: [PATCH 110/117] mixer: bag - fix bag_del_all implementation (missing free) The bag1_t structure must be freed, too. Fixes: https://github.com/alsa-project/alsa-lib/issues/453 Signed-off-by: Jaroslav Kysela --- src/mixer/bag.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/mixer/bag.c b/src/mixer/bag.c index 18a8fbe8..8d06073d 100644 --- a/src/mixer/bag.c +++ b/src/mixer/bag.c @@ -68,6 +68,11 @@ int bag_del(bag_t *bag, void *ptr) void bag_del_all(bag_t *bag) { - while (!list_empty(bag)) - list_del(bag->next); + struct list_head *pos, *npos; + + list_for_each_safe(pos, npos, bag) { + bag1_t *b = list_entry(pos, bag1_t, list); + list_del(&b->list); + free(b); + } } From 07ec2ad34c42dba8656d3f543164f360f481c52e Mon Sep 17 00:00:00 2001 From: Daniel Dadap Date: Thu, 15 May 2025 08:32:35 -0500 Subject: [PATCH 111/117] conf: aliases: add hda-acpi -> HDA-Intel alias The new snd_hda_acpi driver in Linux exposes the existing Azalia interface to non-PCI devices advertised over ACPI. Add an alias to the existing HDA-Intel configuration file so that devices using this driver can be discovered properly. Signed-off-by: Daniel Dadap Signed-off-by: Takashi Iwai --- src/conf/cards/aliases.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/src/conf/cards/aliases.conf b/src/conf/cards/aliases.conf index a54824ae..e2d59aa7 100644 --- a/src/conf/cards/aliases.conf +++ b/src/conf/cards/aliases.conf @@ -57,6 +57,7 @@ CMI8786 cards.CMI8788 CMI8787 cards.CMI8788 pistachio cards.pistachio-card VC4-HDMI cards.vc4-hdmi +hda-acpi cards.HDA-Intel From ee5a58f48e141aeb28df182247c3f789f4160d02 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 26 Jun 2025 11:33:15 +0200 Subject: [PATCH 112/117] ucm: regex: fix the error message (missing argument) Link: https://github.com/alsa-project/alsa-ucm-conf/pull/580 Signed-off-by: Jaroslav Kysela --- src/ucm/ucm_regex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucm/ucm_regex.c b/src/ucm/ucm_regex.c index 1471f076..dfcab042 100644 --- a/src/ucm/ucm_regex.c +++ b/src/ucm/ucm_regex.c @@ -154,7 +154,7 @@ int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name, err = regcomp(&re, s, options); free(s); if (err) { - uc_error("Regex '%s' compilation failed (code %d)", err); + uc_error("Regex '%s' compilation failed (code %d)", s, err); return -EINVAL; } From 5f524e300409f0a7b54c5fe9ee194bad1fc39516 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Thu, 31 Jul 2025 15:37:33 +0300 Subject: [PATCH 113/117] pcm: add a loop to snd_pcm_avail_delay() to avoid bogus delay values snd_pcm_avail_delay() is expected to report avail and delay values in atomic fashion. However the function does two separate syscalls and it cannot guarantee the avail value is the same as was used to calculate the delay. This is a problem as the reported delay is always relative to avail frames value. If application (like e.g. alsa_conformance_test) uses snd_pcm_avail_delay() to estimate the effective play position, it can observe bogus delay values (and effective play position going backwards) if snd_pcm_avail_delay() is called during a DMA burst where hw_ptr moves quickly. This commit adds a loop similar to that used in snd_pcm_hw_htimestamp() to wait until we get a stable avail reading, and only then extract the delay. This will avoid bogus values if function is called during DMA bursts. Closes: https://github.com/alsa-project/alsa-lib/pull/469 Closes: https://github.com/alsa-project/alsa-lib/issues/468 Signed-off-by: Kai Vehmanen Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index da339ace..c59ea3b6 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -3107,7 +3107,7 @@ int snd_pcm_avail_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) { snd_pcm_sframes_t sf; - int err; + int err, ok = 0; assert(pcm && availp && delayp); if (CHECK_SANITY(! pcm->setup)) { @@ -3118,15 +3118,25 @@ int snd_pcm_avail_delay(snd_pcm_t *pcm, err = __snd_pcm_hwsync(pcm); if (err < 0) goto unlock; - sf = __snd_pcm_avail_update(pcm); - if (sf < 0) { - err = (int)sf; - goto unlock; + + /* + * Delay value is relative to avail, so we have to + * loop to avoid reporting stale delay data. + */ + while (1) { + sf = __snd_pcm_avail_update(pcm); + if (sf < 0) { + err = (int)sf; + goto unlock; + } + if (ok && sf == *availp) + break; + *availp = sf; + err = __snd_pcm_delay(pcm, delayp); + if (err < 0) + goto unlock; + ok = 1; } - err = __snd_pcm_delay(pcm, delayp); - if (err < 0) - goto unlock; - *availp = sf; err = 0; unlock: snd_pcm_unlock(pcm->fast_op_arg); From 4ad4d9590ab22276631f84ca59e67af53e5c5776 Mon Sep 17 00:00:00 2001 From: wyjstrong Date: Tue, 29 Jul 2025 14:58:43 +0800 Subject: [PATCH 114/117] Force to use alphasort64() sorting function for Harmony OS Closes: https://github.com/alsa-project/alsa-lib/pull/467 Signed-off-by: wyjstrong Signed-off-by: Jaroslav Kysela --- src/conf.c | 9 ++++++++- src/ucm/parser.c | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/conf.c b/src/conf.c index 468d41f5..905c8f4d 100644 --- a/src/conf.c +++ b/src/conf.c @@ -4140,7 +4140,14 @@ static int config_file_load(snd_config_t *root, const char *fn, int errors) if (!S_ISDIR(st.st_mode)) return config_file_open(root, fn); #ifndef DOC_HIDDEN -#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__sun) && !defined(__ANDROID__) +#if defined(_GNU_SOURCE) && \ + !defined(__NetBSD__) && \ + !defined(__FreeBSD__) && \ + !defined(__OpenBSD__) && \ + !defined(__DragonFly__) && \ + !defined(__sun) && \ + !defined(__ANDROID__) && \ + !defined(__OHOS__) #define SORTFUNC versionsort64 #else #define SORTFUNC alphasort64 diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 488a5c62..ac4a5fbc 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -2928,7 +2928,14 @@ int uc_mgr_scan_master_configs(const char **_list[]) snprintf(filename, sizeof(filename), "%s/ucm2/conf.virt.d", snd_config_topdir()); -#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__sun) && !defined(__ANDROID__) +#if defined(_GNU_SOURCE) && \ + !defined(__NetBSD__) && \ + !defined(__FreeBSD__) && \ + !defined(__OpenBSD__) && \ + !defined(__DragonFly__) && \ + !defined(__sun) && \ + !defined(__ANDROID__) && \ + !defined(__OHOS__) #define SORTFUNC versionsort64 #else #define SORTFUNC alphasort64 From 782b0597c25845cf8c48b3fe906e8374d1175f36 Mon Sep 17 00:00:00 2001 From: Jochen Sprickerhof Date: Sun, 15 Jun 2025 10:10:52 +0200 Subject: [PATCH 115/117] ucm: use close_range on _GNU_SOURCE Closes: https://github.com/alsa-project/alsa-lib/pull/459 Signed-off-by: Jochen Sprickerhof Signed-off-by: Jaroslav Kysela --- src/ucm/ucm_exec.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ucm/ucm_exec.c b/src/ucm/ucm_exec.c index 276cf592..b79a84ae 100644 --- a/src/ucm/ucm_exec.c +++ b/src/ucm/ucm_exec.c @@ -254,8 +254,12 @@ int uc_mgr_exec(const char *prog) close(f); +#if defined(_GNU_SOURCE) + close_range(3, maxfd, 0); +#else for (f = 3; f < maxfd; f++) close(f); +#endif /* install default handlers for the forked process */ signal(SIGINT, SIG_DFL); From 3a9771812405be210e760e4e6667f2c023fe82f4 Mon Sep 17 00:00:00 2001 From: Adam Sampson Date: Sun, 6 Jul 2025 05:07:45 +0100 Subject: [PATCH 116/117] test: update midifile library to ANSI C The midifile library used by the playmidi1 program dates from 1989, and used pre-ANSI function definitions and prototypes. GCC 15 now defaults to C23 where () means the same as (void) in prototypes, which causes type mismatch errors. Update the code to use ANSI function definitions and prototypes, so it'll compile happily as anything from ANSI C to C23. This revealed that playmidi1's do_tempo had the wrong argument type, so correct that as well. Closes: https://github.com/alsa-project/alsa-lib/pull/463 Signed-off-by: Adam Sampson Signed-off-by: Jaroslav Kysela --- test/midifile.c | 246 +++++++++++++++++++---------------------------- test/midifile.h | 71 +++++++------- test/playmidi1.c | 4 +- 3 files changed, 140 insertions(+), 181 deletions(-) diff --git a/test/midifile.c b/test/midifile.c index 4862b199..8580108e 100644 --- a/test/midifile.c +++ b/test/midifile.c @@ -79,34 +79,34 @@ /* public stuff */ /* Functions to be called while processing the MIDI file. */ -int (*Mf_getc) () = NULLFUNC; -void (*Mf_error) () = NULLFUNC; -void (*Mf_header) () = NULLFUNC; -void (*Mf_trackstart) () = NULLFUNC; -void (*Mf_trackend) () = NULLFUNC; -void (*Mf_noteon) () = NULLFUNC; -void (*Mf_noteoff) () = NULLFUNC; -void (*Mf_pressure) () = NULLFUNC; -void (*Mf_parameter) () = NULLFUNC; -void (*Mf_pitchbend) () = NULLFUNC; -void (*Mf_program) () = NULLFUNC; -void (*Mf_chanpressure) () = NULLFUNC; -void (*Mf_sysex) () = NULLFUNC; -void (*Mf_arbitrary) () = NULLFUNC; -void (*Mf_metamisc) () = NULLFUNC; -void (*Mf_seqnum) () = NULLFUNC; -void (*Mf_eot) () = NULLFUNC; -void (*Mf_smpte) () = NULLFUNC; -void (*Mf_tempo) () = NULLFUNC; -void (*Mf_timesig) () = NULLFUNC; -void (*Mf_keysig) () = NULLFUNC; -void (*Mf_seqspecific) () = NULLFUNC; -void (*Mf_text) () = NULLFUNC; +int (*Mf_getc) (void) = NULLFUNC; +void (*Mf_error) (char *s) = NULLFUNC; +void (*Mf_header) (int format, int ntrks, int division) = NULLFUNC; +void (*Mf_trackstart) (void) = NULLFUNC; +void (*Mf_trackend) (void) = NULLFUNC; +void (*Mf_noteon) (int chan, int c1, int c2) = NULLFUNC; +void (*Mf_noteoff) (int chan, int c1, int c2) = NULLFUNC; +void (*Mf_pressure) (int chan, int c1, int c2) = NULLFUNC; +void (*Mf_parameter) (int chan, int c1, int c2) = NULLFUNC; +void (*Mf_pitchbend) (int chan, int c1, int c2) = NULLFUNC; +void (*Mf_program) (int chan, int c1) = NULLFUNC; +void (*Mf_chanpressure) (int chan, int c1) = NULLFUNC; +void (*Mf_sysex) (int len, char *msg) = NULLFUNC; +void (*Mf_arbitrary) (int len, char *msg) = NULLFUNC; +void (*Mf_metamisc) (int type, int len, char *msg) = NULLFUNC; +void (*Mf_seqnum) (int num) = NULLFUNC; +void (*Mf_eot) (void) = NULLFUNC; +void (*Mf_smpte) (char m0, char m1, char m2, char m3, char m4) = NULLFUNC; +void (*Mf_tempo) (long tempo) = NULLFUNC; +void (*Mf_timesig) (char m0, char m1, char m2, char m3) = NULLFUNC; +void (*Mf_keysig) (char m0, char m1) = NULLFUNC; +void (*Mf_seqspecific) (int len, char *msg) = NULLFUNC; +void (*Mf_text) (int type, int len, char *msg) = NULLFUNC; /* Functions to implement in order to write a MIDI file */ -int (*Mf_putc) () = NULLFUNC; -int (*Mf_writetrack) () = NULLFUNC; -int (*Mf_writetempotrack) () = NULLFUNC; +int (*Mf_putc) (unsigned char c) = NULLFUNC; +int (*Mf_writetrack) (int track) = NULLFUNC; +int (*Mf_writetempotrack) (void) = NULLFUNC; int Mf_nomerge = 0; /* 1 => continue'ed system exclusives are */ /* not collapsed. */ @@ -132,29 +132,34 @@ static int tempo_history_count = 0; static long Mf_toberead = 0L; static long Mf_numbyteswritten = 0L; -static long readvarinum (); -static long read32bit (); -static long to32bit (); -static int read16bit (); -static int to16bit (); -static char *msg (); -static void readheader (); -static int readtrack (); -static void badbyte (); -static void metaevent (); -static void sysex (); -static void chanmessage (); -static void msginit (); -static int msgleng (); -static void msgadd (); -static void biggermsg (); -static int eputc (); +static long readvarinum (void); +static long read32bit (void); +static long to32bit (int, int, int, int); +static int read16bit (void); +static int to16bit (int, int); +static char *msg (void); +static void readheader (void); +static int readtrack (void); +static void badbyte (int); +static void metaevent (int); +static void sysex (void); +static void chanmessage (int, int, int); +static void msginit (void); +static int msgleng (void); +static void msgadd (int); +static void biggermsg (void); +static int eputc (unsigned char); double mf_ticks2sec (unsigned long ticks, int division, unsigned long tempo); -int mf_write_meta_event (); -void mf_write_tempo (); -void mf_write_seqnum (); -void WriteVarLen (); +void write32bit (unsigned long data); +void write16bit (int data); +void mf_write_track_chunk (int which_track, FILE *fp); +void mf_write_header_chunk (int format, int ntracks, int division); +int mf_write_meta_event (unsigned long delta_time, unsigned char type, + unsigned char *data, unsigned long size); +void mf_write_tempo (unsigned long delta_time, unsigned long tempo); +void mf_write_seqnum (unsigned long delta_time, unsigned int seqnum); +void WriteVarLen (unsigned long value); #ifdef READ_MODS #include "mp_mod.c" @@ -163,7 +168,7 @@ static int mod_file_flag = 0; static int force_exit; void -mfread () +mfread (void) { force_exit = 0; if (Mf_getc == NULLFUNC) @@ -181,15 +186,13 @@ mfread () /* for backward compatibility with the original lib */ void -midifile () +midifile (void) { mfread (); } -static -int -readmt (s) /* read through the "MThd" or "MTrk" header string */ - char *s; +static int +readmt (char *s) /* read through the "MThd" or "MTrk" header string */ { int n = 0; char *p = s; @@ -211,9 +214,8 @@ readmt (s) /* read through the "MThd" or "MTrk" header string */ return (c); } -static -int -egetc () /* read a single character and abort on EOF */ +static int +egetc (void) /* read a single character and abort on EOF */ { int c = (*Mf_getc) (); @@ -225,9 +227,8 @@ egetc () /* read a single character and abort on EOF */ return (c); } -static -void -readheader () /* read a header chunk */ +static void +readheader (void) /* read a header chunk */ { int format, ntrks, division; @@ -280,9 +281,8 @@ readheader () /* read a header chunk */ /*#define DEBUG_TIMES*/ -static -unsigned long -find_tempo() +static unsigned long +find_tempo(void) { int i; unsigned long old_tempo = Mf_currtempo; @@ -307,9 +307,8 @@ printf("[revised_time %lu, new_tempo %lu]\n", revised_time, new_tempo); return(new_tempo); } -static -int -readtrack () /* read a track chunk */ +static int +readtrack (void) /* read a track chunk */ { /* This array is indexed by the high half of a status byte. It's */ /* value is either the number of bytes needed (1 or 2) for a channel */ @@ -499,10 +498,8 @@ old_f_realtime, delta_secs * 1600.0); return (1); } -static -void -badbyte (c) - int c; +static void +badbyte (int c) { char buff[32]; @@ -510,8 +507,7 @@ badbyte (c) mferror (buff); } -static -void +static void metaevent (int type) { int leng = msgleng (); @@ -577,19 +573,15 @@ metaevent (int type) } } -static -void -sysex () +static void +sysex (void) { if (Mf_sysex) (*Mf_sysex) (msgleng (), msg ()); } -static -void -chanmessage (status, c1, c2) - int status; - int c1, c2; +static void +chanmessage (int status, int c1, int c2) { int chan = status & 0xf; @@ -635,7 +627,7 @@ chanmessage (status, c1, c2) /* number of characters it took. */ static long -readvarinum () +readvarinum (void) { long value; int c; @@ -668,14 +660,13 @@ to32bit (int c1, int c2, int c3, int c4) } static int -to16bit (c1, c2) - int c1, c2; +to16bit (int c1, int c2) { return ((c1 & 0xff) << 8) + (c2 & 0xff); } static long -read32bit () +read32bit (void) { int c1, c2, c3, c4; @@ -687,7 +678,7 @@ read32bit () } static int -read16bit () +read16bit (void) { int c1, c2; c1 = egetc (); @@ -697,8 +688,7 @@ read16bit () /* static */ void -mferror (s) - char *s; +mferror (char *s) { if (Mf_error) (*Mf_error) (s); @@ -714,30 +704,26 @@ static char *Msgbuff = NULL; /* message buffer */ static int Msgsize = 0; /* Size of currently allocated Msg */ static int Msgindex = 0; /* index of next available location in Msg */ -static -void -msginit () +static void +msginit (void) { Msgindex = 0; } static char * -msg () +msg (void) { return (Msgbuff); } -static -int -msgleng () +static int +msgleng (void) { return (Msgindex); } -static -void -msgadd (c) - int c; +static void +msgadd (int c) { /* If necessary, allocate larger message buffer. */ if (Msgindex >= Msgsize) @@ -745,11 +731,9 @@ msgadd (c) Msgbuff[Msgindex++] = c; } -static -void -biggermsg () +static void +biggermsg (void) { -/* char *malloc(); */ char *newmess; char *oldmess = Msgbuff; int oldleng = Msgsize; @@ -805,12 +789,9 @@ static int laststatus = 0; * to work with Mf_putc. */ void -mfwrite (format, ntracks, division, fp) - int format, ntracks, division; - FILE *fp; +mfwrite (int format, int ntracks, int division, FILE *fp) { int i; - void mf_write_track_chunk (), mf_write_header_chunk (); if (Mf_putc == NULLFUNC) mferror ("mfmf_write() called without setting Mf_putc"); @@ -837,14 +818,10 @@ mfwrite (format, ntracks, division, fp) } void -mf_write_track_chunk (which_track, fp) - int which_track; - FILE *fp; +mf_write_track_chunk (int which_track, FILE *fp) { unsigned long trkhdr, trklength; long offset, place_marker; - void write16bit (), write32bit (); - laststatus = 0; @@ -910,11 +887,9 @@ mf_write_track_chunk (which_track, fp) void -mf_write_header_chunk (format, ntracks, division) - int format, ntracks, division; +mf_write_header_chunk (int format, int ntracks, int division) { unsigned long ident, length; - void write16bit (), write32bit (); ident = MThd; /* Head chunk identifier */ length = 6; /* Chunk length */ @@ -948,11 +923,8 @@ mf_write_header_chunk (format, ntracks, division) * size - The length of the meta-event data. */ int -mf_write_midi_event (delta_time, type, chan, data, size) - unsigned long delta_time; - int chan, type; - unsigned long size; - char *data; +mf_write_midi_event (unsigned long delta_time, int type, int chan, + char *data, unsigned long size) { int i; unsigned char c; @@ -999,11 +971,9 @@ mf_write_midi_event (delta_time, type, chan, data, size) * data. * size - The length of the meta-event data. */ -int -mf_write_meta_event (delta_time, type, data, size) - unsigned long delta_time; - unsigned char *data, type; - unsigned long size; +int +mf_write_meta_event (unsigned long delta_time, unsigned char type, + unsigned char *data, unsigned long size) { int i; @@ -1027,9 +997,7 @@ mf_write_meta_event (delta_time, type, data, size) } /* end mf_write_meta_event */ void -mf_write_tempo (delta_time, tempo) - unsigned long delta_time; - unsigned long tempo; +mf_write_tempo (unsigned long delta_time, unsigned long tempo) { /* Write tempo */ /* all tempos are written as 120 beats/minute, */ @@ -1046,9 +1014,7 @@ mf_write_tempo (delta_time, tempo) } void -mf_write_seqnum (delta_time, seqnum) - unsigned long delta_time; - unsigned seqnum; +mf_write_seqnum (unsigned long delta_time, unsigned int seqnum) { WriteVarLen (delta_time); @@ -1060,10 +1026,7 @@ mf_write_seqnum (delta_time, seqnum) } unsigned long -mf_sec2ticks (secs, division, tempo) - int division; - unsigned long tempo; - double secs; +mf_sec2ticks (double secs, int division, unsigned long tempo) { return (unsigned long) (((secs * 1000.0) / 4.0 * division) / tempo); } @@ -1072,8 +1035,7 @@ mf_sec2ticks (secs, division, tempo) * Write multi-length bytes to MIDI format files */ void -WriteVarLen (value) - unsigned long value; +WriteVarLen (unsigned long value) { unsigned long buffer; @@ -1102,10 +1064,7 @@ WriteVarLen (value) * */ double -mf_ticks2sec (ticks, division, tempo) - int division; - unsigned long tempo; - unsigned long ticks; +mf_ticks2sec (unsigned long ticks, int division, unsigned long tempo) { double smpte_format, smpte_resolution; @@ -1133,8 +1092,7 @@ mf_ticks2sec (ticks, division, tempo) * */ void -write32bit (data) - unsigned long data; +write32bit (unsigned long data) { eputc ((unsigned) ((data >> 24) & 0xff)); eputc ((unsigned) ((data >> 16) & 0xff)); @@ -1143,8 +1101,7 @@ write32bit (data) } void -write16bit (data) - int data; +write16bit (int data) { eputc ((unsigned) ((data & 0xff00) >> 8)); eputc ((unsigned) (data & 0xff)); @@ -1152,8 +1109,7 @@ write16bit (data) /* write a single character and abort on error */ static int -eputc (c) - unsigned char c; +eputc (unsigned char c) { int return_val; diff --git a/test/midifile.h b/test/midifile.h index 7dd4626e..5d408421 100644 --- a/test/midifile.h +++ b/test/midifile.h @@ -1,27 +1,27 @@ /* definitions for MIDI file parsing code */ -extern int (*Mf_getc)(); -extern void (*Mf_header)(); -extern void (*Mf_trackstart)(); -extern void (*Mf_trackend)(); -extern void (*Mf_noteon)(); -extern void (*Mf_noteoff)(); -extern void (*Mf_pressure)(); -extern void (*Mf_parameter)(); -extern void (*Mf_pitchbend)(); -extern void (*Mf_program)(); -extern void (*Mf_chanpressure)(); -extern void (*Mf_sysex)(); -extern void (*Mf_metamisc)(); -extern void (*Mf_seqspecific)(); -extern void (*Mf_seqnum)(); -extern void (*Mf_text)(); -extern void (*Mf_eot)(); -extern void (*Mf_timesig)(); -extern void (*Mf_smpte)(); -extern void (*Mf_tempo)(); -extern void (*Mf_keysig)(); -extern void (*Mf_arbitrary)(); -extern void (*Mf_error)(); +extern int (*Mf_getc)(void); +extern void (*Mf_error)(char *s); +extern void (*Mf_header)(int format, int ntrks, int division); +extern void (*Mf_trackstart)(void); +extern void (*Mf_trackend)(void); +extern void (*Mf_noteon)(int chan, int c1, int c2); +extern void (*Mf_noteoff)(int chan, int c1, int c2); +extern void (*Mf_pressure)(int chan, int c1, int c2); +extern void (*Mf_parameter)(int chan, int c1, int c2); +extern void (*Mf_pitchbend)(int chan, int c1, int c2); +extern void (*Mf_program)(int chan, int c1); +extern void (*Mf_chanpressure)(int chan, int c1); +extern void (*Mf_sysex)(int len, char *msg); +extern void (*Mf_arbitrary)(int len, char *msg); +extern void (*Mf_metamisc)(int type, int len, char *msg); +extern void (*Mf_seqnum)(int num); +extern void (*Mf_eot)(void); +extern void (*Mf_smpte)(char m0, char m1, char m2, char m3, char m4); +extern void (*Mf_tempo)(long tempo); +extern void (*Mf_timesig)(char m0, char m1, char m2, char m3); +extern void (*Mf_keysig)(char m0, char m1); +extern void (*Mf_seqspecific)(int len, char *msg); +extern void (*Mf_text)(int type, int len, char *msg); extern unsigned long Mf_currtime; extern unsigned long Mf_realtime; extern unsigned long Mf_currtempo; @@ -33,20 +33,23 @@ extern int Mf_file_size; #endif /* definitions for MIDI file writing code */ -extern int (*Mf_putc)(); -extern int (*Mf_writetrack)(); -extern int (*Mf_writetempotrack)(); +extern int (*Mf_putc)(unsigned char c); +extern int (*Mf_writetrack)(int track); +extern int (*Mf_writetempotrack)(void); -extern void midifile(); -extern unsigned long mf_sec2ticks(); -extern void mfwrite(); -extern int mf_write_meta_event(); +extern void midifile(void); +extern unsigned long mf_sec2ticks(double secs, int division, + unsigned long tempo); +extern void mfwrite(int format, int ntracks, int division, FILE *fp); +extern int mf_write_meta_event(unsigned long delta_time, unsigned char type, + unsigned char *data, unsigned long size); extern int mf_write_midi_event(unsigned long delta_time, int type, int chan, char *data, unsigned long size); -extern double mf_ticks2sec(unsigned long ticks,int division,unsigned long tempo); -extern void mf_write_tempo(); -extern void mf_write_seqnum(); -extern void mfread(); +extern double mf_ticks2sec(unsigned long ticks, int division, + unsigned long tempo); +extern void mf_write_tempo(unsigned long delta_time, unsigned long tempo); +extern void mf_write_seqnum(unsigned long delta_time, unsigned int seqnum); +extern void mfread(void); extern void mferror(char *s); #ifndef NO_LC_DEFINES diff --git a/test/playmidi1.c b/test/playmidi1.c index 286aaa86..6ca0e397 100644 --- a/test/playmidi1.c +++ b/test/playmidi1.c @@ -243,14 +243,14 @@ static void alsa_stop_timer(void) } /* change the tempo */ -static void do_tempo(int us) +static void do_tempo(long us) { snd_seq_event_t ev; if (verbose >= VERB_MUCH) { double bpm; bpm = 60.0E6 / (double) us; - printf("Tempo %d us/beat, %.2f bpm\n", us, bpm); + printf("Tempo %ld us/beat, %.2f bpm\n", us, bpm); } /* store the new tempo and timestamp of the tempo change */ From d62b1d540781c61403681c1d903b7c74af6d1fa9 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 18 Sep 2025 11:00:47 +0200 Subject: [PATCH 117/117] conf: fix parse_array_def override code path The error may cause segmentation fault and incorrect behaviour. Closes: https://github.com/alsa-project/alsa-lib/issues/477 Signed-off-by: Jaroslav Kysela --- src/conf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/conf.c b/src/conf.c index 905c8f4d..a48e0db0 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1268,13 +1268,13 @@ static int parse_array_def(snd_config_t *parent, input_t *input, int *idx, int s snd_config_t *n = NULL; if (!skip) { - snd_config_t *g; char static_id[12]; while (1) { snprintf(static_id, sizeof(static_id), "%i", *idx); - if (_snd_config_search(parent, static_id, -1, &g) == 0) { + if (_snd_config_search(parent, static_id, -1, &n) == 0) { if (override) { snd_config_delete(n); + n = NULL; } else { /* merge */ (*idx)++;