From 378c6a9215ea52eb453a9da8f2632eadcb046c72 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 8 Jul 2022 21:44:44 +0200 Subject: [PATCH 001/267] pcm: direct plugins: make three more symbols private to library Those symbols should not be exported: snd_pcm_direct_poll_descriptors snd_pcm_direct_check_xrun snd_pcm_direct_slave_recover Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_direct.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pcm/pcm_direct.h b/src/pcm/pcm_direct.h index 181f45ee..e7d89e5f 100644 --- a/src/pcm/pcm_direct.h +++ b/src/pcm/pcm_direct.h @@ -224,6 +224,8 @@ struct snd_pcm_direct { snd1_pcm_direct_nonblock #define snd_pcm_direct_async \ snd1_pcm_direct_async +#define snd_pcm_direct_poll_descriptors \ + snd1_pcm_direct_poll_descriptors #define snd_pcm_direct_poll_revents \ snd1_pcm_direct_poll_revents #define snd_pcm_direct_info \ @@ -264,6 +266,10 @@ struct snd_pcm_direct { snd1_pcm_direct_set_chmap #define snd_pcm_direct_reset_slave_ptr \ snd1_pcm_direct_reset_slave_ptr +#define snd_pcm_direct_check_xrun \ + snd1_pcm_direct_check_xrun +#define snd_pcm_direct_slave_recover \ + snd1_pcm_direct_slave_recover int snd_pcm_direct_semaphore_create_or_connect(snd_pcm_direct_t *dmix); From fc86eafa086684c0bebec0ee67ccbcef315ce9d5 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 8 Jul 2022 21:56:57 +0200 Subject: [PATCH 002/267] ucm: fix few memory-leaks in the error paths Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 2 +- src/ucm/parser.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index 58ed49dd..ed6d1c94 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -1481,8 +1481,8 @@ const char *parse_open_variables(snd_use_case_mgr_t *uc_mgr, const char *name) goto skip; } - snd_config_delete(cfg); skip: + snd_config_delete(cfg); return end + 3; } diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 976aa8be..9e62fa9f 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -415,15 +415,15 @@ static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr, n = snd_config_iterator_entry(i); err = snd_config_get_id(n, &id); if (err < 0) - return err; + goto __err_path; err = snd_config_get_ascii(n, &var); if (err < 0) - return err; + goto __err_path; snprintf(name, sizeof(name), "__%s", id); err = uc_mgr_set_variable(uc_mgr, name, var); free(var); if (err < 0) - return err; + goto __err_path; } /* merge + substitute variables */ @@ -443,11 +443,11 @@ static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr, n = snd_config_iterator_entry(i); err = snd_config_get_id(n, &id); if (err < 0) - return err; + goto __err_path; snprintf(name, sizeof(name), "__%s", id); err = uc_mgr_delete_variable(uc_mgr, name); if (err < 0) - return err; + goto __err_path; } __err_path: From d80616c0c1157a8332e8fce968fd778d98379eed Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 8 Jul 2022 22:00:34 +0200 Subject: [PATCH 003/267] ucm: add NULL check for card_name in open Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index ed6d1c94..30ab1e41 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -1518,7 +1518,7 @@ int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr, mgr->suppress_nodev_errors = 1; } - if (card_name[0] == '<' && card_name[1] == '<' && card_name[2] == '<') + if (card_name && card_name[0] == '<' && card_name[1] == '<' && card_name[2] == '<') card_name = parse_open_variables(mgr, card_name); err = uc_mgr_card_open(mgr); From 8033ff9996b7f04531f1e161bcf6af7673346669 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 16 Aug 2022 17:21:12 +0200 Subject: [PATCH 004/267] pcm: fix the dshare delay reporting Pulseaudio uses full ring buffer when the incorrect delay is reported. BugLink: https://github.com/alsa-project/alsa-ucm-conf/issues/198 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_dshare.c | 2 +- src/pcm/pcm_dsnoop.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c index d3a2b456..d7be8fbb 100644 --- a/src/pcm/pcm_dshare.c +++ b/src/pcm/pcm_dshare.c @@ -229,7 +229,7 @@ static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status) case SNDRV_PCM_STATE_DRAINING: case SNDRV_PCM_STATE_RUNNING: snd_pcm_dshare_sync_ptr0(pcm, status->hw_ptr); - status->delay += snd_pcm_mmap_playback_delay(pcm); + status->delay = snd_pcm_mmap_playback_delay(pcm); break; default: break; diff --git a/src/pcm/pcm_dsnoop.c b/src/pcm/pcm_dsnoop.c index 8c4cde05..cb2eee32 100644 --- a/src/pcm/pcm_dsnoop.c +++ b/src/pcm/pcm_dsnoop.c @@ -215,7 +215,7 @@ static int snd_pcm_dsnoop_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) /* Fall through */ case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_SUSPENDED: - *delayp = snd_pcm_mmap_capture_avail(pcm); + *delayp = snd_pcm_mmap_capture_delay(pcm); return 0; case SNDRV_PCM_STATE_XRUN: return -EPIPE; From 0222f45d11e8b71bf651b985b00fdb0addbf3eed Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 18 Aug 2022 11:23:42 +0200 Subject: [PATCH 005/267] pcm: hw_params - copy periods and buffer_time to the local variable It is expected to return the correct numbers for get_periods and get_buffer_time requests. Copy the appropriate fields in the hw_params call. Fixes: https://github.com/alsa-project/alsa-lib/issues/258 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_params.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pcm/pcm_params.c b/src/pcm/pcm_params.c index ceb3b1aa..8140c061 100644 --- a/src/pcm/pcm_params.c +++ b/src/pcm/pcm_params.c @@ -2407,6 +2407,8 @@ int _snd_pcm_hw_params_internal(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) INTERNAL(snd_pcm_hw_params_get_subformat)(params, &pcm->subformat); INTERNAL(snd_pcm_hw_params_get_channels)(params, &pcm->channels); INTERNAL(snd_pcm_hw_params_get_rate)(params, &pcm->rate, 0); + snd_interval_copy(&pcm->periods, ¶ms->intervals[SND_PCM_HW_PARAM_PERIODS - SND_PCM_HW_PARAM_FIRST_INTERVAL]); + snd_interval_copy(&pcm->buffer_time, ¶ms->intervals[SND_PCM_HW_PARAM_BUFFER_TIME - SND_PCM_HW_PARAM_FIRST_INTERVAL]); INTERNAL(snd_pcm_hw_params_get_period_time)(params, &pcm->period_time, 0); INTERNAL(snd_pcm_hw_params_get_period_size)(params, &pcm->period_size, 0); INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &pcm->buffer_size); From 595115758be25e7c17c9e843eb0e418848b99a82 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 16 Sep 2022 19:02:16 +0200 Subject: [PATCH 006/267] doc: ucm - add sequence graphs Signed-off-by: Jaroslav Kysela --- doc/pictures/Makefile.am | 10 +++++++- doc/pictures/ucm-seq-boot.puml | 17 ++++++++++++++ doc/pictures/ucm-seq-boot.svg | 27 +++++++++++++++++++++ doc/pictures/ucm-seq-device.puml | 26 +++++++++++++++++++++ doc/pictures/ucm-seq-device.svg | 36 ++++++++++++++++++++++++++++ doc/pictures/ucm-seq-verb.puml | 30 ++++++++++++++++++++++++ doc/pictures/ucm-seq-verb.svg | 40 ++++++++++++++++++++++++++++++++ src/ucm/ucm_confdoc.h | 7 ++++++ 8 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 doc/pictures/ucm-seq-boot.puml create mode 100644 doc/pictures/ucm-seq-boot.svg create mode 100644 doc/pictures/ucm-seq-device.puml create mode 100644 doc/pictures/ucm-seq-device.svg create mode 100644 doc/pictures/ucm-seq-verb.puml create mode 100644 doc/pictures/ucm-seq-verb.svg diff --git a/doc/pictures/Makefile.am b/doc/pictures/Makefile.am index 17b6e12d..df3128bd 100644 --- a/doc/pictures/Makefile.am +++ b/doc/pictures/Makefile.am @@ -1 +1,9 @@ -EXTRA_DIST=wave1.gif wave2.gif +GIT_FILES=$(wildcard *.gif) +PUML_FILES=$(wildcard *.puml) +SVG_FILES=$(PUML_FILES:.puml=.svg) +EXTRA_DIST=$(GIT_FILES) $(PUML_FILES) $(SVG_FILES) + +all: $(SVG_FILES) + +.puml.svg: + plantuml -tsvg $< diff --git a/doc/pictures/ucm-seq-boot.puml b/doc/pictures/ucm-seq-boot.puml new file mode 100644 index 00000000..3b797280 --- /dev/null +++ b/doc/pictures/ucm-seq-boot.puml @@ -0,0 +1,17 @@ +@startuml +title "UCM sequence boot order (using udev and alsactl)" + +start +:Sound card detected (udev); +:UCM: FixedBootSequence; +if (Card state (/var/lib/alsa/asound.state)) then (not present) + :UCM: BootSequence; +else (present) + :alsactl: Restore state (from asound.state); +endif +:Standard sound card use (with or without UCM); +:Save sound card state (asound.state); +:Sound card detached; +stop + +@enduml diff --git a/doc/pictures/ucm-seq-boot.svg b/doc/pictures/ucm-seq-boot.svg new file mode 100644 index 00000000..867c817e --- /dev/null +++ b/doc/pictures/ucm-seq-boot.svg @@ -0,0 +1,27 @@ +UCM sequence boot order (using udev and alsactl)Sound card detected (udev)UCM: FixedBootSequenceCard state (/var/lib/alsa/asound.state)not presentpresentUCM: BootSequencealsactl: Restore state (from asound.state)Standard sound card use (with or without UCM)Save sound card state (asound.state)Sound card detached \ No newline at end of file diff --git a/doc/pictures/ucm-seq-device.puml b/doc/pictures/ucm-seq-device.puml new file mode 100644 index 00000000..a1c3f8cd --- /dev/null +++ b/doc/pictures/ucm-seq-device.puml @@ -0,0 +1,26 @@ +@startuml +title "UCM device sequence graph" + +start +split + :Enable device; + :EnableSequence; + :Use device; + :Disable device; + :DisableSequence; +split again + :Switch device; + :Disable old device; + if (TransitionSequence) then (present) + :TransitionSequence; + :Enable new device; + else (not present) + :Disable old device; + :DisableSequence; + :Enable new device; + :EnableSequence; + endif +end split +stop + +@enduml diff --git a/doc/pictures/ucm-seq-device.svg b/doc/pictures/ucm-seq-device.svg new file mode 100644 index 00000000..fff208dc --- /dev/null +++ b/doc/pictures/ucm-seq-device.svg @@ -0,0 +1,36 @@ +UCM device sequence graphEnable deviceEnableSequenceUse deviceDisable deviceDisableSequenceSwitch deviceDisable old deviceTransitionSequencepresentnot presentTransitionSequenceEnable new deviceDisable old deviceDisableSequenceEnable new deviceEnableSequence \ No newline at end of file diff --git a/doc/pictures/ucm-seq-verb.puml b/doc/pictures/ucm-seq-verb.puml new file mode 100644 index 00000000..e0e54805 --- /dev/null +++ b/doc/pictures/ucm-seq-verb.puml @@ -0,0 +1,30 @@ +@startuml +title "UCM verb sequence graph" + + +start +split + :Enable verb; + :SectionDefaults sequence; + :EnableSequence; + + :Enable, use and disable verb devices; + + :Disable verb; + :DisableSequence; +split again + :Switch verb; + :Disable old verb; + if (TransitionSequence) then (present) + :TransitionSequence; + :Enable new verb; + else (not present) + :Disable old verb; + :DisableSequence; + :Enable new verb; + :EnableSequence; + endif +end split +stop + +@enduml diff --git a/doc/pictures/ucm-seq-verb.svg b/doc/pictures/ucm-seq-verb.svg new file mode 100644 index 00000000..82eb9da6 --- /dev/null +++ b/doc/pictures/ucm-seq-verb.svg @@ -0,0 +1,40 @@ +UCM verb sequence graphEnable verbSectionDefaults sequenceEnableSequenceEnable, use and disable verb devicesDisable verbDisableSequenceSwitch verbDisable old verbTransitionSequencepresentnot presentTransitionSequenceEnable new verbDisable old verbDisableSequenceEnable new verbEnableSequence \ No newline at end of file diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index 27ec722e..cf928c67 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -218,6 +218,11 @@ SectionModifier."Capture Voice" { } ~~~ +### Sequence graphs + +\image html ucm-seq-verb.svg +\image html ucm-seq-device.svg + ### Sequence commands Command name | Description @@ -278,6 +283,8 @@ configuration like volumes or switches. The alsactl ensures the persistency (sto the state of the controls to the /var tree and loads the previous state in the next boot). +\image html ucm-seq-boot.svg + ### Device volume It is expected that the applications handle the volume settings. It is not recommended From e288ca7c29d9372e68c9704becdaf9261b30c9bf Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 16 Sep 2022 19:24:27 +0200 Subject: [PATCH 007/267] doc: ucm - more volume notes Signed-off-by: Jaroslav Kysela --- doc/pictures/ucm-volume.puml | 19 ++++++++++++++++++ doc/pictures/ucm-volume.svg | 29 ++++++++++++++++++++++++++++ src/ucm/ucm_confdoc.h | 37 +++++++++++++++++++++++------------- 3 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 doc/pictures/ucm-volume.puml create mode 100644 doc/pictures/ucm-volume.svg diff --git a/doc/pictures/ucm-volume.puml b/doc/pictures/ucm-volume.puml new file mode 100644 index 00000000..a698b316 --- /dev/null +++ b/doc/pictures/ucm-volume.puml @@ -0,0 +1,19 @@ +@startuml +title "UCM volume" + +start +if (Card state (/var/lib/alsa/asound.state)) then (not present) + :UCM: BootSequence; +else (present) + :alsactl: Restore state (from asound.state); +endif +:UCM application:set device volume; +note right + The application uses (Playback|Capture)MixerElem or + (Playback/Capture)(Volume/Switch) values to get + the controls. +end note +:alsactl: Store state (to asound.state); +stop + +@enduml diff --git a/doc/pictures/ucm-volume.svg b/doc/pictures/ucm-volume.svg new file mode 100644 index 00000000..d07b2dc2 --- /dev/null +++ b/doc/pictures/ucm-volume.svg @@ -0,0 +1,29 @@ +UCM volumeCard state (/var/lib/alsa/asound.state)not presentpresentUCM: BootSequencealsactl: Restore state (from asound.state)The application uses (Playback|Capture)MixerElem or(Playback/Capture)(Volume/Switch) values to getthe controls.UCM application:set device volumealsactl: Store state (to asound.state) \ No newline at end of file diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index cf928c67..d8948b80 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -63,6 +63,8 @@ use case verbs for that sound card. i.e.: # Example master file for blah sound card # By Joe Blogs +Syntax 6 + # Use Case name for user interface Comment "Nice Abstracted Soundcard" @@ -91,7 +93,11 @@ ValueDefaults { # ALSA card controls which may be modified by user after initial settings. BootSequence [ - cset "name='My control' on" + cset "name='Master Playback Switch',index=2 0,0" + cset "name='Master Playback Volume',index=2 25,25" + msleep 50 + cset "name='Master Playback Switch',index=2 1,1" + cset "name='Master Playback Volume',index=2 50,50" ] # Define fixed boot sequence @@ -117,23 +123,16 @@ SectionVerb { # enable and disable sequences are compulsory EnableSequence [ - cset "name='Master Playback Switch',index=2 0,0" - cset "name='Master Playback Volume',index=2 25,25" - msleep 50 - cset "name='Master Playback Switch',index=2 1,1" - cset "name='Master Playback Volume',index=2 50,50" + disdevall "" # run DisableSequence for all devices ] DisableSequence [ - cset "name='Master Playback Switch',index=2 0,0" - cset "name='Master Playback Volume',index=2 25,25" - msleep 50 - cset "name='Master Playback Switch',index=2 1,1" - cset "name='Master Playback Volume',index=2 50,50" + cset "name='Power Save' on" ] # Optional transition verb TransitionSequence."ToCaseName" [ + disdevall "" # run DisableSequence for all devices msleep 1 ] @@ -212,6 +211,7 @@ SectionModifier."Capture Voice" { Value { TQ Voice CapturePCM "hw:${CardId},11" + PlaybackMixerElem "Master" PlaybackVolume "name='Master Playback Volume',index=2" PlaybackSwitch "name='Master Playback Switch',index=2" } @@ -288,9 +288,20 @@ boot). ### Device volume It is expected that the applications handle the volume settings. It is not recommended -to set the fixed values for the volume settings to the Enable / Disable sequences for +to set the fixed values for the volume settings in the Enable / Disable sequences for verbs or devices, if the device exports the hardware volume (MixerElem or Volume/Switch -values). The default volume settings should be set in the *BootSequence*. +values). The default volume settings should be set in the *BootSequence*. The purpose +for this scheme is to allow users to override defaults using the alsactl sound card +state management. + +Checklist: + +1. Set default volume in BootSequence +2. Verb's EnableSequence should ensure that all devices are turned off (mixer paths) + to avoid simultaneous device use - the previous state is unknown (see *disdevall* + and *disdev2* commands or create a new custom command sequence) + +\image html ucm-volume.svg ### Dynamic configuration tree From b33ef3f73d632f54c27138e585030c50d76f47aa Mon Sep 17 00:00:00 2001 From: SASANO Takayoshi Date: Sun, 10 Jul 2022 11:03:00 +0900 Subject: [PATCH 008/267] add NetBSD/OpenBSD build support (except test/) Fixes: https://github.com/alsa-project/alsa-lib/pull/250 Signed-off-by: SASANO Takayoshi Signed-off-by: Jaroslav Kysela --- configure.ac | 2 +- include/bswap.h | 5 +++++ include/local.h | 8 +++++--- include/sound/type_compat.h | 20 +++++++++----------- include/sound/uapi/asoc.h | 2 ++ src/async.c | 10 ++++++++++ src/conf.c | 2 +- src/pcm/pcm.c | 2 ++ src/pcm/pcm_direct.c | 4 ++++ src/pcm/pcm_mmap.c | 2 ++ src/seq/seq_midi_event.c | 2 ++ src/shmarea.c | 3 +++ src/ucm/parser.c | 2 +- src/ucm/ucm_exec.c | 5 +++++ 14 files changed, 52 insertions(+), 17 deletions(-) diff --git a/configure.ac b/configure.ac index 864df1d0..9a9eda9b 100644 --- a/configure.ac +++ b/configure.ac @@ -335,7 +335,7 @@ else fi dnl Check for headers -AC_CHECK_HEADERS([endian.h sys/endian.h sys/shm.h]) +AC_CHECK_HEADERS([endian.h sys/endian.h sys/shm.h malloc.h]) dnl Check for resmgr support... AC_MSG_CHECKING(for resmgr support) diff --git a/include/bswap.h b/include/bswap.h index 4e5b3e2a..e590124c 100644 --- a/include/bswap.h +++ b/include/bswap.h @@ -27,6 +27,11 @@ #define bswap_16 bswap16 #define bswap_32 bswap32 #define bswap_64 bswap64 +#elif defined(__OpenBSD__) +#include +#define bswap_16 swap16 +#define bswap_32 swap32 +#define bswap_64 swap64 #elif defined (__sun) #include #define bswap_16 BSWAP_16 diff --git a/include/local.h b/include/local.h index 268a9ff1..151e3fd4 100644 --- a/include/local.h +++ b/include/local.h @@ -34,6 +34,9 @@ #include #elif defined(HAVE_SYS_ENDIAN_H) #include +#else +#error Header defining endianness not defined +#endif #ifndef __BYTE_ORDER #define __BYTE_ORDER BYTE_ORDER #endif @@ -43,9 +46,6 @@ #ifndef __BIG_ENDIAN #define __BIG_ENDIAN BIG_ENDIAN #endif -#else -#error Header defining endianness not defined -#endif #include #include #include @@ -85,6 +85,8 @@ #define versionsort64 versionsort #define alphasort64 alphasort #define ino64_t ino_t +#define fstat64 fstat +#define stat64 stat #endif #define _snd_config_iterator list_head diff --git a/include/sound/type_compat.h b/include/sound/type_compat.h index b994ccfe..0c424efc 100644 --- a/include/sound/type_compat.h +++ b/include/sound/type_compat.h @@ -3,7 +3,7 @@ #ifndef DOC_HIDDEN #include -#ifdef __linux__ +#if defined(__linux__) #include #else typedef uint8_t __u8; @@ -15,8 +15,14 @@ typedef int16_t __s16; typedef int32_t __s32; typedef int64_t __s64; +#if defined(__sun) +#include +#define __cpu_to_le32 LE_32(x) +#define __cpu_to_be32 BE_32(x) +#define __cpu_to_le16 LE_16(x) +#define __cpu_to_be16 BE_16(x) +#else #include -#include #if __BYTE_ORDER == __LITTLE_ENDIAN #define __cpu_to_le32(x) (x) #define __cpu_to_be32(x) bswap_32(x) @@ -28,20 +34,12 @@ typedef int64_t __s64; #define __cpu_to_le16(x) bswap_16(x) #define __cpu_to_be16(x) (x) #endif +#endif #define __le32_to_cpu __cpu_to_le32 #define __be32_to_cpu __cpu_to_be32 #define __le16_to_cpu __cpu_to_le16 #define __be16_to_cpu __cpu_to_be16 - -#define __le64 __u64 -#define __le32 __u32 -#define __le16 __u16 -#define __le8 __u8 -#define __be64 __u64 -#define __be32 __u32 -#define __be16 __u16 -#define __be8 __u8 #endif #ifndef __kernel_long_t diff --git a/include/sound/uapi/asoc.h b/include/sound/uapi/asoc.h index f32c5697..3903f2f4 100644 --- a/include/sound/uapi/asoc.h +++ b/include/sound/uapi/asoc.h @@ -16,7 +16,9 @@ #ifndef __LINUX_UAPI_SND_ASOC_H #define __LINUX_UAPI_SND_ASOC_H +#if defined(__linux__) #include +#endif /* * Maximum number of channels topology kcontrol can represent. diff --git a/src/async.c b/src/async.c index e6a8b5f2..8e8481b5 100644 --- a/src/async.c +++ b/src/async.c @@ -54,6 +54,15 @@ static LIST_HEAD(snd_async_handlers); static void snd_async_handler(int signo ATTRIBUTE_UNUSED, siginfo_t *siginfo, void *context ATTRIBUTE_UNUSED) { +#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) + /* siginfo_t does not have si_fd */ + struct list_head *i; + list_for_each(i, &snd_async_handlers) { + snd_async_handler_t *h = list_entry(i, snd_async_handler_t, glist); + if (h->callback) + h->callback(h); + } +#else int fd; struct list_head *i; //assert(siginfo->si_code == SI_SIGIO); @@ -66,6 +75,7 @@ static void snd_async_handler(int signo ATTRIBUTE_UNUSED, siginfo_t *siginfo, vo if (h->fd == fd && h->callback) h->callback(h); } +#endif } /** diff --git a/src/conf.c b/src/conf.c index a996e5f9..029a0db4 100644 --- a/src/conf.c +++ b/src/conf.c @@ -4115,7 +4115,7 @@ 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(__sun) && !defined(ANDROID) +#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sun) && !defined(ANDROID) #define SORTFUNC versionsort64 #else #define SORTFUNC alphasort64 diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 9aec52d1..927aa055 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -660,7 +660,9 @@ playback devices. #include #include +#if HAVE_MALLOC_H #include +#endif #include #include #include diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 39b82b96..c16956f1 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -44,12 +44,16 @@ * */ +#if !defined(__OpenBSD__) union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ +#if defined(__linux__) struct seminfo *__buf; /* Buffer for IPC_INFO (Linux specific) */ +#endif }; +#endif /* * FIXME: diff --git a/src/pcm/pcm_mmap.c b/src/pcm/pcm_mmap.c index 9cbaae05..dcd5c77e 100644 --- a/src/pcm/pcm_mmap.c +++ b/src/pcm/pcm_mmap.c @@ -20,7 +20,9 @@ #include "config.h" #include +#if HAVE_MALLOC_H #include +#endif #include #include #include diff --git a/src/seq/seq_midi_event.c b/src/seq/seq_midi_event.c index 5a12a18c..df09bde3 100644 --- a/src/seq/seq_midi_event.c +++ b/src/seq/seq_midi_event.c @@ -28,7 +28,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#if HAVE_MALLOC_H #include +#endif #include "local.h" #ifndef DOC_HIDDEN diff --git a/src/shmarea.c b/src/shmarea.c index 18937d9e..b566c79e 100644 --- a/src/shmarea.c +++ b/src/shmarea.c @@ -24,7 +24,10 @@ #ifdef HAVE_SYS_SHM_H #include +#include +#if HAVE_MALLOC_H #include +#endif #include #include #include diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 9e62fa9f..56d67e50 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -2907,7 +2907,7 @@ 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(__sun) && !defined(ANDROID) +#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sun) && !defined(ANDROID) #define SORTFUNC versionsort64 #else #define SORTFUNC alphasort64 diff --git a/src/ucm/ucm_exec.c b/src/ucm/ucm_exec.c index fffff55c..d5251fab 100644 --- a/src/ucm/ucm_exec.c +++ b/src/ucm/ucm_exec.c @@ -33,6 +33,11 @@ #include #include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +extern char **environ; +#endif + static pthread_mutex_t fork_lock = PTHREAD_MUTEX_INITIALIZER; /* From f89245ee662cda4c56c0422dc634cc1a07188b05 Mon Sep 17 00:00:00 2001 From: SASANO Takayoshi Date: Tue, 12 Jul 2022 20:37:44 +0900 Subject: [PATCH 009/267] add FreeBSD build support (except test/) Note: NetBSD and OpenBSD has both and Fixes: https://github.com/alsa-project/alsa-lib/pull/250 Signed-off-by: SASANO Takayoshi Signed-off-by: Jaroslav Kysela --- configure.ac | 3 ++- include/sound/type_compat.h | 2 +- include/sound/uapi/asound.h | 2 +- src/ucm/ucm_exec.c | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 9a9eda9b..8256c032 100644 --- a/configure.ac +++ b/configure.ac @@ -764,8 +764,9 @@ AC_OUTPUT(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \ dnl Create asoundlib.h dynamically according to configure options echo "Creating asoundlib.h..." cp "$srcdir"/include/asoundlib-head.h include/asoundlib.h +if test "$ac_cv_header_sys_endian_h" != "yes"; then test "$ac_cv_header_endian_h" = "yes" && echo "#include " >> include/asoundlib.h -if test "$ac_cv_header_sys_endian_h" = "yes"; then +else cat >> include/asoundlib.h < #ifndef __BYTE_ORDER diff --git a/include/sound/type_compat.h b/include/sound/type_compat.h index 0c424efc..c670245c 100644 --- a/include/sound/type_compat.h +++ b/include/sound/type_compat.h @@ -22,7 +22,7 @@ typedef int64_t __s64; #define __cpu_to_le16 LE_16(x) #define __cpu_to_be16 BE_16(x) #else -#include +#include #if __BYTE_ORDER == __LITTLE_ENDIAN #define __cpu_to_le32(x) (x) #define __cpu_to_be32(x) bswap_32(x) diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index c3f65e13..fc18c024 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -28,7 +28,7 @@ #include #include #else -#include +#include #include #endif diff --git a/src/ucm/ucm_exec.c b/src/ucm/ucm_exec.c index d5251fab..500a3b13 100644 --- a/src/ucm/ucm_exec.c +++ b/src/ucm/ucm_exec.c @@ -33,7 +33,7 @@ #include #include -#if defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) #include extern char **environ; #endif From 60c1430b43a0e8f3fcf1a3402dc0a9b2060d9a9c Mon Sep 17 00:00:00 2001 From: SASANO Takayoshi Date: Wed, 13 Jul 2022 21:36:59 +0900 Subject: [PATCH 010/267] add DragonFlyBSD build support (except test/) due to undefined reference to 'environ' error, ucm_exec.c uses workaround. Fixes: https://github.com/alsa-project/alsa-lib/pull/250 Signed-off-by: SASANO Takayoshi Signed-off-by: Jaroslav Kysela --- src/conf.c | 2 +- src/pcm/pcm_direct.c | 2 +- src/ucm/parser.c | 2 +- src/ucm/ucm_exec.c | 6 +++++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/conf.c b/src/conf.c index 029a0db4..eb38c344 100644 --- a/src/conf.c +++ b/src/conf.c @@ -4115,7 +4115,7 @@ 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(__sun) && !defined(ANDROID) +#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__sun) && !defined(ANDROID) #define SORTFUNC versionsort64 #else #define SORTFUNC alphasort64 diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index c16956f1..4803b81b 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -44,7 +44,7 @@ * */ -#if !defined(__OpenBSD__) +#if !defined(__OpenBSD__) && !defined(__DragonFly__) union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 56d67e50..f7c0b527 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -2907,7 +2907,7 @@ 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(__sun) && !defined(ANDROID) +#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__sun) && !defined(ANDROID) #define SORTFUNC versionsort64 #else #define SORTFUNC alphasort64 diff --git a/src/ucm/ucm_exec.c b/src/ucm/ucm_exec.c index 500a3b13..276cf592 100644 --- a/src/ucm/ucm_exec.c +++ b/src/ucm/ucm_exec.c @@ -33,10 +33,14 @@ #include #include -#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) #include +#if defined(__DragonFly__) +#define environ NULL /* XXX */ +#else extern char **environ; #endif +#endif static pthread_mutex_t fork_lock = PTHREAD_MUTEX_INITIALIZER; From dac9059524f6fb6eda32565b19ce145df125e17e Mon Sep 17 00:00:00 2001 From: Swapnil Devesh Date: Sat, 27 Aug 2022 13:33:53 +0530 Subject: [PATCH 011/267] Remove non existent SPDIF output on ThinkPad USB-C Dock Gen2 Fixes: https://github.com/alsa-project/alsa-lib/pull/268 Signed-off-by: Swapnil Devesh 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 c7d9e5fd..839e61cb 100644 --- a/src/conf/cards/USB-Audio.conf +++ b/src/conf/cards/USB-Audio.conf @@ -77,6 +77,7 @@ USB-Audio.pcm.iec958_device { "USB Device 0x46d_0x992" 999 "WD15 Dock" 999 "WD19 Dock" 999 + "ThinkPad USB-C Dock Gen2 USB Au" 999 } # Second iec958 device number, if any. From 9253f08fb784ad6a8b67b63e64732cb2f5694b6a Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 17 Oct 2022 13:52:51 +0200 Subject: [PATCH 012/267] pcm: rate: fix the crash for the partial period copy The size argument in snd_pcm_rate_commit_area can be smaller than cont in drain. Return the original code. It should not make things worse. Perhaps, we can fill the rest of the period with silence in this case. Fixes: https://github.com/alsa-project/alsa-lib/issues/274 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_rate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index e8815e8b..e5d8eddd 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -770,7 +770,7 @@ static snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm ATTRIBUTE_UNUSED, static int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate, snd_pcm_uframes_t appl_offset, - snd_pcm_uframes_t size, + snd_pcm_uframes_t size ATTRIBUTE_UNUSED, snd_pcm_uframes_t slave_size) { snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset; @@ -816,7 +816,7 @@ static int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate, pcm->format); snd_pcm_areas_copy(rate->pareas, cont, areas, 0, - pcm->channels, size - cont, + pcm->channels, pcm->period_size - cont, pcm->format); snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0); From 4633d35171724642fe5884e48bafba7c0151db07 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 18 Oct 2022 15:53:40 +0200 Subject: [PATCH 013/267] control: eld - skip trailing spaces Signed-off-by: Jaroslav Kysela --- src/control/eld.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/control/eld.c b/src/control/eld.c index 9be9605f..b7bf779f 100644 --- a/src/control/eld.c +++ b/src/control/eld.c @@ -47,7 +47,7 @@ int __snd_pcm_info_eld_fixup(snd_pcm_info_t * info) snd_ctl_elem_info_t cinfo = {0}; snd_ctl_elem_value_t value = {0}; unsigned char *eld; - unsigned int l; + unsigned int l, spc; char *s, c; int ret, valid; @@ -82,20 +82,23 @@ int __snd_pcm_info_eld_fixup(snd_pcm_info_t * info) return 0; } s = alloca(l + 1); - s[l] = '\0'; /* sanitize */ valid = 0; + spc = 0; while (l > 0) { l--; c = eld[20 + l]; - if (c < ' ' || c >= 0x7f) { + if (c <= ' ' || c >= 0x7f) { s[l] = ' '; } else { valid += !!isalnum(c); s[l] = c; + if (spc == 0) + spc = l + 1; } } if (valid > 3) { + s[spc] = '\0'; snd_strlcpy((char *)info->name, s, sizeof(info->name)); } else { __present: From 3bb9c44375538cf224f26bb54e7ce3bec564cd95 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 20 Oct 2022 19:37:32 +0200 Subject: [PATCH 014/267] ucm: do not handle multiple Syntax field updates It is useful to include a toplevel configuration file from another toplevel configuration file. Ignore the further Syntax updates (assuming the that the parent knows what to do). Also, parse the Syntax field in own function. Signed-off-by: Jaroslav Kysela --- src/ucm/parser.c | 76 ++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/ucm/parser.c b/src/ucm/parser.c index f7c0b527..b0ac1006 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -246,6 +246,36 @@ static int error_node(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) return -ENXIO; } +/* + * + */ +static int parse_syntax_field(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg, const char *filename) +{ + snd_config_t *n; + long l; + int err; + + err = snd_config_search(cfg, "Syntax", &n); + if (err < 0) { + uc_error("Syntax field not found in %s", filename); + return -EINVAL; + } + err = snd_config_get_integer(n, &l); + if (err < 0) { + uc_error("Syntax field is invalid in %s", filename); + return err; + } + if (l < 2 || l > SYNTAX_VERSION_MAX) { + uc_error("Incompatible syntax %ld in %s", l, filename); + return -EINVAL; + } + /* delete this field to optimize strcmp() call in the parsing loop */ + snd_config_delete(n); + uc_mgr->conf_format = l; + return l; +} + /* * Evaluate variable regex definitions (in-place delete) */ @@ -2366,7 +2396,6 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) snd_config_iterator_t i, next; snd_config_t *n; const char *id; - long l; int err; if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { @@ -2375,23 +2404,9 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) } if (uc_mgr->conf_format >= 2) { - err = snd_config_search(cfg, "Syntax", &n); - if (err < 0) { - uc_error("Syntax field not found in %s", uc_mgr->conf_file_name); - return -EINVAL; - } - err = snd_config_get_integer(n, &l); - if (err < 0) { - uc_error("Syntax field is invalid in %s", uc_mgr->conf_file_name); + err = parse_syntax_field(uc_mgr, cfg, uc_mgr->conf_file_name); + if (err < 0) return err; - } - if (l < 2 || l > SYNTAX_VERSION_MAX) { - uc_error("Incompatible syntax %d in %s", l, uc_mgr->conf_file_name); - return -EINVAL; - } - /* delete this field to avoid strcmp() call in the loop */ - snd_config_delete(n); - uc_mgr->conf_format = l; } /* in-place evaluation */ @@ -2473,6 +2488,10 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) if (strcmp(id, "Error") == 0) return error_node(uc_mgr, n); + /* skip further Syntax value updates (Include) */ + if (strcmp(id, "Syntax") == 0) + continue; + uc_error("unknown master file field %s", id); } return 0; @@ -2701,7 +2720,6 @@ static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr, snd_config_iterator_t i, next; snd_config_t *n; const char *id; - long l; int err; if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { @@ -2709,23 +2727,9 @@ static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr, return -EINVAL; } - err = snd_config_search(cfg, "Syntax", &n); - if (err < 0) { - uc_error("Syntax field not found in %s", filename); - return -EINVAL; - } - err = snd_config_get_integer(n, &l); - if (err < 0) { - uc_error("Syntax field is invalid in %s", filename); + err = parse_syntax_field(uc_mgr, cfg, filename); + if (err < 0) return err; - } - if (l < 2 || l > SYNTAX_VERSION_MAX) { - uc_error("Incompatible syntax %d in %s", l, filename); - return -EINVAL; - } - /* delete this field to avoid strcmp() call in the loop */ - snd_config_delete(n); - uc_mgr->conf_format = l; /* in-place evaluation */ err = uc_mgr_evaluate_inplace(uc_mgr, cfg); @@ -2756,6 +2760,10 @@ static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr, continue; } + /* skip further Syntax value updates (Include) */ + if (strcmp(id, "Syntax") == 0) + continue; + uc_error("unknown toplevel field %s", id); } From 7e678d70c20e5d9f55b72ef0da6d8c0b38067b17 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 24 Oct 2022 10:02:51 +0200 Subject: [PATCH 015/267] control: namehint - do not support 'card' devices The card: devices do not exist. Do not return -ENOENT, but -EINVAL in this case. Fixes: https://github.com/alsa-project/alsa-lib/issues/277 Signed-off-by: Jaroslav Kysela --- src/control/namehint.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/control/namehint.c b/src/control/namehint.c index e4f696ad..64ecafa5 100644 --- a/src/control/namehint.c +++ b/src/control/namehint.c @@ -602,9 +602,7 @@ int snd_device_name_hint(int card, const char *iface, void ***hints) list.siface = iface; list.show_all = 0; list.cardname = NULL; - if (strcmp(iface, "card") == 0) - list.iface = SND_CTL_ELEM_IFACE_CARD; - else if (strcmp(iface, "pcm") == 0) + if (strcmp(iface, "pcm") == 0) list.iface = SND_CTL_ELEM_IFACE_PCM; else if (strcmp(iface, "rawmidi") == 0) list.iface = SND_CTL_ELEM_IFACE_RAWMIDI; From 9447e57d7c1602a861635487ca56c452f3472965 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 24 Oct 2022 10:17:27 +0200 Subject: [PATCH 016/267] Release v1.2.8 Signed-off-by: Jaroslav Kysela --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 8256c032..3989a743 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.7.2) +AC_INIT(alsa-lib, 1.2.8) AC_CONFIG_SRCDIR([src/control/control.c]) AC_CONFIG_MACRO_DIR([m4]) From 1b436862ed6253629d79edc2e09826efd4e0f4e3 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 2 Nov 2022 15:01:56 +0100 Subject: [PATCH 017/267] ucm: fix enhanced ID parsing in snd_use_case_parse_ctl_elem_id() Reported-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index 30ab1e41..28c60565 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -2793,7 +2793,7 @@ int snd_use_case_parse_ctl_elem_id(snd_ctl_elem_id_t *dst, strcmp(ucm_id, "CaptureSwitch")) return -EINVAL; snd_ctl_elem_id_clear(dst); - if (strcasestr(ucm_id, "name=")) + if (strcasestr(value, "name=")) return __snd_ctl_ascii_elem_id_parse(dst, value, NULL); iface = SND_CTL_ELEM_IFACE_MIXER; if (jack_control) From aa4f56c3c952269c36464cc0da9db5a1381648fa Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 9 Nov 2022 08:11:42 +0100 Subject: [PATCH 018/267] pcm: rate - fix the crash in snd_pcm_rate_may_wait_for_avail_min() The pcm argument passed to the conversion function in snd_pcm_plugin_may_wait_for_avail_min_conv() should be pcm->fast_op_arg. Test command: arecord -Dplughw:x -r12000 -c2 -fS16_LE -M temp.wav Fixes: d9dbb57b ("pcm: rate - rewrite the may_wait_for_avail_min callback for the rate plugin") BugLink: https://lore.kernel.org/alsa-devel/1667793912-18957-1-git-send-email-shengjiu.wang@nxp.com/ Fixes: https://github.com/alsa-project/alsa-lib/issues/282 Reported-by: Shengjiu Wang Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index 6bb90b8b..ec64604c 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -622,7 +622,7 @@ int snd_pcm_plugin_may_wait_for_avail_min_conv( * This code is also used by extplug, but extplug does not allow to alter the sampling rate. */ if (conv) - needed_slave_avail_min = conv(pcm, needed_slave_avail_min); + needed_slave_avail_min = conv(pcm->fast_op_arg, needed_slave_avail_min); if (slave->avail_min != needed_slave_avail_min) { snd_pcm_sw_params_t *swparams; From 39060852d810461dc8cd1464cfb2ffe84da42d56 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 9 Nov 2022 09:31:34 +0100 Subject: [PATCH 019/267] pcm: rate - correct the previous fix for snd_pcm_rate_may_wait_for_avail_min() The previous fix in aa4f56c3 was not correct. The root of the cause is implementation in snd_pcm_may_wait_for_avail_min() inline function where the improper pcm argument is passed to the fast_ops function. Fixes: aa4f56c3 ("pcm: rate - fix the crash in snd_pcm_rate_may_wait_for_avail_min()") Fixes: d9dbb57b ("pcm: rate - rewrite the may_wait_for_avail_min callback for the rate plugin") Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_local.h | 2 +- src/pcm/pcm_plugin.c | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 8d25971f..ae0c44bf 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -1144,7 +1144,7 @@ static inline int snd_pcm_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes if (avail >= pcm->avail_min) return 0; if (pcm->fast_ops->may_wait_for_avail_min) - return pcm->fast_ops->may_wait_for_avail_min(pcm, avail); + return pcm->fast_ops->may_wait_for_avail_min(pcm->fast_op_arg, avail); return 1; } diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index ec64604c..b3af1fb7 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -597,8 +597,12 @@ int snd_pcm_plugin_may_wait_for_avail_min_conv( * a) the slave can provide contineous hw_ptr between periods * b) avail_min does not match one slave_period */ - snd_pcm_plugin_t *plugin = pcm->private_data; - snd_pcm_t *slave = plugin->gen.slave; + snd_pcm_generic_t *generic = pcm->private_data; + /* + * do not use snd_pcm_plugin_t pointer here + * this code is used from the generic plugins, too + */ + snd_pcm_t *slave = generic->slave; snd_pcm_uframes_t needed_slave_avail_min; snd_pcm_sframes_t available; @@ -622,7 +626,7 @@ int snd_pcm_plugin_may_wait_for_avail_min_conv( * This code is also used by extplug, but extplug does not allow to alter the sampling rate. */ if (conv) - needed_slave_avail_min = conv(pcm->fast_op_arg, needed_slave_avail_min); + needed_slave_avail_min = conv(pcm, needed_slave_avail_min); if (slave->avail_min != needed_slave_avail_min) { snd_pcm_sw_params_t *swparams; From 161f47da5f196c291ac0e11d066fa5ff5f79fa04 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 9 Nov 2022 14:37:45 +0100 Subject: [PATCH 020/267] include: pcm_old.h - use a macro for the symbol versioning Make the header file more readable and error prone. Signed-off-by: Jaroslav Kysela --- include/pcm_old.h | 133 ++++++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 65 deletions(-) diff --git a/include/pcm_old.h b/include/pcm_old.h index e6e050fc..a9f5308f 100644 --- a/include/pcm_old.h +++ b/include/pcm_old.h @@ -2,11 +2,14 @@ * Old ALSA 0.9.x API */ +#define ___symbol_version(name, version) \ + __asm__ (".symver " #name "," #name "@" version) + #ifdef ALSA_PCM_OLD_HW_PARAMS_API -asm(".symver snd_pcm_hw_params_get_access,snd_pcm_hw_params_get_access@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_access_first,snd_pcm_hw_params_set_access_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_access_last,snd_pcm_hw_params_set_access_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_access, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_access_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_access_last, "ALSA_0.9"); int snd_pcm_hw_params_get_access(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_test_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t val); @@ -16,9 +19,9 @@ snd_pcm_access_t snd_pcm_hw_params_set_access_last(snd_pcm_t *pcm, snd_pcm_hw_pa int snd_pcm_hw_params_set_access_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_mask_t *mask); void snd_pcm_hw_params_get_access_mask(snd_pcm_hw_params_t *params, snd_pcm_access_mask_t *mask); -asm(".symver snd_pcm_hw_params_get_format,snd_pcm_hw_params_get_format@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_format_first,snd_pcm_hw_params_set_format_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_format_last,snd_pcm_hw_params_set_format_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_format, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_format_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_format_last, "ALSA_0.9"); int snd_pcm_hw_params_get_format(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_test_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); @@ -28,9 +31,9 @@ snd_pcm_format_t snd_pcm_hw_params_set_format_last(snd_pcm_t *pcm, snd_pcm_hw_pa int snd_pcm_hw_params_set_format_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask); void snd_pcm_hw_params_get_format_mask(snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask); -asm(".symver snd_pcm_hw_params_get_subformat,snd_pcm_hw_params_get_subformat@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_subformat_first,snd_pcm_hw_params_set_subformat_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_subformat_last,snd_pcm_hw_params_set_subformat_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_subformat, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_subformat_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_subformat_last, "ALSA_0.9"); int snd_pcm_hw_params_test_subformat(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t val); int snd_pcm_hw_params_get_subformat(const snd_pcm_hw_params_t *params); @@ -40,12 +43,12 @@ snd_pcm_subformat_t snd_pcm_hw_params_set_subformat_last(snd_pcm_t *pcm, snd_pcm int snd_pcm_hw_params_set_subformat_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_mask_t *mask); void snd_pcm_hw_params_get_subformat_mask(snd_pcm_hw_params_t *params, snd_pcm_subformat_mask_t *mask); -asm(".symver snd_pcm_hw_params_get_channels,snd_pcm_hw_params_get_channels@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_channels_min,snd_pcm_hw_params_get_channels_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_channels_max,snd_pcm_hw_params_get_channels_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_channels_near,snd_pcm_hw_params_set_channels_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_channels_first,snd_pcm_hw_params_set_channels_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_channels_last,snd_pcm_hw_params_set_channels_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_channels, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_channels_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_channels_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_channels_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_channels_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_channels_last, "ALSA_0.9"); int snd_pcm_hw_params_get_channels(const snd_pcm_hw_params_t *params); unsigned int snd_pcm_hw_params_get_channels_min(const snd_pcm_hw_params_t *params); @@ -59,12 +62,12 @@ unsigned int snd_pcm_hw_params_set_channels_near(snd_pcm_t *pcm, snd_pcm_hw_para unsigned int snd_pcm_hw_params_set_channels_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); unsigned int snd_pcm_hw_params_set_channels_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -asm(".symver snd_pcm_hw_params_get_rate,snd_pcm_hw_params_get_rate@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_rate_min,snd_pcm_hw_params_get_rate_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_rate_max,snd_pcm_hw_params_get_rate_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_rate_near,snd_pcm_hw_params_set_rate_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_rate_first,snd_pcm_hw_params_set_rate_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_rate_last,snd_pcm_hw_params_set_rate_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_rate, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_rate_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_rate_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_rate_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_rate_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_rate_last, "ALSA_0.9"); int snd_pcm_hw_params_get_rate(const snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_get_rate_min(const snd_pcm_hw_params_t *params, int *dir); @@ -80,12 +83,12 @@ unsigned int snd_pcm_hw_params_set_rate_last(snd_pcm_t *pcm, snd_pcm_hw_params_t int snd_pcm_hw_params_set_rate_resample(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); int snd_pcm_hw_params_get_rate_resample(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); -asm(".symver snd_pcm_hw_params_get_period_time,snd_pcm_hw_params_get_period_time@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_time_min,snd_pcm_hw_params_get_period_time_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_time_max,snd_pcm_hw_params_get_period_time_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_time_near,snd_pcm_hw_params_set_period_time_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_time_first,snd_pcm_hw_params_set_period_time_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_time_last,snd_pcm_hw_params_set_period_time_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_time, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_time_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_time_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_time_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_time_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_time_last, "ALSA_0.9"); int snd_pcm_hw_params_get_period_time(const snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_get_period_time_min(const snd_pcm_hw_params_t *params, int *dir); @@ -99,12 +102,12 @@ unsigned int snd_pcm_hw_params_set_period_time_near(snd_pcm_t *pcm, snd_pcm_hw_p unsigned int snd_pcm_hw_params_set_period_time_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_set_period_time_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -asm(".symver snd_pcm_hw_params_get_period_size,snd_pcm_hw_params_get_period_size@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_size_min,snd_pcm_hw_params_get_period_size_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_size_max,snd_pcm_hw_params_get_period_size_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_size_near,snd_pcm_hw_params_set_period_size_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_size_first,snd_pcm_hw_params_set_period_size_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_size_last,snd_pcm_hw_params_set_period_size_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_size, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_size_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_size_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_size_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_size_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_size_last, "ALSA_0.9"); snd_pcm_sframes_t snd_pcm_hw_params_get_period_size(const snd_pcm_hw_params_t *params, int *dir); snd_pcm_uframes_t snd_pcm_hw_params_get_period_size_min(const snd_pcm_hw_params_t *params, int *dir); @@ -119,12 +122,12 @@ snd_pcm_uframes_t snd_pcm_hw_params_set_period_size_first(snd_pcm_t *pcm, snd_pc snd_pcm_uframes_t snd_pcm_hw_params_set_period_size_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); int snd_pcm_hw_params_set_period_size_integer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -asm(".symver snd_pcm_hw_params_get_periods,snd_pcm_hw_params_get_periods@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_periods_min,snd_pcm_hw_params_get_periods_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_periods_max,snd_pcm_hw_params_get_periods_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_periods_near,snd_pcm_hw_params_set_periods_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_periods_first,snd_pcm_hw_params_set_periods_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_periods_last,snd_pcm_hw_params_set_periods_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_periods, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_periods_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_periods_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_periods_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_periods_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_periods_last, "ALSA_0.9"); int snd_pcm_hw_params_get_periods(const snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_get_periods_min(const snd_pcm_hw_params_t *params, int *dir); @@ -139,12 +142,12 @@ unsigned int snd_pcm_hw_params_set_periods_first(snd_pcm_t *pcm, snd_pcm_hw_para unsigned int snd_pcm_hw_params_set_periods_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); int snd_pcm_hw_params_set_periods_integer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -asm(".symver snd_pcm_hw_params_get_buffer_time,snd_pcm_hw_params_get_buffer_time@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_time_min,snd_pcm_hw_params_get_buffer_time_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_time_max,snd_pcm_hw_params_get_buffer_time_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_time_near,snd_pcm_hw_params_set_buffer_time_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_time_first,snd_pcm_hw_params_set_buffer_time_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_time_last,snd_pcm_hw_params_set_buffer_time_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_time, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_time_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_time_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_time_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_time_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_time_last, "ALSA_0.9"); int snd_pcm_hw_params_get_buffer_time(const snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_get_buffer_time_min(const snd_pcm_hw_params_t *params, int *dir); @@ -158,12 +161,12 @@ unsigned int snd_pcm_hw_params_set_buffer_time_near(snd_pcm_t *pcm, snd_pcm_hw_p unsigned int snd_pcm_hw_params_set_buffer_time_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_set_buffer_time_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -asm(".symver snd_pcm_hw_params_get_buffer_size,snd_pcm_hw_params_get_buffer_size@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_size_min,snd_pcm_hw_params_get_buffer_size_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_size_max,snd_pcm_hw_params_get_buffer_size_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_size_near,snd_pcm_hw_params_set_buffer_size_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_size_first,snd_pcm_hw_params_set_buffer_size_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_size_last,snd_pcm_hw_params_set_buffer_size_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_size, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_size_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_size_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_size_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_size_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_size_last, "ALSA_0.9"); snd_pcm_sframes_t snd_pcm_hw_params_get_buffer_size(const snd_pcm_hw_params_t *params); snd_pcm_uframes_t snd_pcm_hw_params_get_buffer_size_min(const snd_pcm_hw_params_t *params); @@ -177,12 +180,12 @@ snd_pcm_uframes_t snd_pcm_hw_params_set_buffer_size_near(snd_pcm_t *pcm, snd_pcm snd_pcm_uframes_t snd_pcm_hw_params_set_buffer_size_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); snd_pcm_uframes_t snd_pcm_hw_params_set_buffer_size_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -asm(".symver snd_pcm_hw_params_get_tick_time,snd_pcm_hw_params_get_tick_time@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_tick_time_min,snd_pcm_hw_params_get_tick_time_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_tick_time_max,snd_pcm_hw_params_get_tick_time_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_tick_time_near,snd_pcm_hw_params_set_tick_time_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_tick_time_first,snd_pcm_hw_params_set_tick_time_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_tick_time_last,snd_pcm_hw_params_set_tick_time_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_tick_time, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_tick_time_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_tick_time_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_tick_time_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_tick_time_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_tick_time_last, "ALSA_0.9"); int snd_pcm_hw_params_get_tick_time(const snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_get_tick_time_min(const snd_pcm_hw_params_t *params, int *dir); @@ -201,14 +204,14 @@ unsigned int snd_pcm_hw_params_set_tick_time_last(snd_pcm_t *pcm, snd_pcm_hw_par #ifdef ALSA_PCM_OLD_SW_PARAMS_API -asm(".symver snd_pcm_sw_params_get_tstamp_mode,snd_pcm_sw_params_get_tstamp_mode@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_sleep_min,snd_pcm_sw_params_get_sleep_min@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_avail_min,snd_pcm_sw_params_get_avail_min@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_xfer_align,snd_pcm_sw_params_get_xfer_align@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_start_threshold,snd_pcm_sw_params_get_start_threshold@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_stop_threshold,snd_pcm_sw_params_get_stop_threshold@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_silence_threshold,snd_pcm_sw_params_get_silence_threshold@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_silence_size,snd_pcm_sw_params_get_silence_size@ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_tstamp_mode, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_sleep_min, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_avail_min, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_xfer_align, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_start_threshold, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_stop_threshold, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_silence_threshold, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_silence_size, "ALSA_0.9"); int snd_pcm_sw_params_set_tstamp_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_tstamp_t val); snd_pcm_tstamp_t snd_pcm_sw_params_get_tstamp_mode(const snd_pcm_sw_params_t *params); From 152983f01b0bc1178ea0d461ebf66e2d2a8e2e02 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 9 Nov 2022 15:04:06 +0100 Subject: [PATCH 021/267] include: alsa-symbols.h - use newer gcc symver function attribute Use the symver function attribute for newer gccs (version 11+). The symver function attribute was introduced probably earlier (gcc-10). We can fix that on demand later. Signed-off-by: Jaroslav Kysela --- include/alsa-symbols.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/alsa-symbols.h b/include/alsa-symbols.h index 344f021a..f8c49103 100644 --- a/include/alsa-symbols.h +++ b/include/alsa-symbols.h @@ -29,10 +29,17 @@ #define INTERNAL_CONCAT2_2(Pre, Post) Pre##Post #define INTERNAL(Name) INTERNAL_CONCAT2_2(__, Name) +#if __GNUC__ > 10 +#define symbol_version(real, name, version) \ + extern __typeof (real) real __attribute__((symver (#name "@" #version))) +#define default_symbol_version(real, name, version) \ + extern __typeof (real) real __attribute__((symver (#name "@@" #version))) +#else #define symbol_version(real, name, version) \ __asm__ (".symver " ASM_NAME(#real) "," ASM_NAME(#name) "@" #version) #define default_symbol_version(real, name, version) \ __asm__ (".symver " ASM_NAME(#real) "," ASM_NAME(#name) "@@" #version) +#endif #ifdef __clang__ #define EXPORT_SYMBOL __attribute__((visibility("default"))) From 78b20e3caa7bba930095e05f3f8cbe665204fcfd Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 14 Nov 2022 12:36:04 +0100 Subject: [PATCH 022/267] test: latency - use snd_pcm_format_physical_width() We need to allocate frames using the physical size not the sample bit size. Signed-off-by: Jaroslav Kysela --- src/topology/ctl.c | 7 ++++++- test/latency.c | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/topology/ctl.c b/src/topology/ctl.c index dd05424d..2c500ffc 100644 --- a/src/topology/ctl.c +++ b/src/topology/ctl.c @@ -17,9 +17,13 @@ Liam Girdwood */ +#define ALSA_PCM_OLD_HW_PARAMS_API 1 +#define ALSA_PCM_OLD_SW_PARAMS_API 1 +#include "../../include/asoundlib.h" #include "list.h" #include "tplg_local.h" + #define ENUM_VAL_SIZE (SNDRV_CTL_ELEM_ID_NAME_MAXLEN >> 2) struct ctl_access_elem { @@ -71,7 +75,8 @@ static int parse_access_values(snd_config_t *cfg, } } } - + return snd_pcm_hw_params_get_channels(NULL); + //return snd_pcm_hw_params_get_access(NULL); return 0; } diff --git a/test/latency.c b/test/latency.c index 298bab8a..95b3c0ee 100644 --- a/test/latency.c +++ b/test/latency.c @@ -354,7 +354,7 @@ long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *frames, size_t *max } // printf("read = %li\n", r); } else { - int frame_bytes = (snd_pcm_format_width(format) / 8) * channels; + int frame_bytes = (snd_pcm_format_physical_width(format) / 8) * channels; do { r = snd_pcm_readi(handle, buf, len); if (r > 0) { @@ -374,7 +374,7 @@ long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *frames, size_t *max long writebuf(snd_pcm_t *handle, char *buf, long len, size_t *frames) { long r; - int frame_bytes = (snd_pcm_format_width(format) / 8) * channels; + int frame_bytes = (snd_pcm_format_physical_width(format) / 8) * channels; while (len > 0) { r = snd_pcm_writei(handle, buf, len); @@ -579,7 +579,7 @@ int main(int argc, char *argv[]) loop_limit = loop_sec * rate; latency = latency_min - 4; - buffer = malloc((latency_max * snd_pcm_format_width(format) / 8) * 2); + buffer = malloc((latency_max * snd_pcm_format_physical_width(format) / 8) * 2); setscheduler(); From 425e4d1fbea4965ea0fb7529b1ee6cbb47eb7227 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 14 Nov 2022 14:34:46 +0100 Subject: [PATCH 023/267] pcm: fix the fast_ops pcm argument for fast_ops The fast_ops callback invocation must always pass the fast_op_arg as the pcm argument. Plugins expect that. Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 4 ++-- src/pcm/pcm_direct.c | 2 +- src/pcm/pcm_hw.c | 2 +- src/pcm/pcm_multi.c | 5 +++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 927aa055..2b966d44 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -1705,7 +1705,7 @@ int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2) assert(pcm1); assert(pcm2); if (pcm1->fast_ops->link) - err = pcm1->fast_ops->link(pcm1, pcm2); + err = pcm1->fast_ops->link(pcm1->fast_op_arg, pcm2); else err = -ENOSYS; return err; @@ -1722,7 +1722,7 @@ int snd_pcm_unlink(snd_pcm_t *pcm) assert(pcm); if (pcm->fast_ops->unlink) - err = pcm->fast_ops->unlink(pcm); + err = pcm->fast_ops->unlink(pcm->fast_op_arg); else err = -ENOSYS; return err; diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 4803b81b..3cc5305f 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -688,7 +688,7 @@ int snd_pcm_direct_check_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm) * so don't increment but just update to actual counter */ direct->recoveries = direct->shmptr->s.recoveries; - pcm->fast_ops->drop(pcm); + pcm->fast_ops->drop(pcm->fast_op_arg); /* trigger_tstamp update is missing in drop callbacks */ gettimestamp(&direct->trigger_tstamp, pcm->tstamp_type); /* no timer clear: diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 5dfe32ee..0588ce5e 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -838,7 +838,7 @@ static int snd_pcm_hw_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2) { if (pcm2->type != SND_PCM_TYPE_HW) { if (pcm2->fast_ops->link_slaves) - return pcm2->fast_ops->link_slaves(pcm2, pcm1); + return pcm2->fast_ops->link_slaves(pcm2->fast_op_arg, pcm1); return -ENOSYS; } return hw_link(pcm1, pcm2); diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index bec6d06f..3e7ce82c 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -759,8 +759,9 @@ static int snd_pcm_multi_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master) static int snd_pcm_multi_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2) { snd_pcm_multi_t *multi = pcm1->private_data; - if (multi->slaves[0].pcm->fast_ops->link) - return multi->slaves[0].pcm->fast_ops->link(multi->slaves[0].pcm, pcm2); + snd_pcm_t *main_pcm = multi->slaves[0].pcm; + if (main_pcm->fast_ops->link) + return main_pcm->fast_ops->link(main_pcm->fast_op_arg, pcm2); return -ENOSYS; } From 3e4aeba25bf4a4808183c4b64270f7321b436c13 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 14 Nov 2022 20:42:10 +0100 Subject: [PATCH 024/267] test: latency - add more realtime tests Add '-x' and '-X' tests and '-U' - I/O update mode based on the system timing. It may be required to check the position updates for the specific hardware. Print the real time / stream time differences. Also include code to make valgrind happy (including the wrong memory llocation for the stream buffer). Signed-off-by: Jaroslav Kysela --- test/latency.c | 163 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 151 insertions(+), 12 deletions(-) diff --git a/test/latency.c b/test/latency.c index 95b3c0ee..91bef1a1 100644 --- a/test/latency.c +++ b/test/latency.c @@ -33,10 +33,13 @@ #include #include #include +#include #include "../include/asoundlib.h" #include #include +typedef struct timespec timestamp_t; + char *pdevice = "hw:0,0"; char *cdevice = "hw:0,0"; snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; @@ -50,10 +53,40 @@ int loop_sec = 30; /* seconds */ int block = 0; /* block mode */ int use_poll = 0; int resample = 1; +int sys_latency = 0; /* data I/O: use system timings instead driver wakeups */ +int pos_dump = 0; /* dump positions */ +int realtime_check = 0; unsigned long loop_limit; +snd_pcm_uframes_t playback_buffer_size; snd_output_t *output = NULL; +static inline long long frames_to_micro(size_t frames) +{ + return (long long)((frames * 1000000LL) + (rate / 2)) / rate; +} + +void timestamp_now(timestamp_t *tstamp) +{ + if (clock_gettime(CLOCK_MONOTONIC_RAW, tstamp)) + printf("clock_gettime() failed\n"); +} + +long long timestamp_diff_micro(timestamp_t *tstamp) +{ + timestamp_t now, diff; + timestamp_now(&now); + if (tstamp->tv_nsec > now.tv_nsec) { + diff.tv_sec = now.tv_sec - tstamp->tv_sec - 1; + diff.tv_nsec = (now.tv_nsec + 1000000000L) - tstamp->tv_nsec; + } else { + diff.tv_sec = now.tv_sec - tstamp->tv_sec; + diff.tv_nsec = now.tv_nsec - tstamp->tv_nsec; + } + /* microseconds */ + return (diff.tv_sec * 1000000) + ((diff.tv_nsec + 500L) / 1000L); +} + int setparams_stream(snd_pcm_t *handle, snd_pcm_hw_params_t *params, const char *id) @@ -96,6 +129,14 @@ int setparams_stream(snd_pcm_t *handle, printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err); return -EINVAL; } + /* we do not want driver wakeups */ + if (sys_latency > 0 && snd_pcm_hw_params_can_disable_period_wakeup(params)) { + err = snd_pcm_hw_params_set_period_wakeup(handle, params, 0); + if (err < 0) { + printf("Cannot disable period wakeups for %s\n", id); + return err; + } + } return 0; } @@ -227,6 +268,7 @@ int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int *bufsize) goto __again; snd_pcm_hw_params_get_buffer_size(p_params, &p_size); + playback_buffer_size = p_size; if (p_psize * 2 < p_size) { snd_pcm_hw_params_get_periods_min(p_params, &val, NULL); if (val > 2) { @@ -390,7 +432,7 @@ long writebuf(snd_pcm_t *handle, char *buf, long len, size_t *frames) } return 0; } - + #define FILTERSWEEP_LFO_CENTER 2000. #define FILTERSWEEP_LFO_DEPTH 1800. #define FILTERSWEEP_LFO_FREQ 0.2 @@ -434,6 +476,19 @@ void applyeffect(char* buffer,int r) } } +static ssize_t get_avail(snd_pcm_t *pcm) +{ + ssize_t avail; + + while (1) { + avail = snd_pcm_avail(pcm); + if (avail == -EAGAIN) + continue; + break; + } + return avail; +} + void help(void) { int k; @@ -444,6 +499,7 @@ void help(void) "-C,--cdevice capture device\n" "-m,--min minimum latency in frames\n" "-M,--max maximum latency in frames\n" +"-U,--updates I/O updates in milliseconds (0 = off)\n" "-F,--frames frames to transfer\n" "-f,--format sample format\n" "-c,--channels channels\n" @@ -454,6 +510,8 @@ void help(void) "-b,--block block mode\n" "-p,--poll use poll (wait for event - reduces CPU usage)\n" "-e,--effect apply an effect (bandpass filter sweep)\n" +"-x,--posdump dump buffer positions\n" +"-X,--realtime do a realtime check (buffering)\n" ); printf("Recognized sample formats are:"); for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) { @@ -480,6 +538,7 @@ int main(int argc, char *argv[]) {"cdevice", 1, NULL, 'C'}, {"min", 1, NULL, 'm'}, {"max", 1, NULL, 'M'}, + {"updates", 1, NULL, 'U'}, {"frames", 1, NULL, 'F'}, {"format", 1, NULL, 'f'}, {"channels", 1, NULL, 'c'}, @@ -490,20 +549,23 @@ int main(int argc, char *argv[]) {"block", 0, NULL, 'b'}, {"poll", 0, NULL, 'p'}, {"effect", 0, NULL, 'e'}, + {"posdump", 0, NULL, 'x'}, + {"realtime", 0, NULL, 'X'}, {NULL, 0, NULL, 0}, }; snd_pcm_t *phandle, *chandle; char *buffer; int err, latency, morehelp; - int ok; + int ok, first_avail; snd_timestamp_t p_tstamp, c_tstamp; - ssize_t r; + ssize_t r, cap_avail, cap_avail_max, pbk_fill, pbk_fill_min; size_t frames_in, frames_out, in_max; + timestamp_t tstamp_start; int effect = 0; morehelp = 0; while (1) { int c; - if ((c = getopt_long(argc, argv, "hP:C:m:M:F:f:c:r:B:E:s:bpen", long_option, NULL)) < 0) + if ((c = getopt_long(argc, argv, "hP:C:m:M:U:F:f:c:r:B:E:s:bpenxX", long_option, NULL)) < 0) break; switch (c) { case 'h': @@ -525,6 +587,10 @@ int main(int argc, char *argv[]) err = atoi(optarg) / 2; latency_max = latency_min > err ? latency_min : err; break; + case 'U': + err = atoi(optarg); + sys_latency = err <= 0 ? 0 : err; + break; case 'f': format = snd_pcm_format_value(optarg); if (format == SND_PCM_FORMAT_UNKNOWN) { @@ -564,6 +630,12 @@ int main(int argc, char *argv[]) case 'n': resample = 0; break; + case 'x': + pos_dump = 1; + break; + case 'X': + realtime_check = 1; + break; } } @@ -579,15 +651,27 @@ int main(int argc, char *argv[]) loop_limit = loop_sec * rate; latency = latency_min - 4; - buffer = malloc((latency_max * snd_pcm_format_physical_width(format) / 8) * 2); + buffer = malloc((latency_max * 2 * snd_pcm_format_physical_width(format) / 8) * channels); + + /* I/O updates based on a system timer */ + if (sys_latency > 0) { + block = 0; + use_poll = 0; + } setscheduler(); printf("Playback device is %s\n", pdevice); printf("Capture device is %s\n", cdevice); - printf("Parameters are %iHz, %s, %i channels, %s mode\n", rate, snd_pcm_format_name(format), channels, block ? "blocking" : "non-blocking"); - printf("Poll mode: %s\n", use_poll ? "yes" : "no"); - printf("Loop limit is %lu frames, minimum latency = %i, maximum latency = %i\n", loop_limit, latency_min * 2, latency_max * 2); + printf("Parameters are %iHz, %s, %i channels, %s mode, use poll %s\n", + rate, snd_pcm_format_name(format), + channels, block ? "blocking" : "non-blocking", + use_poll ? "yes" : "no"); + printf("Loop limit is %lu frames, minimum latency = %i, maximum latency = %i", + loop_limit, latency_min * 2, latency_max * 2); + if (sys_latency > 0) + printf(", I/O updates %ims", sys_latency); + printf("\n"); if ((err = snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK, block ? 0 : SND_PCM_NONBLOCK)) < 0) { printf("Playback open error: %s\n", snd_strerror(err)); @@ -613,6 +697,9 @@ int main(int argc, char *argv[]) y[1] = (float*) malloc(channels*sizeof(float)); y[2] = (float*) malloc(channels*sizeof(float)); } + + cap_avail_max = 0; + pbk_fill_min = latency * 2; while (1) { frames_in = frames_out = 0; @@ -623,7 +710,7 @@ int main(int argc, char *argv[]) printf("Streams link error: %s\n", snd_strerror(err)); exit(0); } - if (snd_pcm_format_set_silence(format, buffer, latency*channels) < 0) { + if (snd_pcm_format_set_silence(format, buffer, latency * channels) < 0) { fprintf(stderr, "silence error\n"); break; } @@ -640,6 +727,8 @@ int main(int argc, char *argv[]) printf("Go error: %s\n", snd_strerror(err)); exit(0); } + if (realtime_check) + timestamp_now(&tstamp_start); gettimestamp(phandle, &p_tstamp); gettimestamp(chandle, &c_tstamp); #if 0 @@ -651,16 +740,54 @@ int main(int argc, char *argv[]) ok = 1; in_max = 0; + first_avail = 1; while (ok && frames_in < loop_limit) { - if (use_poll) { + cap_avail = latency; + if (sys_latency > 0) { + poll(NULL, 0, sys_latency); + cap_avail = get_avail(chandle); + if (cap_avail < 0) { + printf("Avail failed: %s\n", snd_strerror(cap_avail)); + ok = 0; + break; + } + if (first_avail && realtime_check) { + long long diff = timestamp_diff_micro(&tstamp_start); + long long pos = frames_to_micro(cap_avail); + printf("POS FIRST CHECK: c=%zd (rt=%lldus)\n", cap_avail, pos - diff); + first_avail = 0; + } + } else if (use_poll) { /* use poll to wait for next event */ snd_pcm_wait(chandle, 1000); } - if ((r = readbuf(chandle, buffer, latency, &frames_in, &in_max)) < 0) + if (pos_dump || realtime_check) { + if (sys_latency <= 0) + cap_avail = get_avail(chandle); + pbk_fill = get_avail(phandle); + if (pbk_fill >= 0) + pbk_fill = playback_buffer_size - pbk_fill; + if (cap_avail > cap_avail_max) + cap_avail_max = cap_avail; + if (pbk_fill >= 0 && pbk_fill < pbk_fill_min) + pbk_fill_min = pbk_fill; + if (realtime_check) { + long long diff = timestamp_diff_micro(&tstamp_start); + long long cap_pos = frames_to_micro(frames_in + cap_avail); + long long pbk_pos = frames_to_micro(frames_out - pbk_fill); + printf("POS: p=%zd (min=%zd, rt=%lldus) c=%zd (max=%zd, rt=%lldus)\n", + pbk_fill, pbk_fill_min, pbk_pos - diff, + cap_avail, cap_avail_max, cap_pos - diff); + } else if (pos_dump) { + printf("POS: p=%zd (min=%zd), c=%zd (max=%zd)\n", + pbk_fill, pbk_fill_min, cap_avail, cap_avail_max); + } + } + if ((r = readbuf(chandle, buffer, cap_avail, &frames_in, &in_max)) < 0) ok = 0; else { if (effect) - applyeffect(buffer,r); + applyeffect(buffer, r); if (writebuf(phandle, buffer, r, &frames_out) < 0) ok = 0; } @@ -677,6 +804,13 @@ int main(int argc, char *argv[]) if (p_tstamp.tv_sec == c_tstamp.tv_sec && p_tstamp.tv_usec == c_tstamp.tv_usec) printf("Hardware sync\n"); + if (realtime_check) { + long long diff = timestamp_diff_micro(&tstamp_start); + long long mtime = frames_to_micro(frames_in); + printf("Elapsed real time: %lldus\n", diff); + printf("Elapsed device time: %lldus\n", mtime); + printf("Test time diff (device - real): %lldus\n", mtime - diff); + } snd_pcm_drop(chandle); snd_pcm_nonblock(phandle, 0); snd_pcm_drain(phandle); @@ -698,5 +832,10 @@ int main(int argc, char *argv[]) } snd_pcm_close(phandle); snd_pcm_close(chandle); + snd_output_close(output); + snd_config_update_free_global(); + free(buffer); + free(pdevice); + free(cdevice); return 0; } From a0836e2af1f2c37b66e723d8caf399e80b76825b Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 14 Nov 2022 21:26:39 +0100 Subject: [PATCH 025/267] latency: add timestamps to the POS lines - remove first capture pos line - measure the snd_pcm_start() call, too Signed-off-by: Jaroslav Kysela --- test/latency.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/test/latency.c b/test/latency.c index 91bef1a1..3aff37c1 100644 --- a/test/latency.c +++ b/test/latency.c @@ -556,7 +556,7 @@ int main(int argc, char *argv[]) snd_pcm_t *phandle, *chandle; char *buffer; int err, latency, morehelp; - int ok, first_avail; + int ok; snd_timestamp_t p_tstamp, c_tstamp; ssize_t r, cap_avail, cap_avail_max, pbk_fill, pbk_fill_min; size_t frames_in, frames_out, in_max; @@ -723,12 +723,14 @@ int main(int argc, char *argv[]) break; } + if (realtime_check) + timestamp_now(&tstamp_start); if ((err = snd_pcm_start(chandle)) < 0) { printf("Go error: %s\n", snd_strerror(err)); exit(0); } if (realtime_check) - timestamp_now(&tstamp_start); + printf("[%lldus] Stream start\n", timestamp_diff_micro(&tstamp_start)); gettimestamp(phandle, &p_tstamp); gettimestamp(chandle, &c_tstamp); #if 0 @@ -740,7 +742,6 @@ int main(int argc, char *argv[]) ok = 1; in_max = 0; - first_avail = 1; while (ok && frames_in < loop_limit) { cap_avail = latency; if (sys_latency > 0) { @@ -751,12 +752,6 @@ int main(int argc, char *argv[]) ok = 0; break; } - if (first_avail && realtime_check) { - long long diff = timestamp_diff_micro(&tstamp_start); - long long pos = frames_to_micro(cap_avail); - printf("POS FIRST CHECK: c=%zd (rt=%lldus)\n", cap_avail, pos - diff); - first_avail = 0; - } } else if (use_poll) { /* use poll to wait for next event */ snd_pcm_wait(chandle, 1000); @@ -775,8 +770,8 @@ int main(int argc, char *argv[]) long long diff = timestamp_diff_micro(&tstamp_start); long long cap_pos = frames_to_micro(frames_in + cap_avail); long long pbk_pos = frames_to_micro(frames_out - pbk_fill); - printf("POS: p=%zd (min=%zd, rt=%lldus) c=%zd (max=%zd, rt=%lldus)\n", - pbk_fill, pbk_fill_min, pbk_pos - diff, + printf("[%lldus] POS: p=%zd (min=%zd, rt=%lldus) c=%zd (max=%zd, rt=%lldus)\n", + diff, pbk_fill, pbk_fill_min, pbk_pos - diff, cap_avail, cap_avail_max, cap_pos - diff); } else if (pos_dump) { printf("POS: p=%zd (min=%zd), c=%zd (max=%zd)\n", From 8b64f22459b6c55ec54f985f35ff701e18800616 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 15 Nov 2022 17:25:59 +0100 Subject: [PATCH 026/267] ucm: clarify set_defaults calls - do full reset in snd_use_case_mgr_reload Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index 28c60565..4f36648c 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -998,13 +998,14 @@ static int add_auto_values(snd_use_case_mgr_t *uc_mgr) /** * \brief execute default commands * \param uc_mgr Use case manager + * \param force Force run * \return zero on success, otherwise a negative error code */ -static int set_defaults(snd_use_case_mgr_t *uc_mgr) +static int set_defaults(snd_use_case_mgr_t *uc_mgr, bool force) { int err; - if (uc_mgr->default_list_executed) + if (!force && uc_mgr->default_list_executed) return 0; err = execute_sequence(uc_mgr, NULL, &uc_mgr->default_list, &uc_mgr->value_list, NULL, NULL); @@ -1351,7 +1352,7 @@ static int set_verb(snd_use_case_mgr_t *uc_mgr, int err; if (enable) { - err = set_defaults(uc_mgr); + err = set_defaults(uc_mgr, false); if (err < 0) return err; seq = &verb->enable_list; @@ -1435,6 +1436,22 @@ static int set_device(snd_use_case_mgr_t *uc_mgr, return err; } +/** + * \brief Do the full reset + * \param uc_mgr Use case manager + * \return zero on success, otherwise a negative error code + */ +static int do_reset(snd_use_case_mgr_t *uc_mgr) +{ + int err; + + err = set_defaults(uc_mgr, true); + INIT_LIST_HEAD(&uc_mgr->active_modifiers); + INIT_LIST_HEAD(&uc_mgr->active_devices); + uc_mgr->active_verb = NULL; + return err; +} + /** * \brief Parse open arguments * \param uc_mgr Use case manager @@ -1569,6 +1586,8 @@ int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr) pthread_mutex_lock(&uc_mgr->mutex); + do_reset(uc_mgr); + uc_mgr_free_verb(uc_mgr); uc_mgr->default_list_executed = 0; @@ -1633,8 +1652,7 @@ static int dismantle_use_case(snd_use_case_mgr_t *uc_mgr) } uc_mgr->active_verb = NULL; - err = execute_sequence(uc_mgr, NULL, &uc_mgr->default_list, - &uc_mgr->value_list, NULL, NULL); + err = set_defaults(uc_mgr, true); return err; } @@ -1649,11 +1667,7 @@ int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr) int err; pthread_mutex_lock(&uc_mgr->mutex); - err = execute_sequence(uc_mgr, NULL, &uc_mgr->default_list, - &uc_mgr->value_list, NULL, NULL); - INIT_LIST_HEAD(&uc_mgr->active_modifiers); - INIT_LIST_HEAD(&uc_mgr->active_devices); - uc_mgr->active_verb = NULL; + err = do_reset(uc_mgr); pthread_mutex_unlock(&uc_mgr->mutex); return err; } @@ -2512,7 +2526,7 @@ static int set_defaults_user(snd_use_case_mgr_t *uc_mgr, uc_error("error: wrong value for _defaults (%s)", value); return -EINVAL; } - return set_defaults(uc_mgr); + return set_defaults(uc_mgr, false); } static int handle_transition_verb(snd_use_case_mgr_t *uc_mgr, From 9649b64c6f72984c53f469dad8dd4221d307e06d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 22 Nov 2022 09:59:04 +0100 Subject: [PATCH 027/267] ucm: handle empty string also for ${env:} substitution Signed-off-by: Jaroslav Kysela --- src/ucm/ucm_subs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c index 2261bdc2..e62290ea 100644 --- a/src/ucm/ucm_subs.c +++ b/src/ucm/ucm_subs.c @@ -490,7 +490,13 @@ static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *i { char *e; - e = getenv(id); + if (*id == '-') { + e = getenv(id + 1); + if (e == NULL) + e = ""; + } else { + e = getenv(id); + } if (e) return strdup(e); return NULL; From f0f054517c05ff4ef7a1615851a686a3a202b9ff Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 23 Nov 2022 17:56:20 +0100 Subject: [PATCH 028/267] test: latency - add -y option (I/O usleep) Signed-off-by: Jaroslav Kysela --- test/latency.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/latency.c b/test/latency.c index 3aff37c1..161d1f68 100644 --- a/test/latency.c +++ b/test/latency.c @@ -52,6 +52,7 @@ int latency_max = 2048; /* in frames / 2 */ int loop_sec = 30; /* seconds */ int block = 0; /* block mode */ int use_poll = 0; +int usleep_val = 0; int resample = 1; int sys_latency = 0; /* data I/O: use system timings instead driver wakeups */ int pos_dump = 0; /* dump positions */ @@ -509,6 +510,8 @@ void help(void) "-s,--seconds duration of test in seconds\n" "-b,--block block mode\n" "-p,--poll use poll (wait for event - reduces CPU usage)\n" +"-y,--usleep sleep for the specified amount of microseconds between\n" +" stream updates (default 0 - off)\n" "-e,--effect apply an effect (bandpass filter sweep)\n" "-x,--posdump dump buffer positions\n" "-X,--realtime do a realtime check (buffering)\n" @@ -548,6 +551,7 @@ int main(int argc, char *argv[]) {"seconds", 1, NULL, 's'}, {"block", 0, NULL, 'b'}, {"poll", 0, NULL, 'p'}, + {"usleep", 1, NULL, 'y'}, {"effect", 0, NULL, 'e'}, {"posdump", 0, NULL, 'x'}, {"realtime", 0, NULL, 'X'}, @@ -565,7 +569,7 @@ int main(int argc, char *argv[]) morehelp = 0; while (1) { int c; - if ((c = getopt_long(argc, argv, "hP:C:m:M:U:F:f:c:r:B:E:s:bpenxX", long_option, NULL)) < 0) + if ((c = getopt_long(argc, argv, "hP:C:m:M:U:F:f:c:r:B:E:s:y:bpenxX", long_option, NULL)) < 0) break; switch (c) { case 'h': @@ -624,6 +628,9 @@ int main(int argc, char *argv[]) case 'p': use_poll = 1; break; + case 'y': + usleep_val = atoi(optarg); + break; case 'e': effect = 1; break; @@ -671,6 +678,8 @@ int main(int argc, char *argv[]) loop_limit, latency_min * 2, latency_max * 2); if (sys_latency > 0) printf(", I/O updates %ims", sys_latency); + else if (!block) + printf(", I/O usleep %ius", usleep_val); printf("\n"); if ((err = snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK, block ? 0 : SND_PCM_NONBLOCK)) < 0) { @@ -755,6 +764,8 @@ int main(int argc, char *argv[]) } else if (use_poll) { /* use poll to wait for next event */ snd_pcm_wait(chandle, 1000); + } else if (usleep_val > 0) { + usleep(usleep_val); } if (pos_dump || realtime_check) { if (sys_latency <= 0) From 536c93928bc57d941a7cd146dbcbd62df0be2d83 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 23 Nov 2022 19:45:15 +0100 Subject: [PATCH 029/267] test: latency - usleep should not be used in the block mode 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 161d1f68..1b4848d6 100644 --- a/test/latency.c +++ b/test/latency.c @@ -678,7 +678,7 @@ int main(int argc, char *argv[]) loop_limit, latency_min * 2, latency_max * 2); if (sys_latency > 0) printf(", I/O updates %ims", sys_latency); - else if (!block) + else if (!block && !use_poll) printf(", I/O usleep %ius", usleep_val); printf("\n"); @@ -764,7 +764,7 @@ int main(int argc, char *argv[]) } else if (use_poll) { /* use poll to wait for next event */ snd_pcm_wait(chandle, 1000); - } else if (usleep_val > 0) { + } else if (!block && usleep_val > 0) { usleep(usleep_val); } if (pos_dump || realtime_check) { From ea0850f3f3780652869c2b4550576894bc21684f Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 24 Nov 2022 08:33:47 +0100 Subject: [PATCH 030/267] test: latency - add --policy option to allow using SCHED_FIFO Signed-off-by: Jaroslav Kysela --- test/latency.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/test/latency.c b/test/latency.c index 1b4848d6..3b20e1c1 100644 --- a/test/latency.c +++ b/test/latency.c @@ -40,6 +40,7 @@ typedef struct timespec timestamp_t; +char *sched_policy = "rr"; char *pdevice = "hw:0,0"; char *cdevice = "hw:0,0"; snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; @@ -354,18 +355,24 @@ void gettimestamp(snd_pcm_t *handle, snd_timestamp_t *timestamp) void setscheduler(void) { struct sched_param sched_param; + int policy = SCHED_RR; + const char *spolicy = "Round Robin"; + if (strcasecmp(sched_policy, "fifo") == 0) { + policy = SCHED_FIFO; + spolicy = "FIFO"; + } if (sched_getparam(0, &sched_param) < 0) { printf("Scheduler getparam failed...\n"); return; } - sched_param.sched_priority = sched_get_priority_max(SCHED_RR); - if (!sched_setscheduler(0, SCHED_RR, &sched_param)) { - printf("Scheduler set to Round Robin with priority %i...\n", sched_param.sched_priority); + sched_param.sched_priority = sched_get_priority_max(policy); + if (!sched_setscheduler(0, policy, &sched_param)) { + printf("Scheduler set to %s with priority %i...\n", spolicy, sched_param.sched_priority); fflush(stdout); return; } - printf("!!!Scheduler set to Round Robin with priority %i FAILED!!!\n", sched_param.sched_priority); + printf("!!!Scheduler set to %s with priority %i FAILED!!!\n", spolicy, sched_param.sched_priority); } long timediff(snd_timestamp_t t1, snd_timestamp_t t2) @@ -515,6 +522,7 @@ void help(void) "-e,--effect apply an effect (bandpass filter sweep)\n" "-x,--posdump dump buffer positions\n" "-X,--realtime do a realtime check (buffering)\n" +"-O,--policy set scheduler policy (RR or FIFO)\n" ); printf("Recognized sample formats are:"); for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) { @@ -555,6 +563,7 @@ int main(int argc, char *argv[]) {"effect", 0, NULL, 'e'}, {"posdump", 0, NULL, 'x'}, {"realtime", 0, NULL, 'X'}, + {"policy", 1, NULL, 'O'}, {NULL, 0, NULL, 0}, }; snd_pcm_t *phandle, *chandle; @@ -569,7 +578,7 @@ int main(int argc, char *argv[]) morehelp = 0; while (1) { int c; - if ((c = getopt_long(argc, argv, "hP:C:m:M:U:F:f:c:r:B:E:s:y:bpenxX", long_option, NULL)) < 0) + if ((c = getopt_long(argc, argv, "hP:C:m:M:U:F:f:c:r:B:E:s:y:O:bpenxX", long_option, NULL)) < 0) break; switch (c) { case 'h': @@ -643,6 +652,9 @@ int main(int argc, char *argv[]) case 'X': realtime_check = 1; break; + case 'O': + sched_policy = optarg; + break; } } From 75f8e2e73e03f628a4f2ba55ca8aa3e9f50cdbd9 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 24 Nov 2022 10:55:32 +0100 Subject: [PATCH 031/267] test: latency - --policy option - allow using SCHED_OTHER Signed-off-by: Jaroslav Kysela --- test/latency.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/latency.c b/test/latency.c index 3b20e1c1..5e67015c 100644 --- a/test/latency.c +++ b/test/latency.c @@ -361,6 +361,9 @@ void setscheduler(void) if (strcasecmp(sched_policy, "fifo") == 0) { policy = SCHED_FIFO; spolicy = "FIFO"; + } else if (strcasecmp(sched_policy, "other") == 0) { + policy = SCHED_OTHER; + spolicy = "OTHER"; } if (sched_getparam(0, &sched_param) < 0) { printf("Scheduler getparam failed...\n"); @@ -522,7 +525,7 @@ void help(void) "-e,--effect apply an effect (bandpass filter sweep)\n" "-x,--posdump dump buffer positions\n" "-X,--realtime do a realtime check (buffering)\n" -"-O,--policy set scheduler policy (RR or FIFO)\n" +"-O,--policy set scheduler policy (RR, FIFO or OTHER)\n" ); printf("Recognized sample formats are:"); for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) { From d28e8cb29485cc93f741b01dc65893c798359963 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 29 Nov 2022 19:42:13 +0100 Subject: [PATCH 032/267] topology: ctl - remove the wrong (debug) code This code was commited by mistake. It was used for testing of ALSA_PCM_OLD_HW/SW_PARAMS_API. BugLink: https://github.com/thesofproject/sof/issues/6667 Related-to: 78b20e3c ("test: latency - use snd_pcm_format_physical_width()") Reported-by: Jaska Uimonen Signed-off-by: Jaroslav Kysela --- src/topology/ctl.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/topology/ctl.c b/src/topology/ctl.c index 2c500ffc..dd05424d 100644 --- a/src/topology/ctl.c +++ b/src/topology/ctl.c @@ -17,13 +17,9 @@ Liam Girdwood */ -#define ALSA_PCM_OLD_HW_PARAMS_API 1 -#define ALSA_PCM_OLD_SW_PARAMS_API 1 -#include "../../include/asoundlib.h" #include "list.h" #include "tplg_local.h" - #define ENUM_VAL_SIZE (SNDRV_CTL_ELEM_ID_NAME_MAXLEN >> 2) struct ctl_access_elem { @@ -75,8 +71,7 @@ static int parse_access_values(snd_config_t *cfg, } } } - return snd_pcm_hw_params_get_channels(NULL); - //return snd_pcm_hw_params_get_access(NULL); + return 0; } From e29413a2205099b2bffe584210d7a2b59f531f90 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 7 Dec 2022 14:49:48 +0100 Subject: [PATCH 033/267] ucm: execute_sysw - fix possible use-after-free Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index 4f36648c..2ff4d3f3 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -572,16 +572,17 @@ static int execute_sysw(const char *sysw) wlen = write(fd, value, len); myerrno = errno; close(fd); - free(s); if (ignore_error) - return 0; + goto __end; if (wlen != (ssize_t)len) { uc_error("unable to write '%s' to '%s': %s", value, path, strerror(myerrno)); return -EINVAL; } +__end: + free(s); return 0; } From 13e31fb1ecd5f666ffda09e87ef1aa53b4fae022 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 7 Dec 2022 14:54:30 +0100 Subject: [PATCH 034/267] alsa-lib: conf - fix possible use-after-free in get_char_skip_comments Signed-off-by: Jaroslav Kysela --- src/conf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conf.c b/src/conf.c index eb38c344..65f2e1a7 100644 --- a/src/conf.c +++ b/src/conf.c @@ -814,11 +814,12 @@ static int get_char_skip_comments(input_t *input) closedir(dirp); err = add_include_path(input->current, str); - free(str); if (err < 0) { SNDERR("Cannot add search dir %s", str); + free(str); return err; } + free(str); continue; } From 9f2c68cef716aa45942b502a42d94b84289f23bc Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 13 Dec 2022 10:31:32 +0100 Subject: [PATCH 035/267] pcm: route/softvol use snd_config_get_ireal vs get_real to handle also integers Link: https://lore.kernel.org/alsa-devel/f9a7ad6a256d4ad7a31642dcf875d436@axis.com/ Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_route.c | 11 +++-------- src/pcm/pcm_softvol.c | 4 ++-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c index d3e5f3ff..21b869cc 100644 --- a/src/pcm/pcm_route.c +++ b/src/pcm/pcm_route.c @@ -1182,15 +1182,10 @@ static int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_ent return -EINVAL; } - err = snd_config_get_real(jnode, &value); + err = snd_config_get_ireal(jnode, &value); if (err < 0) { - long v; - err = snd_config_get_integer(jnode, &v); - if (err < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - value = v; + SNDERR("Invalid type for %s", id); + return -EINVAL; } for (k = 0; (int) k < ss; k++) { diff --git a/src/pcm/pcm_softvol.c b/src/pcm/pcm_softvol.c index 99d0d32e..3e3dbc79 100644 --- a/src/pcm/pcm_softvol.c +++ b/src/pcm/pcm_softvol.c @@ -1190,7 +1190,7 @@ int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name, continue; } if (strcmp(id, "min_dB") == 0) { - err = snd_config_get_real(n, &min_dB); + err = snd_config_get_ireal(n, &min_dB); if (err < 0) { SNDERR("Invalid min_dB value"); return err; @@ -1198,7 +1198,7 @@ int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name, continue; } if (strcmp(id, "max_dB") == 0) { - err = snd_config_get_real(n, &max_dB); + err = snd_config_get_ireal(n, &max_dB); if (err < 0) { SNDERR("Invalid max_dB value"); return err; From 2e82060ebcd68f5ea1fe3dccc5a6518008132a54 Mon Sep 17 00:00:00 2001 From: Alan Young Date: Fri, 30 Dec 2022 16:48:14 +0000 Subject: [PATCH 036/267] pcm: rate: fix last_commit_ptr boundary wrapping Wrap last_commit_ptr using boundary. Was just wrapped to 0, which is correct only if the buffer size, and hence the boundary, is an integer multiple of the period size. Fixes: 467d69c5bc1 ("Fix CPU hog with combination of rate plugin") Fixes: 29041c52207 ("fix infinite draining of the rate plugin in SND_PCM_NONBLOCK mode") Link: https://lore.kernel.org/alsa-devel/20221230164814.901457-1-consult.awy@gmail.com/ Signed-off-by: Alan Young Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_rate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index e5d8eddd..c8076859 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -1018,7 +1018,7 @@ static int snd_pcm_rate_sync_playback_area(snd_pcm_t *pcm, snd_pcm_uframes_t app slave_size -= rate->gen.slave->period_size; rate->last_commit_ptr += pcm->period_size; if (rate->last_commit_ptr >= pcm->boundary) - rate->last_commit_ptr = 0; + rate->last_commit_ptr -= pcm->boundary; } return 0; } @@ -1163,7 +1163,7 @@ static int snd_pcm_rate_drain(snd_pcm_t *pcm) if (commit_err == 1) { rate->last_commit_ptr += psize; if (rate->last_commit_ptr >= pcm->boundary) - rate->last_commit_ptr = 0; + rate->last_commit_ptr -= pcm->boundary; } else if (commit_err == 0) { if (pcm->mode & SND_PCM_NONBLOCK) { commit_err = -EAGAIN; From d25ddafaa752666e14471230c1e01389e0440579 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 5 Jan 2023 15:42:31 +0100 Subject: [PATCH 037/267] ucm: fix possible memory leak in execute_sysw() Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ucm/main.c b/src/ucm/main.c index 2ff4d3f3..98762844 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -578,6 +578,7 @@ static int execute_sysw(const char *sysw) if (wlen != (ssize_t)len) { uc_error("unable to write '%s' to '%s': %s", value, path, strerror(myerrno)); + free(s); return -EINVAL; } From 436cd5b6d01edf7aedd58c209dc0d3b9adb111c6 Mon Sep 17 00:00:00 2001 From: Curtis Malainey Date: Fri, 13 Jan 2023 13:21:55 -0800 Subject: [PATCH 038/267] ucm: add existence checks to geti calls Right now in snd_use_case_geti you cannot tell if the item being queried exists or not when being checked. This also means the only way to check for the existence of something in the client of the library is to iterate over the list of mods/devs even if we know exactly the name we are looking for. We have functions that do exactly this internally so lets return this information in a logical fashion through geti. Also clean up some trailing white space nearby. Signed-off-by: Curtis Malainey Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 83 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 16 deletions(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index 98762844..ebbbf0b5 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -2415,12 +2415,73 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, return err; } +/** + * \brief check device status and existance + * \param uc_mgr Use case manager + * \param str device identifier + * \param value Value pointer + * \return Zero if success, otherwise a negative error code + */ +static long check_device(snd_use_case_mgr_t *uc_mgr, + const char *str, + long *value) +{ + struct use_case_device *dev; + int err; + + if (!str) { + return -EINVAL; + } + err = device_status(uc_mgr, str); + if (err > 0) { + *value = err; + err = 0; + } else if (err < 0) { + return err; + } + dev = find_device(uc_mgr, uc_mgr->active_verb, str, 0); + if (!dev) { + return -ENOENT; + } + return 0; +} + +/** + * \brief check modifier status and existance + * \param uc_mgr Use case manager + * \param str modifier identifier + * \param value Value pointer + * \return Zero if success, otherwise a negative error code + */ +static long check_modifier(snd_use_case_mgr_t *uc_mgr, + const char *str, + long *value) +{ + struct use_case_modifier *mod; + long err; + + if (!str) { + return -EINVAL; + } + err = modifier_status(uc_mgr, str); + if (err > 0) { + *value = err; + return 0; + } else if (err < 0) { + return err; + } + mod = find_modifier(uc_mgr, uc_mgr->active_verb, str, 0); + if (!mod) { + return -ENOENT; + } + return 0; +} /** * \brief Get current - integer * \param uc_mgr Use case manager - * \param identifier - * \return Value if success, otherwise a negative error code + * \param identifier + * \return Value if success, otherwise a negative error code */ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, const char *identifier, @@ -2444,25 +2505,15 @@ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, str = NULL; } if (check_identifier(identifier, "_devstatus")) { - if (!str) { - err = -EINVAL; + err = check_device(uc_mgr, str, value); + if (err < 0) { goto __end; } - err = device_status(uc_mgr, str); - if (err >= 0) { - *value = err; - err = 0; - } } else if (check_identifier(identifier, "_modstatus")) { - if (!str) { - err = -EINVAL; + err = check_modifier(uc_mgr, str, value); + if (err < 0) { goto __end; } - err = modifier_status(uc_mgr, str); - if (err >= 0) { - *value = err; - err = 0; - } #if 0 /* * enable this block if the else clause below is expanded to query From c083417b723a3485657234a21da3264419da1411 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 15 Jan 2023 18:32:20 +0100 Subject: [PATCH 039/267] ucm: simplify and fix the previous patch (geti) Use macro to maintain only one code. The status may be 0 or 1 - handle both values correctly. Also, fix the possible memory leak in snd_use_case_geti() - string str should be freed even when the error is returned. Fixes: 436cd5b6 ("ucm: add existence checks to geti calls") Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 90 +++++++++++--------------------------------------- 1 file changed, 20 insertions(+), 70 deletions(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index ebbbf0b5..346c5dc8 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -2415,67 +2415,23 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, return err; } -/** - * \brief check device status and existance - * \param uc_mgr Use case manager - * \param str device identifier - * \param value Value pointer - * \return Zero if success, otherwise a negative error code +/* + * a helper macro to obtain status and existence */ -static long check_device(snd_use_case_mgr_t *uc_mgr, - const char *str, - long *value) -{ - struct use_case_device *dev; - int err; - - if (!str) { - return -EINVAL; - } - err = device_status(uc_mgr, str); - if (err > 0) { - *value = err; - err = 0; - } else if (err < 0) { - return err; - } - dev = find_device(uc_mgr, uc_mgr->active_verb, str, 0); - if (!dev) { - return -ENOENT; - } - return 0; -} - -/** - * \brief check modifier status and existance - * \param uc_mgr Use case manager - * \param str modifier identifier - * \param value Value pointer - * \return Zero if success, otherwise a negative error code - */ -static long check_modifier(snd_use_case_mgr_t *uc_mgr, - const char *str, - long *value) -{ - struct use_case_modifier *mod; - long err; - - if (!str) { - return -EINVAL; - } - err = modifier_status(uc_mgr, str); - if (err > 0) { - *value = err; - return 0; - } else if (err < 0) { - return err; - } - mod = find_modifier(uc_mgr, uc_mgr->active_verb, str, 0); - if (!mod) { - return -ENOENT; - } - return 0; -} +#define geti(uc_mgr, status, ifind, str, value) ({ \ + long val = -EINVAL; \ + if (str) { \ + val = (status)((uc_mgr), (str)); \ + if (val >= 0) { \ + if ((ifind)((uc_mgr), (uc_mgr)->active_verb, (str), 0)) { \ + *(value) = val; \ + } else { \ + val = -ENOENT; \ + } \ + } \ + } \ + ; val; /* return value */ \ +}) /** * \brief Get current - integer @@ -2488,7 +2444,7 @@ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, long *value) { char *str, *str1; - long err; + int err; pthread_mutex_lock(&uc_mgr->mutex); if (0) { @@ -2505,21 +2461,15 @@ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, str = NULL; } if (check_identifier(identifier, "_devstatus")) { - err = check_device(uc_mgr, str, value); - if (err < 0) { - goto __end; - } + err = geti(uc_mgr, device_status, find_device, str, value); } else if (check_identifier(identifier, "_modstatus")) { - err = check_modifier(uc_mgr, str, value); - if (err < 0) { - goto __end; - } + err = geti(uc_mgr, modifier_status, find_modifier, str, value); #if 0 /* * enable this block if the else clause below is expanded to query * user-supplied values */ - } else if (identifier[0] == '_') + } else if (identifier[0] == '_') { err = -ENOENT; #endif } else From 496c4e031b49216c2fcda83c052cc243ba425a76 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 15 Jan 2023 18:57:56 +0100 Subject: [PATCH 040/267] ucm: fix geti() macro - return zero on success Fixes: c083417b ("ucm: simplify and fix the previous patch (geti)") Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ucm/main.c b/src/ucm/main.c index 346c5dc8..66ffd00d 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -2425,6 +2425,7 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, if (val >= 0) { \ if ((ifind)((uc_mgr), (uc_mgr)->active_verb, (str), 0)) { \ *(value) = val; \ + val = 0; \ } else { \ val = -ENOENT; \ } \ From 1fc1c1b7dcdcd60ff87737b03dc915eb1a8c2152 Mon Sep 17 00:00:00 2001 From: Sam James Date: Sun, 30 Oct 2022 18:06:37 +0000 Subject: [PATCH 041/267] utils/alsa.m4: include for exit() Clang 16 makes -Wimplicit-function-declaration an error by default and it's easier to just explicitly include rather than hope the ALSA headers always have it. Fixes: https://github.com/alsa-project/alsa-lib/pull/278 Signed-off-by: Sam James Signed-off-by: Jaroslav Kysela --- utils/alsa.m4 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/alsa.m4 b/utils/alsa.m4 index 40e50a19..461d8d5e 100644 --- a/utils/alsa.m4 +++ b/utils/alsa.m4 @@ -85,6 +85,7 @@ AC_LANG_PUSH([C]) AC_MSG_CHECKING([for libasound headers version >= $alsa_min_major_version.$alsa_min_minor_version.$alsa_min_micro_version ($min_alsa_version)]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include +#include ]], [[ /* ensure backward compatibility */ #if !defined(SND_LIB_MAJOR) && defined(SOUNDLIB_VERSION_MAJOR) @@ -130,6 +131,7 @@ AC_MSG_CHECKING([for libatopology (sound headers version > 1.1.9)]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include +#include ]], [[ /* ensure backward compatibility */ #if !defined(SND_LIB_VERSION) From d72da154a377926da6d03bda96427f3756226445 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 27 Mar 2019 15:45:04 +0100 Subject: [PATCH 042/267] topology: Parse ignore_suspend flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit XXX_VOICE_WAKEUP also known as 'ignore_suspend' is already processed by kernel in soc-topology, but there is no way of setting it via topology file like it's the case for other flags. Fix this by adding it to pcm, dai and link parsing mechanism. Signed-off-by: Piotr Maziarz Signed-off-by: Cezary Rojewski Signed-off-by: Amadeusz Sławiński Signed-off-by: Jaroslav Kysela --- src/topology/pcm.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/topology/pcm.c b/src/topology/pcm.c index 76085d05..b1e5dc59 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -929,6 +929,15 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, continue; } + if (strcmp(id, "ignore_suspend") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP, + &pcm->flag_mask, &pcm->flags); + if (err < 0) + return err; + continue; + } + /* private data */ if (strcmp(id, "data") == 0) { err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); @@ -1066,6 +1075,15 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, continue; } + if (strcmp(id, "ignore_suspend") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP, + &dai->flag_mask, &dai->flags); + if (err < 0) + return err; + continue; + } + /* private data */ if (strcmp(id, "data") == 0) { err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); @@ -1220,6 +1238,15 @@ int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg, continue; } + if (strcmp(id, "ignore_suspend") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP, + &link->flag_mask, &link->flags); + if (err < 0) + return err; + continue; + } + /* private data */ if (strcmp(id, "data") == 0) { err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); From e824f70d620404c2242d159d3e63074b5761e0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amadeusz=20S=C5=82awi=C5=84ski?= Date: Thu, 27 May 2021 14:24:49 +0200 Subject: [PATCH 043/267] topology: Add decompilation support for ignore_suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous patch added build support for ignore_suspend, however it should be also possible to decompile binary back to human readable form and not lose data. Signed-off-by: Amadeusz Sławiński Signed-off-by: Jaroslav Kysela --- src/topology/pcm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/topology/pcm.c b/src/topology/pcm.c index b1e5dc59..ed4a2d71 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -812,15 +812,17 @@ static int parse_flag(snd_config_t *n, unsigned int mask_in, static int save_flags(unsigned int flags, unsigned int mask, struct tplg_buf *dst, const char *pfx) { - static unsigned int flag_masks[3] = { + static unsigned int flag_masks[4] = { SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES, SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS, SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS, + SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP, }; - static const char *flag_ids[3] = { + static const char *flag_ids[4] = { "symmetric_rates", "symmetric_channels", "symmetric_sample_bits", + "ignore_suspend", }; unsigned int i; int err = 0; From e11ea20645b46d88c3de6d995512e67143bd7f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amadeusz=20S=C5=82awi=C5=84ski?= Date: Tue, 25 May 2021 17:09:21 +0200 Subject: [PATCH 044/267] topology: Parse ignore_suspend property for dapm widget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit snd_soc_tplg_dapm_widget has ignore_suspend field, but there is no way to set value for it, because we don't parse topology field for it. Fix this by adding parsing code. Fixes: https://github.com/alsa-project/alsa-lib/pull/230 Signed-off-by: Amadeusz Sławiński Signed-off-by: Jaroslav Kysela --- src/topology/dapm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/topology/dapm.c b/src/topology/dapm.c index f6a84a60..1c15ce8a 100644 --- a/src/topology/dapm.c +++ b/src/topology/dapm.c @@ -605,6 +605,17 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, continue; } + if (strcmp(id, "ignore_suspend") == 0) { + ival = snd_config_get_bool(n); + if (ival < 0) + return -EINVAL; + + widget->ignore_suspend = ival; + + tplg_dbg("\t%s: %s", id, val); + continue; + } + if (strcmp(id, "subseq") == 0) { if (tplg_get_integer(n, &ival, 0)) return -EINVAL; @@ -700,6 +711,9 @@ int tplg_save_dapm_widget(snd_tplg_t *tplg ATTRIBUTE_UNUSED, if (err >= 0 && widget->invert) err = tplg_save_printf(dst, pfx, "\tinvert %u\n", widget->invert); + if (err >= 0 && widget->ignore_suspend) + err = tplg_save_printf(dst, pfx, "\tignore_suspend %u\n", + widget->ignore_suspend); if (err >= 0 && widget->subseq) err = tplg_save_printf(dst, pfx, "\tsubseq %u\n", widget->subseq); From 2f9bbefb8d2a9766810eee3a80bdce4ec813f45a Mon Sep 17 00:00:00 2001 From: Gunnar Dibbern Date: Thu, 19 Jan 2023 16:32:42 +0100 Subject: [PATCH 045/267] pcm: hw - fix return code checking in snd_pcm_hw_hw_refine The isse was introduced in commit 7f2d6c3 Fixes: 7f2d6c3a ("pcm: hw: change rate range syntax") Fixes: https://github.com/alsa-project/alsa-lib/issues/271 Fixes: https://github.com/alsa-project/alsa-lib/pull/293 Signed-off-by: Gunnar Dibbern Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 0588ce5e..daa3e1ff 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -356,7 +356,7 @@ static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) if (hw->rates.min > 0) { err = _snd_pcm_hw_param_set_minmax(params, SND_PCM_HW_PARAM_RATE, hw->rates.min, 0, hw->rates.max + 1, -1); - + if (err < 0) return err; } From fea9fc229892695afa4e41598ebb79ea58ffb4b2 Mon Sep 17 00:00:00 2001 From: SASANO Takayoshi Date: Tue, 3 Jan 2023 11:13:19 +0900 Subject: [PATCH 046/267] configure: avoid libdl detecion on *BSD NetBSD and OpenBSD has no libdl FreeBSD and DragonFlyBSD has libdl but dummy These OSes are no need to use -ldl to use dlopen() Fixes: https://github.com/alsa-project/alsa-lib/pull/290 Signed-off-by: SASANO Takayoshi Signed-off-by: Jaroslav Kysela --- configure.ac | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 3989a743..ff82c6e8 100644 --- a/configure.ac +++ b/configure.ac @@ -255,9 +255,8 @@ AC_ARG_WITH(libdl, [ have_libdl="$withval" ], [ have_libdl="yes" ]) HAVE_LIBDL= if test "$have_libdl" = "yes"; then - AC_CHECK_LIB([dl], [dlsym], [HAVE_LIBDL="yes"]) + AC_SEARCH_LIBS([dlsym], [dl], [HAVE_LIBDL="yes"]) if test "$HAVE_LIBDL" = "yes" ; then - ALSA_DEPLIBS="$ALSA_DEPLIBS -ldl" AC_DEFINE([HAVE_LIBDL], 1, [Have libdl]) fi else From 3b4cdbdf19c70255d2d4c31b5a7d5ae2548eb9ef Mon Sep 17 00:00:00 2001 From: SASANO Takayoshi Date: Thu, 2 Feb 2023 15:58:47 +0900 Subject: [PATCH 047/267] type_compat.h: use ESPIPE instead of EPIPE when ESTRPIPE not defined Fixes: https://github.com/alsa-project/alsa-lib/pull/298 Signed-off-by: SASANO Takayoshi Signed-off-by: Jaroslav Kysela --- include/type_compat.h | 2 +- test/pcm.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/type_compat.h b/include/type_compat.h index 000057f1..b43379c0 100644 --- a/include/type_compat.h +++ b/include/type_compat.h @@ -26,7 +26,7 @@ #define EBADFD EBADF #endif #ifndef ESTRPIPE -#define ESTRPIPE EPIPE +#define ESTRPIPE ESPIPE #endif #ifndef __u16 diff --git a/test/pcm.c b/test/pcm.c index b8d4fe69..0484ea39 100644 --- a/test/pcm.c +++ b/test/pcm.c @@ -12,6 +12,10 @@ #include #include +#ifndef ESTRPIPE +#define ESTRPIPE ESPIPE +#endif + static char *device = "plughw:0,0"; /* playback device */ static snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ static unsigned int rate = 44100; /* stream rate */ From 44705e3a2050162c4f434d712d22bfe3f7fb5021 Mon Sep 17 00:00:00 2001 From: SASANO Takayoshi Date: Thu, 2 Feb 2023 16:00:45 +0900 Subject: [PATCH 048/267] test: BSD-like fixes - rename devname -> pcmdev, it conflicts *BSD function - replace -> and fix K&R style related warning - use config.h to determine include - add OpenBSD support and fix printf() warning - fix warning Fixes: https://github.com/alsa-project/alsa-lib/pull/298 Signed-off-by: SASANO Takayoshi Signed-off-by: Jaroslav Kysela --- test/audio_time.c | 3 +++ test/latency.c | 13 +++++++++++-- test/midifile.c | 6 +++--- test/namehint.c | 5 +++-- test/pcm-multi-thread.c | 8 ++++---- test/queue_timer.c | 4 ++-- 6 files changed, 26 insertions(+), 13 deletions(-) diff --git a/test/audio_time.c b/test/audio_time.c index 919bebe3..1c8481be 100644 --- a/test/audio_time.c +++ b/test/audio_time.c @@ -4,8 +4,11 @@ * helpful to verify the information reported by drivers. */ +#include "../include/config.h" #include +#if HAVE_MALLOC_H #include +#endif #include #include #include diff --git a/test/latency.c b/test/latency.c index 5e67015c..40c63e24 100644 --- a/test/latency.c +++ b/test/latency.c @@ -38,6 +38,15 @@ #include #include +#ifndef CLOCK_MONOTONIC_RAW +#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC +#endif + +#if defined(__OpenBSD__) +#define sched_getparam(pid, parm) (-1) +#define sched_setscheduler(pid, policy, parm) (-1) +#endif + typedef struct timespec timestamp_t; char *sched_policy = "rr"; @@ -839,9 +848,9 @@ int main(int argc, char *argv[]) if (ok) { #if 1 printf("Playback time = %li.%i, Record time = %li.%i, diff = %li\n", - p_tstamp.tv_sec, + (long)p_tstamp.tv_sec, (int)p_tstamp.tv_usec, - c_tstamp.tv_sec, + (long)c_tstamp.tv_sec, (int)c_tstamp.tv_usec, timediff(p_tstamp, c_tstamp)); #endif diff --git a/test/midifile.c b/test/midifile.c index 3d72b9f9..4862b199 100644 --- a/test/midifile.c +++ b/test/midifile.c @@ -71,7 +71,7 @@ #endif #include -#include +#include #include /*void exit(), free();*/ @@ -148,7 +148,7 @@ static void msginit (); static int msgleng (); static void msgadd (); static void biggermsg (); -static int eputc (unsigned char c); +static int eputc (); double mf_ticks2sec (unsigned long ticks, int division, unsigned long tempo); int mf_write_meta_event (); @@ -328,7 +328,7 @@ readtrack () /* read a track chunk */ if (Mf_interactive) { - Mf_toberead = MAXINT; + Mf_toberead = INT_MAX; } else { diff --git a/test/namehint.c b/test/namehint.c index e978d5ca..18bad1d8 100644 --- a/test/namehint.c +++ b/test/namehint.c @@ -4,7 +4,8 @@ int main(int argc, char *argv[]) { const char *iface = "pcm"; - char **hints, **n; + void **hints; + char **n; int err; if (argc > 1) @@ -12,7 +13,7 @@ int main(int argc, char *argv[]) err = snd_device_name_hint(-1, iface, &hints); if (err < 0) errx(1, "snd_device_name_hint error: %s", snd_strerror(err)); - n = hints; + n = (char **)hints; while (*n != NULL) { printf("%s\n", *n); n++; diff --git a/test/pcm-multi-thread.c b/test/pcm-multi-thread.c index da1b87c6..de178899 100644 --- a/test/pcm-multi-thread.c +++ b/test/pcm-multi-thread.c @@ -37,7 +37,7 @@ static char mode_suffix[] = { 'a', 's', 'h', 't', 'd', 'r' }; -static const char *devname = "default"; +static const char *pcmdev = "default"; static int stream = SND_PCM_STREAM_PLAYBACK; static int num_threads = 1; static int periodsize = 16 * 1024; @@ -127,7 +127,7 @@ static int parse_options(int argc, char **argv) while ((c = getopt(argc, argv, "D:r:f:p:b:s:t:m:vq")) >= 0) { switch (c) { case 'D': - devname = optarg; + pcmdev = optarg; break; case 'r': rate = atoi(optarg); @@ -213,9 +213,9 @@ int main(int argc, char **argv) if (parse_options(argc, argv)) return 1; - err = snd_pcm_open(&pcm, devname, stream, 0); + err = snd_pcm_open(&pcm, pcmdev, stream, 0); if (err < 0) { - fprintf(stderr, "cannot open pcm %s\n", devname); + fprintf(stderr, "cannot open pcm %s\n", pcmdev); return 1; } diff --git a/test/queue_timer.c b/test/queue_timer.c index c4ffb192..4e1fa967 100644 --- a/test/queue_timer.c +++ b/test/queue_timer.c @@ -100,9 +100,9 @@ main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) prevdiff = diff; fprintf(stderr, " real time: %12ld sec %8ld usec\nqueue time: %12ld sec %8ld usec\n diff: %12ld sec %8ld usec\n diffdiff: %12ld sec %8ld usec\n", - tv.tv_sec, tv.tv_usec, + (long)tv.tv_sec, tv.tv_usec, (long)rtime->tv_sec, (long)rtime->tv_nsec / 1000, - diff.tv_sec, diff.tv_usec, + (long)diff.tv_sec, diff.tv_usec, (long)diffdiff.tv_sec, (long)diffdiff.tv_usec); if (diffdiff.tv_usec > 5000 || From a8afb0449c6a898180982369f543b50393bb90b6 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sat, 25 Feb 2023 18:03:37 +0100 Subject: [PATCH 049/267] pcm: avoid endless loop in snd_pcm_sw_params_default() BugLink: https://lore.kernel.org/alsa-devel/167734349294.22.1388671545195719262@mailman-web.alsa-project.org/ Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_params.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pcm/pcm_params.c b/src/pcm/pcm_params.c index 8140c061..26c70132 100644 --- a/src/pcm/pcm_params.c +++ b/src/pcm/pcm_params.c @@ -2335,6 +2335,9 @@ static int snd_pcm_sw_params_default(snd_pcm_t *pcm, snd_pcm_sw_params_t *params params->silence_threshold = 0; params->silence_size = 0; params->boundary = pcm->buffer_size; + /* this should not happen (bad child?) */ + if (params->boundary == 0) + return -EINVAL; while (params->boundary * 2 <= LONG_MAX - pcm->buffer_size) params->boundary *= 2; return 0; @@ -2431,7 +2434,9 @@ int _snd_pcm_hw_params_internal(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) /* Default sw params */ memset(&sw, 0, sizeof(sw)); - snd_pcm_sw_params_default(pcm, &sw); + err = snd_pcm_sw_params_default(pcm, &sw); + if (err < 0) + return err; err = snd_pcm_sw_params(pcm, &sw); if (err < 0) return err; From b40fcda7fb5583ce516cd585a5f8f25ec00aa80e Mon Sep 17 00:00:00 2001 From: Maxython Date: Fri, 10 Feb 2023 23:00:51 +0300 Subject: [PATCH 050/267] compilation: fix ANDROID -> __ANDROID__ define detection Fixes: https://github.com/alsa-project/alsa-lib/pull/301/ Signed-off-by: Maxython Signed-off-by: Jaroslav Kysela --- src/conf.c | 2 +- src/pcm/pcm_direct.c | 2 +- src/ucm/parser.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/conf.c b/src/conf.c index 65f2e1a7..da51182e 100644 --- a/src/conf.c +++ b/src/conf.c @@ -4116,7 +4116,7 @@ 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__) #define SORTFUNC versionsort64 #else #define SORTFUNC alphasort64 diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 3cc5305f..8ee9b4db 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -44,7 +44,7 @@ * */ -#if !defined(__OpenBSD__) && !defined(__DragonFly__) +#if !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__) union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ diff --git a/src/ucm/parser.c b/src/ucm/parser.c index b0ac1006..c111661f 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -2915,7 +2915,7 @@ 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__) #define SORTFUNC versionsort64 #else #define SORTFUNC alphasort64 From 2115cdb4dc314d66e92a9e04413bcedc543da007 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 21 Apr 2023 16:12:24 +0200 Subject: [PATCH 051/267] pcm: hw: setup explicit silencing for snd_pcm_drain by default Some applications may not alignt transfers to the period size and also the driver developers may not follow the consequeces of the access beyond valid samples in the playback DMA buffer. To avoid clicks, fill a little silence at the end of the playback ring buffer when snd_pcm_drain() is called. Related: https://lore.kernel.org/alsa-devel/20230420113324.877164-2-oswald.buddenhagen@gmx.de/ Related: https://lore.kernel.org/alsa-devel/20230405201219.2197789-2-oswald.buddenhagen@gmx.de/ Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 33 ++++++++++++++++++------------- src/pcm/pcm_hw.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ src/pcm/pcm_local.h | 4 ++++ 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 2b966d44..88b13ed4 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -6167,6 +6167,25 @@ int snd_pcm_hw_params_get_min_align(const snd_pcm_hw_params_t *params, snd_pcm_u return 0; } +#ifndef DOXYGEN +void snd_pcm_sw_params_current_no_lock(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) +{ + params->proto = SNDRV_PCM_VERSION; + params->tstamp_mode = pcm->tstamp_mode; + params->tstamp_type = pcm->tstamp_type; + params->period_step = pcm->period_step; + params->sleep_min = 0; + params->avail_min = pcm->avail_min; + sw_set_period_event(params, pcm->period_event); + params->xfer_align = 1; + params->start_threshold = pcm->start_threshold; + params->stop_threshold = pcm->stop_threshold; + params->silence_threshold = pcm->silence_threshold; + params->silence_size = pcm->silence_size; + params->boundary = pcm->boundary; +} +#endif + /** * \brief Return current software configuration for a PCM * \param pcm PCM handle @@ -6183,19 +6202,7 @@ int snd_pcm_sw_params_current(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) return -EIO; } __snd_pcm_lock(pcm); /* forced lock due to pcm field changes */ - params->proto = SNDRV_PCM_VERSION; - params->tstamp_mode = pcm->tstamp_mode; - params->tstamp_type = pcm->tstamp_type; - params->period_step = pcm->period_step; - params->sleep_min = 0; - params->avail_min = pcm->avail_min; - sw_set_period_event(params, pcm->period_event); - params->xfer_align = 1; - params->start_threshold = pcm->start_threshold; - params->stop_threshold = pcm->stop_threshold; - params->silence_threshold = pcm->silence_threshold; - params->silence_size = pcm->silence_size; - params->boundary = pcm->boundary; + snd_pcm_sw_params_current_no_lock(pcm, params); __snd_pcm_unlock(pcm); return 0; } diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index daa3e1ff..d6bc7ee0 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -98,6 +98,8 @@ typedef struct { bool mmap_control_fallbacked; struct snd_pcm_sync_ptr *sync_ptr; + bool prepare_reset_sw_params; + int period_event; snd_timer_t *period_timer; struct pollfd period_timer_pfd; @@ -534,6 +536,7 @@ static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err); goto out; } + hw->prepare_reset_sw_params = false; if ((snd_pcm_tstamp_type_t) params->tstamp_type != pcm->tstamp_type) { if (hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 12)) { int on = (snd_pcm_tstamp_type_t) params->tstamp_type == @@ -660,7 +663,18 @@ static int snd_pcm_hw_hwsync(snd_pcm_t *pcm) static int snd_pcm_hw_prepare(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private_data; + snd_pcm_sw_params_t sw_params; int fd = hw->fd, err; + + if (hw->prepare_reset_sw_params) { + snd_pcm_sw_params_current_no_lock(pcm, &sw_params); + if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params) < 0) { + err = -errno; + SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err); + return err; + } + hw->prepare_reset_sw_params = false; + } if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE) < 0) { err = -errno; SYSMSG("SNDRV_PCM_IOCTL_PREPARE failed (%i)", err); @@ -718,7 +732,40 @@ static int snd_pcm_hw_drop(snd_pcm_t *pcm) static int snd_pcm_hw_drain(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private_data; + snd_pcm_sw_params_t sw_params; + snd_pcm_uframes_t silence_size; int err; + + if (pcm->stream != SND_PCM_STREAM_PLAYBACK) + goto __skip_silence; + /* compute end silence size, align to period size + extra time */ + snd_pcm_sw_params_current_no_lock(pcm, &sw_params); + if ((pcm->boundary % pcm->period_size) == 0) { + silence_size = pcm->period_size - (*pcm->appl.ptr % pcm->period_size); + if (silence_size == pcm->period_size) + silence_size = 0; + } else { + /* it not not easy to compute the period crossing point + * in this case because the period is not aligned to the boundary + * - use the full range (one period) in this case + */ + silence_size = pcm->period_size; + } + silence_size += pcm->rate / 10; /* 1/10th of second */ + if (sw_params.silence_size < silence_size) { + /* fill the silence soon as possible (in the bellow ioctl + * or the next period wake up) + */ + sw_params.silence_threshold = pcm->buffer_size; + sw_params.silence_size = silence_size; + if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params) < 0) { + err = -errno; + SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err); + return err; + } + hw->prepare_reset_sw_params = true; + } +__skip_silence: if (ioctl(hw->fd, SNDRV_PCM_IOCTL_DRAIN) < 0) { err = -errno; SYSMSG("SNDRV_PCM_IOCTL_DRAIN failed (%i)", err); diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index ae0c44bf..4a859cd1 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -366,6 +366,8 @@ struct _snd_pcm { snd1_pcm_hw_param_get_max #define snd_pcm_hw_param_name \ snd1_pcm_hw_param_name +#define snd_pcm_sw_params_current_no_lock \ + snd1_pcm_sw_params_current_no_lock int snd_pcm_new(snd_pcm_t **pcmp, snd_pcm_type_t type, const char *name, snd_pcm_stream_t stream, int mode); @@ -390,6 +392,8 @@ void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames); void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames); void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames); +void snd_pcm_sw_params_current_no_lock(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); + snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); snd_pcm_sframes_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size); snd_pcm_sframes_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size); From dd9cb31a90594e19f7a3ad53dc1a6344ddf78f7e Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 25 Apr 2023 14:33:46 +0200 Subject: [PATCH 052/267] pcm: hw: add drain_silence configuration keyword # Add silence in drain (-1 = auto /default/, 0 = off, > 0 milliseconds) [drain_silence INT] Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_hw.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index d6bc7ee0..6d7b2d48 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -111,6 +111,7 @@ typedef struct { int max; } rates; int channels; + int drain_silence; /* for chmap */ unsigned int chmap_caps; snd_pcm_chmap_query_t **chmap_override; @@ -738,8 +739,16 @@ static int snd_pcm_hw_drain(snd_pcm_t *pcm) if (pcm->stream != SND_PCM_STREAM_PLAYBACK) goto __skip_silence; - /* compute end silence size, align to period size + extra time */ + if (hw->drain_silence == 0) + goto __skip_silence; snd_pcm_sw_params_current_no_lock(pcm, &sw_params); + if (hw->drain_silence > 0) { + silence_size = (pcm->rate * hw->drain_silence) / 1000; + if (silence_size > pcm->buffer_size) + silence_size = pcm->buffer_size; + goto __manual_silence; + } + /* compute end silence size, align to period size + extra time */ if ((pcm->boundary % pcm->period_size) == 0) { silence_size = pcm->period_size - (*pcm->appl.ptr % pcm->period_size); if (silence_size == pcm->period_size) @@ -752,6 +761,7 @@ static int snd_pcm_hw_drain(snd_pcm_t *pcm) silence_size = pcm->period_size; } silence_size += pcm->rate / 10; /* 1/10th of second */ +__manual_silence: if (sw_params.silence_size < silence_size) { /* fill the silence soon as possible (in the bellow ioctl * or the next period wake up) @@ -1818,6 +1828,7 @@ pcm.name { [rate INT] # Restrict only to the given rate or [rate [INT INT]] # Restrict only to the given rate range (min max) [chmap MAP] # Override channel maps; MAP is a string array + [drain_silence INT] # Add silence in drain (-1 = auto /default/, 0 = off, > 0 milliseconds) } \endcode @@ -1850,7 +1861,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, long card = -1, device = 0, subdevice = -1; const char *str; int err, sync_ptr_ioctl = 0; - int min_rate = 0, max_rate = 0, channels = 0; + int min_rate = 0, max_rate = 0, channels = 0, drain_silence = -1; snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN; snd_config_t *n; int nonblock = 1; /* non-block per default */ @@ -1991,6 +2002,16 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, } continue; } + if (strcmp(id, "drain_silence") == 0) { + long val; + err = snd_config_get_integer(n, &val); + if (err < 0) { + SNDERR("Invalid type for %s", id); + goto fail; + } + drain_silence = val; + continue; + } SNDERR("Unknown field %s", id); err = -EINVAL; goto fail; @@ -2033,6 +2054,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, } if (chmap) hw->chmap_override = chmap; + hw->drain_silence = drain_silence; return 0; From 90e25887d24661a0bdc169b7b14525a97a4645c1 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 25 Apr 2023 14:36:19 +0200 Subject: [PATCH 053/267] pcm: hw: introduce SNDRV_PCM_INFO_PERFECT_DRAIN Handle the driver informing us that it is not necessary to set up silencing upon playback draining. This will be the case for drivers which are guaranteed to not read any samples beyond the application pointer. Signed-off-by: Jaroslav Kysela --- include/pcm.h | 1 + include/sound/uapi/asound.h | 1 + src/pcm/pcm.c | 23 +++++++++++++++++++++++ src/pcm/pcm_hw.c | 4 +++- src/pcm/pcm_local.h | 2 ++ 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/include/pcm.h b/include/pcm.h index b5a514fa..dd031b6c 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -722,6 +722,7 @@ int snd_pcm_hw_params_is_half_duplex(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_is_joint_duplex(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_can_sync_start(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *params); +int snd_pcm_hw_params_is_perfect_drain(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_supports_audio_wallclock_ts(const snd_pcm_hw_params_t *params); /* deprecated, use audio_ts_type */ int snd_pcm_hw_params_supports_audio_ts_type(const snd_pcm_hw_params_t *params, int type); int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params, diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index fc18c024..0b8834f2 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -281,6 +281,7 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_DOUBLE 0x00000004 /* Double buffering needed for PCM start/stop */ #define SNDRV_PCM_INFO_BATCH 0x00000010 /* double buffering */ #define SNDRV_PCM_INFO_SYNC_APPLPTR 0x00000020 /* need the explicit sync of appl_ptr update */ +#define SNDRV_PCM_INFO_PERFECT_DRAIN 0x00000040 /* silencing at the end of stream is not required */ #define SNDRV_PCM_INFO_INTERLEAVED 0x00000100 /* channels are interleaved */ #define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200 /* channels are not interleaved */ #define SNDRV_PCM_INFO_COMPLEX 0x00000400 /* complex frame organization (mmap only) */ diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 88b13ed4..d387da3d 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -3707,6 +3707,29 @@ int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *param return !!(params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP); } +/** + * \brief Check if hardware is capable of perfect drain + * \param params Configuration space + * \retval 0 Hardware doesn't do perfect drain + * \retval 1 Hardware does perfect drain + * + * 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. + * + * Perfect drain means that the hardware does not use samples + * beyond the stream application pointer. + */ +int snd_pcm_hw_params_is_perfect_drain(const snd_pcm_hw_params_t *params) +{ + assert(params); + if (CHECK_SANITY(params->info == ~0U)) { + SNDMSG("invalid PCM info field"); + return 0; /* FIXME: should be a negative error? */ + } + return !!(params->info & SNDRV_PCM_INFO_PERFECT_DRAIN); +} + /** * \brief Check if hardware supports audio wallclock timestamps * \param params Configuration space diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 6d7b2d48..37a18b86 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -99,6 +99,7 @@ typedef struct { struct snd_pcm_sync_ptr *sync_ptr; bool prepare_reset_sw_params; + bool perfect_drain; int period_event; snd_timer_t *period_timer; @@ -398,6 +399,7 @@ static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) params->info &= ~0xf0000000; if (pcm->tstamp_type != SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY) params->info |= SND_PCM_INFO_MONOTONIC; + hw->perfect_drain = !!(params->info & SND_PCM_INFO_PERFECT_DRAIN); return query_status_data(hw); } @@ -739,7 +741,7 @@ static int snd_pcm_hw_drain(snd_pcm_t *pcm) if (pcm->stream != SND_PCM_STREAM_PLAYBACK) goto __skip_silence; - if (hw->drain_silence == 0) + if (hw->drain_silence == 0 || hw->perfect_drain) goto __skip_silence; snd_pcm_sw_params_current_no_lock(pcm, &sw_params); if (hw->drain_silence > 0) { diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 4a859cd1..b039dda0 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -79,6 +79,8 @@ #define SND_PCM_INFO_DOUBLE SNDRV_PCM_INFO_DOUBLE /** device transfers samples in batch */ #define SND_PCM_INFO_BATCH SNDRV_PCM_INFO_BATCH +/** device does perfect drain (silencing not required) */ +#define SND_PCM_INFO_PERFECT_DRAIN SNDRV_PCM_INFO_PERFECT_DRAIN /** device accepts interleaved samples */ #define SND_PCM_INFO_INTERLEAVED SNDRV_PCM_INFO_INTERLEAVED /** device accepts non-interleaved samples */ From 264d6c43ee382bf4d6b1cf35c3920a8793c575e2 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 2 May 2023 10:34:41 +0200 Subject: [PATCH 054/267] pcm: hw: introduce SNDRV_PCM_HW_PARAMS_DRAIN_SILENCE The application may not require to touch the playback sample stream for the drain operation at all. In this case, the application is responsible to setup a silencing mechanism for the playback or another graceful stop (like using the rewind operation). Signed-off-by: Jaroslav Kysela --- include/pcm.h | 2 ++ include/sound/uapi/asound.h | 3 +++ src/pcm/pcm.c | 37 +++++++++++++++++++++++++++++++++++++ src/pcm/pcm_hw.c | 3 ++- src/pcm/pcm_local.h | 1 + 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/include/pcm.h b/include/pcm.h index dd031b6c..966d6ffd 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -822,6 +822,8 @@ int snd_pcm_hw_params_set_export_buffer(snd_pcm_t *pcm, snd_pcm_hw_params_t *par int snd_pcm_hw_params_get_export_buffer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); int snd_pcm_hw_params_set_period_wakeup(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); int snd_pcm_hw_params_get_period_wakeup(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); +int snd_pcm_hw_params_set_drain_silence(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); +int snd_pcm_hw_params_get_drain_silence(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); int snd_pcm_hw_params_get_period_time(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); int snd_pcm_hw_params_get_period_time_min(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index 0b8834f2..62a0364f 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -390,6 +390,9 @@ 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 + */ struct snd_interval { unsigned int min, max; diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index d387da3d..997a8a6a 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -4958,6 +4958,43 @@ int snd_pcm_hw_params_get_period_wakeup(snd_pcm_t *pcm, snd_pcm_hw_params_t *par return 0; } +/** + * \brief Restrict a configuration space to fill the end of playback stream with silence when drain() is invoked + * \param pcm PCM handle + * \param params Configuration space + * \param val 0 = disabled, 1 = enabled (default) fill the end of the playback stream with silence when drain() is invoked + * \return Zero on success, otherwise a negative error code. + * + * When disabled, the application should handle the end of stream gracefully + * (fill the silent samples to align to the period size plus some extra + * samples for hardware / driver without perfect drain). Note that the rewind + * may be used for this purpose or the sw_params silencing mechanism. + */ +int snd_pcm_hw_params_set_drain_silence(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val) +{ + assert(pcm && params); + if (val) + params->flags &= ~SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE; + else + params->flags |= SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE; + params->rmask = ~0; + return snd_pcm_hw_refine(pcm, params); +} + +/** + * \brief Extract drain with the filling of silence samples from a configuration space + * \param pcm PCM handle + * \param params Configuration space + * \param val 0 = disabled, 1 = enabled + * \return 0 otherwise a negative error code + */ +int snd_pcm_hw_params_get_drain_silence(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val) +{ + assert(pcm && params && val); + *val = params->flags & SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE ? 0 : 1; + return 0; +} + /** * \brief Extract period time from a configuration space * \param params Configuration space diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 37a18b86..180a3e84 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -399,7 +399,8 @@ static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) params->info &= ~0xf0000000; if (pcm->tstamp_type != SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY) params->info |= SND_PCM_INFO_MONOTONIC; - hw->perfect_drain = !!(params->info & SND_PCM_INFO_PERFECT_DRAIN); + hw->perfect_drain = !!(params->info & SND_PCM_INFO_PERFECT_DRAIN) || + !!(params->flags & SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE); return query_status_data(hw); } diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index b039dda0..abea6654 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -107,6 +107,7 @@ #define SND_PCM_HW_PARAMS_NORESAMPLE SNDRV_PCM_HW_PARAMS_NORESAMPLE #define SND_PCM_HW_PARAMS_EXPORT_BUFFER SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER #define SND_PCM_HW_PARAMS_NO_PERIOD_WAKEUP SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP +#define SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE SNDRV_PCM_HW_PARAMS_NO_DRAIN_SILENCE #define SND_PCM_INFO_MONOTONIC 0x80000000 From 1de6f99c7d058634b5550fa59c01f3b6546f830a Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 3 May 2023 13:43:17 +0200 Subject: [PATCH 055/267] pcm: add new symbols to Versions.in Signed-off-by: Jaroslav Kysela --- src/Versions.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Versions.in b/src/Versions.in index 85031b38..ed737681 100644 --- a/src/Versions.in +++ b/src/Versions.in @@ -140,3 +140,11 @@ ALSA_1.2.6 { @SYMBOL_PREFIX@_snd_safe_strto*; } ALSA_1.1.6; + +ALSA_1.2.9 { + global: + + @SYMBOL_PREFIX@snd_pcm_hw_params_is_perfect_drain; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_drain_silence; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_drain_silence; +} ALSA_1.2.6; From e2d9e411744dede5a71e04a107cdc0ab7c1d0ed9 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 2 May 2023 16:56:49 +0200 Subject: [PATCH 056/267] pcm: improve handling for snd_pcm_wait() The snd_pcm_wait() function is called also internally from the various plugins to wait for the drain with -1 and from i/o routines in pcm.c. Define two special negative timeout values to distinguish the drain and i/o wait and calculate the maximal timeout according the wait place. Fixes: https://github.com/alsa-project/alsa-lib/issues/228 Signed-off-by: Jaroslav Kysela --- include/pcm.h | 7 +++++++ src/pcm/pcm.c | 44 +++++++++++++++++++++++++++++++++++++++++--- src/pcm/pcm_dmix.c | 2 +- src/pcm/pcm_dshare.c | 2 +- src/pcm/pcm_dsnoop.c | 2 +- src/pcm/pcm_ioplug.c | 2 +- src/pcm/pcm_rate.c | 2 +- src/pcm/pcm_share.c | 2 +- src/pcm/pcm_shm.c | 2 +- 9 files changed, 55 insertions(+), 10 deletions(-) diff --git a/include/pcm.h b/include/pcm.h index 966d6ffd..25cd12d1 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -498,6 +498,13 @@ typedef union _snd_pcm_sync_id { unsigned int id32[4]; } snd_pcm_sync_id_t; +/** Infinite wait for snd_pcm_wait() */ +#define SND_PCM_WAIT_INFINITE (-1) +/** Wait for next i/o in snd_pcm_wait() */ +#define SND_PCM_WAIT_IO (-10001) +/** Wait for drain in snd_pcm_wait() */ +#define SND_PCM_WAIT_DRAIN (-10002) + /** #SND_PCM_TYPE_METER scope handle */ typedef struct _snd_pcm_scope snd_pcm_scope_t; diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 997a8a6a..01c624d3 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -2834,7 +2834,8 @@ int snd_pcm_open_named_slave(snd_pcm_t **pcmp, const char *name, * \brief Wait for a PCM to become ready * \param pcm PCM handle * \param timeout maximum time in milliseconds to wait, - * a negative value means infinity + * a -1 value means infinity (SND_PCM_WAIT_INFINITE), + * see also SND_PCM_WAIT_IO and SND_PCM_WAIT_DRAIN * \return a positive value on success otherwise a negative error code * (-EPIPE for the xrun and -ESTRPIPE for the suspended status, * others for general errors) @@ -2869,6 +2870,37 @@ int __snd_pcm_wait_in_lock(snd_pcm_t *pcm, int timeout) return snd_pcm_wait_nocheck(pcm, timeout); } +static int __snd_pcm_wait_io_timeout(snd_pcm_t *pcm) +{ + int timeout; + + /* period size is the time boundary */ + timeout = (pcm->period_size * 1000ULL) / pcm->rate; + /* should not happen */ + if (timeout < 0) + timeout = 0; + /* add extra time of 200 milliseconds */ + timeout += 200; + return timeout; +} + +static int __snd_pcm_wait_drain_timeout(snd_pcm_t *pcm) +{ + int timeout; + + /* for capture, there's no reason to wait, just one iteration */ + if (snd_pcm_stream(pcm) == SND_PCM_STREAM_CAPTURE) + return 0; + /* result is in milliseconds */ + timeout = (snd_pcm_mmap_playback_delay(pcm) * 1000LL) / pcm->rate; + /* should not happen */ + if (timeout < 0) + timeout = 0; + /* add extra time of 200 milliseconds */ + timeout += 200; + return timeout; +} + /* * like snd_pcm_wait() but doesn't check mmap_avail before calling poll() * @@ -2895,6 +2927,12 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout) SNDMSG("invalid poll descriptors %d\n", err); return -EIO; } + if (timeout == SND_PCM_WAIT_IO) + timeout = __snd_pcm_wait_io_timeout(pcm); + else if (timeout == SND_PCM_WAIT_DRAIN) + timeout = __snd_pcm_wait_drain_timeout(pcm); + else if (timeout < -1) + SNDMSG("invalid snd_pcm_wait timeout argument %d\n", timeout); do { __snd_pcm_unlock(pcm->fast_op_arg); err_poll = poll(pfd, npfds, timeout); @@ -7525,7 +7563,7 @@ snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_ goto _end; } - err = __snd_pcm_wait_in_lock(pcm, -1); + err = __snd_pcm_wait_in_lock(pcm, SND_PCM_WAIT_IO); if (err < 0) break; goto _again; @@ -7594,7 +7632,7 @@ snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area goto _end; } - err = snd_pcm_wait_nocheck(pcm, -1); + err = snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_IO); if (err < 0) break; goto _again; diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c index b93c955c..b0d0a43e 100644 --- a/src/pcm/pcm_dmix.c +++ b/src/pcm/pcm_dmix.c @@ -623,7 +623,7 @@ static int __snd_pcm_dmix_drain(snd_pcm_t *pcm) if (dmix->state == SND_PCM_STATE_DRAINING) { snd_pcm_dmix_sync_area(pcm); if ((pcm->mode & SND_PCM_NONBLOCK) == 0) { - snd_pcm_wait_nocheck(pcm, -1); + snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_DRAIN); snd_pcm_direct_clear_timer_queue(dmix); /* force poll to wait */ } diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c index d7be8fbb..454b39a9 100644 --- a/src/pcm/pcm_dshare.c +++ b/src/pcm/pcm_dshare.c @@ -400,7 +400,7 @@ static int __snd_pcm_dshare_drain(snd_pcm_t *pcm) } if (dshare->state == SND_PCM_STATE_DRAINING) { snd_pcm_dshare_sync_area(pcm); - snd_pcm_wait_nocheck(pcm, -1); + snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_DRAIN); snd_pcm_direct_clear_timer_queue(dshare); /* force poll to wait */ switch (snd_pcm_state(dshare->spcm)) { diff --git a/src/pcm/pcm_dsnoop.c b/src/pcm/pcm_dsnoop.c index cb2eee32..d3ce300c 100644 --- a/src/pcm/pcm_dsnoop.c +++ b/src/pcm/pcm_dsnoop.c @@ -301,7 +301,7 @@ static int __snd_pcm_dsnoop_drain(snd_pcm_t *pcm) break; if (pcm->mode & SND_PCM_NONBLOCK) return -EAGAIN; - __snd_pcm_wait_in_lock(pcm, -1); + __snd_pcm_wait_in_lock(pcm, SND_PCM_WAIT_DRAIN); } pcm->stop_threshold = stop_threshold; return snd_pcm_dsnoop_drop(pcm); diff --git a/src/pcm/pcm_ioplug.c b/src/pcm/pcm_ioplug.c index 98184398..09454a29 100644 --- a/src/pcm/pcm_ioplug.c +++ b/src/pcm/pcm_ioplug.c @@ -522,7 +522,7 @@ static int ioplug_drain_via_poll(snd_pcm_t *pcm) /* in non-blocking mode, let application to poll() by itself */ if (io->data->nonblock) return -EAGAIN; - if (snd_pcm_wait_nocheck(pcm, -1) < 0) + if (snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_DRAIN) < 0) break; } diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index c8076859..44d7e760 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -1146,7 +1146,7 @@ static int snd_pcm_rate_drain(snd_pcm_t *pcm) snd_pcm_uframes_t psize, spsize; int err; - err = __snd_pcm_wait_in_lock(rate->gen.slave, -1); + err = __snd_pcm_wait_in_lock(rate->gen.slave, SND_PCM_WAIT_DRAIN); if (err < 0) break; if (size > pcm->period_size) { diff --git a/src/pcm/pcm_share.c b/src/pcm/pcm_share.c index ac088472..677f4132 100644 --- a/src/pcm/pcm_share.c +++ b/src/pcm/pcm_share.c @@ -1194,7 +1194,7 @@ static int snd_pcm_share_drain(snd_pcm_t *pcm) _snd_pcm_share_update(pcm); Pthread_mutex_unlock(&slave->mutex); if (!(pcm->mode & SND_PCM_NONBLOCK)) - snd_pcm_wait(pcm, -1); + snd_pcm_wait(pcm, SND_PCM_WAIT_DRAIN); return 0; default: assert(0); diff --git a/src/pcm/pcm_shm.c b/src/pcm/pcm_shm.c index 12828958..f0bfd934 100644 --- a/src/pcm/pcm_shm.c +++ b/src/pcm/pcm_shm.c @@ -495,7 +495,7 @@ static int snd_pcm_shm_drain(snd_pcm_t *pcm) if (err < 0) return err; if (!(pcm->mode & SND_PCM_NONBLOCK)) - snd_pcm_wait(pcm, -1); + snd_pcm_wait(pcm, SND_PCM_WAIT_DRAIN); return err; } From d6d5982d3ae68b72a1a53420b77ac269ef93d40e Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 3 May 2023 11:42:35 +0200 Subject: [PATCH 057/267] pcm: add SND_PCM_EINTR open mode Add possibility to return -EINTR instead waiting for the event. The applications may want to handle -EINTR condition themselves. BugLink: https://github.com/alsa-project/alsa-lib/issues/228 Signed-off-by: Jaroslav Kysela --- include/pcm.h | 2 ++ src/pcm/pcm.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/pcm.h b/include/pcm.h index 25cd12d1..a29ea980 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -393,6 +393,8 @@ typedef long snd_pcm_sframes_t; #define SND_PCM_NONBLOCK 0x00000001 /** Async notification (flag for open mode) \hideinitializer */ #define SND_PCM_ASYNC 0x00000002 +/** Return EINTR instead blocking (wait operation) */ +#define SND_PCM_EINTR 0x00000080 /** In an abort state (internal, not allowed for open) */ #define SND_PCM_ABORT 0x00008000 /** Disable automatic (but not forced!) rate resamplinig */ diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 01c624d3..41a36b36 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -2938,7 +2938,7 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout) err_poll = poll(pfd, npfds, timeout); __snd_pcm_lock(pcm->fast_op_arg); if (err_poll < 0) { - if (errno == EINTR && !PCMINABORT(pcm)) + if (errno == EINTR && !PCMINABORT(pcm) && !(pcm->mode & SND_PCM_EINTR)) continue; return -errno; } From 507d906abbf7f314d59a0b470ca6c29ca958b63c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 3 May 2023 11:48:35 +0200 Subject: [PATCH 058/267] pcm: add SND_CTL_EINTR open mode Add possibility to return -EINTR instead waiting for the event. The applications may want to handle -EINTR condition themselves. BugLink: https://github.com/alsa-project/alsa-lib/issues/228 Signed-off-by: Jaroslav Kysela --- include/control.h | 3 +++ src/control/control.c | 3 ++- src/control/control_ext.c | 2 +- src/control/control_hw.c | 2 +- src/control/control_local.h | 3 ++- src/control/control_remap.c | 4 ++-- src/control/control_shm.c | 2 +- src/control/hcontrol.c | 2 +- 8 files changed, 13 insertions(+), 8 deletions(-) diff --git a/include/control.h b/include/control.h index e386ecec..d0c40d2f 100644 --- a/include/control.h +++ b/include/control.h @@ -356,6 +356,9 @@ typedef enum _snd_ctl_type { /** Read only (flag for open mode) \hideinitializer */ #define SND_CTL_READONLY 0x0004 +/** Return EINTR instead blocking (flag for open mode) \hideinitializer */ +#define SND_CTL_EINTR 0x0080 + /** CTL handle */ typedef struct _snd_ctl snd_ctl_t; diff --git a/src/control/control.c b/src/control/control.c index 91415b51..c4ca74ec 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -265,13 +265,14 @@ int snd_ctl_nonblock(snd_ctl_t *ctl, int nonblock) } #ifndef DOC_HIDDEN -int snd_ctl_new(snd_ctl_t **ctlp, snd_ctl_type_t type, const char *name) +int snd_ctl_new(snd_ctl_t **ctlp, snd_ctl_type_t type, const char *name, int mode) { snd_ctl_t *ctl; ctl = calloc(1, sizeof(*ctl)); if (!ctl) return -ENOMEM; ctl->type = type; + ctl->mode = mode; if (name) ctl->name = strdup(name); INIT_LIST_HEAD(&ctl->async_handlers); diff --git a/src/control/control_ext.c b/src/control/control_ext.c index 515f7882..0dcc8538 100644 --- a/src/control/control_ext.c +++ b/src/control/control_ext.c @@ -716,7 +716,7 @@ int snd_ctl_ext_create(snd_ctl_ext_t *ext, const char *name, int mode) return -ENXIO; } - err = snd_ctl_new(&ctl, SND_CTL_TYPE_EXT, name); + err = snd_ctl_new(&ctl, SND_CTL_TYPE_EXT, name, mode); if (err < 0) return err; diff --git a/src/control/control_hw.c b/src/control/control_hw.c index 0ed9f6b2..02636910 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -444,7 +444,7 @@ int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode) hw->fd = fd; hw->protocol = ver; - err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name); + err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name, mode); if (err < 0) { close(fd); free(hw); diff --git a/src/control/control_local.h b/src/control/control_local.h index 973fa04c..b84dc429 100644 --- a/src/control/control_local.h +++ b/src/control/control_local.h @@ -62,6 +62,7 @@ struct _snd_ctl { snd_ctl_type_t type; const snd_ctl_ops_t *ops; void *private_data; + int mode; int nonblock; int poll_fd; struct list_head async_handlers; @@ -93,7 +94,7 @@ struct _snd_hctl { /* make local functions really local */ #define snd_ctl_new snd1_ctl_new -int snd_ctl_new(snd_ctl_t **ctlp, snd_ctl_type_t type, const char *name); +int snd_ctl_new(snd_ctl_t **ctlp, snd_ctl_type_t type, const char *name, int mode); int _snd_ctl_poll_descriptor(snd_ctl_t *ctl); #define _snd_ctl_async_descriptor _snd_ctl_poll_descriptor int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode); diff --git a/src/control/control_remap.c b/src/control/control_remap.c index 4914f960..5bc56608 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -1148,7 +1148,7 @@ 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 ATTRIBUTE_UNUSED) + snd_config_t *map, snd_ctl_t *child, int mode) { snd_ctl_remap_t *priv; snd_ctl_t *ctl; @@ -1195,7 +1195,7 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema priv->numid_remap_active = priv->map_items > 0; priv->child = child; - err = snd_ctl_new(&ctl, SND_CTL_TYPE_REMAP, name); + err = snd_ctl_new(&ctl, SND_CTL_TYPE_REMAP, name, mode); if (err < 0) { result = err; goto _err; diff --git a/src/control/control_shm.c b/src/control/control_shm.c index d7297e6c..3d1555ee 100644 --- a/src/control/control_shm.c +++ b/src/control/control_shm.c @@ -502,7 +502,7 @@ int snd_ctl_shm_open(snd_ctl_t **handlep, const char *name, const char *sockname shm->socket = sock; shm->ctrl = ctrl; - err = snd_ctl_new(&ctl, SND_CTL_TYPE_SHM, name); + err = snd_ctl_new(&ctl, SND_CTL_TYPE_SHM, name, mode); if (err < 0) { result = err; goto _err; diff --git a/src/control/hcontrol.c b/src/control/hcontrol.c index 0cac8956..18ddc16b 100644 --- a/src/control/hcontrol.c +++ b/src/control/hcontrol.c @@ -696,7 +696,7 @@ int snd_hctl_wait(snd_hctl_t *hctl, int timeout) pollio = 0; err_poll = poll(pfd, npfds, timeout); if (err_poll < 0) { - if (errno == EINTR && !CTLINABORT(hctl->ctl)) + if (errno == EINTR && !CTLINABORT(hctl->ctl) && !(hctl->ctl->mode & SND_CTL_EINTR)) continue; return -errno; } From 3da32ca4db1d368980ea1c55c8b067fcb3c3e3b6 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Wed, 3 May 2023 17:55:34 +0200 Subject: [PATCH 059/267] pcm: remove duplicate SND_PCM_HW_PARAM_{FIRST,LAST}_* #defines Signed-off-by: Oswald Buddenhagen Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_local.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index abea6654..6a0e71e7 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -48,12 +48,9 @@ #include "mask.h" #define SND_PCM_HW_PARAM_ACCESS SNDRV_PCM_HW_PARAM_ACCESS -#define SND_PCM_HW_PARAM_FIRST_MASK SNDRV_PCM_HW_PARAM_FIRST_MASK #define SND_PCM_HW_PARAM_FORMAT SNDRV_PCM_HW_PARAM_FORMAT #define SND_PCM_HW_PARAM_SUBFORMAT SNDRV_PCM_HW_PARAM_SUBFORMAT -#define SND_PCM_HW_PARAM_LAST_MASK SNDRV_PCM_HW_PARAM_LAST_MASK #define SND_PCM_HW_PARAM_SAMPLE_BITS SNDRV_PCM_HW_PARAM_SAMPLE_BITS -#define SND_PCM_HW_PARAM_FIRST_INTERVAL SNDRV_PCM_HW_PARAM_FIRST_INTERVAL #define SND_PCM_HW_PARAM_FRAME_BITS SNDRV_PCM_HW_PARAM_FRAME_BITS #define SND_PCM_HW_PARAM_CHANNELS SNDRV_PCM_HW_PARAM_CHANNELS #define SND_PCM_HW_PARAM_RATE SNDRV_PCM_HW_PARAM_RATE @@ -65,7 +62,6 @@ #define SND_PCM_HW_PARAM_BUFFER_SIZE SNDRV_PCM_HW_PARAM_BUFFER_SIZE #define SND_PCM_HW_PARAM_BUFFER_BYTES SNDRV_PCM_HW_PARAM_BUFFER_BYTES #define SND_PCM_HW_PARAM_TICK_TIME SNDRV_PCM_HW_PARAM_TICK_TIME -#define SND_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_LAST_INTERVAL #define SND_PCM_HW_PARAM_LAST_MASK SNDRV_PCM_HW_PARAM_LAST_MASK #define SND_PCM_HW_PARAM_FIRST_MASK SNDRV_PCM_HW_PARAM_FIRST_MASK #define SND_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_LAST_INTERVAL From 798f387fe9e5b5ddd705229ec7fff14f54c4e24e Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Wed, 3 May 2023 17:55:30 +0200 Subject: [PATCH 060/267] conf/emu10k1: remove compat with two decades old drivers There really isn't much of a point to clutter the files with this. Signed-off-by: Oswald Buddenhagen Signed-off-by: Jaroslav Kysela --- src/conf/cards/Audigy.conf | 65 ------------------------------ src/conf/cards/Audigy2.conf | 79 ------------------------------------- src/conf/cards/EMU10K1.conf | 48 ---------------------- 3 files changed, 192 deletions(-) diff --git a/src/conf/cards/Audigy.conf b/src/conf/cards/Audigy.conf index 1c924966..42692cfd 100644 --- a/src/conf/cards/Audigy.conf +++ b/src/conf/cards/Audigy.conf @@ -26,14 +26,6 @@ Audigy.pcm.front.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -42,15 +34,6 @@ Audigy.pcm.front.0 { optional true value [ 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 ] - } - ] } } @@ -79,14 +62,6 @@ Audigy.pcm.rear.0 { optional true value [ 0 0 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 0 0 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 ] - } ] } } @@ -115,14 +90,6 @@ Audigy.pcm.center_lfe.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -131,14 +98,6 @@ Audigy.pcm.center_lfe.0 { optional true value [ 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 ] - } ] } } @@ -265,14 +224,6 @@ Audigy.pcm.iec958.0 { optional true value [ $AES0 $AES1 $AES2 $AES3 ] } - { - # for compatibility with older drivers - name "IEC958 Playback Default" - lock true - preserve true - optional true - value [ $AES0 $AES1 $AES2 $AES3 ] - } { name "IEC958 Optical Raw Playback Switch" lock true @@ -287,14 +238,6 @@ Audigy.pcm.iec958.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -303,14 +246,6 @@ Audigy.pcm.iec958.0 { optional true value [ 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 ] - } { name "Audigy Analog/Digital Output Jack" lock true diff --git a/src/conf/cards/Audigy2.conf b/src/conf/cards/Audigy2.conf index cbec7835..35126d23 100644 --- a/src/conf/cards/Audigy2.conf +++ b/src/conf/cards/Audigy2.conf @@ -26,14 +26,6 @@ Audigy2.pcm.front.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -42,14 +34,6 @@ Audigy2.pcm.front.0 { optional true value [ 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 ] - } ] } @@ -79,14 +63,6 @@ Audigy2.pcm.rear.0 { optional true value [ 0 0 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 0 0 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 ] - } ] } } @@ -115,14 +91,6 @@ Audigy2.pcm.center_lfe.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -131,14 +99,6 @@ Audigy2.pcm.center_lfe.0 { optional true value [ 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 ] - } ] } } @@ -167,14 +127,6 @@ Audigy2.pcm.side.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -183,14 +135,6 @@ Audigy2.pcm.side.0 { optional true value [ 14 15 0 0 0 0 0 0 14 15 0 0 0 0 0 0 14 15 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 14 15 0 0 0 0 0 0 14 15 0 0 0 0 0 0 14 15 0 0 0 0 0 0 ] - } ] } } @@ -374,13 +318,6 @@ Audigy2.pcm.iec958.0 { optional true value [ $AES0 $AES1 $AES2 $AES3 ] } - { - # for compatibility with older drivers - name "IEC958 Playback Default" - preserve true - optional true - value [ $AES0 $AES1 $AES2 $AES3 ] - } { name "IEC958 Optical Raw Playback Switch" lock true @@ -395,14 +332,6 @@ Audigy2.pcm.iec958.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -411,14 +340,6 @@ Audigy2.pcm.iec958.0 { optional true value [ 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 ] - } { name "Audigy Analog/Digital Output Jack" lock true diff --git a/src/conf/cards/EMU10K1.conf b/src/conf/cards/EMU10K1.conf index ef193fe0..430926c7 100644 --- a/src/conf/cards/EMU10K1.conf +++ b/src/conf/cards/EMU10K1.conf @@ -28,14 +28,6 @@ EMU10K1.pcm.front.0 { optional true value [ 255 255 0 0 255 0 0 0 0 255 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - # lock true - optional true - value [ 255 255 0 0 255 0 0 0 0 255 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -44,14 +36,6 @@ EMU10K1.pcm.front.0 { optional true value [ 8 9 0 0 8 9 0 0 8 9 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - # lock true - optional true - value [ 8 9 0 0 8 9 0 0 8 9 0 0 ] - } ] } } @@ -87,14 +71,6 @@ EMU10K1.pcm.rear.0 { optional true value [ 0 0 255 255 0 0 255 0 0 0 0 255 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - # lock true - optional true - value [ 0 0 255 255 0 0 255 0 0 0 0 255 ] - } ] } } @@ -152,14 +128,6 @@ EMU10K1.pcm.center_lfe.0 { optional true value [ 255 255 0 0 255 0 0 0 0 255 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - # lock true - optional true - value [ 255 255 0 0 255 0 0 0 0 255 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -168,14 +136,6 @@ EMU10K1.pcm.center_lfe.0 { optional true value [ 6 7 0 0 6 7 0 0 6 7 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - # lock true - optional true - value [ 6 7 0 0 6 7 0 0 6 7 0 0 ] - } ] } } @@ -304,14 +264,6 @@ EMU10K1.pcm.iec958.0 { optional true value [ $AES0 $AES1 $AES2 $AES3 ] } - { - # for compatibility with older drivers - name "IEC958 Playback Default" - lock true - preserve true - optional true - value [ $AES0 $AES1 $AES2 $AES3 ] - } { name "IEC958 Optical Raw Playback Switch" lock true From 58077e2f0d896ff848f3551518b1d9fc6c97d695 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 3 May 2023 22:24:47 +0200 Subject: [PATCH 061/267] pcm: hw: fix the silence size setup in drain The silence size cannot exceed the silence threshold. Move the check from the manual condition to the common code. This may happen for small ring buffers (where the 1/10th second is too large). Suggested-by: Oswald Buddenhagen Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_hw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 180a3e84..8ffebed9 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -747,8 +747,6 @@ static int snd_pcm_hw_drain(snd_pcm_t *pcm) snd_pcm_sw_params_current_no_lock(pcm, &sw_params); if (hw->drain_silence > 0) { silence_size = (pcm->rate * hw->drain_silence) / 1000; - if (silence_size > pcm->buffer_size) - silence_size = pcm->buffer_size; goto __manual_silence; } /* compute end silence size, align to period size + extra time */ @@ -770,6 +768,8 @@ __manual_silence: * or the next period wake up) */ sw_params.silence_threshold = pcm->buffer_size; + if (silence_size > pcm->buffer_size) + silence_size = pcm->buffer_size; sw_params.silence_size = silence_size; if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params) < 0) { err = -errno; From 904f0f7e7a29244c05e5874bc36486638d1f7e7f Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 4 May 2023 09:16:16 +0200 Subject: [PATCH 062/267] Release v1.2.9 Signed-off-by: Jaroslav Kysela --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ff82c6e8..b6266651 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.8) +AC_INIT(alsa-lib, 1.2.9) AC_CONFIG_SRCDIR([src/control/control.c]) AC_CONFIG_MACRO_DIR([m4]) From 3661bdae35961e4a4dd67a60682550e30b878207 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 16 May 2023 08:57:31 +0200 Subject: [PATCH 063/267] pcm: hw - prevent divide by zero for broken apps Fixes: https://github.com/alsa-project/alsa-lib/issues/318 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_hw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 8ffebed9..26ff62c2 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -742,6 +742,9 @@ static int snd_pcm_hw_drain(snd_pcm_t *pcm) if (pcm->stream != SND_PCM_STREAM_PLAYBACK) goto __skip_silence; + /* stream probably in SETUP, prevent divide by zero */ + if (pcm->period_size == 0) + goto __skip_silence; if (hw->drain_silence == 0 || hw->perfect_drain) goto __skip_silence; snd_pcm_sw_params_current_no_lock(pcm, &sw_params); From 55cdf2fd639534be97d2eb6d8b5aa2f7c4ad0bb6 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 16 May 2023 09:14:46 +0200 Subject: [PATCH 064/267] include: fix SND_DLSYM_BUILD_VERSION() for static build Correct the dlsym_name string for nested macros. See Fixes:. Fixes: https://github.com/alsa-project/alsa-lib/issues/316 Signed-off-by: Jaroslav Kysela --- include/global.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/global.h b/include/global.h index 71a1b12f..dfe9bc2b 100644 --- a/include/global.h +++ b/include/global.h @@ -71,6 +71,11 @@ struct snd_dlsym_link { extern struct snd_dlsym_link *snd_dlsym_start; +#ifndef __STRING +/** \brief Return 'x' argument as string */ +#define __STRING(x) #x +#endif + /** \hideinitializer \brief Helper macro for #SND_DLSYM_BUILD_VERSION. */ #define __SND_DLSYM_VERSION(prefix, name, version) _ ## prefix ## name ## version /** @@ -82,18 +87,13 @@ extern struct snd_dlsym_link *snd_dlsym_start; void __SND_DLSYM_VERSION(snd_dlsym_constructor_, name, version) (void) __attribute__ ((constructor)); \ void __SND_DLSYM_VERSION(snd_dlsym_constructor_, name, version) (void) { \ __SND_DLSYM_VERSION(snd_dlsym_, name, version).next = snd_dlsym_start; \ - __SND_DLSYM_VERSION(snd_dlsym_, name, version).dlsym_name = # name; \ + __SND_DLSYM_VERSION(snd_dlsym_, name, version).dlsym_name = __STRING(name); \ __SND_DLSYM_VERSION(snd_dlsym_, name, version).dlsym_ptr = (void *)&name; \ snd_dlsym_start = &__SND_DLSYM_VERSION(snd_dlsym_, name, version); \ } #endif -#ifndef __STRING -/** \brief Return 'x' argument as string */ -#define __STRING(x) #x -#endif - /** \brief Returns the version of a dynamic symbol as a string. */ #define SND_DLSYM_VERSION(version) __STRING(version) From 325210acc8540972b7a7cac10a2e13549261fe2f Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 16 May 2023 15:57:15 +0200 Subject: [PATCH 065/267] usecase: add CaptureMicInfoFile field to documentation Signed-off-by: Jaroslav Kysela --- include/use-case.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/use-case.h b/include/use-case.h index 04185e2b..e5d4fd68 100644 --- a/include/use-case.h +++ b/include/use-case.h @@ -373,6 +373,9 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, * - CaptureMasterType * - type of the master volume control * - Valid values: "soft" (software attenuation) + * - CaptureMicInfoFile + * - json file with the microphone array placement and type description + * (e.g. output from nhlt-dmic-info) * - EDIDFile * - Path to EDID file for HDMI devices * - JackCTL From cbdb428ab963ed52e5cef636d189207adde9b239 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 6 Jun 2023 13:46:53 +0200 Subject: [PATCH 066/267] remove extra trailing new line in SNDMSG and SNDERR calls Signed-off-by: Jaroslav Kysela --- src/control/control.c | 4 ++-- src/control/control_ext.c | 2 +- src/control/control_hw.c | 2 +- src/control/eld.c | 6 +++--- src/control/hcontrol.c | 4 ++-- src/control/setup.c | 2 +- src/pcm/pcm.c | 10 +++++----- src/pcm/pcm_direct.c | 8 ++++---- src/pcm/pcm_extplug.c | 2 +- src/pcm/pcm_hw.c | 32 ++++++++++++++++---------------- src/pcm/pcm_ioplug.c | 2 +- src/pcm/pcm_multi.c | 2 +- src/topology/pcm.c | 4 ++-- 13 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/control/control.c b/src/control/control.c index c4ca74ec..d3ecc6e9 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -1321,7 +1321,7 @@ int snd_ctl_wait(snd_ctl_t *ctl, int timeout) npfds = snd_ctl_poll_descriptors_count(ctl); if (npfds <= 0 || npfds >= 16) { - SNDERR("Invalid poll_fds %d\n", npfds); + SNDERR("Invalid poll_fds %d", npfds); return -EIO; } pfd = alloca(sizeof(*pfd) * npfds); @@ -1329,7 +1329,7 @@ int snd_ctl_wait(snd_ctl_t *ctl, int timeout) if (err < 0) return err; if (err != npfds) { - SNDMSG("invalid poll descriptors %d\n", err); + SNDMSG("invalid poll descriptors %d", err); return -EIO; } for (;;) { diff --git a/src/control/control_ext.c b/src/control/control_ext.c index 0dcc8538..cf154f85 100644 --- a/src/control/control_ext.c +++ b/src/control/control_ext.c @@ -712,7 +712,7 @@ int snd_ctl_ext_create(snd_ctl_ext_t *ext, const char *name, int mode) if (ext->version < SNDRV_PROTOCOL_VERSION(1, 0, 0) || ext->version > SND_CTL_EXT_VERSION) { - SNDERR("ctl_ext: Plugin version mismatch\n"); + SNDERR("ctl_ext: Plugin version mismatch"); return -ENXIO; } diff --git a/src/control/control_hw.c b/src/control/control_hw.c index 02636910..a927cdfe 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -348,7 +348,7 @@ static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event) if (res <= 0) return -errno; if (CHECK_SANITY(res != sizeof(*event))) { - SNDMSG("snd_ctl_hw_read: read size error (req:%d, got:%d)\n", + SNDMSG("snd_ctl_hw_read: read size error (req:%d, got:%d)", sizeof(*event), res); return -EINVAL; } diff --git a/src/control/eld.c b/src/control/eld.c index b7bf779f..7e826cb5 100644 --- a/src/control/eld.c +++ b/src/control/eld.c @@ -53,7 +53,7 @@ int __snd_pcm_info_eld_fixup(snd_pcm_info_t * info) ret = snd_ctl_hw_open(&ctl, NULL, info->card, 0); if (ret < 0) { - SYSMSG("Cannot open the associated CTL\n"); + SYSMSG("Cannot open the associated CTL"); return ret; } @@ -66,7 +66,7 @@ int __snd_pcm_info_eld_fixup(snd_pcm_info_t * info) if (ret == -ENOENT || cinfo.type != SND_CTL_ELEM_TYPE_BYTES || cinfo.count == 0) return 0; if (ret < 0) { - SYSMSG("Cannot read ELD\n"); + SYSMSG("Cannot read ELD"); return ret; } /* decode connected HDMI device name */ @@ -78,7 +78,7 @@ int __snd_pcm_info_eld_fixup(snd_pcm_info_t * info) /* no monitor name detected */ goto __present; if (l > 16 || 20 + l > cinfo.count) { - SNDERR("ELD decode failed, using old HDMI output names\n"); + SNDERR("ELD decode failed, using old HDMI output names"); return 0; } s = alloca(l + 1); diff --git a/src/control/hcontrol.c b/src/control/hcontrol.c index 18ddc16b..ce3fec3e 100644 --- a/src/control/hcontrol.c +++ b/src/control/hcontrol.c @@ -680,7 +680,7 @@ int snd_hctl_wait(snd_hctl_t *hctl, int timeout) npfds = snd_hctl_poll_descriptors_count(hctl); if (npfds <= 0 || npfds >= 16) { - SNDERR("Invalid poll_fds %d\n", npfds); + SNDERR("Invalid poll_fds %d", npfds); return -EIO; } pfd = alloca(sizeof(*pfd) * npfds); @@ -689,7 +689,7 @@ int snd_hctl_wait(snd_hctl_t *hctl, int timeout) if (err < 0) return err; if (err != npfds) { - SNDMSG("invalid poll descriptors %d\n", err); + SNDMSG("invalid poll descriptors %d", err); return -EIO; } do { diff --git a/src/control/setup.c b/src/control/setup.c index ab84bbee..88635e42 100644 --- a/src/control/setup.c +++ b/src/control/setup.c @@ -311,7 +311,7 @@ static int snd_config_get_ctl_elem_value(snd_config_t *conf, unsigned int idx = 0; if (len % 2 != 0 || len > count * 2) { _bad_content: - SNDERR("bad value content\n"); + SNDERR("bad value content"); return -EINVAL; } while (*buf) { diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 41a36b36..e55ad7f7 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -2916,7 +2916,7 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout) npfds = __snd_pcm_poll_descriptors_count(pcm); if (npfds <= 0 || npfds >= 16) { - SNDERR("Invalid poll_fds %d\n", npfds); + SNDERR("Invalid poll_fds %d", npfds); return -EIO; } pfd = alloca(sizeof(*pfd) * npfds); @@ -2924,7 +2924,7 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout) if (err < 0) return err; if (err != npfds) { - SNDMSG("invalid poll descriptors %d\n", err); + SNDMSG("invalid poll descriptors %d", err); return -EIO; } if (timeout == SND_PCM_WAIT_IO) @@ -2932,7 +2932,7 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout) else if (timeout == SND_PCM_WAIT_DRAIN) timeout = __snd_pcm_wait_drain_timeout(pcm); else if (timeout < -1) - SNDMSG("invalid snd_pcm_wait timeout argument %d\n", timeout); + SNDMSG("invalid snd_pcm_wait timeout argument %d", timeout); do { __snd_pcm_unlock(pcm->fast_op_arg); err_poll = poll(pfd, npfds, timeout); @@ -6399,7 +6399,7 @@ int snd_pcm_sw_params_set_start_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params params->start_threshold = pcm->boundary; break; default: - SNDMSG("invalid start mode value %d\n", val); + SNDMSG("invalid start mode value %d", val); return -EINVAL; } return 0; @@ -6447,7 +6447,7 @@ int snd_pcm_sw_params_set_xrun_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, params->stop_threshold = pcm->boundary; break; default: - SNDMSG("invalid xrun mode value %d\n", val); + SNDMSG("invalid xrun mode value %d", val); return -EINVAL; } return 0; diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 8ee9b4db..040fc160 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -1741,7 +1741,7 @@ int snd_pcm_direct_parse_bindings(snd_pcm_direct_t *dmix, continue; err = safe_strtol(id, &cchannel); if (err < 0 || cchannel < 0) { - SNDERR("invalid client channel in binding: %s\n", id); + SNDERR("invalid client channel in binding: %s", id); return -EINVAL; } if ((unsigned)cchannel >= count) @@ -1766,7 +1766,7 @@ int snd_pcm_direct_parse_bindings(snd_pcm_direct_t *dmix, continue; safe_strtol(id, &cchannel); if (snd_config_get_integer(n, &schannel) < 0) { - SNDERR("unable to get slave channel (should be integer type) in binding: %s\n", id); + SNDERR("unable to get slave channel (should be integer type) in binding: %s", id); free(bindings); return -EINVAL; } @@ -1870,11 +1870,11 @@ static int _snd_pcm_direct_get_slave_ipc_offset(snd_config_t *root, if (strcmp(id, "type") == 0) { err = snd_config_get_string(n, &str); if (err < 0) { - SNDERR("Invalid value for PCM type definition\n"); + SNDERR("Invalid value for PCM type definition"); return -EINVAL; } if (strcmp(str, "hw")) { - SNDERR("Invalid type '%s' for slave PCM\n", str); + SNDERR("Invalid type '%s' for slave PCM", str); return -EINVAL; } continue; diff --git a/src/pcm/pcm_extplug.c b/src/pcm/pcm_extplug.c index 99455d9c..a0ff003c 100644 --- a/src/pcm/pcm_extplug.c +++ b/src/pcm/pcm_extplug.c @@ -690,7 +690,7 @@ int snd_pcm_extplug_create(snd_pcm_extplug_t *extplug, const char *name, /* We support 1.0.0 to current */ if (extplug->version < 0x010000 || extplug->version > SND_PCM_EXTPLUG_VERSION) { - SNDERR("extplug: Plugin version mismatch: 0x%x\n", + SNDERR("extplug: Plugin version mismatch: 0x%x", extplug->version); return -ENXIO; } diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 26ff62c2..b468a071 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -547,7 +547,7 @@ static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) SND_PCM_TSTAMP_TYPE_MONOTONIC; if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) { err = -errno; - SNDMSG("TSTAMP failed\n"); + SNDMSG("TSTAMP failed"); goto out; } } @@ -1156,7 +1156,7 @@ static int snd_pcm_hw_close(snd_pcm_t *pcm) int err = 0; if (close(hw->fd)) { err = -errno; - SYSMSG("close failed (%i)\n", err); + SYSMSG("close failed (%i)", err); } unmap_status_and_control_data(hw); @@ -1279,7 +1279,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev, ret = snd_ctl_hw_open(&ctl, NULL, card, 0); if (ret < 0) { - SYSMSG("Cannot open the associated CTL\n"); + SYSMSG("Cannot open the associated CTL"); return NULL; } @@ -1287,7 +1287,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev, ret = snd_ctl_elem_tlv_read(ctl, &id, tlv, sizeof(tlv)); snd_ctl_close(ctl); if (ret < 0) { - SYSMSG("Cannot read Channel Map TLV\n"); + SYSMSG("Cannot read Channel Map TLV"); return NULL; } @@ -1301,7 +1301,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev, type = tlv[SNDRV_CTL_TLVO_TYPE]; if (type != SND_CTL_TLVT_CONTAINER) { if (!is_chmap_type(type)) { - SYSMSG("Invalid TLV type %d\n", type); + SYSMSG("Invalid TLV type %d", type); return NULL; } start = tlv; @@ -1314,7 +1314,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev, nums = 0; for (p = start; size > 0; ) { if (!is_chmap_type(p[0])) { - SYSMSG("Invalid TLV type %d\n", p[0]); + SYSMSG("Invalid TLV type %d", p[0]); return NULL; } nums++; @@ -1405,7 +1405,7 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm) case SNDRV_PCM_STATE_SUSPENDED: break; default: - SYSMSG("Invalid PCM state for chmap_get: %s\n", + SYSMSG("Invalid PCM state for chmap_get: %s", snd_pcm_state_name(FAST_PCM_STATE(hw))); return NULL; } @@ -1416,7 +1416,7 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm) ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0); if (ret < 0) { free(map); - SYSMSG("Cannot open the associated CTL\n"); + SYSMSG("Cannot open the associated CTL"); chmap_caps_set_error(hw, CHMAP_CTL_GET); return NULL; } @@ -1426,7 +1426,7 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm) snd_ctl_close(ctl); if (ret < 0) { free(map); - SYSMSG("Cannot read Channel Map ctl\n"); + SYSMSG("Cannot read Channel Map ctl"); chmap_caps_set_error(hw, CHMAP_CTL_GET); return NULL; } @@ -1452,17 +1452,17 @@ static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) return -ENXIO; if (map->channels > 128) { - SYSMSG("Invalid number of channels %d\n", map->channels); + SYSMSG("Invalid number of channels %d", map->channels); return -EINVAL; } if (FAST_PCM_STATE(hw) != SNDRV_PCM_STATE_PREPARED) { - SYSMSG("Invalid PCM state for chmap_set: %s\n", + SYSMSG("Invalid PCM state for chmap_set: %s", snd_pcm_state_name(FAST_PCM_STATE(hw))); return -EBADFD; } ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0); if (ret < 0) { - SYSMSG("Cannot open the associated CTL\n"); + SYSMSG("Cannot open the associated CTL"); chmap_caps_set_error(hw, CHMAP_CTL_SET); return ret; } @@ -1480,7 +1480,7 @@ static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) ret = -ENXIO; } if (ret < 0) - SYSMSG("Cannot write Channel Map ctl\n"); + SYSMSG("Cannot write Channel Map ctl"); return ret; } @@ -1644,7 +1644,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd, unsigned int user_ver = SNDRV_PCM_VERSION; if (ioctl(fd, SNDRV_PCM_IOCTL_USER_PVERSION, &user_ver) < 0) { ret = -errno; - SNDMSG("USER_PVERSION failed\n"); + SNDMSG("USER_PVERSION failed"); return ret; } } @@ -1656,7 +1656,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd, int on = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; if (ioctl(fd, SNDRV_PCM_IOCTL_TTSTAMP, &on) < 0) { ret = -errno; - SNDMSG("TTSTAMP failed\n"); + SNDMSG("TTSTAMP failed"); return ret; } tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC; @@ -1667,7 +1667,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd, int on = 1; if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) { ret = -errno; - SNDMSG("TSTAMP failed\n"); + SNDMSG("TSTAMP failed"); return ret; } } diff --git a/src/pcm/pcm_ioplug.c b/src/pcm/pcm_ioplug.c index 09454a29..df2c7f81 100644 --- a/src/pcm/pcm_ioplug.c +++ b/src/pcm/pcm_ioplug.c @@ -1086,7 +1086,7 @@ int snd_pcm_ioplug_create(snd_pcm_ioplug_t *ioplug, const char *name, /* We support 1.0.0 to current */ if (ioplug->version < 0x010000 || ioplug->version > SND_PCM_IOPLUG_VERSION) { - SNDERR("ioplug: Plugin version mismatch: 0x%x\n", + SNDERR("ioplug: Plugin version mismatch: 0x%x", ioplug->version); return -ENXIO; } diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index 3e7ce82c..3680fd03 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -1303,7 +1303,7 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, ++slaves_count; } if (master_slave < 0 || master_slave >= (long)slaves_count) { - SNDERR("Master slave is out of range (0-%u)\n", slaves_count-1); + SNDERR("Master slave is out of range (0-%u)", slaves_count-1); return -EINVAL; } snd_config_for_each(i, inext, bindings) { diff --git a/src/topology/pcm.c b/src/topology/pcm.c index ed4a2d71..4de1d266 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -1479,7 +1479,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, provider_legacy = false; if (strcmp(id, "bclk_master") == 0) { - SNDERR("deprecated option %s, please use 'bclk'\n", id); + SNDERR("deprecated option %s, please use 'bclk'", id); provider_legacy = true; } @@ -1531,7 +1531,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, provider_legacy = false; if (strcmp(id, "fsync_master") == 0) { - SNDERR("deprecated option %s, please use 'fsync'\n", id); + SNDERR("deprecated option %s, please use 'fsync'", id); provider_legacy = true; } From c86a4f5cf6ec24ae0284ccbc27f7968bad373626 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 16 Nov 2022 17:06:27 +0100 Subject: [PATCH 067/267] uapi: Update rawmidi API to 2.0.3 Copied from the kernel uapi header for rawmidi API. A few new structs and constants for UMP are defined in addition to a few new ioctls. Signed-off-by: Takashi Iwai --- include/sound/uapi/asound.h | 57 ++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index 62a0364f..cf11a6b7 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -706,7 +706,7 @@ enum { * Raw MIDI section - /dev/snd/midi?? */ -#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 2) +#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 3) enum { SNDRV_RAWMIDI_STREAM_OUTPUT = 0, @@ -717,6 +717,7 @@ enum { #define SNDRV_RAWMIDI_INFO_OUTPUT 0x00000001 #define SNDRV_RAWMIDI_INFO_INPUT 0x00000002 #define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004 +#define SNDRV_RAWMIDI_INFO_UMP 0x00000008 struct snd_rawmidi_info { unsigned int device; /* RO/WR (control): device number */ @@ -775,6 +776,57 @@ struct snd_rawmidi_status { unsigned char reserved[16]; /* reserved for future use */ }; +/* UMP EP Protocol / JRTS capability bits */ +#define SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK 0x0300 +#define SNDRV_UMP_EP_INFO_PROTO_MIDI1 0x0100 /* MIDI 1.0 */ +#define SNDRV_UMP_EP_INFO_PROTO_MIDI2 0x0200 /* MIDI 2.0 */ +#define SNDRV_UMP_EP_INFO_PROTO_JRTS_MASK 0x0003 +#define SNDRV_UMP_EP_INFO_PROTO_JRTS_TX 0x0001 /* JRTS Transmit */ +#define SNDRV_UMP_EP_INFO_PROTO_JRTS_RX 0x0002 /* JRTS Receive */ + +/* UMP Endpoint information */ +struct snd_ump_endpoint_info { + int card; /* card number */ + int device; /* device number */ + unsigned int flags; /* additional info */ + unsigned int protocol_caps; /* protocol capabilities */ + unsigned int protocol; /* current protocol */ + unsigned int num_blocks; /* # of function blocks */ + unsigned short version; /* UMP major/minor version */ + unsigned short padding[7]; + unsigned char name[128]; /* endpoint name string */ + unsigned char product_id[128]; /* unique product id string */ + unsigned char reserved[32]; +} __packed; + +/* UMP direction */ +#define SNDRV_UMP_DIR_INPUT 0x01 +#define SNDRV_UMP_DIR_OUTPUT 0x02 +#define SNDRV_UMP_DIR_BIDIRECTION 0x03 + +/* UMP block info flags */ +#define SNDRV_UMP_BLOCK_IS_MIDI1 (1U << 0) /* MIDI 1.0 port w/o restrict */ +#define SNDRV_UMP_BLOCK_IS_LOWSPEED (1U << 1) /* 31.25Kbps B/W MIDI1 port */ + +/* UMP groups and blocks */ +#define SNDRV_UMP_MAX_GROUPS 16 +#define SNDRV_UMP_MAX_BLOCKS 32 + +/* UMP Block information */ +struct snd_ump_block_info { + int card; /* card number */ + int device; /* device number */ + unsigned char block_id; /* block ID (R/W) */ + unsigned char direction; /* UMP direction */ + unsigned char active; /* Activeness */ + unsigned char first_group; /* first group ID */ + unsigned char num_groups; /* number of groups */ + unsigned char padding[3]; + unsigned int flags; /* various info flags */ + unsigned char name[128]; /* block name string */ + unsigned char reserved[32]; +} __packed; + #define SNDRV_RAWMIDI_IOCTL_PVERSION _IOR('W', 0x00, int) #define SNDRV_RAWMIDI_IOCTL_INFO _IOR('W', 0x01, struct snd_rawmidi_info) #define SNDRV_RAWMIDI_IOCTL_USER_PVERSION _IOW('W', 0x02, int) @@ -782,6 +834,9 @@ struct snd_rawmidi_status { #define SNDRV_RAWMIDI_IOCTL_STATUS _IOWR('W', 0x20, struct snd_rawmidi_status) #define SNDRV_RAWMIDI_IOCTL_DROP _IOW('W', 0x30, int) #define SNDRV_RAWMIDI_IOCTL_DRAIN _IOW('W', 0x31, int) +/* Additional ioctls for UMP rawmidi devices */ +#define SNDRV_UMP_IOCTL_ENDPOINT_INFO _IOR('W', 0x40, struct snd_ump_endpoint_info) +#define SNDRV_UMP_IOCTL_BLOCK_INFO _IOR('W', 0x41, struct snd_ump_block_info) /* * Timer section - /dev/snd/timer From a034b847059fb2a7f9829b2c66b2ebe8674b5b3e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 22 May 2023 17:55:51 +0200 Subject: [PATCH 068/267] uapi: Update control API to 2.0.9 Copied from the kernel uapi header for control API. A few new ioctls have been added for the support of UMP next device and inquiries of UMP Endpoint and Block info. Signed-off-by: Takashi Iwai --- include/sound/uapi/asound.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index cf11a6b7..80cd57e9 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -1008,7 +1008,7 @@ struct snd_timer_tread { * * ****************************************************************************/ -#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 8) +#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 9) struct snd_ctl_card_info { int card; /* card number */ @@ -1169,6 +1169,9 @@ struct snd_ctl_tlv { #define SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE _IOWR('U', 0x40, int) #define SNDRV_CTL_IOCTL_RAWMIDI_INFO _IOWR('U', 0x41, struct snd_rawmidi_info) #define SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE _IOW('U', 0x42, int) +#define SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE _IOWR('U', 0x43, int) +#define SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO _IOWR('U', 0x44, struct snd_ump_endpoint_info) +#define SNDRV_CTL_IOCTL_UMP_BLOCK_INFO _IOWR('U', 0x45, struct snd_ump_block_info) #define SNDRV_CTL_IOCTL_POWER _IOWR('U', 0xd0, int) #define SNDRV_CTL_IOCTL_POWER_STATE _IOR('U', 0xd1, int) From 6627953de06f7d26a2f339c42b2ff200c764bcbb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Nov 2022 15:24:31 +0100 Subject: [PATCH 069/267] rawmidi: Add UMP ioctl support Just implement internal callbacks for two new ioctls for UMP (ump_endpoint_info and ump_block_info). No public API functions are added yet here. Signed-off-by: Takashi Iwai --- src/rawmidi/rawmidi.c | 19 +++++++++++++++++++ src/rawmidi/rawmidi_hw.c | 26 +++++++++++++++++++++++++- src/rawmidi/rawmidi_local.h | 5 +++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index 570e0675..8dca9312 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -1120,3 +1120,22 @@ ssize_t snd_rawmidi_tread(snd_rawmidi_t *rawmidi, struct timespec *tstamp, void return -ENOTSUP; return (rawmidi->ops->tread)(rawmidi, tstamp, buffer, size); } + +#ifndef DOXYGEN +/* + * internal API functions for obtaining UMP info from rawmidi instance + */ +int _snd_rawmidi_ump_endpoint_info(snd_rawmidi_t *rmidi, void *info) +{ + if (!rmidi->ops->ump_endpoint_info) + return -ENXIO; + return rmidi->ops->ump_endpoint_info(rmidi, info); +} + +int _snd_rawmidi_ump_block_info(snd_rawmidi_t *rmidi, void *info) +{ + if (!rmidi->ops->ump_block_info) + return -ENXIO; + return rmidi->ops->ump_block_info(rmidi, info); +} +#endif /* DOXYGEN */ diff --git a/src/rawmidi/rawmidi_hw.c b/src/rawmidi/rawmidi_hw.c index e5bb3ee3..69e5e02f 100644 --- a/src/rawmidi/rawmidi_hw.c +++ b/src/rawmidi/rawmidi_hw.c @@ -272,6 +272,28 @@ static ssize_t snd_rawmidi_hw_tread(snd_rawmidi_t *rmidi, struct timespec *tstam return ret + result; } +static int snd_rawmidi_hw_ump_endpoint_info(snd_rawmidi_t *rmidi, void *buf) +{ + snd_rawmidi_hw_t *hw = rmidi->private_data; + + if (rmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 3)) + return -ENXIO; + if (ioctl(hw->fd, SNDRV_UMP_IOCTL_ENDPOINT_INFO, buf) < 0) + return -errno; + return 0; +} + +static int snd_rawmidi_hw_ump_block_info(snd_rawmidi_t *rmidi, void *buf) +{ + snd_rawmidi_hw_t *hw = rmidi->private_data; + + if (rmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 3)) + return -ENXIO; + if (ioctl(hw->fd, SNDRV_UMP_IOCTL_BLOCK_INFO, buf) < 0) + return -errno; + return 0; +} + static const snd_rawmidi_ops_t snd_rawmidi_hw_ops = { .close = snd_rawmidi_hw_close, .nonblock = snd_rawmidi_hw_nonblock, @@ -282,7 +304,9 @@ static const snd_rawmidi_ops_t snd_rawmidi_hw_ops = { .drain = snd_rawmidi_hw_drain, .write = snd_rawmidi_hw_write, .read = snd_rawmidi_hw_read, - .tread = snd_rawmidi_hw_tread + .tread = snd_rawmidi_hw_tread, + .ump_endpoint_info = snd_rawmidi_hw_ump_endpoint_info, + .ump_block_info = snd_rawmidi_hw_ump_block_info, }; diff --git a/src/rawmidi/rawmidi_local.h b/src/rawmidi/rawmidi_local.h index 4a88e7e4..7025542f 100644 --- a/src/rawmidi/rawmidi_local.h +++ b/src/rawmidi/rawmidi_local.h @@ -35,6 +35,8 @@ typedef struct { ssize_t (*write)(snd_rawmidi_t *rawmidi, const void *buffer, size_t size); ssize_t (*read)(snd_rawmidi_t *rawmidi, void *buffer, size_t size); ssize_t (*tread)(snd_rawmidi_t *rawmidi, struct timespec *tstamp, void *buffer, size_t size); + int (*ump_endpoint_info)(snd_rawmidi_t *rmidi, void *buf); + int (*ump_block_info)(snd_rawmidi_t *rmidi, void *buf); } snd_rawmidi_ops_t; struct _snd_rawmidi { @@ -62,3 +64,6 @@ int snd_rawmidi_virtual_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, int merge, int mode); #define snd_rawmidi_conf_generic_id(id) _snd_conf_generic_id(id) + +int _snd_rawmidi_ump_endpoint_info(snd_rawmidi_t *rmidi, void *info); +int _snd_rawmidi_ump_block_info(snd_rawmidi_t *rmidi, void *info); From f47763d2c1fcb4905042fd807127ef25e3d96e19 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Nov 2022 16:02:25 +0100 Subject: [PATCH 070/267] ump: Add initial support This patch adds the initial support for UMP rawmidi access. It's merely the wrapper for the standard rawmidi to access to the UMP rawmidi device. Signed-off-by: Takashi Iwai --- configure.ac | 1 + include/Makefile.am | 2 +- include/rawmidi.h | 3 + include/ump.h | 119 +++++++ src/Versions.in | 6 + src/rawmidi/Makefile.am | 5 +- src/rawmidi/rawmidi_hw.c | 10 +- src/rawmidi/rawmidi_local.h | 2 + src/rawmidi/ump.c | 616 ++++++++++++++++++++++++++++++++++++ src/rawmidi/ump_local.h | 9 + 10 files changed, 769 insertions(+), 4 deletions(-) create mode 100644 include/ump.h create mode 100644 src/rawmidi/ump.c create mode 100644 src/rawmidi/ump_local.h diff --git a/configure.ac b/configure.ac index b6266651..0588eec3 100644 --- a/configure.ac +++ b/configure.ac @@ -795,6 +795,7 @@ cat >> include/asoundlib.h <" >> include/asoundlib.h test "$build_rawmidi" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_rawmidi" = "yes" && echo "#include " >> include/asoundlib.h test "$build_pcm" = "yes" && echo "#include " >> include/asoundlib.h test "$build_hwdep" = "yes" && echo "#include " >> include/asoundlib.h echo "#include " >> include/asoundlib.h diff --git a/include/Makefile.am b/include/Makefile.am index 9909fb73..b7f67b25 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -34,7 +34,7 @@ endif endif if BUILD_RAWMIDI -alsainclude_HEADERS += rawmidi.h +alsainclude_HEADERS += rawmidi.h ump.h endif if BUILD_HWDEP diff --git a/include/rawmidi.h b/include/rawmidi.h index 71681053..2630d1e6 100644 --- a/include/rawmidi.h +++ b/include/rawmidi.h @@ -93,6 +93,9 @@ typedef enum _snd_rawmidi_read_mode { SND_RAWMIDI_READ_TSTAMP = 1, } snd_rawmidi_read_mode_t; +/** rawmidi info bit flags */ +#define SND_RAWMIDI_INFO_UMP 0x00000008 /* rawmidi is UMP */ + int snd_rawmidi_open(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi, const char *name, int mode); int snd_rawmidi_open_lconf(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi, diff --git a/include/ump.h b/include/ump.h new file mode 100644 index 00000000..c79b2335 --- /dev/null +++ b/include/ump.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/** + * \file include/ump.h + * \brief API library for ALSA rawmidi/UMP interface + * + * API library for ALSA rawmidi/UMP interface + */ + +#ifndef __ALSA_UMP_H +#define __ALSA_UMP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** UMP (Endpoint) RawMIDI device */ +typedef struct _snd_ump snd_ump_t; +/** UMP Endpoint information container */ +typedef struct snd_ump_endpoint_info snd_ump_endpoint_info_t; +/** UMP Block information container */ +typedef struct snd_ump_block_info snd_ump_block_info_t; + +int snd_ump_open(snd_ump_t **inputp, snd_ump_t **outputp, const char *name, int mode); +int snd_ump_close(snd_ump_t *ump); +snd_rawmidi_t *snd_ump_rawmidi(snd_ump_t *ump); +const char *snd_ump_name(snd_ump_t *ump); +int snd_ump_poll_descriptors_count(snd_ump_t *ump); +int snd_ump_poll_descriptors(snd_ump_t *ump, struct pollfd *pfds, unsigned int space); +int snd_ump_poll_descriptors_revents(snd_ump_t *ump, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); +int snd_ump_nonblock(snd_ump_t *ump, int nonblock); +int snd_ump_rawmidi_info(snd_ump_t *ump, snd_rawmidi_info_t *info); +int snd_ump_rawmidi_params(snd_ump_t *ump, snd_rawmidi_params_t *params); +int snd_ump_rawmidi_params_current(snd_ump_t *ump, snd_rawmidi_params_t *params); +int snd_ump_rawmidi_status(snd_ump_t *ump, snd_rawmidi_status_t *status); +int snd_ump_drop(snd_ump_t *ump); +int snd_ump_drain(snd_ump_t *ump); +ssize_t snd_ump_write(snd_ump_t *ump, const void *buffer, size_t size); +ssize_t snd_ump_read(snd_ump_t *ump, void *buffer, size_t size); +ssize_t snd_ump_tread(snd_ump_t *ump, struct timespec *tstamp, void *buffer, size_t size); + +/** Max number of UMP Groups */ +#define SND_UMP_MAX_GROUPS 16 +/** Max number of UMP Blocks */ +#define SND_UMP_MAX_BLOCKS 32 + +/** UMP direction */ +enum _snd_ump_direction { + /** Input only */ + SND_UMP_DIR_INPUT = 0x01, + /** Output only */ + SND_UMP_DIR_OUTPUT = 0x02, + /** Bidirectional */ + SND_UMP_DIR_BIDIRECTION = 0x03, +}; + +/** Bitmask for UMP EP MIDI protocols */ +#define SND_UMP_EP_INFO_PROTO_MIDI_MASK 0x0300 +/** Bit flag for MIDI 1.0 protocol */ +#define SND_UMP_EP_INFO_PROTO_MIDI1 0x0100 +/** Bit flag for MIDI 2.0 protocol */ +#define SND_UMP_EP_INFO_PROTO_MIDI2 0x0200 +/** Bitmask for UMP Jitter-reduction timestamp */ +#define SND_UMP_EP_INFO_PROTO_JRTS_MASK 0x0003 +/** Bit flag for JRTS in Transmit */ +#define SND_UMP_EP_INFO_PROTO_JRTS_TX 0x0001 +/** Bit flag for JRTS in Receive */ +#define SND_UMP_EP_INFO_PROTO_JRTS_RX 0x0002 + +size_t snd_ump_endpoint_info_sizeof(void); +/** \hideinitializer + * \brief allocate an invalid #snd_ump_endpoint_info_t using standard alloca + * \param ptr returned pointer + */ +#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_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); +unsigned int snd_ump_endpoint_info_get_flags(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_protocol_caps(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_protocol(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_num_blocks(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_version(const snd_ump_endpoint_info_t *info); +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); + +/** 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 */ +#define SND_UMP_BLOCK_IS_LOWSPEED (1U << 1) + +size_t snd_ump_block_info_sizeof(void); +/** \hideinitializer + * \brief allocate an invalid #snd_ump_block_info_t using standard alloca + * \param ptr returned pointer + */ +#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_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); +unsigned int snd_ump_block_info_get_first_group(const snd_ump_block_info_t *info); +unsigned int snd_ump_block_info_get_num_groups(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); + +#ifdef __cplusplus +} +#endif + +#endif /* __ALSA_UMP_H */ diff --git a/src/Versions.in b/src/Versions.in index ed737681..3315fa28 100644 --- a/src/Versions.in +++ b/src/Versions.in @@ -148,3 +148,9 @@ ALSA_1.2.9 { @SYMBOL_PREFIX@snd_pcm_hw_params_set_drain_silence; @SYMBOL_PREFIX@snd_pcm_hw_params_get_drain_silence; } ALSA_1.2.6; + +ALSA_1.2.10 { + global: + + @SYMBOL_PREFIX@snd_ump_*; +} ALSA_1.2.9; diff --git a/src/rawmidi/Makefile.am b/src/rawmidi/Makefile.am index 41858a1f..26918659 100644 --- a/src/rawmidi/Makefile.am +++ b/src/rawmidi/Makefile.am @@ -1,10 +1,11 @@ EXTRA_LTLIBRARIES=librawmidi.la -librawmidi_la_SOURCES = rawmidi.c rawmidi_hw.c rawmidi_symbols.c +librawmidi_la_SOURCES = rawmidi.c rawmidi_hw.c rawmidi_symbols.c \ + ump.c if BUILD_SEQ librawmidi_la_SOURCES += rawmidi_virt.c endif -noinst_HEADERS = rawmidi_local.h +noinst_HEADERS = rawmidi_local.h ump_local.h all: librawmidi.la diff --git a/src/rawmidi/rawmidi_hw.c b/src/rawmidi/rawmidi_hw.c index 69e5e02f..3119db9d 100644 --- a/src/rawmidi/rawmidi_hw.c +++ b/src/rawmidi/rawmidi_hw.c @@ -35,6 +35,7 @@ const char *_snd_module_rawmidi_hw = ""; #endif #define SNDRV_FILE_RAWMIDI ALSA_DEVICE_DIRECTORY "midiC%iD%i" +#define SNDRV_FILE_UMP_RAWMIDI ALSA_DEVICE_DIRECTORY "umpC%iD%i" #define SNDRV_RAWMIDI_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 0) #ifndef DOC_HIDDEN @@ -321,8 +322,12 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, snd_rawmidi_t *rmidi; snd_rawmidi_hw_t *hw = NULL; snd_rawmidi_info_t info; + int is_ump; int fmode; + is_ump = !!(mode & _SND_RAWMIDI_OPEN_UMP); + mode &= ~_SND_RAWMIDI_OPEN_UMP; + if (inputp) *inputp = NULL; if (outputp) @@ -332,7 +337,10 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, if ((ret = snd_ctl_hw_open(&ctl, NULL, card, 0)) < 0) return ret; - sprintf(filename, SNDRV_FILE_RAWMIDI, card, device); + if (is_ump) + sprintf(filename, SNDRV_FILE_UMP_RAWMIDI, card, device); + else + sprintf(filename, SNDRV_FILE_RAWMIDI, card, device); __again: if (attempt++ > 3) { diff --git a/src/rawmidi/rawmidi_local.h b/src/rawmidi/rawmidi_local.h index 7025542f..19dbf725 100644 --- a/src/rawmidi/rawmidi_local.h +++ b/src/rawmidi/rawmidi_local.h @@ -67,3 +67,5 @@ int snd_rawmidi_virtual_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, int _snd_rawmidi_ump_endpoint_info(snd_rawmidi_t *rmidi, void *info); int _snd_rawmidi_ump_block_info(snd_rawmidi_t *rmidi, void *info); + +#define _SND_RAWMIDI_OPEN_UMP (1U << 16) /* internal open mode bit */ diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c new file mode 100644 index 00000000..5da79459 --- /dev/null +++ b/src/rawmidi/ump.c @@ -0,0 +1,616 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/** + * \file rawmidi/ump.c + * \brief Universal MIDI Protocol (UMP) Interface + */ + +#include +#include +#include +#include "local.h" +#include "rawmidi_local.h" +#include "ump_local.h" + +static int get_rawmidi_flags(snd_ump_t *ump) +{ + snd_rawmidi_info_t info; + int err; + + err = snd_rawmidi_info(ump->rawmidi, &info); + if (err < 0) + return err; + if (!(info.flags & SNDRV_RAWMIDI_INFO_UMP)) + return -EINVAL; + ump->flags = info.flags; + return 0; +} + +/** + * \brief Opens a new connection to the UMP interface. + * \param inputp Returned input handle (NULL if not wanted) + * \param outputp Returned output handle (NULL if not wanted) + * \param name ASCII identifier of the UMP handle + * \param mode Open mode + * \return 0 on success otherwise a negative error code + * + * Opens a new connection to the UMP interface specified with + * an ASCII identifier and mode. + */ +int snd_ump_open(snd_ump_t **inputp, snd_ump_t **outputp, const char *name, + int mode) +{ + snd_ump_t *input = NULL, *output = NULL; + int err; + + if (inputp) + *inputp = NULL; + if (outputp) + *outputp = NULL; + if (!inputp && !outputp) + return -EINVAL; + + err = -ENOMEM; + if (inputp) { + input = calloc(1, sizeof(*input)); + if (!input) + goto error; + input->is_input = 1; + } + if (outputp) { + output = calloc(1, sizeof(*output)); + if (!output) + goto error; + } + + err = snd_rawmidi_open(input ? &input->rawmidi : NULL, + output ? &output->rawmidi : NULL, + name, mode | _SND_RAWMIDI_OPEN_UMP); + if (err < 0) + goto error; + + if (input) { + err = get_rawmidi_flags(input); + if (err < 0) + goto error; + } + if (output) { + err = get_rawmidi_flags(output); + if (err < 0) + goto error; + } + + if (inputp) + *inputp = input; + if (outputp) + *outputp = output; + + return 0; + + error: + if (input) { + if (input->rawmidi) + snd_rawmidi_close(input->rawmidi); + free(input); + } + if (output) { + if (output->rawmidi) + snd_rawmidi_close(output->rawmidi); + free(output); + } + return err; +} + +/** + * \brief close UMP handle + * \param ump UMP handle + * \return 0 on success otherwise a negative error code + * + * Closes the specified UMP handle and frees all associated + * resources. + */ +int snd_ump_close(snd_ump_t *ump) +{ + int err; + + err = snd_rawmidi_close(ump->rawmidi); + free(ump); + return err; +} + +/** + * \brief get RawMidi instance associated with the UMP handle + * \param ump UMP handle + * \return the associated RawMidi handle + * + * Returns the associated RawMidi instance with the given UMP handle + */ +snd_rawmidi_t *snd_ump_rawmidi(snd_ump_t *ump) +{ + return ump->rawmidi; +} + +/** + * \brief get identifier of UMP handle + * \param ump UMP handle + * \return ascii identifier of UMP handle + * + * Returns the ASCII identifier of given UMP handle. It's the same + * identifier specified in snd_ump_open(). + */ +const char *snd_ump_name(snd_ump_t *ump) +{ + return snd_rawmidi_name(ump->rawmidi); +} + +/** + * \brief get count of poll descriptors for UMP handle + * \param ump UMP handle + * \return count of poll descriptors + */ +int snd_ump_poll_descriptors_count(snd_ump_t *ump) +{ + return snd_rawmidi_poll_descriptors_count(ump->rawmidi); +} + +/** + * \brief get poll descriptors + * \param ump UMP handle + * \param pfds array of poll descriptors + * \param space space in the poll descriptor array + * \return count of filled descriptors + */ +int snd_ump_poll_descriptors(snd_ump_t *ump, struct pollfd *pfds, + unsigned int space) +{ + return snd_rawmidi_poll_descriptors(ump->rawmidi, pfds, space); +} + +/** + * \brief get returned events from poll descriptors + * \param ump UMP handle + * \param pfds array of poll descriptors + * \param nfds count of poll descriptors + * \param revents returned events + * \return zero if success, otherwise a negative error code + */ +int snd_ump_poll_descriptors_revents(snd_ump_t *ump, struct pollfd *pfds, + unsigned int nfds, unsigned short *revents) +{ + return snd_rawmidi_poll_descriptors_revents(ump->rawmidi, pfds, nfds, + revents); +} + +/** + * \brief set nonblock mode + * \param ump UMP handle + * \param nonblock 0 = block, 1 = nonblock mode + * \return 0 on success otherwise a negative error code + * + * The nonblock mode cannot be used when the stream is in + * #SND_RAWMIDI_APPEND state. + */ +int snd_ump_nonblock(snd_ump_t *ump, int nonblock) +{ + return snd_rawmidi_nonblock(ump->rawmidi, nonblock); +} + +/** + * \brief get information about associated RawMidi handle + * \param ump UMP handle + * \param info pointer to a snd_rawmidi_info_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_ump_rawmidi_info(snd_ump_t *ump, snd_rawmidi_info_t *info) +{ + return snd_rawmidi_info(ump->rawmidi, info); +} + +/** + * \brief set parameters about associated RawMidi stream + * \param ump UMP handle + * \param params pointer to a snd_rawmidi_params_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_ump_rawmidi_params(snd_ump_t *ump, snd_rawmidi_params_t *params) +{ + return snd_rawmidi_params(ump->rawmidi, params); +} + +/** + * \brief get current parameters about associated RawMidi stream + * \param ump UMP handle + * \param params pointer to a snd_rawmidi_params_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_ump_rawmidi_params_current(snd_ump_t *ump, snd_rawmidi_params_t *params) +{ + return snd_rawmidi_params_current(ump->rawmidi, params); +} + +/** + * \brief get status of associated RawMidi stream + * \param ump UMP handle + * \param status pointer to a snd_rawmidi_status_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_ump_rawmidi_status(snd_ump_t *ump, snd_rawmidi_status_t *status) +{ + return snd_rawmidi_status(ump->rawmidi, status); +} + +/** + * \brief drop all packets in the rawmidi I/O ring buffer immediately + * \param ump UMP handle + * \return 0 on success otherwise a negative error code + */ +int snd_ump_drop(snd_ump_t *ump) +{ + return snd_rawmidi_drop(ump->rawmidi); +} + +/** + * \brief drain all packets in the UMP I/O ring buffer + * \param ump UMP handle + * \return 0 on success otherwise a negative error code + * + * Waits until all MIDI packets are not drained (sent) to the + * hardware device. + */ +int snd_ump_drain(snd_ump_t *ump) +{ + return snd_rawmidi_drain(ump->rawmidi); +} + +/** + * \brief write UMP packets to UMP stream + * \param ump UMP handle + * \param buffer buffer containing UMP packets + * \param size output buffer size in bytes + */ +ssize_t snd_ump_write(snd_ump_t *ump, const void *buffer, size_t size) +{ + if (ump->is_input) + return -EINVAL; + return snd_rawmidi_write(ump->rawmidi, buffer, size); +} + +/** + * \brief read UMP packets from UMP stream + * \param ump UMP handle + * \param buffer buffer to store the input MIDI bytes + * \param size input buffer size in bytes + * \retval count of UMP packet in bytes otherwise a negative error code + */ +ssize_t snd_ump_read(snd_ump_t *ump, void *buffer, size_t size) +{ + if (!ump->is_input) + return -EINVAL; + return snd_rawmidi_read(ump->rawmidi, buffer, size); +} + +/** + * \brief read UMP packets from UMP stream with timestamp + * \param ump UMP handle + * \param[out] tstamp timestamp for the returned UMP packets + * \param buffer buffer to store the input UMP packets + * \param size input buffer size in bytes + * \retval count of UMP packet in bytes otherwise a negative error code + */ +ssize_t snd_ump_tread(snd_ump_t *ump, struct timespec *tstamp, void *buffer, + size_t size) +{ + if (!ump->is_input) + return -EINVAL; + return snd_rawmidi_tread(ump->rawmidi, tstamp, buffer, size); +} + +/** + * \brief get size of the snd_ump_endpoint_info_t structure in bytes + * \return size of the snd_ump_endpoint_info_t structure in bytes + */ +size_t snd_ump_endpoint_info_sizeof(void) +{ + return sizeof(snd_ump_endpoint_info_t); +} + +/** + * \brief allocate the snd_ump_endpoint_info_t structure + * \param ptr returned pointer + * \return 0 on success otherwise a negative error code if fails + * + * Allocates a new snd_rawmidi_status_t structure using the standard + * malloc C library function. + */ +int snd_ump_endpoint_info_malloc(snd_ump_endpoint_info_t **info) +{ + *info = calloc(1, sizeof(snd_ump_endpoint_info_t)); + if (!*info) + return -ENOMEM; + return 0; +} + +/** + * \brief frees the snd_ump_endpoint_info_t structure + * \param status pointer to the snd_ump_endpoint_info_t structure to free + * + * Frees the given snd_ump_endpoint_info_t structure using the standard + * free C library function. + */ +void snd_ump_endpoint_info_free(snd_ump_endpoint_info_t *info) +{ + free(info); +} + +/** + * \brief copy one snd_ump_endpoint_info_t structure to another + * \param dst destination snd_ump_endpoint_info_t structure + * \param src source snd_ump_endpoint_info_t structure + */ +void snd_ump_endpoint_info_copy(snd_ump_endpoint_info_t *dst, + const snd_ump_endpoint_info_t *src) +{ + *dst = *src; +} + +/** + * \brief get card number of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return the card number of the given UMP endpoint + */ +int snd_ump_endpoint_info_get_card(const snd_ump_endpoint_info_t *info) +{ + return info->card; +} + +/** + * \brief get device number of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return the device number of the given UMP endpoint + */ +int snd_ump_endpoint_info_get_device(const snd_ump_endpoint_info_t *info) +{ + return info->device; +} + +/** + * \brief get UMP endpoint info flags + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP endpoint flag bits + */ +unsigned int snd_ump_endpoint_info_get_flags(const snd_ump_endpoint_info_t *info) +{ + return info->flags; +} + +/** + * \brief get UMP endpoint protocol capability bits + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP endpoint protocol capability bits + */ +unsigned int snd_ump_endpoint_info_get_protocol_caps(const snd_ump_endpoint_info_t *info) +{ + return info->protocol_caps; +} + +/** + * \brief get the current UMP endpoint protocol + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP endpoint protocol bits + */ +unsigned int snd_ump_endpoint_info_get_protocol(const snd_ump_endpoint_info_t *info) +{ + return info->protocol; +} + +/** + * \brief get the number of UMP blocks belonging to the endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return number of UMP blocks + */ +unsigned int snd_ump_endpoint_info_get_num_blocks(const snd_ump_endpoint_info_t *info) +{ + return info->num_blocks; +} + +/** + * \brief get UMP version number + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP version number + */ +unsigned int snd_ump_endpoint_info_get_version(const snd_ump_endpoint_info_t *info) +{ + return info->version; +} + +/** + * \brief get UMP endpoint name string + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP endpoint name string + */ +const char *snd_ump_endpoint_info_get_name(const snd_ump_endpoint_info_t *info) +{ + return (const char *)info->name; +} + +/** + * \brief get UMP endpoint product ID string + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP endpoint product ID string + */ +const char *snd_ump_endpoint_info_get_product_id(const snd_ump_endpoint_info_t *info) +{ + return (const char *)info->product_id; +} + +/** + * \brief get endpoint information about UMP handle + * \param ump UMP handle + * \param info pointer to a snd_ump_endpoint_info_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_ump_endpoint_info(snd_ump_t *ump, snd_ump_endpoint_info_t *info) +{ + return _snd_rawmidi_ump_endpoint_info(ump->rawmidi, info); +} + +/** + * \brief get size of the snd_ump_block_info_t structure in bytes + * \return size of the snd_ump_block_info_t structure in bytes + */ +size_t snd_ump_block_info_sizeof(void) +{ + return sizeof(snd_ump_block_info_t); +} + +/** + * \brief allocate the snd_ump_block_info_t structure + * \param ptr returned pointer + * \return 0 on success otherwise a negative error code if fails + * + * Allocates a new snd_ump_block_info_t structure using the standard + * malloc C library function. + */ +int snd_ump_block_info_malloc(snd_ump_block_info_t **info) +{ + *info = calloc(1, sizeof(snd_ump_block_info_t)); + if (!*info) + return -ENOMEM; + return 0; +} + +/** + * \brief frees the snd_ump_block_info_t structure + * \param status pointer to the snd_ump_block_info_t structure to free + * + * Frees the given snd_ump_block_info_t structure using the standard + * free C library function. + */ +void snd_ump_block_info_free(snd_ump_block_info_t *info) +{ + free(info); +} + +/** + * \brief copy one snd_ump_block_info_t structure to another + * \param dst destination snd_ump_block_info_t structure + * \param src source snd_ump_block_info_t structure + */ +void snd_ump_block_info_copy(snd_ump_block_info_t *dst, + const snd_ump_block_info_t *src) +{ + *dst = *src; +} + +/** + * \brief get card number of UMP block + * \param info pointer to a snd_ump_block_info_t structure + * \return the card number of the given UMP block + */ +int snd_ump_block_info_get_card(const snd_ump_block_info_t *info) +{ + return info->card; +} + +/** + * \brief get device number of UMP block + * \param info pointer to a snd_ump_block_info_t structure + * \return the device number of the given UMP block + */ +int snd_ump_block_info_get_device(const snd_ump_block_info_t *info) +{ + return info->device; +} + +/** + * \brief get UMP block ID + * \param info pointer to a snd_ump_block_info_t structure + * \return ID number of the given UMP block + */ +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 + * \return 1 if the block is active or 0 if inactive + */ +unsigned int snd_ump_block_info_get_active(const snd_ump_block_info_t *info) +{ + return info->active; +} + +/** + * \brief get UMP block information flags + * \param info pointer to a snd_ump_block_info_t structure + * \return info flag bits for the given UMP block + */ +unsigned int snd_ump_block_info_get_flags(const snd_ump_block_info_t *info) +{ + return info->flags; +} + +/** + * \brief get UMP block direction + * \param info pointer to a snd_ump_block_info_t structure + * \return direction of UMP block (input,output,bidirectional) + */ +unsigned int snd_ump_block_info_get_direction(const snd_ump_block_info_t *info) +{ + return info->direction; +} + +/** + * \brief get first UMP group ID belonging to the block + * \param info pointer to a snd_ump_block_info_t structure + * \return the first UMP group ID belonging to the block + */ +unsigned int snd_ump_block_info_get_first_group(const snd_ump_block_info_t *info) +{ + return info->first_group; +} + +/** + * \brief get number of UMP groups belonging to the block + * \param info pointer to a snd_ump_block_info_t structure + * \return the number of UMP groups belonging to the block + */ +unsigned int snd_ump_block_info_get_num_groups(const snd_ump_block_info_t *info) +{ + return info->num_groups; +} + +/** + * \brief get the name string of UMP block + * \param info pointer to a snd_ump_block_info_t structure + * \return the name string of UMP block + */ +const char *snd_ump_block_info_get_name(const snd_ump_block_info_t *info) +{ + return (const char *)info->name; +} + +/** + * \brief get UMP block information + * \param info pointer to a snd_ump_block_info_t structure + * \return 0 on success otherwise a negative error code + * + * The caller should fill the block ID to query at first via + * snd_ump_block_info_set_block_id(). + */ +int snd_ump_block_info(snd_ump_t *ump, snd_ump_block_info_t *info) +{ + return _snd_rawmidi_ump_block_info(ump->rawmidi, info); +} diff --git a/src/rawmidi/ump_local.h b/src/rawmidi/ump_local.h new file mode 100644 index 00000000..424051ae --- /dev/null +++ b/src/rawmidi/ump_local.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#include "rawmidi.h" +#include "ump.h" + +struct _snd_ump { + snd_rawmidi_t *rawmidi; + unsigned int flags; + int is_input; +}; From 8bc10c83b155c5cff2ae6ddf44a8bf899ff4397c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 25 Dec 2022 12:09:51 +0100 Subject: [PATCH 071/267] control: Add UMP device query support Add a function to query the next available UMP device via control interface, just like the existing one for rawmidi. As the UMP rawmidi is compatible with the standard rawmidi, no extra helper for the rawmidi_info is present. Ditto for the preferred subdevice, too. Signed-off-by: Takashi Iwai --- include/control.h | 1 + src/Versions.in | 1 + src/control/control.c | 14 ++++++++++++++ src/control/control_hw.c | 9 +++++++++ src/control/control_local.h | 1 + 5 files changed, 26 insertions(+) diff --git a/include/control.h b/include/control.h index d0c40d2f..a2439d78 100644 --- a/include/control.h +++ b/include/control.h @@ -417,6 +417,7 @@ int snd_ctl_pcm_prefer_subdevice(snd_ctl_t *ctl, int subdev); int snd_ctl_rawmidi_next_device(snd_ctl_t *ctl, int * device); int snd_ctl_rawmidi_info(snd_ctl_t *ctl, snd_rawmidi_info_t * info); int snd_ctl_rawmidi_prefer_subdevice(snd_ctl_t *ctl, int subdev); +int snd_ctl_ump_next_device(snd_ctl_t *ctl, int *device); #endif int snd_ctl_set_power_state(snd_ctl_t *ctl, unsigned int state); int snd_ctl_get_power_state(snd_ctl_t *ctl, unsigned int *state); diff --git a/src/Versions.in b/src/Versions.in index 3315fa28..ee17cf28 100644 --- a/src/Versions.in +++ b/src/Versions.in @@ -153,4 +153,5 @@ ALSA_1.2.10 { global: @SYMBOL_PREFIX@snd_ump_*; + @SYMBOL_PREFIX@snd_ctl_ump_next_device; } ALSA_1.2.9; diff --git a/src/control/control.c b/src/control/control.c index d3ecc6e9..7168ff8d 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -1267,6 +1267,20 @@ int snd_ctl_rawmidi_prefer_subdevice(snd_ctl_t *ctl, int subdev) return ctl->ops->rawmidi_prefer_subdevice(ctl, subdev); } +/** + * \brief Get next UMP device number + * \param ctl CTL handle + * \param device current device on entry and next device on return + * \return 0 on success otherwise a negative error code + */ +int snd_ctl_ump_next_device(snd_ctl_t *ctl, int *device) +{ + assert(ctl && device); + if (ctl->ops->ump_next_device) + return ctl->ops->ump_next_device(ctl, device); + return -ENXIO; +} + /** * \brief Set Power State to given SND_CTL_POWER_* value and do the power management * \param ctl CTL handle diff --git a/src/control/control_hw.c b/src/control/control_hw.c index a927cdfe..95d6d07d 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -325,6 +325,14 @@ static int snd_ctl_hw_rawmidi_prefer_subdevice(snd_ctl_t *handle, int subdev) return 0; } +static int snd_ctl_hw_ump_next_device(snd_ctl_t *handle, int *device) +{ + snd_ctl_hw_t *hw = handle->private_data; + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE, device) < 0) + return -errno; + return 0; +} + static int snd_ctl_hw_set_power_state(snd_ctl_t *handle, unsigned int state) { snd_ctl_hw_t *hw = handle->private_data; @@ -379,6 +387,7 @@ static const snd_ctl_ops_t snd_ctl_hw_ops = { .rawmidi_next_device = snd_ctl_hw_rawmidi_next_device, .rawmidi_info = snd_ctl_hw_rawmidi_info, .rawmidi_prefer_subdevice = snd_ctl_hw_rawmidi_prefer_subdevice, + .ump_next_device = snd_ctl_hw_ump_next_device, .set_power_state = snd_ctl_hw_set_power_state, .get_power_state = snd_ctl_hw_get_power_state, .read = snd_ctl_hw_read, diff --git a/src/control/control_local.h b/src/control/control_local.h index b84dc429..aa05bac8 100644 --- a/src/control/control_local.h +++ b/src/control/control_local.h @@ -47,6 +47,7 @@ typedef struct _snd_ctl_ops { int (*rawmidi_next_device)(snd_ctl_t *handle, int *device); int (*rawmidi_info)(snd_ctl_t *handle, snd_rawmidi_info_t * info); int (*rawmidi_prefer_subdevice)(snd_ctl_t *handle, int subdev); + int (*ump_next_device)(snd_ctl_t *handle, int *device); int (*set_power_state)(snd_ctl_t *handle, unsigned int state); int (*get_power_state)(snd_ctl_t *handle, unsigned int *state); int (*read)(snd_ctl_t *handle, snd_ctl_event_t *event); From 81b0cf46d16a9cff39037d3b91f7b74d1658f7fe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 22 May 2023 18:04:40 +0200 Subject: [PATCH 072/267] control: Add UMP Endpoint and Block info query support Add functions to query the UMP Endpoint and Block info via control interface. Signed-off-by: Takashi Iwai --- include/control.h | 2 ++ include/local.h | 1 + src/Versions.in | 2 ++ src/control/control.c | 24 ++++++++++++++++++++++++ src/control/control_hw.c | 20 ++++++++++++++++++++ src/control/control_local.h | 2 ++ 6 files changed, 51 insertions(+) diff --git a/include/control.h b/include/control.h index a2439d78..41892de2 100644 --- a/include/control.h +++ b/include/control.h @@ -418,6 +418,8 @@ int snd_ctl_rawmidi_next_device(snd_ctl_t *ctl, int * device); int snd_ctl_rawmidi_info(snd_ctl_t *ctl, snd_rawmidi_info_t * info); int snd_ctl_rawmidi_prefer_subdevice(snd_ctl_t *ctl, int subdev); 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); +int snd_ctl_ump_block_info(snd_ctl_t *ctl, snd_ump_block_info_t *info); #endif int snd_ctl_set_power_state(snd_ctl_t *ctl, unsigned int state); int snd_ctl_get_power_state(snd_ctl_t *ctl, unsigned int *state); diff --git a/include/local.h b/include/local.h index 151e3fd4..4206d681 100644 --- a/include/local.h +++ b/include/local.h @@ -180,6 +180,7 @@ #include "pcm.h" #include "pcm_plugin.h" #include "rawmidi.h" +#include "ump.h" #include "timer.h" #include "hwdep.h" #include "control.h" diff --git a/src/Versions.in b/src/Versions.in index ee17cf28..2acf3d18 100644 --- a/src/Versions.in +++ b/src/Versions.in @@ -154,4 +154,6 @@ ALSA_1.2.10 { @SYMBOL_PREFIX@snd_ump_*; @SYMBOL_PREFIX@snd_ctl_ump_next_device; + @SYMBOL_PREFIX@snd_ctl_ump_endpoint_info; + @SYMBOL_PREFIX@snd_ctl_ump_block_info; } ALSA_1.2.9; diff --git a/src/control/control.c b/src/control/control.c index 7168ff8d..ec01518e 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -1281,6 +1281,30 @@ int snd_ctl_ump_next_device(snd_ctl_t *ctl, int *device) return -ENXIO; } +/** + * \brief Get UMP Endpoint info about a UMP RawMidi device + * \param ctl CTL handle + * \param info UMP Endpoint info pointer + * \return 0 on success otherwise a negative error code + */ +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); +} + +/** + * \brief Get UMP Block info about a UMP RawMidi device + * \param ctl CTL handle + * \param info UMP Block info pointer + * \return 0 on success otherwise a negative error code + */ +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); +} + /** * \brief Set Power State to given SND_CTL_POWER_* value and do the power management * \param ctl CTL handle diff --git a/src/control/control_hw.c b/src/control/control_hw.c index 95d6d07d..df76990b 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -333,6 +333,24 @@ static int snd_ctl_hw_ump_next_device(snd_ctl_t *handle, int *device) return 0; } +static int snd_ctl_hw_ump_endpoint_info(snd_ctl_t *handle, + snd_ump_endpoint_info_t *info) +{ + snd_ctl_hw_t *hw = handle->private_data; + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO, info) < 0) + return -errno; + return 0; +} + +static int snd_ctl_hw_ump_block_info(snd_ctl_t *handle, + snd_ump_block_info_t *info) +{ + snd_ctl_hw_t *hw = handle->private_data; + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_BLOCK_INFO, info) < 0) + return -errno; + return 0; +} + static int snd_ctl_hw_set_power_state(snd_ctl_t *handle, unsigned int state) { snd_ctl_hw_t *hw = handle->private_data; @@ -388,6 +406,8 @@ static const snd_ctl_ops_t snd_ctl_hw_ops = { .rawmidi_info = snd_ctl_hw_rawmidi_info, .rawmidi_prefer_subdevice = snd_ctl_hw_rawmidi_prefer_subdevice, .ump_next_device = snd_ctl_hw_ump_next_device, + .ump_endpoint_info = snd_ctl_hw_ump_endpoint_info, + .ump_block_info = snd_ctl_hw_ump_block_info, .set_power_state = snd_ctl_hw_set_power_state, .get_power_state = snd_ctl_hw_get_power_state, .read = snd_ctl_hw_read, diff --git a/src/control/control_local.h b/src/control/control_local.h index aa05bac8..2afa62cc 100644 --- a/src/control/control_local.h +++ b/src/control/control_local.h @@ -48,6 +48,8 @@ typedef struct _snd_ctl_ops { int (*rawmidi_info)(snd_ctl_t *handle, snd_rawmidi_info_t * info); int (*rawmidi_prefer_subdevice)(snd_ctl_t *handle, int subdev); int (*ump_next_device)(snd_ctl_t *handle, int *device); + int (*ump_endpoint_info)(snd_ctl_t *handle, snd_ump_endpoint_info_t *info); + int (*ump_block_info)(snd_ctl_t *handle, snd_ump_block_info_t *info); int (*set_power_state)(snd_ctl_t *handle, unsigned int state); int (*get_power_state)(snd_ctl_t *handle, unsigned int *state); int (*read)(snd_ctl_t *handle, snd_ctl_event_t *event); From 040356ecf06b692fc74a2746bf91fabb2a78425b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Nov 2022 16:30:47 +0100 Subject: [PATCH 073/267] ump: Add helpers to parse / set UMP packet data This patch defines the structs / unions that can be used for encoding and decoding UMP packets, as well as inline helper functions. Signed-off-by: Takashi Iwai --- include/Makefile.am | 2 +- include/local.h | 2 + include/ump_msg.h | 571 ++++++++++++++++++++++++++++++++++++++++ src/rawmidi/ump_local.h | 1 + 4 files changed, 575 insertions(+), 1 deletion(-) create mode 100644 include/ump_msg.h diff --git a/include/Makefile.am b/include/Makefile.am index b7f67b25..16d68b51 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -34,7 +34,7 @@ endif endif if BUILD_RAWMIDI -alsainclude_HEADERS += rawmidi.h ump.h +alsainclude_HEADERS += rawmidi.h ump.h ump_msg.h endif if BUILD_HWDEP diff --git a/include/local.h b/include/local.h index 4206d681..c6be21ee 100644 --- a/include/local.h +++ b/include/local.h @@ -69,9 +69,11 @@ #if __BYTE_ORDER == __LITTLE_ENDIAN #define SND_LITTLE_ENDIAN #define SNDRV_LITTLE_ENDIAN +#define SNDRV_LITTLE_ENDIAN_BITFIELD #elif __BYTE_ORDER == __BIG_ENDIAN #define SND_BIG_ENDIAN #define SNDRV_BIG_ENDIAN +#define SNDRV_BIG_ENDIAN_BITFIELD #else #error "Unsupported endian..." #endif diff --git a/include/ump_msg.h b/include/ump_msg.h new file mode 100644 index 00000000..1b7f7066 --- /dev/null +++ b/include/ump_msg.h @@ -0,0 +1,571 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/** + * \file include/ump_msg.h + * \brief API library for ALSA rawmidi/UMP interface + * + * API library for ALSA rawmidi/UMP interface + */ + +#ifndef __ALSA_UMP_MSG_H +#define __ALSA_UMP_MSG_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** general UMP packet header in 32bit word */ +typedef struct _snd_ump_msg_hdr { +#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 channel:4; /**< Channel */ + uint8_t byte1; /**< First data byte */ + uint8_t byte2; /**< Second data byte */ +#else + uint8_t byte2; /**< Second data byte */ + uint8_t byte1; /**< First data byte */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_hdr_t; + +/** MIDI 1.0 Note Off / Note On (32bit) */ +typedef struct _snd_ump_msg_midi1_note { +#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 channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t velocity; /**< Velocity (7bit) */ +#else + uint8_t velocity; /**< Velocity (7bit) */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_note_t; + +/** MIDI 1.0 Poly Pressure (32bit) */ +typedef struct _snd_ump_msg_midi1_paf { +#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 channel:4; /**< Channel */ + uint8_t note; /** Note (7bit) */ + uint8_t data; /** Pressure (7bit) */ +#else + 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 */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_paf_t; + +/** MIDI 1.0 Control Change (32bit) */ +typedef struct _snd_ump_msg_midi1_cc { +#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 channel:4; /**< Channel */ + 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 channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_cc_t; + +/** MIDI 1.0 Program Change (32bit) */ +typedef struct _snd_ump_msg_midi1_program { +#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 channel:4; /**< Channel */ + uint8_t program; /**< Program number (7bit) */ + uint8_t reserved; /**< Unused */ +#else + uint8_t reserved; /**< Unused */ + uint8_t program; /**< Program number (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_program_t; + +/** MIDI 1.0 Channel Pressure (32bit) */ +typedef struct _snd_ump_msg_midi1_caf { +#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 channel:4; /**< Channel */ + uint8_t data; /**< Pressure (7bit) */ + uint8_t reserved; /**< Unused */ +#else + uint8_t reserved; /**< Unused */ + uint8_t data; /**< Pressure (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_caf_t; + +/** MIDI 1.0 Pitch Bend (32bit) */ +typedef struct _snd_ump_msg_midi1_pitchbend { +#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 channel:4; /**< Channel */ + uint8_t data_lsb; /**< LSB of pitchbend (7bit) */ + uint8_t data_msb; /**< MSB of pitchbend (7bit) */ +#else + uint8_t data_msb; /**< MSB of pitchbend (7bit) */ + uint8_t data_lsb; /**< LSB of pitchbend (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_pitchbend_t; + +/** System Common and Real Time messages (32bit); no channel field */ +typedef struct snd_ump_msg_system { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status; /**< Status */ + uint8_t parm1; /**< First parameter */ + uint8_t parm2; /**< Second parameter */ +#else + uint8_t parm1; /**< First parameter */ + uint8_t parm2; /**< Second parameter */ + uint8_t status; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_system_t; + +/** 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_t; + +/** MIDI 2.0 Note-on/off attribute type */ +enum { + SND_UMP_MIDI2_NOTE_ATTR_NO_DATA = 0x00, /**< No attribute data */ + SND_UMP_MIDI2_NOTE_ATTR_MANUFACTURER = 0x01, /**< Manufacturer specific */ + SND_UMP_MIDI2_NOTE_ATTR_PROFILE = 0x02, /**< Profile specific */ + SND_UMP_MIDI2_NOTE_ATTR_PITCH79 = 0x03, /**< Pitch 7.9 */ +}; + +/* 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 */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t attr_type; /**< Attribute type */ + + uint16_t velocity; /**< Velocity (16bit) */ + uint16_t attr_data; /**< Attribute data (16bit) */ +#else + uint8_t attr_type; /**< Attribute type */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint16_t attr_data; /**< Attribute data (16bit) */ + uint16_t velocity; /**< Velocity (16bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_note_t; + +/** MIDI 2.0 Poly Pressure (64bit) */ +typedef struct _snd_ump_msg_midi2_paf { +#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 channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t reserved; /**< Unused */ + + uint32_t data; /**< Pressure (32bit) */ +#else + uint8_t reserved; /**< Unused */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /**< Pressure (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_paf_t; + +/** MIDI 2.0 Per-Note Controller (64bit) */ +typedef struct _snd_ump_msg_midi2_per_note_cc { +#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 channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t index; /**< Control index (8bit) */ + + uint32_t data; /**< Data (32bit) */ +#else + uint8_t index; /**< Control index (8bit) */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /**< Data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_per_note_cc_t; + +/** MIDI 2.0 per-note management flag bits */ +enum { + SND_UMP_MIDI2_PNMGMT_RESET_CONTROLLERS = 0x01, /**< Reset (set) per-note controllers */ + SND_UMP_MIDI2_PNMGMT_DETACH_CONTROLLERS = 0x02, /**< Detach per-note controllers */ +}; + +/** MIDI 2.0 Per-Note Management (64bit) */ +typedef struct _snd_ump_msg_midi2_per_note_mgmt { +#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 channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t flags; /**< Option flags (8bit) */ + + uint32_t reserved; /**< Unused */ +#else + uint8_t flags; /**< Option flags (8bit) */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t reserved; /**< Unused */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_per_note_mgmt_t; + +/** MIDI 2.0 Control Change (64bit) */ +typedef struct _snd_ump_msg_midi2_cc { +#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 channel:4; /**< Channel */ + uint8_t index; /**< Control index (7bit) */ + uint8_t reserved; /**< Unused */ + + uint32_t data; /**< Control data (32bit) */ +#else + uint8_t reserved; /**< Unused */ + uint8_t index; /**< Control index (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /**< Control data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_cc_t; + +/** MIDI 2.0 Registered Controller (RPN) / Assignable Controller (NRPN) (64bit) */ +typedef struct _snd_ump_msg_midi2_rpn { +#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 channel:4; /**< Channel */ + uint8_t bank; /**< Bank number (7bit) */ + uint8_t index; /**< Control index (7bit) */ + + uint32_t data; /**< Data (32bit) */ +#else + uint8_t index; /**< Control index (7bit) */ + uint8_t bank; /**< Bank number (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /**< Data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_rpn_t; + +/** MIDI 2.0 Program Change (64bit) */ +typedef struct _snd_ump_msg_midi2_program { +#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 channel:4; /**< Channel */ + uint16_t reserved:15; /**< Unused */ + uint16_t bank_valid:1; /**< Option flag: bank valid */ + + uint8_t program; /**< Program number (7bit) */ + uint8_t reserved2; /**< Unused */ + uint8_t bank_msb; /**< MSB of bank (8bit) */ + uint8_t bank_lsb; /**< LSB of bank (7bit) */ +#else + uint16_t bank_valid:1; /**< Option flag: bank valid */ + uint16_t reserved:15; /**< Unused */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint8_t bank_lsb; /**< LSB of bank (7bit) */ + uint8_t bank_msb; /**< MSB of bank (8bit) */ + uint8_t reserved2; /**< Unused */ + uint8_t program; /**< Program number (7bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_program_t; + +/** MIDI 2.0 Channel Pressure (64bit) */ +typedef struct _snd_ump_msg_midi2_caf { +#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 channel:4; /**< Channel */ + uint16_t reserved; /**< Unused */ + + uint32_t data; /** Data (32bit) */ +#else + uint16_t reserved; /**< Unused */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /** Data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_caf_t; + +/* 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 */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint16_t reserved; /**< Unused */ + + uint32_t data; /** Data (32bit) */ +#else + uint16_t reserved; /**< Unused */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /** Data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_pitchbend_t; + +/* 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 */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t reserved; /**< Unused */ + + uint32_t data; /**< Data (32bit) */ +#else + /* 0 */ + uint8_t reserved; /**< Unused */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /**< Data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_per_note_pitchbend_t; + +/** 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_t; + +/** + * UMP message type + */ +enum { + SND_UMP_MSG_TYPE_SYSTEM = 0x01, /* System messages */ + SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE = 0x02, /* MIDI 1.0 messages */ + SND_UMP_MSG_TYPE_DATA = 0x03, /* 7bit SysEx messages */ + SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE = 0x04, /* MIDI 2.0 messages */ + SND_UMP_MSG_TYPE_EXTENDED_DATA = 0x05, /* 8bit data message */ +}; + +/** + * UMP MIDI 1.0 / 2.0 message status code (4bit) + */ +enum { + SND_UMP_MSG_PER_NOTE_RCC = 0x0, + SND_UMP_MSG_PER_NOTE_ACC = 0x1, + SND_UMP_MSG_RPN = 0x2, + SND_UMP_MSG_NRPN = 0x3, + SND_UMP_MSG_RELATIVE_RPN = 0x4, + SND_UMP_MSG_RELATIVE_NRPN = 0x5, + SND_UMP_MSG_PER_NOTE_PITCHBEND = 0x6, + SND_UMP_MSG_NOTE_OFF = 0x8, + SND_UMP_MSG_NOTE_ON = 0x9, + SND_UMP_MSG_POLY_PRESSURE = 0xa, + SND_UMP_MSG_CONTROL_CHANGE = 0xb, + SND_UMP_MSG_PROGRAM_CHANGE = 0xc, + SND_UMP_MSG_CHANNEL_PRESSURE = 0xd, + SND_UMP_MSG_PITCHBEND = 0xe, + SND_UMP_MSG_PER_NOTE_MGMT = 0xf, +}; + +/** + * MIDI System / Realtime message status code (8bit) + */ +enum { + SND_UMP_MSG_REALTIME = 0xf0, /* mask */ + SND_UMP_MSG_SYSEX_START = 0xf0, + SND_UMP_MSG_MIDI_TIME_CODE = 0xf1, + SND_UMP_MSG_SONG_POSITION = 0xf2, + SND_UMP_MSG_SONG_SELECT = 0xf3, + SND_UMP_MSG_TUNE_REQUEST = 0xf6, + SND_UMP_MSG_SYSEX_END = 0xf7, + SND_UMP_MSG_TIMING_CLOCK = 0xf8, + SND_UMP_MSG_START = 0xfa, + SND_UMP_MSG_CONTINUE = 0xfb, + SND_UMP_MSG_STOP = 0xfc, + SND_UMP_MSG_ACTIVE_SENSING = 0xfe, + SND_UMP_MSG_RESET = 0xff, +}; + +/** + * \brief get UMP status (4bit) from 32bit UMP message header + */ +static inline uint8_t snd_ump_msg_hdr_status(uint32_t ump) +{ + return (ump >> 20) & 0x0f; +} + +/** + * \brief get UMP channel (4bit) from 32bit UMP message header + */ +static inline uint8_t snd_ump_msg_hdr_channel(uint32_t ump) +{ + return (ump >> 16) & 0x0f; +} + +/** + * \brief get UMP message type (4bit) from 32bit UMP message header + */ +static inline uint8_t snd_ump_msg_hdr_type(uint32_t ump) +{ + return (ump >> 28); +} + +/** + * \brief get UMP group (4bit) from 32bit UMP message header + */ +static inline uint8_t snd_ump_msg_hdr_group(uint32_t ump) +{ + return (ump >> 24) & 0x0f; +} + +/** + * \brief get UMP status from UMP packet pointer + */ +static inline uint8_t snd_ump_msg_status(const uint32_t *ump) +{ + return snd_ump_msg_hdr_status(*ump); +} + +/** + * \brief get UMP channel from UMP packet pointer + */ +static inline uint8_t snd_ump_msg_channel(const uint32_t *ump) +{ + return snd_ump_msg_hdr_channel(*ump); +} + +/** + * \brief get UMP message type from UMP packet pointer + */ +static inline uint8_t snd_ump_msg_type(const uint32_t *ump) +{ + return snd_ump_msg_hdr_type(*ump); +} + +/** + * \brief get UMP group from UMP packet pointer + */ +static inline uint8_t snd_ump_msg_group(const uint32_t *ump) +{ + return snd_ump_msg_hdr_group(*ump); +} + +#ifdef __cplusplus +} +#endif + +#endif /* __ALSA_UMP_MSG_H */ diff --git a/src/rawmidi/ump_local.h b/src/rawmidi/ump_local.h index 424051ae..53ce9a66 100644 --- a/src/rawmidi/ump_local.h +++ b/src/rawmidi/ump_local.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "rawmidi.h" #include "ump.h" +#include "ump_msg.h" struct _snd_ump { snd_rawmidi_t *rawmidi; From e80ee6ae759cd6b1ed84c86d3aa49cc981964077 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Nov 2022 16:42:45 +0100 Subject: [PATCH 074/267] ump: Add helpers for handling SysEx data Yet a few more helpers for handling SysEx data with UMP packets. Signed-off-by: Takashi Iwai --- include/ump_msg.h | 27 +++++++++++++++ src/rawmidi/ump.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/include/ump_msg.h b/include/ump_msg.h index 1b7f7066..4ccce546 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -500,6 +500,14 @@ enum { SND_UMP_MSG_RESET = 0xff, }; +/** MIDI 2.0 SysEx / Data Status; same values for both 7-bit and 8-bit SysEx */ +enum { + SND_UMP_SYSEX_STATUS_SINGLE = 0, + SND_UMP_SYSEX_STATUS_START = 1, + SND_UMP_SYSEX_STATUS_CONTINUE = 2, + SND_UMP_SYSEX_STATUS_END = 3, +}; + /** * \brief get UMP status (4bit) from 32bit UMP message header */ @@ -564,6 +572,25 @@ static inline uint8_t snd_ump_msg_group(const uint32_t *ump) return snd_ump_msg_hdr_group(*ump); } +/** + * \brief get UMP sysex message status + */ +static inline uint8_t snd_ump_sysex_msg_status(const uint32_t *ump) +{ + return (*ump >> 20) & 0xf; +} + +/** + * \brief get UMP sysex message length + */ +static inline uint8_t snd_ump_sysex_msg_length(const uint32_t *ump) +{ + return (*ump >> 16) & 0xf; +} + +int snd_ump_msg_sysex_expand(const uint32_t *ump, uint8_t *buf, size_t maxlen, + size_t *filled); + #ifdef __cplusplus } #endif diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index 5da79459..2482884f 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -614,3 +614,89 @@ int snd_ump_block_info(snd_ump_t *ump, snd_ump_block_info_t *info) { return _snd_rawmidi_ump_block_info(ump->rawmidi, info); } + +/* + * UMP sysex helpers + */ +static int expand_sysex_data(const uint32_t *data, uint8_t *buf, + size_t maxlen, unsigned char bytes, int offset) +{ + int size = 0; + + for (; bytes; bytes--, size++) { + if (!maxlen) + break; + buf[size] = (*data >> offset) & 0x7f; + if (!offset) { + offset = 24; + data++; + } else { + offset -= 8; + } + } + + return size; +} + +static int expand_sysex7(const uint32_t *ump, uint8_t *buf, size_t maxlen, + size_t *filled) +{ + unsigned char status; + unsigned char bytes; + + *filled = 0; + if (!maxlen) + return 0; + + status = snd_ump_sysex_msg_status(ump); + bytes = snd_ump_sysex_msg_length(ump); + if (bytes > 6) + return 0; // invalid - skip + + *filled = expand_sysex_data(ump, buf, maxlen, bytes, 8); + return (status == SND_UMP_SYSEX_STATUS_SINGLE || + status == SND_UMP_SYSEX_STATUS_END); +} + +static int expand_sysex8(const uint32_t *ump, uint8_t *buf, size_t maxlen, + size_t *filled) +{ + unsigned char status; + unsigned char bytes; + + *filled = 0; + if (!maxlen) + return 0; + + status = snd_ump_sysex_msg_status(ump); + if (status > SND_UMP_SYSEX_STATUS_END) + return 0; // unsupported, skip + bytes = snd_ump_sysex_msg_length(ump); + if (!bytes || bytes > 14) + return 0; // skip + + *filled = expand_sysex_data(ump, buf, maxlen, bytes - 1, 0); + return (status == SND_UMP_SYSEX_STATUS_SINGLE || + status == SND_UMP_SYSEX_STATUS_END); +} + +/** + * \brief fill sysex byte from a UMP packet + * \param ump UMP packet pointer + * \param buf buffer point to fill sysex bytes + * \param maxlen max buffer size in bytes + * \param filled the size of filled sysex bytes on the buffer + * \return 1 if the sysex finished, otherwise 0 + */ +int snd_ump_msg_sysex_expand(const uint32_t *ump, uint8_t *buf, size_t maxlen, + size_t *filled) +{ + switch (snd_ump_msg_type(ump)) { + case SND_UMP_MSG_TYPE_DATA: + return expand_sysex7(ump, buf, maxlen, filled); + case SND_UMP_MSG_TYPE_EXTENDED_DATA: + return expand_sysex8(ump, buf, maxlen, filled); + default: + return -EINVAL; + } +} From c40dc19a57cce62a4072cb494fc1d96ccd09d611 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 16 Nov 2022 17:08:43 +0100 Subject: [PATCH 075/267] uapi: Update asequencer.h definitions for 1.0.3 Updated from kernel for supporting UMP on sequencer API. Signed-off-by: Takashi Iwai --- include/sound/uapi/asequencer.h | 91 +++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 21 deletions(-) diff --git a/include/sound/uapi/asequencer.h b/include/sound/uapi/asequencer.h index 2d600320..3653a3f3 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, 2) +#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 3) /** * definition of sequencer event types @@ -190,6 +190,7 @@ struct snd_seq_connect { #define SNDRV_SEQ_PRIORITY_HIGH (1<<4) /* event should be processed before others */ #define SNDRV_SEQ_PRIORITY_MASK (1<<4) +#define SNDRV_SEQ_EVENT_UMP (1<<5) /* event holds a UMP packet */ /* note event */ struct snd_seq_ev_note { @@ -268,6 +269,19 @@ struct snd_seq_ev_quote { struct snd_seq_event *event; /* quoted event */ } __attribute__((packed)); +union snd_seq_event_data { /* event data... */ + struct snd_seq_ev_note note; + struct snd_seq_ev_ctrl control; + struct snd_seq_ev_raw8 raw8; + struct snd_seq_ev_raw32 raw32; + struct snd_seq_ev_ext ext; + struct snd_seq_ev_queue_control queue; + union snd_seq_timestamp time; + struct snd_seq_addr addr; + struct snd_seq_connect connect; + struct snd_seq_result result; + struct snd_seq_ev_quote quote; +}; /* sequencer event */ struct snd_seq_event { @@ -278,25 +292,27 @@ struct snd_seq_event { unsigned char queue; /* schedule queue */ union snd_seq_timestamp time; /* schedule time */ - struct snd_seq_addr source; /* source address */ struct snd_seq_addr dest; /* destination address */ - union { /* event data... */ - struct snd_seq_ev_note note; - struct snd_seq_ev_ctrl control; - struct snd_seq_ev_raw8 raw8; - struct snd_seq_ev_raw32 raw32; - struct snd_seq_ev_ext ext; - struct snd_seq_ev_queue_control queue; - union snd_seq_timestamp time; - struct snd_seq_addr addr; - struct snd_seq_connect connect; - struct snd_seq_result result; - struct snd_seq_ev_quote quote; - } data; + union snd_seq_event_data data; }; + /* (compatible) event for UMP-capable clients */ +struct snd_seq_ump_event { + snd_seq_event_type_t type; /* event type */ + unsigned char flags; /* event flags */ + char tag; + unsigned char queue; /* schedule queue */ + union snd_seq_timestamp time; /* schedule time */ + struct snd_seq_addr source; /* source address */ + struct snd_seq_addr dest; /* destination address */ + + union { + union snd_seq_event_data data; + unsigned int ump[4]; + }; +}; /* * bounce event - stored as variable size data @@ -344,10 +360,11 @@ typedef int __bitwise snd_seq_client_type_t; #define KERNEL_CLIENT ((snd_seq_client_type_t) 2) /* event filter flags */ -#define SNDRV_SEQ_FILTER_BROADCAST (1<<0) /* accept broadcast messages */ -#define SNDRV_SEQ_FILTER_MULTICAST (1<<1) /* accept multicast messages */ -#define SNDRV_SEQ_FILTER_BOUNCE (1<<2) /* accept bounce event in error */ -#define SNDRV_SEQ_FILTER_USE_EVENT (1<<31) /* use event filter */ +#define SNDRV_SEQ_FILTER_BROADCAST (1U<<0) /* accept broadcast messages */ +#define SNDRV_SEQ_FILTER_MULTICAST (1U<<1) /* accept multicast messages */ +#define SNDRV_SEQ_FILTER_BOUNCE (1U<<2) /* accept bounce event in error */ +#define SNDRV_SEQ_FILTER_NO_CONVERT (1U<<30) /* don't convert UMP events */ +#define SNDRV_SEQ_FILTER_USE_EVENT (1U<<31) /* use event filter */ struct snd_seq_client_info { int client; /* client number to inquire */ @@ -360,9 +377,15 @@ struct snd_seq_client_info { int event_lost; /* number of lost events */ int card; /* RO: card number[kernel] */ int pid; /* RO: pid[user] */ - char reserved[56]; /* for future use */ + unsigned int midi_version; /* MIDI version */ + unsigned int group_filter; /* UMP group filter bitmap */ + char reserved[48]; /* for future use */ }; +/* MIDI version numbers in client info */ +#define SNDRV_SEQ_CLIENT_LEGACY_MIDI 0 /* Legacy client */ +#define SNDRV_SEQ_CLIENT_UMP_MIDI_1_0 1 /* UMP MIDI 1.0 */ +#define SNDRV_SEQ_CLIENT_UMP_MIDI_2_0 2 /* UMP MIDI 2.0 */ /* client pool size */ struct snd_seq_client_pool { @@ -422,6 +445,8 @@ struct snd_seq_remove_events { #define SNDRV_SEQ_PORT_CAP_SUBS_READ (1<<5) /* allow read subscription */ #define SNDRV_SEQ_PORT_CAP_SUBS_WRITE (1<<6) /* allow write subscription */ #define SNDRV_SEQ_PORT_CAP_NO_EXPORT (1<<7) /* routing not allowed */ +#define SNDRV_SEQ_PORT_CAP_INACTIVE (1<<8) /* inactive port */ +#define SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT (1<<9) /* MIDI 2.0 UMP Endpoint port */ /* port type */ #define SNDRV_SEQ_PORT_TYPE_SPECIFIC (1<<0) /* hardware specific */ @@ -431,6 +456,7 @@ struct snd_seq_remove_events { #define SNDRV_SEQ_PORT_TYPE_MIDI_XG (1<<4) /* XG compatible device */ #define SNDRV_SEQ_PORT_TYPE_MIDI_MT32 (1<<5) /* MT-32 compatible device */ #define SNDRV_SEQ_PORT_TYPE_MIDI_GM2 (1<<6) /* General MIDI 2 compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_UMP (1<<7) /* UMP */ /* other standards...*/ #define SNDRV_SEQ_PORT_TYPE_SYNTH (1<<10) /* Synth device (no MIDI compatible - direct wavetable) */ @@ -448,6 +474,12 @@ struct snd_seq_remove_events { #define SNDRV_SEQ_PORT_FLG_TIMESTAMP (1<<1) #define SNDRV_SEQ_PORT_FLG_TIME_REAL (1<<2) +/* port direction */ +#define SNDRV_SEQ_PORT_DIR_UNKNOWN 0 +#define SNDRV_SEQ_PORT_DIR_INPUT 1 +#define SNDRV_SEQ_PORT_DIR_OUTPUT 2 +#define SNDRV_SEQ_PORT_DIR_BIDIRECTION 3 + struct snd_seq_port_info { struct snd_seq_addr addr; /* client/port numbers */ char name[64]; /* port name */ @@ -464,7 +496,9 @@ struct snd_seq_port_info { void *kernel; /* reserved for kernel use (must be NULL) */ unsigned int flags; /* misc. conditioning */ unsigned char time_queue; /* queue # for timestamping */ - char reserved[59]; /* for future use */ + unsigned char direction; /* port usage direction (r/w/bidir) */ + unsigned char ump_group; /* 0 = UMP EP (no conversion), 1-16 = UMP group number */ + char reserved[57]; /* for future use */ }; @@ -568,6 +602,18 @@ struct snd_seq_query_subs { char reserved[64]; /* for future use */ }; +/* + * UMP-specific information + */ +/* type of UMP info query */ +#define SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT 0 +#define SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK 1 + +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; /* * IOCTL commands @@ -577,9 +623,12 @@ struct snd_seq_query_subs { #define SNDRV_SEQ_IOCTL_CLIENT_ID _IOR ('S', 0x01, int) #define SNDRV_SEQ_IOCTL_SYSTEM_INFO _IOWR('S', 0x02, struct snd_seq_system_info) #define SNDRV_SEQ_IOCTL_RUNNING_MODE _IOWR('S', 0x03, struct snd_seq_running_info) +#define SNDRV_SEQ_IOCTL_USER_PVERSION _IOW('S', 0x04, int) #define SNDRV_SEQ_IOCTL_GET_CLIENT_INFO _IOWR('S', 0x10, struct snd_seq_client_info) #define SNDRV_SEQ_IOCTL_SET_CLIENT_INFO _IOW ('S', 0x11, struct snd_seq_client_info) +#define SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO _IOWR('S', 0x12, struct snd_seq_client_ump_info) +#define SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO _IOWR('S', 0x13, struct snd_seq_client_ump_info) #define SNDRV_SEQ_IOCTL_CREATE_PORT _IOWR('S', 0x20, struct snd_seq_port_info) #define SNDRV_SEQ_IOCTL_DELETE_PORT _IOW ('S', 0x21, struct snd_seq_port_info) From 2aefb5c41cc03b72b3161b57336dd4f924b7a90b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Nov 2022 15:49:44 +0100 Subject: [PATCH 076/267] seq: Add UMP support This patch adds the basic support of UMP on ALSA sequencer API. An extended event type, snd_seq_ump_event_t, is defined. It's compatible with the existing type, snd_seq_event_t, but it has a larger payload of 16 bytes instead of 12 bytes, for holding the full 128bit UMP packet. The new snd_seq_ump_event_t must have the bit SND_SEQ_EVENT_UMP in the event flags. A few new API functions have been added such as snd_seq_ump_event_output() and snd_seq_ump_event_input() for reading/writing this new event object. The support of UMP in the sequencer client is switched by the function snd_seq_client_set_midi_version(). It can switch from the default legacy MIDI to UMP MIDI 1.0 or 2.0 on the fly. The automatic event conversion among UMP and legacy clients can be suppressed via snd_seq_client_set_ump_conversion(). The inquiry of the associated UMP Endpoints and UMP Blocks can be done via snd_seq_get_ump_endpoint_info() and snd_seq_get_ump_block_info(). Signed-off-by: Takashi Iwai --- include/local.h | 2 + include/seq.h | 44 +++++ include/seq_event.h | 42 +++-- include/seqmid.h | 24 +++ src/Versions.in | 17 ++ src/seq/seq.c | 410 ++++++++++++++++++++++++++++++++++++++++---- src/seq/seq_hw.c | 72 +++++++- src/seq/seq_local.h | 6 +- src/seq/seqmid.c | 38 ++++ 9 files changed, 604 insertions(+), 51 deletions(-) diff --git a/include/local.h b/include/local.h index c6be21ee..512e4455 100644 --- a/include/local.h +++ b/include/local.h @@ -196,7 +196,9 @@ #define snd_seq_real_time sndrv_seq_real_time #define snd_seq_timestamp sndrv_seq_timestamp #define snd_seq_event_type_t sndrv_seq_event_type_t +#define snd_seq_event_data sndrv_seq_event_data #define snd_seq_event sndrv_seq_event +#define snd_seq_ump_event sndrv_seq_ump_event #define snd_seq_connect sndrv_seq_connect #define snd_seq_ev_note sndrv_seq_ev_note #define snd_seq_ev_ctrl sndrv_seq_ev_ctrl diff --git a/include/seq.h b/include/seq.h index 123a1057..7faf4367 100644 --- a/include/seq.h +++ b/include/seq.h @@ -130,6 +130,13 @@ typedef enum snd_seq_client_type { SND_SEQ_KERNEL_CLIENT = 2 /**< kernel client */ } snd_seq_client_type_t; +/** client MIDI version */ +enum { + SND_SEQ_CLIENT_LEGACY_MIDI = 0, /**< Legacy client */ + SND_SEQ_CLIENT_UMP_MIDI_1_0 = 1, /**< UMP MIDI 1.0 */ + SND_SEQ_CLIENT_UMP_MIDI_2_0 = 2 /**< UMP MIDI 2.0 */ +}; + size_t snd_seq_client_info_sizeof(void); /** allocate a #snd_seq_client_info_t container on stack */ #define snd_seq_client_info_alloca(ptr) \ @@ -149,11 +156,19 @@ const unsigned char *snd_seq_client_info_get_event_filter(const snd_seq_client_i int snd_seq_client_info_get_num_ports(const snd_seq_client_info_t *info); int snd_seq_client_info_get_event_lost(const snd_seq_client_info_t *info); +int snd_seq_client_info_get_midi_version(const snd_seq_client_info_t *info); +int snd_seq_client_info_get_ump_group_enabled(const snd_seq_client_info_t *info, + int group); +int snd_seq_client_info_get_ump_conversion(const snd_seq_client_info_t *info); void snd_seq_client_info_set_client(snd_seq_client_info_t *info, int client); void snd_seq_client_info_set_name(snd_seq_client_info_t *info, const char *name); void snd_seq_client_info_set_broadcast_filter(snd_seq_client_info_t *info, int val); void snd_seq_client_info_set_error_bounce(snd_seq_client_info_t *info, int val); void snd_seq_client_info_set_event_filter(snd_seq_client_info_t *info, unsigned char *filter); +void snd_seq_client_info_set_midi_version(snd_seq_client_info_t *info, int midi_version); +void snd_seq_client_info_set_ump_group_enabled(snd_seq_client_info_t *info, + int group, int enable); +void snd_seq_client_info_set_ump_conversion(snd_seq_client_info_t *info, int enable); void snd_seq_client_info_event_filter_clear(snd_seq_client_info_t *info); void snd_seq_client_info_event_filter_add(snd_seq_client_info_t *info, int event_type); @@ -165,6 +180,11 @@ int snd_seq_get_any_client_info(snd_seq_t *handle, int client, snd_seq_client_in int snd_seq_set_client_info(snd_seq_t *handle, snd_seq_client_info_t *info); int snd_seq_query_next_client(snd_seq_t *handle, snd_seq_client_info_t *info); +int snd_seq_get_ump_endpoint_info(snd_seq_t *seq, int client, void *info); +int snd_seq_get_ump_block_info(snd_seq_t *seq, int client, int blk, void *info); +int snd_seq_set_ump_endpoint_info(snd_seq_t *seq, const void *info); +int snd_seq_set_ump_block_info(snd_seq_t *seq, int blk, const void *info); + /* */ @@ -222,6 +242,14 @@ typedef struct _snd_seq_port_info snd_seq_port_info_t; #define SND_SEQ_PORT_CAP_SUBS_READ (1<<5) /**< allow read subscription */ #define SND_SEQ_PORT_CAP_SUBS_WRITE (1<<6) /**< allow write subscription */ #define SND_SEQ_PORT_CAP_NO_EXPORT (1<<7) /**< routing not allowed */ +#define SND_SEQ_PORT_CAP_INACTIVE (1<<8) /**< inactive port */ +#define SND_SEQ_PORT_CAP_UMP_ENDPOINT (1<<9) /**< UMP Endpoint port */ + +/** 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_BIDIRECTION 3 /**< Input/output bidirectional */ /* port type */ /** Messages sent from/to this port have device-specific semantics. */ @@ -238,6 +266,8 @@ typedef struct _snd_seq_port_info snd_seq_port_info_t; #define SND_SEQ_PORT_TYPE_MIDI_MT32 (1<<5) /** This port is compatible with the General MIDI 2 specification. */ #define SND_SEQ_PORT_TYPE_MIDI_GM2 (1<<6) +/** This port is a UMP port. */ +#define SND_SEQ_PORT_TYPE_MIDI_UMP (1<<7) /** This port understands SND_SEQ_EVENT_SAMPLE_xxx messages (these are not MIDI messages). */ #define SND_SEQ_PORT_TYPE_SYNTH (1<<10) @@ -283,6 +313,8 @@ int snd_seq_port_info_get_port_specified(const snd_seq_port_info_t *info); int snd_seq_port_info_get_timestamping(const snd_seq_port_info_t *info); 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); 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); @@ -297,6 +329,8 @@ void snd_seq_port_info_set_port_specified(snd_seq_port_info_t *info, int val); void snd_seq_port_info_set_timestamping(snd_seq_port_info_t *info, int enable); void snd_seq_port_info_set_timestamp_real(snd_seq_port_info_t *info, int realtime); 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_gruop(snd_seq_port_info_t *info, int ump_group); 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); @@ -572,6 +606,12 @@ void snd_seq_remove_events_set_tag(snd_seq_remove_events_t *info, int tag); int snd_seq_remove_events(snd_seq_t *handle, snd_seq_remove_events_t *info); +int snd_seq_ump_event_output(snd_seq_t *seq, snd_seq_ump_event_t *ev); +int snd_seq_ump_event_output_buffer(snd_seq_t *seq, snd_seq_ump_event_t *ev); +int snd_seq_ump_extract_output(snd_seq_t *seq, snd_seq_ump_event_t **ev_res); +int snd_seq_ump_event_output_direct(snd_seq_t *seq, snd_seq_ump_event_t *ev); +int snd_seq_ump_event_input(snd_seq_t *seq, snd_seq_ump_event_t **ev); + /** \} */ /** @@ -729,6 +769,10 @@ extern const unsigned int snd_seq_event_types[]; #define snd_seq_ev_is_direct(ev) \ ((ev)->queue == SND_SEQ_QUEUE_DIRECT) +/** UMP events */ +#define snd_seq_ev_is_ump(ev) \ + ((ev)->flags & SND_SEQ_EVENT_UMP) + /** \} */ #ifdef __cplusplus diff --git a/include/seq_event.h b/include/seq_event.h index 60727f52..9ca384ee 100644 --- a/include/seq_event.h +++ b/include/seq_event.h @@ -225,6 +225,7 @@ typedef union snd_seq_timestamp { #define SND_SEQ_PRIORITY_HIGH (1<<4) /**< event should be processed before others */ #define SND_SEQ_PRIORITY_MASK (1<<4) /**< mask for priority bits */ +#define SND_SEQ_EVENT_UMP (1<<5) /**< UMP packet event */ /** Note event */ typedef struct snd_seq_ev_note { @@ -291,6 +292,19 @@ typedef struct snd_seq_ev_queue_control { } param; /**< data value union */ } snd_seq_ev_queue_control_t; +/** Sequencer event data */ +typedef union snd_seq_event_data { + snd_seq_ev_note_t note; /**< note information */ + snd_seq_ev_ctrl_t control; /**< MIDI control information */ + snd_seq_ev_raw8_t raw8; /**< raw8 data */ + snd_seq_ev_raw32_t raw32; /**< raw32 data */ + snd_seq_ev_ext_t ext; /**< external data */ + snd_seq_ev_queue_control_t queue; /**< queue control */ + snd_seq_timestamp_t time; /**< timestamp */ + snd_seq_addr_t addr; /**< address */ + snd_seq_connect_t connect; /**< connect information */ + snd_seq_result_t result; /**< operation result code */ +} snd_seq_event_data_t; /** Sequencer event */ typedef struct snd_seq_event { @@ -304,20 +318,24 @@ typedef struct snd_seq_event { snd_seq_addr_t source; /**< source address */ snd_seq_addr_t dest; /**< destination address */ - union { - snd_seq_ev_note_t note; /**< note information */ - snd_seq_ev_ctrl_t control; /**< MIDI control information */ - snd_seq_ev_raw8_t raw8; /**< raw8 data */ - snd_seq_ev_raw32_t raw32; /**< raw32 data */ - snd_seq_ev_ext_t ext; /**< external data */ - snd_seq_ev_queue_control_t queue; /**< queue control */ - snd_seq_timestamp_t time; /**< timestamp */ - snd_seq_addr_t addr; /**< address */ - snd_seq_connect_t connect; /**< connect information */ - snd_seq_result_t result; /**< operation result code */ - } data; /**< event data... */ + snd_seq_event_data_t data; /**< event data... */ } snd_seq_event_t; +/** UMP sequencer event; compatible with legacy sequencer event */ +typedef struct snd_seq_ump_event { + snd_seq_event_type_t type; /**< event type */ + unsigned char flags; /**< event flags */ + unsigned char tag; /**< tag */ + unsigned char queue; /**< schedule queue */ + snd_seq_timestamp_t time; /**< schedule time */ + snd_seq_addr_t source; /**< source address */ + snd_seq_addr_t dest; /**< destination address */ + + union { + snd_seq_event_data_t data; /**< (shared) legacy data */ + unsigned int ump[4]; /**< UMP data bytes */ + }; +} snd_seq_ump_event_t; /** \} */ diff --git a/include/seqmid.h b/include/seqmid.h index 3986628a..4089ac20 100644 --- a/include/seqmid.h +++ b/include/seqmid.h @@ -284,6 +284,28 @@ extern "C" { (ev)->data.queue.queue = (q),\ (ev)->data.queue.param.time.tick = (ttime)) +/** + * \brief set the event UMP flag + * \param ev event record + */ +static inline void snd_seq_ev_set_ump(snd_seq_ump_event_t *ev) +{ + ev->flags |= SND_SEQ_EVENT_UMP; + ev->type = 0; /* unused for UMP */ +} + +/** + * \brief set the event UMP flag and fill UMP raw bytes + * \param ev event record + * \param data UMP packet data + * \param bytes UMP packet size in bytes + */ +static inline void snd_seq_ev_set_ump_data(snd_seq_ump_event_t *ev, void *data, size_t bytes) +{ + snd_seq_ev_set_ump(ev); + memcpy(ev->ump, data, bytes); +} + /* set and send a queue control event */ int snd_seq_control_queue(snd_seq_t *seq, int q, int type, int value, snd_seq_event_t *ev); @@ -343,6 +365,8 @@ int snd_seq_disconnect_to(snd_seq_t *seq, int my_port, int dest_client, int dest */ int snd_seq_set_client_name(snd_seq_t *seq, const char *name); int snd_seq_set_client_event_filter(snd_seq_t *seq, int event_type); +int snd_seq_set_client_midi_version(snd_seq_t *seq, int midi_version); +int snd_seq_set_client_ump_conversion(snd_seq_t *seq, int enable); int snd_seq_set_client_pool_output(snd_seq_t *seq, size_t size); int snd_seq_set_client_pool_output_room(snd_seq_t *seq, size_t size); int snd_seq_set_client_pool_input(snd_seq_t *seq, size_t size); diff --git a/src/Versions.in b/src/Versions.in index 2acf3d18..0c283730 100644 --- a/src/Versions.in +++ b/src/Versions.in @@ -156,4 +156,21 @@ ALSA_1.2.10 { @SYMBOL_PREFIX@snd_ctl_ump_next_device; @SYMBOL_PREFIX@snd_ctl_ump_endpoint_info; @SYMBOL_PREFIX@snd_ctl_ump_block_info; + @SYMBOL_PREFIX@snd_seq_ump_*; + @SYMBOL_PREFIX@snd_seq_client_info_get_midi_version; + @SYMBOL_PREFIX@snd_seq_seq_client_info_get_ump_group_enabled; + @SYMBOL_PREFIX@snd_seq_seq_client_get_ump_conversion; + @SYMBOL_PREFIX@snd_seq_client_info_set_midi_version; + @SYMBOL_PREFIX@snd_seq_seq_client_info_set_ump_group_enabled; + @SYMBOL_PREFIX@snd_seq_seq_client_set_ump_conversion; + @SYMBOL_PREFIX@snd_seq_get_ump_endpoint_info; + @SYMBOL_PREFIX@snd_seq_get_ump_block_info; + @SYMBOL_PREFIX@snd_seq_set_ump_endpoint_info; + @SYMBOL_PREFIX@snd_seq_set_ump_block_info; + @SYMBOL_PREFIX@snd_seq_port_info_get_direction; + @SYMBOL_PREFIX@snd_seq_port_info_get_ump_group; + @SYMBOL_PREFIX@snd_seq_port_info_set_direction; + @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; } ALSA_1.2.9; diff --git a/src/seq/seq.c b/src/seq/seq.c index f051426f..65ccaaed 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -1204,6 +1204,11 @@ size_t snd_seq_get_output_buffer_size(snd_seq_t *seq) return seq->obufsize; } +static inline size_t get_packet_size(snd_seq_t *seq) +{ + return seq->packet_size ? seq->packet_size : sizeof(snd_seq_event_t); +} + /** * \brief Return the size of input buffer * \param seq sequencer handle @@ -1219,7 +1224,7 @@ size_t snd_seq_get_input_buffer_size(snd_seq_t *seq) assert(seq); if (!seq->ibuf) return 0; - return seq->ibufsize * sizeof(snd_seq_event_t); + return seq->ibufsize * get_packet_size(seq); } /** @@ -1261,13 +1266,17 @@ int snd_seq_set_output_buffer_size(snd_seq_t *seq, size_t size) */ int snd_seq_set_input_buffer_size(snd_seq_t *seq, size_t size) { + size_t packet_size; + assert(seq && seq->ibuf); - assert(size >= sizeof(snd_seq_event_t)); + assert(size >= packet_size); snd_seq_drop_input(seq); - size = (size + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t); + packet_size = get_packet_size(seq); + size = (size + packet_size - 1) / packet_size; if (size != seq->ibufsize) { - snd_seq_event_t *newbuf; - newbuf = calloc(sizeof(snd_seq_event_t), size); + char *newbuf; + /* use ump event size for avoiding reallocation at switching */ + newbuf = calloc(sizeof(snd_seq_ump_event_t), size); if (newbuf == NULL) return -ENOMEM; free(seq->ibuf); @@ -1726,6 +1735,47 @@ int snd_seq_client_info_get_event_lost(const snd_seq_client_info_t *info) return info->event_lost; } +/** + * \brief Get the MIDI protocol version number of a client_info container + * \param info client_info container + * \return MIDI protocol version + * + * \sa snd_seq_get_client_info() + */ +int snd_seq_client_info_get_midi_version(const snd_seq_client_info_t *info) +{ + assert(info); + return info->midi_version; +} + +/** + * \brief Get the UMP group filter status + * \param info client_info container + * \param group 0-based group index + * \return 0 if the group is filtered / disabled, 1 if it's processed + * + * \sa snd_seq_get_client_info() + */ +int snd_seq_client_info_get_ump_group_enabled(const snd_seq_client_info_t *info, + int group) +{ + assert(info); + return !(info->group_filter & (1U << group)); +} + +/** + * \brief Get the automatic conversion mode for UMP + * \param info client_info container + * \return 1 if the conversion is enabled, 0 if not + * + * \sa snd_seq_get_client_info() + */ +int snd_seq_client_info_get_ump_conversion(const snd_seq_client_info_t *info) +{ + assert(info); + return info->midi_version; +} + /** * \brief Set the client id of a client_info container * \param info client_info container @@ -1769,6 +1819,54 @@ void snd_seq_client_info_set_broadcast_filter(snd_seq_client_info_t *info, int v info->filter &= ~SNDRV_SEQ_FILTER_BROADCAST; } +/** + * \brief Set the MIDI protocol version of a client_info container + * \param info client_info container + * \param midi_version MIDI protocol version to set + * + * \sa snd_seq_get_client_info(), snd_seq_client_info_get_midi_version() + */ +void snd_seq_client_info_set_midi_version(snd_seq_client_info_t *info, int midi_version) +{ + assert(info); + info->midi_version = midi_version; +} + +/** + * \brief Set the UMP group filter status + * \param info client_info container + * \param group 0-based group index + * \param enable 0 to filter/disable the group, non-zero to enable + * + * \sa snd_seq_set_client_info(), snd_seq_client_info_get_ump_group_enabled() + */ +void snd_seq_client_info_set_ump_group_enabled(snd_seq_client_info_t *info, + int group, int enable) +{ + assert(info); + if (enable) + info->group_filter &= ~(1U << group); + else + info->group_filter |= (1U << group); +} + +/** + * \brief Set the automatic conversion mode for UMP + * \param info client_info container + * \param enable 0 or 1 for disabling/enabling the conversion + * + * \sa snd_seq_set_client_info(), snd_seq_client_info_get_ump_conversion() + */ +void snd_seq_client_info_set_ump_conversion(snd_seq_client_info_t *info, + int enable) +{ + assert(info); + if (enable) + info->filter &= ~SNDRV_SEQ_FILTER_NO_CONVERT; + else + info->filter |= SNDRV_SEQ_FILTER_NO_CONVERT; +} + /** * \brief Set the error-bounce usage of a client_info container * \param info client_info container @@ -1887,6 +1985,65 @@ int snd_seq_query_next_client(snd_seq_t *seq, snd_seq_client_info_t *info) return seq->ops->query_next_client(seq, info); } +/** + * \brief Get UMP Endpoint information + * \param seq sequencer handle + * \param client client number to query + * \param info the pointer to store snd_ump_endpoint_info_t data + * \return 0 on success otherwise a negative error code + */ +int snd_seq_get_ump_endpoint_info(snd_seq_t *seq, int client, void *info) +{ + assert(seq && info); + return seq->ops->get_ump_info(seq, client, + SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT, + info); +} + +/** + * \brief Get UMP Block information + * \param seq sequencer handle + * \param client sequencer client number to query + * \param blk UMP block number (0-based) to query + * \param info the pointer to store snd_ump_block_info_t data + * \return 0 on success otherwise a negative error code + */ +int snd_seq_get_ump_block_info(snd_seq_t *seq, int client, int blk, void *info) +{ + assert(seq && info); + return seq->ops->get_ump_info(seq, client, + SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK + blk, + info); +} + +/** + * \brief Set UMP Endpoint information to the current client + * \param seq sequencer handle + * \param info the pointer to send snd_ump_endpoint_info_t data + * \return 0 on success otherwise a negative error code + */ +int snd_seq_set_ump_endpoint_info(snd_seq_t *seq, const void *info) +{ + assert(seq && info); + return seq->ops->set_ump_info(seq, + SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT, + info); +} + +/** + * \brief Set UMP Block information to the current client + * \param seq sequencer handle + * \param blk UMP block number (0-based) to send + * \param info the pointer to send snd_ump_block_info_t data + * \return 0 on success otherwise a negative error code + */ +int snd_seq_set_ump_block_info(snd_seq_t *seq, int blk, const void *info) +{ + assert(seq && info); + return seq->ops->set_ump_info(seq, + SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK + blk, + info); +} /*----------------------------------------------------------------*/ @@ -2134,6 +2291,32 @@ int snd_seq_port_info_get_timestamp_queue(const snd_seq_port_info_t *info) return info->time_queue; } +/** + * \brief Get the direction of the port + * \param info port_info container + * \return the direction of the port + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_direction() + */ +int snd_seq_port_info_get_direction(const snd_seq_port_info_t *info) +{ + assert(info); + return info->direction; +} + +/** + * \brief Get the UMP Group assigned to the port + * \param info port_info container + * \return 0 for no conversion, or the (1-based) UMP Group number assigned to the port + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_ump_group() + */ +int snd_seq_port_info_get_ump_group(const snd_seq_port_info_t *info) +{ + assert(info); + return info->ump_group; +} + /** * \brief Set the client id of a port_info container * \param info port_info container @@ -2312,6 +2495,31 @@ void snd_seq_port_info_set_timestamp_queue(snd_seq_port_info_t *info, int queue) info->time_queue = queue; } +/** + * \brief Set the direction of the port + * \param info port_info container + * \param direction the port direction + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_direction() + */ +void snd_seq_port_info_set_direction(snd_seq_port_info_t *info, int direction) +{ + assert(info); + info->direction = direction; +} + +/** + * \brief Set the UMP Group assigned to the port + * \param info port_info container + * \param ump_group 0 for no conversion, or the (1-based) UMP Group number + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_ump_gruop() + */ +void snd_seq_port_info_set_ump_group(snd_seq_port_info_t *info, int ump_group) +{ + assert(info); + info->ump_group = ump_group; +} /** * \brief create a sequencer port on the current client @@ -3874,7 +4082,9 @@ ssize_t snd_seq_event_length(snd_seq_event_t *ev) { ssize_t len = sizeof(snd_seq_event_t); assert(ev); - if (snd_seq_ev_is_variable(ev)) + if (snd_seq_ev_is_ump(ev)) + len = sizeof(snd_seq_ump_event_t); + else if (snd_seq_ev_is_variable(ev)) len += ev->data.ext.len; return len; } @@ -3925,7 +4135,10 @@ int snd_seq_event_output(snd_seq_t *seq, snd_seq_event_t *ev) * * This function doesn't drain buffer unlike snd_seq_event_output(). * - * \sa snd_seq_event_output() + * \note + * For a UMP event, use snd_seq_ump_event_output_buffer() instead. + * + * \sa snd_seq_event_output(), snd_seq_ump_event_output_buffer() */ int snd_seq_event_output_buffer(snd_seq_t *seq, snd_seq_event_t *ev) { @@ -3938,12 +4151,15 @@ int snd_seq_event_output_buffer(snd_seq_t *seq, snd_seq_event_t *ev) return -EINVAL; if ((seq->obufsize - seq->obufused) < (size_t) len) return -EAGAIN; - memcpy(seq->obuf + seq->obufused, ev, sizeof(snd_seq_event_t)); - seq->obufused += sizeof(snd_seq_event_t); - if (snd_seq_ev_is_variable(ev)) { - memcpy(seq->obuf + seq->obufused, ev->data.ext.ptr, ev->data.ext.len); - seq->obufused += ev->data.ext.len; + if (snd_seq_ev_is_ump(ev)) { + memcpy(seq->obuf + seq->obufused, ev, sizeof(snd_seq_ump_event_t)); + } else { + memcpy(seq->obuf + seq->obufused, ev, sizeof(snd_seq_event_t)); + if (snd_seq_ev_is_variable(ev)) + memcpy(seq->obuf + seq->obufused + sizeof(snd_seq_event_t), + ev->data.ext.ptr, ev->data.ext.len); } + seq->obufused += len; return seq->obufused; } @@ -3991,7 +4207,7 @@ int snd_seq_event_output_direct(snd_seq_t *seq, snd_seq_event_t *ev) len = snd_seq_event_length(ev); if (len < 0) return len; - else if (len == sizeof(*ev)) { + if (snd_seq_ev_is_ump(ev) || !snd_seq_ev_is_variable(ev)) { buf = ev; } else { if (alloc_tmpbuf(seq, (size_t)len) < 0) @@ -4049,6 +4265,36 @@ int snd_seq_drain_output(snd_seq_t *seq) return 0; } +static int extract_output(snd_seq_t *seq, snd_seq_event_t **ev_res, int ump_allowed) +{ + size_t len, olen; + assert(seq); + if (ev_res) + *ev_res = NULL; + repeat: + if ((olen = seq->obufused) < sizeof(snd_seq_event_t)) + return -ENOENT; + len = snd_seq_event_length((snd_seq_event_t *)seq->obuf); + if (olen < len) + return -ENOENT; + /* skip invalid UMP events */ + if (snd_seq_ev_is_ump((snd_seq_event_t *)seq->obuf) && !ump_allowed) { + seq->obufused -= len; + memmove(seq->obuf, seq->obuf + len, seq->obufused); + goto repeat; + } + if (ev_res) { + /* extract the event */ + if (alloc_tmpbuf(seq, len) < 0) + return -ENOMEM; + memcpy(seq->tmpbuf, seq->obuf, len); + *ev_res = (snd_seq_event_t *)seq->tmpbuf; + } + seq->obufused = olen - len; + memmove(seq->obuf, seq->obuf + len, seq->obufused); + return 0; +} + /** * \brief extract the first event in output buffer * \param seq sequencer handle @@ -4062,25 +4308,7 @@ int snd_seq_drain_output(snd_seq_t *seq) */ int snd_seq_extract_output(snd_seq_t *seq, snd_seq_event_t **ev_res) { - size_t len, olen; - snd_seq_event_t ev; - assert(seq); - if (ev_res) - *ev_res = NULL; - if ((olen = seq->obufused) < sizeof(snd_seq_event_t)) - return -ENOENT; - memcpy(&ev, seq->obuf, sizeof(snd_seq_event_t)); - len = snd_seq_event_length(&ev); - if (ev_res) { - /* extract the event */ - if (alloc_tmpbuf(seq, len) < 0) - return -ENOMEM; - memcpy(seq->tmpbuf, seq->obuf, len); - *ev_res = seq->tmpbuf; - } - seq->obufused = olen - len; - memmove(seq->obuf, seq->obuf + len, seq->obufused); - return 0; + return extract_output(seq, ev_res, 0); } /*----------------------------------------------------------------*/ @@ -4094,32 +4322,35 @@ int snd_seq_extract_output(snd_seq_t *seq, snd_seq_event_t **ev_res) */ static ssize_t snd_seq_event_read_buffer(snd_seq_t *seq) { + size_t packet_size = get_packet_size(seq); ssize_t len; - len = (seq->ops->read)(seq, seq->ibuf, seq->ibufsize * sizeof(snd_seq_event_t)); + + len = (seq->ops->read)(seq, seq->ibuf, seq->ibufsize * packet_size); if (len < 0) return len; - seq->ibuflen = len / sizeof(snd_seq_event_t); + seq->ibuflen = len / packet_size; seq->ibufptr = 0; return seq->ibuflen; } static int snd_seq_event_retrieve_buffer(snd_seq_t *seq, snd_seq_event_t **retp) { + size_t packet_size = get_packet_size(seq); size_t ncells; snd_seq_event_t *ev; - *retp = ev = &seq->ibuf[seq->ibufptr]; + *retp = ev = (snd_seq_event_t *)(seq->ibuf + seq->ibufptr * packet_size); seq->ibufptr++; seq->ibuflen--; if (! snd_seq_ev_is_variable(ev)) return 1; - ncells = (ev->data.ext.len + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t); + ncells = (ev->data.ext.len + packet_size - 1) / packet_size; if (seq->ibuflen < ncells) { seq->ibuflen = 0; /* clear buffer */ *retp = NULL; return -EINVAL; } - ev->data.ext.ptr = ev + 1; + ev->data.ext.ptr = (char *)ev + packet_size; seq->ibuflen -= ncells; seq->ibufptr += ncells; return 1; @@ -4212,6 +4443,111 @@ int snd_seq_event_input_pending(snd_seq_t *seq, int fetch_sequencer) /*----------------------------------------------------------------*/ +/* + * I/O for UMP packets + */ + +/** + * \brief output a UMP event + * \param seq sequencer handle + * \param ev UMP event to be output + * \return the number of remaining events or a negative error code + * + * Just like snd_seq_event_output(), it puts an event onto the buffer, + * draining the buffer automatically when needed, but the event is + * snd_seq_ump_event_t type instead snd_seq_event_t. + * + * Calling this function is allowed only when the client is set to + * \c SND_SEQ_CLIENT_UMP_MIDI_1_0 or \c SND_SEQ_CLIENT_UMP_MIDI_2_0. + * + * The flushing and clearing of the buffer is done via the same functions, + * snd_seq_event_drain_output() and snd_seq_drop_output(). + * + * \sa snd_seq_event_output() + */ +int snd_seq_ump_event_output(snd_seq_t *seq, snd_seq_ump_event_t *ev) +{ + if (!seq->midi_version) + return -EBADFD; + return snd_seq_event_output(seq, (snd_seq_event_t *)ev); +} + +/** + * \brief output an event onto the lib buffer without draining buffer + * \param seq sequencer handle + * \param ev UMP event to be output + * \return the byte size of remaining events. \c -EAGAIN if the buffer becomes full. + * + * This is a UMP event version of snd_seq_event_output_buffer(). + * + * \sa snd_seq_event_output_buffer(), snd_seq_ump_event_output() + */ +int snd_seq_ump_event_output_buffer(snd_seq_t *seq, snd_seq_ump_event_t *ev) +{ + if (!seq->midi_version) + return -EBADFD; + return snd_seq_event_output_buffer(seq, (snd_seq_event_t *)ev); +} + +/** + * \brief extract the first UMP event in output buffer + * \param seq sequencer handle + * \param ev_res UMP event pointer to be extracted + * \return 0 on success otherwise a negative error code + * + * This is a UMP event version of snd_seq_extract_output(). + * + * \sa snd_seq_extract_output(), snd_seq_ump_event_output() + */ +int snd_seq_ump_extract_output(snd_seq_t *seq, snd_seq_ump_event_t **ev_res) +{ + if (!seq->midi_version) + return -EBADFD; + return extract_output(seq, (snd_seq_event_t **)ev_res, 1); +} + +/** + * \brief output a UMP event directly to the sequencer NOT through output buffer + * \param seq sequencer handle + * \param ev UMP event to be output + * \return the byte size sent to sequencer or a negative error code + * + * This is a UMP event version of snd_seq_event_output_direct(). + * + * \sa snd_seq_event_output_direct() + */ +int snd_seq_ump_event_output_direct(snd_seq_t *seq, snd_seq_ump_event_t *ev) +{ + if (!seq->midi_version) + return -EBADFD; + return snd_seq_event_output_direct(seq, (snd_seq_event_t *)ev); +} + +/** + * \brief retrieve a UMP event from sequencer + * \param seq sequencer handle + * \param ev UMP event pointer to be stored + * + * Like snd_seq_event_input(), this reads out the input event, but in + * snd_seq_ump_event_t type instead of snd_seq_event_t type. + * + * Calling this function is allowed only when the client is set to + * \c SND_SEQ_CLIENT_UMP_MIDI_1_0 or \c SND_SEQ_CLIENT_UMP_MIDI_2_0. + * + * For other input operations, the same function like + * snd_seq_event_input_pending() or snd_seq_drop_input() can be still used. + * + * \sa snd_seq_event_input() + */ +int snd_seq_ump_event_input(snd_seq_t *seq, snd_seq_ump_event_t **ev) +{ + if (!seq->midi_version) + return -EBADFD; + return snd_seq_event_input(seq, (snd_seq_event_t **)ev); +} + +/*----------------------------------------------------------------*/ + /* * clear event buffers */ diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c index e4b4d2a0..196de970 100644 --- a/src/seq/seq_hw.c +++ b/src/seq/seq_hw.c @@ -94,6 +94,20 @@ static int snd_seq_hw_system_info(snd_seq_t *seq, snd_seq_system_info_t * info) return 0; } +static void update_midi_version(snd_seq_t *seq, snd_seq_client_info_t *info) +{ + snd_seq_hw_t *hw = seq->private_data; + + if (SNDRV_PROTOCOL_VERSION(1, 0, 3) <= hw->version && + seq->midi_version != (int)info->midi_version) { + seq->midi_version = info->midi_version; + if (info->midi_version > 0) + seq->packet_size = sizeof(snd_seq_ump_event_t); + else + seq->packet_size = sizeof(snd_seq_event_t); + } +} + static int snd_seq_hw_get_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) { snd_seq_hw_t *hw = seq->private_data; @@ -105,16 +119,64 @@ static int snd_seq_hw_get_client_info(snd_seq_t *seq, snd_seq_client_info_t * in info->card = -1; info->pid = -1; } + update_midi_version(seq, info); return 0; } static int snd_seq_hw_set_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) { snd_seq_hw_t *hw = seq->private_data; + if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_CLIENT_INFO failed");*/ return -errno; } + update_midi_version(seq, info); + return 0; +} + +static int snd_seq_hw_get_ump_info(snd_seq_t *seq, int client, int type, void *info) +{ + snd_seq_hw_t *hw = seq->private_data; + struct snd_seq_client_ump_info buf; + size_t size; + + if (type < 0 || type >= SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK + 32) + return -EINVAL; + if (hw->version < SNDRV_PROTOCOL_VERSION(1, 0, 3)) + return -ENOTTY; + if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) + size = sizeof(struct snd_ump_endpoint_info); + else + size = sizeof(struct snd_ump_block_info); + buf.client = client; + buf.type = type; + if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO, &buf) < 0) + return -errno; + memcpy(info, buf.info, size); + return 0; +} + +static int snd_seq_hw_set_ump_info(snd_seq_t *seq, int type, const void *info) +{ + snd_seq_hw_t *hw = seq->private_data; + struct snd_seq_client_ump_info buf; + size_t size; + + if (type < 0 || type >= SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK + 32) + return -EINVAL; + if (hw->version < SNDRV_PROTOCOL_VERSION(1, 0, 3)) + return -ENOTTY; + if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) + size = sizeof(struct snd_ump_endpoint_info); + else + size = sizeof(struct snd_ump_block_info); + buf.client = seq->client; + buf.type = type; + memcpy(buf.info, info, size); + *(int *)buf.info = -1; /* invalidate the card number */ + if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO, &buf) < 0) + return -errno; return 0; } @@ -396,6 +458,8 @@ static const snd_seq_ops_t snd_seq_hw_ops = { .system_info = snd_seq_hw_system_info, .get_client_info = snd_seq_hw_get_client_info, .set_client_info = snd_seq_hw_set_client_info, + .get_ump_info = snd_seq_hw_get_ump_info, + .set_ump_info = snd_seq_hw_set_ump_info, .create_port = snd_seq_hw_create_port, .delete_port = snd_seq_hw_delete_port, .get_port_info = snd_seq_hw_get_port_info, @@ -476,6 +540,11 @@ int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode) close(fd); return -SND_ERROR_INCOMPATIBLE_VERSION; } + if (SNDRV_PROTOCOL_VERSION(1, 0, 3) <= ver) { + /* inform the protocol version we're supporting */ + unsigned int user_ver = SNDRV_SEQ_VERSION; + ioctl(fd, SNDRV_SEQ_IOCTL_USER_PVERSION, &user_ver); + } hw = calloc(1, sizeof(snd_seq_hw_t)); if (hw == NULL) { close(fd); @@ -500,7 +569,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 = (snd_seq_event_t *) calloc(sizeof(snd_seq_event_t), seq->ibufsize = SND_SEQ_IBUF_SIZE); + seq->ibuf = (char *) calloc(sizeof(snd_seq_ump_event_t), seq->ibufsize = SND_SEQ_IBUF_SIZE); if (!seq->ibuf) { free(seq->obuf); free(hw); @@ -519,6 +588,7 @@ int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode) seq->poll_fd = fd; seq->ops = &snd_seq_hw_ops; seq->private_data = hw; + seq->packet_size = sizeof(snd_seq_event_t); 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 f97a5200..9b4a6545 100644 --- a/src/seq/seq_local.h +++ b/src/seq/seq_local.h @@ -41,6 +41,8 @@ typedef struct { int (*system_info)(snd_seq_t *seq, snd_seq_system_info_t * info); int (*get_client_info)(snd_seq_t *seq, snd_seq_client_info_t * info); int (*set_client_info)(snd_seq_t *seq, snd_seq_client_info_t * info); + int (*get_ump_info)(snd_seq_t *seq, int client, int type, void *info); + int (*set_ump_info)(snd_seq_t *seq, int type, const void *info); int (*create_port)(snd_seq_t *seq, snd_seq_port_info_t * port); int (*delete_port)(snd_seq_t *seq, snd_seq_port_info_t * port); int (*get_port_info)(snd_seq_t *seq, snd_seq_port_info_t * info); @@ -84,12 +86,14 @@ struct _snd_seq { char *obuf; /* output buffer */ size_t obufsize; /* output buffer size */ size_t obufused; /* output buffer used size */ - snd_seq_event_t *ibuf; /* input buffer */ + char *ibuf; /* input buffer */ size_t ibufptr; /* current pointer of input buffer */ size_t ibuflen; /* queued length */ size_t ibufsize; /* input buffer size */ snd_seq_event_t *tmpbuf; /* temporary event for extracted event */ size_t tmpbufsize; /* size of errbuf */ + size_t packet_size; /* input packet alignment size */ + int midi_version; /* current protocol version */ }; 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 75061a57..55651f38 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -255,6 +255,44 @@ int snd_seq_set_client_event_filter(snd_seq_t *seq, int event_type) return snd_seq_set_client_info(seq, &info); } +/** + * \brief set client MIDI protocol version + * \param seq sequencer handle + * \param midi_version MIDI protocol version to set + * \return 0 on success or negative error code + * + * \sa snd_seq_set_client_info() + */ +int snd_seq_set_client_midi_version(snd_seq_t *seq, int midi_version) +{ + snd_seq_client_info_t info; + int err; + + if ((err = snd_seq_get_client_info(seq, &info)) < 0) + return err; + snd_seq_client_info_set_midi_version(&info, midi_version); + return snd_seq_set_client_info(seq, &info); +} + +/** + * \brief enable/disable client's automatic conversion of UMP/legacy events + * \param seq sequencer handle + * \param enable 0 or 1 to disable/enable the conversion + * \return 0 on success or negative error code + * + * \sa snd_seq_set_client_info() + */ +int snd_seq_set_client_ump_conversion(snd_seq_t *seq, int enable) +{ + snd_seq_client_info_t info; + int err; + + if ((err = snd_seq_get_client_info(seq, &info)) < 0) + return err; + snd_seq_client_info_set_ump_conversion(&info, enable); + return snd_seq_set_client_info(seq, &info); +} + /** * \brief change the output pool size of the given client * \param seq sequencer handle From 517957a24b16ebaef5ab6ed3f49fba164475220b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 22 Mar 2023 14:02:15 +0100 Subject: [PATCH 077/267] uapi: Update rawmidi API to 2.0.4 Sync with the kernel change of rawmidi API for supporting UMP 1.1 features. Signed-off-by: Takashi Iwai --- include/sound/uapi/asound.h | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index 80cd57e9..32095ef7 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -706,7 +706,7 @@ enum { * Raw MIDI section - /dev/snd/midi?? */ -#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 3) +#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 4) enum { SNDRV_RAWMIDI_STREAM_OUTPUT = 0, @@ -776,6 +776,9 @@ struct snd_rawmidi_status { unsigned char reserved[16]; /* reserved for future use */ }; +/* UMP EP info flags */ +#define SNDRV_UMP_EP_INFO_STATIC_BLOCKS 0x01 + /* UMP EP Protocol / JRTS capability bits */ #define SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK 0x0300 #define SNDRV_UMP_EP_INFO_PROTO_MIDI1 0x0100 /* MIDI 1.0 */ @@ -793,7 +796,11 @@ struct snd_ump_endpoint_info { unsigned int protocol; /* current protocol */ unsigned int num_blocks; /* # of function blocks */ unsigned short version; /* UMP major/minor version */ - unsigned short padding[7]; + unsigned short family_id; /* MIDI device family ID */ + unsigned short model_id; /* MIDI family model ID */ + unsigned int manufacturer_id; /* MIDI manufacturer ID */ + unsigned char sw_revision[4]; /* software revision */ + unsigned short padding; unsigned char name[128]; /* endpoint name string */ unsigned char product_id[128]; /* unique product id string */ unsigned char reserved[32]; @@ -808,6 +815,12 @@ struct snd_ump_endpoint_info { #define SNDRV_UMP_BLOCK_IS_MIDI1 (1U << 0) /* MIDI 1.0 port w/o restrict */ #define SNDRV_UMP_BLOCK_IS_LOWSPEED (1U << 1) /* 31.25Kbps B/W MIDI1 port */ +/* UMP block user-interface hint */ +#define SNDRV_UMP_BLOCK_UI_HINT_UNKNOWN 0x00 +#define SNDRV_UMP_BLOCK_UI_HINT_RECEIVER 0x01 +#define SNDRV_UMP_BLOCK_UI_HINT_SENDER 0x02 +#define SNDRV_UMP_BLOCK_UI_HINT_BOTH 0x03 + /* UMP groups and blocks */ #define SNDRV_UMP_MAX_GROUPS 16 #define SNDRV_UMP_MAX_BLOCKS 32 @@ -821,7 +834,9 @@ struct snd_ump_block_info { unsigned char active; /* Activeness */ unsigned char first_group; /* first group ID */ unsigned char num_groups; /* number of groups */ - unsigned char padding[3]; + unsigned char midi_ci_version; /* MIDI-CI support version */ + unsigned char sysex8_streams; /* max number of sysex8 streams */ + unsigned char ui_hint; /* user interface hint */ unsigned int flags; /* various info flags */ unsigned char name[128]; /* block name string */ unsigned char reserved[32]; From 80e20a1052d7a307e135750a453a76b54804c58d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 22 Mar 2023 14:04:35 +0100 Subject: [PATCH 078/267] ump: Add UMP 1.1 features Add a few new features for UMP 1.1: - New attributes in UMP Endpoint and Block info - Static block bit flag for EP info Signed-off-by: Takashi Iwai --- include/ump.h | 22 +++++++++++++++ include/ump_msg.h | 67 +++++++++++++++++++++++++++++++++++++++++++++ src/rawmidi/ump.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) diff --git a/include/ump.h b/include/ump.h index c79b2335..1e5e0534 100644 --- a/include/ump.h +++ b/include/ump.h @@ -53,6 +53,9 @@ enum _snd_ump_direction { SND_UMP_DIR_BIDIRECTION = 0x03, }; +/** UMP EP holds only static blocks */ +#define SND_UMP_EP_INFO_STATIC_BLOCKS 0x01 + /** Bitmask for UMP EP MIDI protocols */ #define SND_UMP_EP_INFO_PROTO_MIDI_MASK 0x0300 /** Bit flag for MIDI 1.0 protocol */ @@ -82,6 +85,10 @@ unsigned int snd_ump_endpoint_info_get_protocol_caps(const snd_ump_endpoint_info unsigned int snd_ump_endpoint_info_get_protocol(const snd_ump_endpoint_info_t *info); unsigned int snd_ump_endpoint_info_get_num_blocks(const snd_ump_endpoint_info_t *info); unsigned int snd_ump_endpoint_info_get_version(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_manufacturer_id(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_family_id(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_model_id(const snd_ump_endpoint_info_t *info); +const unsigned char *snd_ump_endpoint_info_get_sw_revision(const snd_ump_endpoint_info_t *info); 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); @@ -91,6 +98,18 @@ int snd_ump_endpoint_info(snd_ump_t *ump, snd_ump_endpoint_info_t *info); /** Bit flag for 31.25Kbps B/W MIDI1 port in UMP Block info flags */ #define SND_UMP_BLOCK_IS_LOWSPEED (1U << 1) +/** UMP block user-interface hint */ +enum _snd_ump_block_ui_hint { + /** Unknown or undeclared */ + SND_UMP_BLOCK_UI_HINT_UNKNOWN = 0x00, + /** Primarily a receiver or a destination for MIDI messages */ + SND_UMP_BLOCK_UI_HINT_RECEIVER = 0x01, + /** Primarily a sender or a source of MIDI messages */ + SND_UMP_BLOCK_UI_HINT_SENDER = 0x02, + /** Both a sender and receiver of MIDI messages */ + SND_UMP_BLOCK_UI_HINT_BOTH = 0x03, +}; + size_t snd_ump_block_info_sizeof(void); /** \hideinitializer * \brief allocate an invalid #snd_ump_block_info_t using standard alloca @@ -109,6 +128,9 @@ 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); unsigned int snd_ump_block_info_get_first_group(const snd_ump_block_info_t *info); unsigned int snd_ump_block_info_get_num_groups(const snd_ump_block_info_t *info); +unsigned int snd_ump_block_info_get_midi_ci_version(const snd_ump_block_info_t *info); +unsigned int snd_ump_block_info_get_sysex8_streams(const snd_ump_block_info_t *info); +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); diff --git a/include/ump_msg.h b/include/ump_msg.h index 4ccce546..e0264f6e 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -453,11 +453,14 @@ typedef union _snd_ump_msg_midi2 { * UMP message type */ enum { + SND_UMP_MSG_TYPE_UTILITY = 0x00, /* Utility messages */ SND_UMP_MSG_TYPE_SYSTEM = 0x01, /* System messages */ SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE = 0x02, /* MIDI 1.0 messages */ SND_UMP_MSG_TYPE_DATA = 0x03, /* 7bit SysEx messages */ SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE = 0x04, /* MIDI 2.0 messages */ SND_UMP_MSG_TYPE_EXTENDED_DATA = 0x05, /* 8bit data message */ + SND_UMP_MSG_TYPE_FLEX_DATA = 0x0d, /* Flexible data messages */ + SND_UMP_MSG_TYPE_STREAM = 0x0f, /* Stream messages */ }; /** @@ -508,6 +511,62 @@ enum { SND_UMP_SYSEX_STATUS_END = 3, }; +/** UMP Utility Type Status (type 0x0) **/ +enum { + SND_UMP_UTILITY_MSG_STATUS_NOOP = 0x00, + SND_UMP_UTILITY_MSG_STATUS_JR_CLOCK = 0x01, + SND_UMP_UTILITY_MSG_STATUS_JR_TSTAMP = 0x02, + SND_UMP_UTILITY_MSG_STATUS_DCTPQ = 0x03, + SND_UMP_UTILITY_MSG_STATUS_DC = 0x04, +}; + +/** UMP Stream Message Status (type 0xf) */ +enum { + SND_UMP_STREAM_MSG_STATUS_EP_DISCOVERY = 0x00, + SND_UMP_STREAM_MSG_STATUS_EP_INFO = 0x01, + SND_UMP_STREAM_MSG_STATUS_DEVICE_INFO = 0x02, + SND_UMP_STREAM_MSG_STATUS_EP_NAME = 0x03, + SND_UMP_STREAM_MSG_STATUS_PRODUCT_ID = 0x04, + SND_UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST = 0x05, + SND_UMP_STREAM_MSG_STATUS_STREAM_CFG = 0x06, + SND_UMP_STREAM_MSG_STATUS_FB_DISCOVERY = 0x10, + SND_UMP_STREAM_MSG_STATUS_FB_INFO = 0x11, + SND_UMP_STREAM_MSG_STATUS_FB_NAME = 0x12, + SND_UMP_STREAM_MSG_STATUS_START_CLIP = 0x20, + SND_UMP_STREAM_MSG_STATUS_END_CLIP = 0x21, +}; + +/** UMP Endpoint Discovery filter bitmap */ +enum { + SND_UMP_STREAM_MSG_REQUEST_EP_INFO = (1U << 0), + SND_UMP_STREAM_MSG_REQUEST_DEVICE_INFO = (1U << 1), + SND_UMP_STREAM_MSG_REQUEST_EP_NAME = (1U << 2), + SND_UMP_STREAM_MSG_REQUEST_PRODUCT_ID = (1U << 3), + SND_UMP_STREAM_MSG_REQUEST_STREAM_CFG = (1U << 4), +}; + +/** UMP Function Block Discovery filter bitmap */ +enum { + SND_UMP_STREAM_MSG_REQUEST_FB_INFO = (1U << 0), + SND_UMP_STREAM_MSG_REQUEST_FB_NAME = (1U << 1), +}; + +/** UMP Endpoint Info capability bits (used for protocol request/notify, too) */ +enum { + SND_UMP_STREAM_MSG_EP_INFO_CAP_TXJR = (1U << 0), /* Sending JRTS */ + SND_UMP_STREAM_MSG_EP_INFO_CAP_RXJR = (1U << 1), /* Receiving JRTS */ + SND_UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 = (1U << 8), /* MIDI 1.0 */ + SND_UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 = (1U << 9), /* MIDI 2.0 */ +}; + +/** UMP Endpoint / Function Block name string format bits */ +enum { + SND_UMP_STREAM_MSG_FORMAT_SINGLE = 0, + SND_UMP_STREAM_MSG_FORMAT_START = 1, + SND_UMP_STREAM_MSG_FORMAT_CONTINUE = 2, + SND_UMP_STREAM_MSG_FORMAT_END = 3, +}; + /** * \brief get UMP status (4bit) from 32bit UMP message header */ @@ -532,6 +591,14 @@ static inline uint8_t snd_ump_msg_hdr_type(uint32_t ump) return (ump >> 28); } +/** + * \brief check if the given UMP type is a groupless message + */ +static inline int snd_ump_msg_type_is_groupless(uint8_t type) +{ + return type == SND_UMP_MSG_TYPE_UTILITY || type == SND_UMP_MSG_TYPE_STREAM; +} + /** * \brief get UMP group (4bit) from 32bit UMP message header */ diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index 2482884f..80fa5140 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -422,6 +422,46 @@ unsigned int snd_ump_endpoint_info_get_version(const snd_ump_endpoint_info_t *in return info->version; } +/** + * \brief get UMP manufacturer ID + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP manufacturer ID + */ +unsigned int snd_ump_endpoint_info_get_manufacturer_id(const snd_ump_endpoint_info_t *info) +{ + return info->manufacturer_id; +} + +/** + * \brief get UMP family ID + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP family ID + */ +unsigned int snd_ump_endpoint_info_get_family_id(const snd_ump_endpoint_info_t *info) +{ + return info->family_id; +} + +/** + * \brief get UMP model ID + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP model ID + */ +unsigned int snd_ump_endpoint_info_get_model_id(const snd_ump_endpoint_info_t *info) +{ + return info->model_id; +} + +/** + * \brief get UMP software revision + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP software revision + */ +const unsigned char *snd_ump_endpoint_info_get_sw_revision(const snd_ump_endpoint_info_t *info) +{ + return info->sw_revision; +} + /** * \brief get UMP endpoint name string * \param info pointer to a snd_ump_endpoint_info_t structure @@ -592,6 +632,36 @@ unsigned int snd_ump_block_info_get_num_groups(const snd_ump_block_info_t *info) return info->num_groups; } +/** + * \brief get MIDI-CI version number + * \param info pointer to a snd_ump_block_info_t structure + * \return MIDI-CI version number + */ +unsigned int snd_ump_block_info_get_midi_ci_version(const snd_ump_block_info_t *info) +{ + return info->midi_ci_version; +} + +/** + * \brief get number of supported SysEx8 streams + * \param info pointer to a snd_ump_block_info_t structure + * \return number of supported SysEx8 streams + */ +unsigned int snd_ump_block_info_get_sysex8_streams(const snd_ump_block_info_t *info) +{ + return info->sysex8_streams; +} + +/** + * \brief get UI hint of the given UMP block + * \param info pointer to a snd_ump_block_info_t structure + * \return the hint bits + */ +unsigned int snd_ump_block_info_get_ui_hint(const snd_ump_block_info_t *info) +{ + return info->ui_hint; +} + /** * \brief get the name string of UMP block * \param info pointer to a snd_ump_block_info_t structure From 77247f51c4ef155b99efe7af30a6a60ff9929392 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 27 Mar 2023 10:43:50 +0200 Subject: [PATCH 079/267] seq: Add UMP 1.1 features Add APIs for groupless message filtering. Signed-off-by: Takashi Iwai --- include/seq.h | 3 +++ include/sound/uapi/asequencer.h | 5 ++++- src/Versions.in | 2 ++ src/seq/seq.c | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/include/seq.h b/include/seq.h index 7faf4367..68934037 100644 --- a/include/seq.h +++ b/include/seq.h @@ -159,6 +159,7 @@ int snd_seq_client_info_get_event_lost(const snd_seq_client_info_t *info); int snd_seq_client_info_get_midi_version(const snd_seq_client_info_t *info); int snd_seq_client_info_get_ump_group_enabled(const snd_seq_client_info_t *info, int group); +int snd_seq_client_info_get_ump_groupless_enabled(const snd_seq_client_info_t *info); int snd_seq_client_info_get_ump_conversion(const snd_seq_client_info_t *info); void snd_seq_client_info_set_client(snd_seq_client_info_t *info, int client); void snd_seq_client_info_set_name(snd_seq_client_info_t *info, const char *name); @@ -168,6 +169,8 @@ void snd_seq_client_info_set_event_filter(snd_seq_client_info_t *info, unsigned void snd_seq_client_info_set_midi_version(snd_seq_client_info_t *info, int midi_version); void snd_seq_client_info_set_ump_group_enabled(snd_seq_client_info_t *info, int group, int enable); +void snd_seq_client_info_set_ump_groupless_enabled(snd_seq_client_info_t *info, + int enable); void snd_seq_client_info_set_ump_conversion(snd_seq_client_info_t *info, int enable); void snd_seq_client_info_event_filter_clear(snd_seq_client_info_t *info); diff --git a/include/sound/uapi/asequencer.h b/include/sound/uapi/asequencer.h index 3653a3f3..b913f31d 100644 --- a/include/sound/uapi/asequencer.h +++ b/include/sound/uapi/asequencer.h @@ -378,7 +378,10 @@ struct snd_seq_client_info { int card; /* RO: card number[kernel] */ int pid; /* RO: pid[user] */ unsigned int midi_version; /* MIDI version */ - unsigned int group_filter; /* UMP group filter bitmap */ + unsigned int group_filter; /* UMP group filter bitmap + * (bit 0 = groupless messages, + * bit 1-16 = messages for groups 1-16) + */ char reserved[48]; /* for future use */ }; diff --git a/src/Versions.in b/src/Versions.in index 0c283730..c8ac1c82 100644 --- a/src/Versions.in +++ b/src/Versions.in @@ -159,9 +159,11 @@ ALSA_1.2.10 { @SYMBOL_PREFIX@snd_seq_ump_*; @SYMBOL_PREFIX@snd_seq_client_info_get_midi_version; @SYMBOL_PREFIX@snd_seq_seq_client_info_get_ump_group_enabled; + @SYMBOL_PREFIX@snd_seq_client_info_get_ump_groupless_enabled; @SYMBOL_PREFIX@snd_seq_seq_client_get_ump_conversion; @SYMBOL_PREFIX@snd_seq_client_info_set_midi_version; @SYMBOL_PREFIX@snd_seq_seq_client_info_set_ump_group_enabled; + @SYMBOL_PREFIX@snd_seq_client_info_set_ump_groupless_enabled; @SYMBOL_PREFIX@snd_seq_seq_client_set_ump_conversion; @SYMBOL_PREFIX@snd_seq_get_ump_endpoint_info; @SYMBOL_PREFIX@snd_seq_get_ump_block_info; diff --git a/src/seq/seq.c b/src/seq/seq.c index 65ccaaed..9d3a18d3 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -1763,6 +1763,21 @@ int snd_seq_client_info_get_ump_group_enabled(const snd_seq_client_info_t *info, return !(info->group_filter & (1U << group)); } +#define UMP_GROUPLESS_FILTER (1U << 0) + +/** + * \brief Get the UMP groupless message handling status + * \param info client_info container + * \return 1 if UMP groupless messages is processed, 0 if filtered/disabled + * + * \sa snd_seq_get_client_info() + */ +int snd_seq_client_info_get_ump_groupless_enabled(const snd_seq_client_info_t *info) +{ + assert(info); + return !(info->group_filter & UMP_GROUPLESS_FILTER); +} + /** * \brief Get the automatic conversion mode for UMP * \param info client_info container @@ -1850,6 +1865,23 @@ void snd_seq_client_info_set_ump_group_enabled(snd_seq_client_info_t *info, info->group_filter |= (1U << group); } +/** + * \brief Enable/disable the UMP groupless message handling + * \param info client_info container + * \param enable enable the UMP groupless messages + * + * \sa snd_seq_set_client_info(), snd_seq_client_info_get_ump_groupless_enabled() + */ +void snd_seq_client_info_set_ump_groupless_enabled(snd_seq_client_info_t *info, + int enable) +{ + assert(info); + if (enable) + info->group_filter &= ~UMP_GROUPLESS_FILTER; + else + info->group_filter |= UMP_GROUPLESS_FILTER; +} + /** * \brief Set the automatic conversion mode for UMP * \param info client_info container From b7389e2e2e11c8168e0c35a92a0d9c6ebb45aa59 Mon Sep 17 00:00:00 2001 From: Dan Cross Date: Wed, 14 Jun 2023 21:09:10 +0000 Subject: [PATCH 080/267] pcm: hw: fix minor bug in sw_params ioctl Commit 2115cdb added a new call to the `SNDRV_PCM_IOCTL_SW_PARAMS` ioctl on line 675 of src/pcm/pcm_hw.c, but passed the `sw_params` argument by value; this should be passed by pointer. I ran across this in the context of the direwolf software modem for amateur radio; debugging details are in https://groups.io/g/direwolf/message/8286 Fixes: https://github.com/alsa-project/alsa-lib/pull/330 Signed-off-by: Dan Cross Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index b468a071..f488023a 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -672,7 +672,7 @@ static int snd_pcm_hw_prepare(snd_pcm_t *pcm) if (hw->prepare_reset_sw_params) { snd_pcm_sw_params_current_no_lock(pcm, &sw_params); - if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params) < 0) { + if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params) < 0) { err = -errno; SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err); return err; From 58da05803b1851ff5661cccb5b8ba11f7a8a6e53 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 28 Jul 2023 17:35:02 +0200 Subject: [PATCH 081/267] seq: ump: Fix typo in function name containing "group" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a couple places the function names were misspelled as "gruop" instead of "group". Fix them. Reported-by: Jean-Michaël Celerier Fixes: https://github.com/alsa-project/alsa-lib/pull/337 Signed-off-by: Takashi Iwai --- include/seq.h | 2 +- src/seq/seq.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/seq.h b/include/seq.h index 68934037..e55f5c16 100644 --- a/include/seq.h +++ b/include/seq.h @@ -333,7 +333,7 @@ void snd_seq_port_info_set_timestamping(snd_seq_port_info_t *info, int enable); void snd_seq_port_info_set_timestamp_real(snd_seq_port_info_t *info, int realtime); 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_gruop(snd_seq_port_info_t *info, int ump_group); +void snd_seq_port_info_set_ump_group(snd_seq_port_info_t *info, int ump_group); 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/src/seq/seq.c b/src/seq/seq.c index 9d3a18d3..c4f218df 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -2545,7 +2545,7 @@ void snd_seq_port_info_set_direction(snd_seq_port_info_t *info, int direction) * \param info port_info container * \param ump_group 0 for no conversion, or the (1-based) UMP Group number * - * \sa snd_seq_get_port_info(), snd_seq_port_info_get_ump_gruop() + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_ump_group() */ void snd_seq_port_info_set_ump_group(snd_seq_port_info_t *info, int ump_group) { From 403d687b95d9ac1008ef03ca3e1aa0b63c4aaff6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Aug 2023 14:29:03 +0200 Subject: [PATCH 082/267] rawmidi: Suppress error messages for non-fatal errors Align the behavior of rawmidi errors at open & co with PCM, i.e. use SYSMSG() instead of SYSERR() for suppressing the error messages as default. Closes: https://github.com/alsa-project/alsa-lib/issues/344 Signed-off-by: Takashi Iwai --- src/rawmidi/rawmidi_hw.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rawmidi/rawmidi_hw.c b/src/rawmidi/rawmidi_hw.c index 3119db9d..cc4ffd8e 100644 --- a/src/rawmidi/rawmidi_hw.c +++ b/src/rawmidi/rawmidi_hw.c @@ -68,7 +68,7 @@ static int snd_rawmidi_hw_close(snd_rawmidi_t *rmidi) return 0; if (close(hw->fd)) { err = -errno; - SYSERR("close failed\n"); + SYSMSG("close failed"); } free(hw->buf); free(hw); @@ -81,7 +81,7 @@ static int snd_rawmidi_hw_nonblock(snd_rawmidi_t *rmidi, int nonblock) long flags; if ((flags = fcntl(hw->fd, F_GETFL)) < 0) { - SYSERR("F_GETFL failed"); + SYSMSG("F_GETFL failed"); return -errno; } if (nonblock) @@ -89,7 +89,7 @@ static int snd_rawmidi_hw_nonblock(snd_rawmidi_t *rmidi, int nonblock) else flags &= ~O_NONBLOCK; if (fcntl(hw->fd, F_SETFL, flags) < 0) { - SYSERR("F_SETFL for O_NONBLOCK failed"); + SYSMSG("F_SETFL for O_NONBLOCK failed"); return -errno; } return 0; @@ -100,7 +100,7 @@ static int snd_rawmidi_hw_info(snd_rawmidi_t *rmidi, snd_rawmidi_info_t * info) snd_rawmidi_hw_t *hw = rmidi->private_data; info->stream = rmidi->stream; if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_INFO, info) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_INFO failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_INFO failed"); return -errno; } return 0; @@ -112,7 +112,7 @@ static int snd_rawmidi_hw_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * pa int tstamp; params->stream = rmidi->stream; if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_PARAMS, params) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_PARAMS failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_PARAMS failed"); return -errno; } buf_reset(hw); @@ -144,7 +144,7 @@ static int snd_rawmidi_hw_status(snd_rawmidi_t *rmidi, snd_rawmidi_status_t * st snd_rawmidi_hw_t *hw = rmidi->private_data; status->stream = rmidi->stream; if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_STATUS, status) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_STATUS failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_STATUS failed"); return -errno; } return 0; @@ -155,7 +155,7 @@ static int snd_rawmidi_hw_drop(snd_rawmidi_t *rmidi) snd_rawmidi_hw_t *hw = rmidi->private_data; int str = rmidi->stream; if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_DROP, &str) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_DROP failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_DROP failed"); return -errno; } buf_reset(hw); @@ -167,7 +167,7 @@ static int snd_rawmidi_hw_drain(snd_rawmidi_t *rmidi) snd_rawmidi_hw_t *hw = rmidi->private_data; int str = rmidi->stream; if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_DRAIN, &str) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_DRAIN failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_DRAIN failed"); return -errno; } return 0; @@ -381,13 +381,13 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, fd = snd_open_device(filename, fmode); if (fd < 0) { snd_ctl_close(ctl); - SYSERR("open %s failed", filename); + SYSMSG("open %s failed", filename); return -errno; } } if (ioctl(fd, SNDRV_RAWMIDI_IOCTL_PVERSION, &ver) < 0) { ret = -errno; - SYSERR("SNDRV_RAWMIDI_IOCTL_PVERSION failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_PVERSION failed"); close(fd); snd_ctl_close(ctl); return ret; @@ -406,7 +406,7 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, memset(&info, 0, sizeof(info)); info.stream = outputp ? SNDRV_RAWMIDI_STREAM_OUTPUT : SNDRV_RAWMIDI_STREAM_INPUT; if (ioctl(fd, SNDRV_RAWMIDI_IOCTL_INFO, &info) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_INFO failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_INFO failed"); ret = -errno; close(fd); snd_ctl_close(ctl); From ec20272797cef39772cdbfd7ae40e213c93893ff Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 22 Aug 2023 16:51:39 +0200 Subject: [PATCH 083/267] test: oldapi - fix the clang-16 compilation error Fixes: https://github.com/alsa-project/alsa-lib/issues/323 From: Fabio Scaccabarozzi Signed-off-by: Jaroslav Kysela --- test/oldapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/oldapi.c b/test/oldapi.c index 7abc98e0..82450478 100644 --- a/test/oldapi.c +++ b/test/oldapi.c @@ -31,7 +31,7 @@ #include "../include/asoundlib.h" #include -typedef void (myfcn)(void *); +typedef int (myfcn)(const snd_pcm_hw_params_t *); int main(int argc ATTRIBUTE_UNUSED, char *argv[] ATTRIBUTE_UNUSED) { From 91bbe55894458a98a6f9f1d8aad6b237c7d22f3f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Aug 2023 19:24:03 +0200 Subject: [PATCH 084/267] seq: Add overflow check in snd_seq_ev_set_ump_data() It's better to add a sanity check than sorry for breaking. Now the function return -EINVAL. Closes: https://github.com/alsa-project/alsa-lib/issues/346 Signed-off-by: Takashi Iwai --- include/seqmid.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/seqmid.h b/include/seqmid.h index 4089ac20..da346999 100644 --- a/include/seqmid.h +++ b/include/seqmid.h @@ -300,10 +300,13 @@ static inline void snd_seq_ev_set_ump(snd_seq_ump_event_t *ev) * \param data UMP packet data * \param bytes UMP packet size in bytes */ -static inline void snd_seq_ev_set_ump_data(snd_seq_ump_event_t *ev, void *data, size_t bytes) +static inline int snd_seq_ev_set_ump_data(snd_seq_ump_event_t *ev, void *data, size_t bytes) { + if (bytes > 16) + return -EINVAL; snd_seq_ev_set_ump(ev); memcpy(ev->ump, data, bytes); + return 0; } /* set and send a queue control event */ From dc1e683cc2d2aaed938f9459605d51d4a2cb7ba0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 26 Aug 2023 21:21:13 +0200 Subject: [PATCH 085/267] seq: Fix wrong seq version update at snd_seq_hw_get_client_info() snd_seq_hw_get_client_info() calls mistakenly update_midi_version() that leads to the update of seq->version from another client info. It may lead to the inconsistent packet size calculation and the unaligned read, eventually a program may hit segfault. Drop the incorrect call of update_midi_version() for fixing it. Fixes: 2aefb5c41cc0 ("seq: Add UMP support") Closes: https://github.com/alsa-project/alsa-utils/issues/232 Signed-off-by: Takashi Iwai --- src/seq/seq_hw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c index 196de970..a51ebfb6 100644 --- a/src/seq/seq_hw.c +++ b/src/seq/seq_hw.c @@ -119,7 +119,6 @@ static int snd_seq_hw_get_client_info(snd_seq_t *seq, snd_seq_client_info_t * in info->card = -1; info->pid = -1; } - update_midi_version(seq, info); return 0; } From 6142ff0ca734cb719c5750f9205438b3e11256a7 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 30 Aug 2023 17:03:24 +0200 Subject: [PATCH 086/267] configure: add AC_SYS_LARGEFILE The support for the large files may be disabled using --disable-largefile configure parameter. Related: https://github.com/alsa-project/alsa-lib/pull/333 Signed-off-by: Jaroslav Kysela --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index 0588eec3..ac8e15c3 100644 --- a/configure.ac +++ b/configure.ac @@ -52,6 +52,9 @@ AC_PROG_GCC_TRADITIONAL AC_CHECK_FUNCS([uselocale]) AC_CHECK_FUNCS([eaccess]) +dnl Enable largefile support +AC_SYS_LARGEFILE + SAVE_LIBRARY_VERSION AC_SUBST(LIBTOOL_VERSION_INFO) From ad3a8b8b314e0bcd254e149d7a20c1455ecaf480 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 30 Aug 2023 18:22:59 +0200 Subject: [PATCH 087/267] reshuffle included files to include config.h as first config.h may contain defines like _FILE_OFFSET_BITS which influence the system wide include files (off_t types, open -> open64 function usage etc.). Related: https://github.com/alsa-project/alsa-lib/pull/333 Signed-off-by: Jaroslav Kysela --- aserver/aserver.c | 3 ++- include/aserver.h | 2 +- src/alisp/alisp.c | 11 ++++++----- src/confeval.c | 2 +- src/confmisc.c | 2 +- src/control/cards.c | 2 +- src/control/control.c | 2 +- src/control/control_ext.c | 4 ++-- src/control/control_hw.c | 2 +- src/control/control_remap.c | 2 +- src/control/ctlparse.c | 2 +- src/control/eld.c | 2 +- src/control/hcontrol.c | 2 +- src/control/tlv.c | 2 +- src/dlmisc.c | 2 +- src/error.c | 2 +- src/hwdep/hwdep.c | 2 +- src/hwdep/hwdep_hw.c | 2 +- src/hwdep/hwdep_local.h | 2 +- src/input.c | 2 +- src/mixer/mixer.c | 2 +- src/mixer/simple.c | 5 ++--- src/mixer/simple_abst.c | 4 ++-- src/mixer/simple_none.c | 5 ++--- src/names.c | 2 +- src/output.c | 2 +- src/pcm/interval.c | 2 +- src/pcm/mask.c | 1 + src/pcm/pcm.c | 2 +- src/pcm/pcm_adpcm.c | 3 +-- src/pcm/pcm_alaw.c | 2 +- src/pcm/pcm_copy.c | 2 +- src/pcm/pcm_dmix.c | 1 + src/pcm/pcm_file.c | 4 ++-- src/pcm/pcm_generic.c | 4 ++-- src/pcm/pcm_hw.c | 6 +++--- src/pcm/pcm_iec958.c | 3 +-- src/pcm/pcm_ladspa.c | 5 ++--- src/pcm/pcm_lfloat.c | 3 +-- src/pcm/pcm_linear.c | 3 +-- src/pcm/pcm_meter.c | 4 ++-- src/pcm/pcm_misc.c | 4 ++-- src/pcm/pcm_mmap.c | 3 +-- src/pcm/pcm_mulaw.c | 3 +-- src/pcm/pcm_multi.c | 4 ++-- src/pcm/pcm_null.c | 4 ++-- src/pcm/pcm_plugin.c | 2 +- src/pcm/pcm_rate.c | 5 ++--- src/pcm/pcm_rate_linear.c | 5 ++--- src/pcm/pcm_route.c | 5 ++--- src/pcm/pcm_share.c | 2 +- src/pcm/pcm_softvol.c | 5 ++--- src/rawmidi/rawmidi_hw.c | 1 + src/socket.c | 2 +- src/topology/builder.c | 1 - src/topology/channel.c | 1 - src/topology/ctl.c | 1 - src/topology/dapm.c | 1 - src/topology/data.c | 1 - src/topology/decoder.c | 1 - src/topology/elem.c | 1 - src/topology/log.c | 1 - src/topology/ops.c | 1 - src/topology/parser.c | 3 +-- src/topology/pcm.c | 1 - src/topology/save.c | 1 - src/topology/text.c | 1 - src/topology/tplg_local.h | 3 ++- test/audio_time.c | 2 +- test/chmap.c | 1 + test/control.c | 1 + test/latency.c | 1 + test/pcm.c | 2 ++ test/playmidi1.c | 2 ++ test/seq.c | 2 ++ test/timer.c | 1 + test/user-ctl-element-set.c | 1 + 77 files changed, 92 insertions(+), 101 deletions(-) diff --git a/aserver/aserver.c b/aserver/aserver.c index 71b8a3cb..1742f628 100644 --- a/aserver/aserver.c +++ b/aserver/aserver.c @@ -18,6 +18,8 @@ * */ +#include "aserver.h" + #include #include #include @@ -33,7 +35,6 @@ #include #include -#include "aserver.h" char *command; diff --git a/include/aserver.h b/include/aserver.h index 6fb9480c..ffddf2b4 100644 --- a/include/aserver.h +++ b/include/aserver.h @@ -18,9 +18,9 @@ * */ -#include #include "../src/pcm/pcm_local.h" #include "../src/control/control_local.h" +#include int snd_receive_fd(int sock, void *data, size_t len, int *fd); diff --git a/src/alisp/alisp.c b/src/alisp/alisp.c index 476d0d52..bb841119 100644 --- a/src/alisp/alisp.c +++ b/src/alisp/alisp.c @@ -21,6 +21,12 @@ * */ +#define alisp_seq_iterator alisp_object + +#include "local.h" +#include "alisp.h" +#include "alisp_local.h" + #include #include @@ -31,11 +37,6 @@ #include #include -#define alisp_seq_iterator alisp_object - -#include "local.h" -#include "alisp.h" -#include "alisp_local.h" struct alisp_object alsa_lisp_nil; struct alisp_object alsa_lisp_t; diff --git a/src/confeval.c b/src/confeval.c index 4a6f8c2e..a27ee1d1 100644 --- a/src/confeval.c +++ b/src/confeval.c @@ -31,12 +31,12 @@ * */ +#include "local.h" #include #include #include #include #include -#include "local.h" typedef long long value_type_t; diff --git a/src/confmisc.c b/src/confmisc.c index 64af96fa..b55d86f7 100644 --- a/src/confmisc.c +++ b/src/confmisc.c @@ -74,12 +74,12 @@ */ +#include "local.h" #include #include #include #include #include -#include "local.h" /** * \brief Gets the boolean value from the given ASCII string. diff --git a/src/control/cards.c b/src/control/cards.c index 6145ebcd..a93f10a4 100644 --- a/src/control/cards.c +++ b/src/control/cards.c @@ -25,6 +25,7 @@ * */ +#include "control_local.h" #include #include #include @@ -32,7 +33,6 @@ #include #include #include -#include "control_local.h" #ifndef DOC_HIDDEN #define SND_FILE_CONTROL ALSA_DEVICE_DIRECTORY "controlC%i" diff --git a/src/control/control.c b/src/control/control.c index ec01518e..fe23dfd9 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -185,6 +185,7 @@ in-kernel implementations utilize this feature for I/O operations. This is against the original design. */ +#include "control_local.h" #include #include #include @@ -196,7 +197,6 @@ against the original design. #include #include #include -#include "control_local.h" /** * \brief get identifier of CTL handle diff --git a/src/control/control_ext.c b/src/control/control_ext.c index cf154f85..09106df1 100644 --- a/src/control/control_ext.c +++ b/src/control/control_ext.c @@ -27,12 +27,12 @@ * */ +#include "control_local.h" +#include "control_external.h" #include #include #include #include -#include "control_local.h" -#include "control_external.h" #ifndef PIC /* entry for static linking */ diff --git a/src/control/control_hw.c b/src/control/control_hw.c index df76990b..d21ef226 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -26,6 +26,7 @@ * */ +#include "control_local.h" #include #include #include @@ -33,7 +34,6 @@ #include #include #include -#include "control_local.h" #ifndef PIC /* entry for static linking */ diff --git a/src/control/control_remap.c b/src/control/control_remap.c index 5bc56608..faf9a739 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -25,13 +25,13 @@ * */ +#include "control_local.h" #include #include #include #include #include #include -#include "control_local.h" #if 0 #define REMAP_DEBUG 1 diff --git a/src/control/ctlparse.c b/src/control/ctlparse.c index 3ed1c647..ecb5cf91 100644 --- a/src/control/ctlparse.c +++ b/src/control/ctlparse.c @@ -25,11 +25,11 @@ * */ +#include "control_local.h" #include #include #include #include -#include "control_local.h" /* Function to convert from percentage to volume. val = percentage */ diff --git a/src/control/eld.c b/src/control/eld.c index 7e826cb5..78dd4382 100644 --- a/src/control/eld.c +++ b/src/control/eld.c @@ -26,12 +26,12 @@ * */ +#include "control_local.h" #include #include #include #include #include -#include "control_local.h" static void __fill_eld_ctl_id(snd_ctl_elem_id_t *id, int dev, int subdev) { diff --git a/src/control/hcontrol.c b/src/control/hcontrol.c index ce3fec3e..204d2e50 100644 --- a/src/control/hcontrol.c +++ b/src/control/hcontrol.c @@ -42,13 +42,13 @@ to reduce overhead accessing the real controls in kernel drivers. */ +#include "control_local.h" #include #include #include #include #include #include -#include "control_local.h" #ifdef HAVE_LIBPTHREAD #include #endif diff --git a/src/control/tlv.c b/src/control/tlv.c index d5044eb5..3a2b731d 100644 --- a/src/control/tlv.c +++ b/src/control/tlv.c @@ -26,6 +26,7 @@ * */ +#include "control_local.h" #include #include #include @@ -33,7 +34,6 @@ #ifndef HAVE_SOFT_FLOAT #include #endif -#include "control_local.h" #ifndef DOC_HIDDEN /* convert to index of integer array */ diff --git a/src/dlmisc.c b/src/dlmisc.c index f64c716a..bc527f83 100644 --- a/src/dlmisc.c +++ b/src/dlmisc.c @@ -27,8 +27,8 @@ * */ -#include "list.h" #include "local.h" +#include "list.h" #ifdef HAVE_LIBPTHREAD #include #endif diff --git a/src/error.c b/src/error.c index 2e617f87..c06af7c7 100644 --- a/src/error.c +++ b/src/error.c @@ -28,11 +28,11 @@ * */ +#include "local.h" #include #include #include #include -#include "local.h" /** * Array of error codes in US ASCII. diff --git a/src/hwdep/hwdep.c b/src/hwdep/hwdep.c index c5b3513d..72f6f0d0 100644 --- a/src/hwdep/hwdep.c +++ b/src/hwdep/hwdep.c @@ -28,13 +28,13 @@ * */ +#include "hwdep_local.h" #include #include #include #include #include #include -#include "hwdep_local.h" static int snd_hwdep_open_conf(snd_hwdep_t **hwdep, const char *name, snd_config_t *hwdep_root, diff --git a/src/hwdep/hwdep_hw.c b/src/hwdep/hwdep_hw.c index 1d3cf8e1..0f28f23b 100644 --- a/src/hwdep/hwdep_hw.c +++ b/src/hwdep/hwdep_hw.c @@ -19,13 +19,13 @@ * */ +#include "hwdep_local.h" #include #include #include #include #include #include -#include "hwdep_local.h" #ifndef PIC /* entry for static linking */ diff --git a/src/hwdep/hwdep_local.h b/src/hwdep/hwdep_local.h index 6cc95b04..9424272c 100644 --- a/src/hwdep/hwdep_local.h +++ b/src/hwdep/hwdep_local.h @@ -19,10 +19,10 @@ * */ +#include "local.h" #include #include #include -#include "local.h" typedef struct { int (*close)(snd_hwdep_t *hwdep); diff --git a/src/input.c b/src/input.c index 35324f1f..aa1cef5f 100644 --- a/src/input.c +++ b/src/input.c @@ -27,11 +27,11 @@ * */ +#include "local.h" #include #include #include #include -#include "local.h" #ifndef DOC_HIDDEN diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c index 7019d6f4..d6456278 100644 --- a/src/mixer/mixer.c +++ b/src/mixer/mixer.c @@ -39,13 +39,13 @@ This is an abstraction layer over the hcontrol layer. */ +#include "mixer_local.h" #include #include #include #include #include #include -#include "mixer_local.h" #ifndef DOC_HIDDEN typedef struct _snd_mixer_slave { diff --git a/src/mixer/simple.c b/src/mixer/simple.c index 571fa664..1ef2f975 100644 --- a/src/mixer/simple.c +++ b/src/mixer/simple.c @@ -29,6 +29,8 @@ * */ +#include "mixer_local.h" +#include "mixer_simple.h" #include #include #include @@ -36,9 +38,6 @@ #include #include #include -#include "config.h" -#include "mixer_local.h" -#include "mixer_simple.h" /** * \brief Register mixer simple element class diff --git a/src/mixer/simple_abst.c b/src/mixer/simple_abst.c index 4dcc4cc7..ffc92e86 100644 --- a/src/mixer/simple_abst.c +++ b/src/mixer/simple_abst.c @@ -27,6 +27,8 @@ * */ +#include "mixer_local.h" +#include "mixer_simple.h" #include #include #include @@ -35,8 +37,6 @@ #include #include #include -#include "mixer_local.h" -#include "mixer_simple.h" #ifndef DOC_HIDDEN diff --git a/src/mixer/simple_none.c b/src/mixer/simple_none.c index 9b9f0000..846b0ca9 100644 --- a/src/mixer/simple_none.c +++ b/src/mixer/simple_none.c @@ -29,6 +29,8 @@ * */ +#include "local.h" +#include "mixer_simple.h" #include #include #include @@ -38,9 +40,6 @@ #include #include #include -#include "local.h" -#include "config.h" -#include "mixer_simple.h" #ifndef DOC_HIDDEN diff --git a/src/names.c b/src/names.c index d909a11d..922ef781 100644 --- a/src/names.c +++ b/src/names.c @@ -30,10 +30,10 @@ * */ +#include "local.h" #include #include #include -#include "local.h" /** * \brief This function is unimplemented. diff --git a/src/output.c b/src/output.c index 70e6d65d..0e402b51 100644 --- a/src/output.c +++ b/src/output.c @@ -27,11 +27,11 @@ * */ +#include "local.h" #include #include #include #include -#include "local.h" #ifndef DOC_HIDDEN typedef struct _snd_output_ops { diff --git a/src/pcm/interval.c b/src/pcm/interval.c index ef4c2ed9..719e5086 100644 --- a/src/pcm/interval.c +++ b/src/pcm/interval.c @@ -22,9 +22,9 @@ #define SND_INTERVAL_C #define SND_INTERVAL_INLINE +#include "pcm_local.h" #include #include -#include "pcm_local.h" static inline void div64_32(uint64_t *n, uint32_t d, uint32_t *rem) { diff --git a/src/pcm/mask.c b/src/pcm/mask.c index f85357ca..6bd218da 100644 --- a/src/pcm/mask.c +++ b/src/pcm/mask.c @@ -22,6 +22,7 @@ #define SND_MASK_C #define SND_MASK_INLINE +#include "config.h" #include #include #include "pcm_local.h" diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index e55ad7f7..75ceab6e 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -658,6 +658,7 @@ playback devices. \example ../../test/latency.c */ +#include "pcm_local.h" #include #include #if HAVE_MALLOC_H @@ -669,7 +670,6 @@ playback devices. #include #include #include -#include "pcm_local.h" #ifndef DOC_HIDDEN /* return specific error codes for known bad PCM states */ diff --git a/src/pcm/pcm_adpcm.c b/src/pcm/pcm_adpcm.c index ed065318..efd41451 100644 --- a/src/pcm/pcm_adpcm.c +++ b/src/pcm/pcm_adpcm.c @@ -56,11 +56,10 @@ IMA compatibility project proceedings, Vol 2, Issue 2, May 1992. come across a good description of XA yet. */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_alaw.c b/src/pcm/pcm_alaw.c index 540ba25f..715b04c7 100644 --- a/src/pcm/pcm_alaw.c +++ b/src/pcm/pcm_alaw.c @@ -26,8 +26,8 @@ * */ -#include "bswap.h" #include "pcm_local.h" +#include "bswap.h" #include "pcm_plugin.h" #include "plugin_ops.h" diff --git a/src/pcm/pcm_copy.c b/src/pcm/pcm_copy.c index 4c099acd..1bf745d2 100644 --- a/src/pcm/pcm_copy.c +++ b/src/pcm/pcm_copy.c @@ -26,9 +26,9 @@ * */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" +#include "bswap.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c index b0d0a43e..7cd3c508 100644 --- a/src/pcm/pcm_dmix.c +++ b/src/pcm/pcm_dmix.c @@ -26,6 +26,7 @@ * */ +#include "config.h" #include #include #include diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c index 7709a554..90b3f3f5 100644 --- a/src/pcm/pcm_file.c +++ b/src/pcm/pcm_file.c @@ -26,11 +26,11 @@ * */ +#include "pcm_local.h" +#include "pcm_plugin.h" #include "bswap.h" #include #include -#include "pcm_local.h" -#include "pcm_plugin.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_generic.c b/src/pcm/pcm_generic.c index 4a10c6b4..8177adc7 100644 --- a/src/pcm/pcm_generic.c +++ b/src/pcm/pcm_generic.c @@ -26,10 +26,10 @@ * */ -#include -#include #include "pcm_local.h" #include "pcm_generic.h" +#include +#include #ifndef DOC_HIDDEN diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index f488023a..bd3ecfc9 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -27,6 +27,9 @@ * */ +#include "pcm_local.h" +#include "../control/control_local.h" +#include "../timer/timer_local.h" #include #include #include @@ -37,9 +40,6 @@ #include #include #include -#include "pcm_local.h" -#include "../control/control_local.h" -#include "../timer/timer_local.h" //#define DEBUG_RW /* use to debug readi/writei/readn/writen */ //#define DEBUG_MMAP /* debug mmap_commit */ diff --git a/src/pcm/pcm_iec958.c b/src/pcm/pcm_iec958.c index a11a0439..7b8459fb 100644 --- a/src/pcm/pcm_iec958.c +++ b/src/pcm/pcm_iec958.c @@ -26,11 +26,10 @@ * */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_ladspa.c b/src/pcm/pcm_ladspa.c index 9b2b32d7..56ee138f 100644 --- a/src/pcm/pcm_ladspa.c +++ b/src/pcm/pcm_ladspa.c @@ -32,12 +32,11 @@ * http://www.medianet.ag */ -#include "config.h" +#include "pcm_local.h" +#include "pcm_plugin.h" #include #include #include -#include "pcm_local.h" -#include "pcm_plugin.h" #include "ladspa.h" diff --git a/src/pcm/pcm_lfloat.c b/src/pcm/pcm_lfloat.c index f32a82a7..d9aa136d 100644 --- a/src/pcm/pcm_lfloat.c +++ b/src/pcm/pcm_lfloat.c @@ -26,11 +26,10 @@ * */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" #ifndef DOC_HIDDEN diff --git a/src/pcm/pcm_linear.c b/src/pcm/pcm_linear.c index 33e7fc45..81edccaa 100644 --- a/src/pcm/pcm_linear.c +++ b/src/pcm/pcm_linear.c @@ -26,11 +26,10 @@ * */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_meter.c b/src/pcm/pcm_meter.c index 2dcf8e45..18d921cc 100644 --- a/src/pcm/pcm_meter.c +++ b/src/pcm/pcm_meter.c @@ -27,12 +27,12 @@ */ +#include "pcm_local.h" +#include "pcm_plugin.h" #include "bswap.h" #include #include #include -#include "pcm_local.h" -#include "pcm_plugin.h" #define atomic_read(ptr) __atomic_load_n(ptr, __ATOMIC_SEQ_CST ) #define atomic_add(ptr, n) __atomic_add_fetch(ptr, n, __ATOMIC_SEQ_CST) diff --git a/src/pcm/pcm_misc.c b/src/pcm/pcm_misc.c index ce8d8189..04b13125 100644 --- a/src/pcm/pcm_misc.c +++ b/src/pcm/pcm_misc.c @@ -19,12 +19,12 @@ * */ +#include "pcm_local.h" +#include "bswap.h" #include #include #include #include -#include "bswap.h" -#include "pcm_local.h" /** diff --git a/src/pcm/pcm_mmap.c b/src/pcm/pcm_mmap.c index dcd5c77e..0b62978e 100644 --- a/src/pcm/pcm_mmap.c +++ b/src/pcm/pcm_mmap.c @@ -18,7 +18,7 @@ * */ -#include "config.h" +#include "pcm_local.h" #include #if HAVE_MALLOC_H #include @@ -29,7 +29,6 @@ #ifdef HAVE_SYS_SHM_H #include #endif -#include "pcm_local.h" void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) { diff --git a/src/pcm/pcm_mulaw.c b/src/pcm/pcm_mulaw.c index 73b0c3bc..177a61bb 100644 --- a/src/pcm/pcm_mulaw.c +++ b/src/pcm/pcm_mulaw.c @@ -26,11 +26,10 @@ * */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index 3680fd03..74e1e3f1 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -26,13 +26,13 @@ * */ +#include "pcm_local.h" +#include "pcm_generic.h" #include #include #include #include #include -#include "pcm_local.h" -#include "pcm_generic.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_null.c b/src/pcm/pcm_null.c index c8ea9b38..f7b096bc 100644 --- a/src/pcm/pcm_null.c +++ b/src/pcm/pcm_null.c @@ -26,10 +26,10 @@ * */ -#include "bswap.h" -#include #include "pcm_local.h" #include "pcm_plugin.h" +#include "bswap.h" +#include #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index b3af1fb7..9d7e233e 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -82,9 +82,9 @@ pcm.rate44100Hz { */ -#include #include "pcm_local.h" #include "pcm_plugin.h" +#include #ifndef DOC_HIDDEN diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index 44d7e760..ef6b8006 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -27,13 +27,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ -#include -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" #include "pcm_rate.h" - #include "plugin_ops.h" +#include "bswap.h" +#include #if 0 #define DEBUG_REFINE diff --git a/src/pcm/pcm_rate_linear.c b/src/pcm/pcm_rate_linear.c index 53ce902d..35a4d8ea 100644 --- a/src/pcm/pcm_rate_linear.c +++ b/src/pcm/pcm_rate_linear.c @@ -20,13 +20,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" #include "pcm_rate.h" - #include "plugin_ops.h" +#include "bswap.h" +#include /* LINEAR_DIV needs to be large enough to handle resampling from 768000 -> 8000 */ diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c index 21b869cc..94292fc7 100644 --- a/src/pcm/pcm_route.c +++ b/src/pcm/pcm_route.c @@ -26,12 +26,11 @@ * */ -#include "bswap.h" -#include #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" +#include #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_share.c b/src/pcm/pcm_share.c index 677f4132..0699fc87 100644 --- a/src/pcm/pcm_share.c +++ b/src/pcm/pcm_share.c @@ -26,6 +26,7 @@ * */ +#include "pcm_local.h" #include #include #include @@ -36,7 +37,6 @@ #include #include #include -#include "pcm_local.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_softvol.c b/src/pcm/pcm_softvol.c index 3e3dbc79..75f2aa0a 100644 --- a/src/pcm/pcm_softvol.c +++ b/src/pcm/pcm_softvol.c @@ -26,11 +26,10 @@ * */ -#include "bswap.h" -#include #include "pcm_local.h" #include "pcm_plugin.h" - +#include "bswap.h" +#include #include #ifndef PIC diff --git a/src/rawmidi/rawmidi_hw.c b/src/rawmidi/rawmidi_hw.c index cc4ffd8e..3b1d941e 100644 --- a/src/rawmidi/rawmidi_hw.c +++ b/src/rawmidi/rawmidi_hw.c @@ -20,6 +20,7 @@ * */ +#include "config.h" #include #include #include diff --git a/src/socket.c b/src/socket.c index 99da23e7..c68fa300 100644 --- a/src/socket.c +++ b/src/socket.c @@ -25,6 +25,7 @@ * */ +#include "local.h" #include #include #include @@ -37,7 +38,6 @@ #include #include #include -#include "local.h" #ifndef DOC_HIDDEN int snd_send_fd(int sock, void *data, size_t len, int fd) diff --git a/src/topology/builder.c b/src/topology/builder.c index f8aba830..9c52c9cc 100644 --- a/src/topology/builder.c +++ b/src/topology/builder.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" /* write a block, track the position */ diff --git a/src/topology/channel.c b/src/topology/channel.c index ebdff469..9239c3b2 100644 --- a/src/topology/channel.c +++ b/src/topology/channel.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" /* mapping of channel text names to types */ diff --git a/src/topology/ctl.c b/src/topology/ctl.c index dd05424d..04f39d77 100644 --- a/src/topology/ctl.c +++ b/src/topology/ctl.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" #define ENUM_VAL_SIZE (SNDRV_CTL_ELEM_ID_NAME_MAXLEN >> 2) diff --git a/src/topology/dapm.c b/src/topology/dapm.c index 1c15ce8a..55bb2fab 100644 --- a/src/topology/dapm.c +++ b/src/topology/dapm.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" /* mapping of widget text names to types */ diff --git a/src/topology/data.c b/src/topology/data.c index 972b4b7a..2f077d36 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" #include diff --git a/src/topology/decoder.c b/src/topology/decoder.c index 66ebe5d8..c8df7e35 100644 --- a/src/topology/decoder.c +++ b/src/topology/decoder.c @@ -15,7 +15,6 @@ Authors: Jaroslav Kysela */ -#include "list.h" #include "tplg_local.h" int tplg_decode_template(snd_tplg_t *tplg, diff --git a/src/topology/elem.c b/src/topology/elem.c index cbd7f4b6..a7a7241d 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" struct tplg_table tplg_table[] = { diff --git a/src/topology/log.c b/src/topology/log.c index 1ca36528..b06e0898 100644 --- a/src/topology/log.c +++ b/src/topology/log.c @@ -15,7 +15,6 @@ Authors: Jaroslav Kysela */ -#include "list.h" #include "tplg_local.h" /* verbose output detailing each object size and file position */ diff --git a/src/topology/ops.c b/src/topology/ops.c index daab3577..74f7cbab 100644 --- a/src/topology/ops.c +++ b/src/topology/ops.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" /* mapping of kcontrol text names to types */ diff --git a/src/topology/parser.c b/src/topology/parser.c index e70173f6..874ec524 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -17,9 +17,8 @@ Liam Girdwood */ -#include -#include "list.h" #include "tplg_local.h" +#include /* * Get integer value diff --git a/src/topology/pcm.c b/src/topology/pcm.c index 4de1d266..539f258d 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" #define RATE(v) [SND_PCM_RATE_##v] = #v diff --git a/src/topology/save.c b/src/topology/save.c index fecbc6a5..59f4759b 100644 --- a/src/topology/save.c +++ b/src/topology/save.c @@ -15,7 +15,6 @@ Authors: Jaroslav Kysela */ -#include "list.h" #include "tplg_local.h" #define SAVE_ALLOC_SHIFT (13) /* 8192 bytes */ diff --git a/src/topology/text.c b/src/topology/text.c index b07feb99..47abb8d8 100644 --- a/src/topology/text.c +++ b/src/topology/text.c @@ -18,7 +18,6 @@ */ -#include "list.h" #include "tplg_local.h" #define TEXT_SIZE_MAX \ diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 1cb8b694..63639274 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -10,11 +10,12 @@ * Lesser General Public License for more details. */ +#include "local.h" + #include #include #include -#include "local.h" #include "list.h" #include "bswap.h" #include "topology.h" diff --git a/test/audio_time.c b/test/audio_time.c index 1c8481be..00619966 100644 --- a/test/audio_time.c +++ b/test/audio_time.c @@ -4,7 +4,7 @@ * helpful to verify the information reported by drivers. */ -#include "../include/config.h" +#include "config.h" #include #if HAVE_MALLOC_H #include diff --git a/test/chmap.c b/test/chmap.c index ad3b305b..52f29a76 100644 --- a/test/chmap.c +++ b/test/chmap.c @@ -2,6 +2,7 @@ * channel mapping API test program */ +#include "config.h" #include #include #include diff --git a/test/control.c b/test/control.c index f4b437ee..b0a3877e 100644 --- a/test/control.c +++ b/test/control.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include "../include/asoundlib.h" diff --git a/test/latency.c b/test/latency.c index 40c63e24..ae37c11a 100644 --- a/test/latency.c +++ b/test/latency.c @@ -27,6 +27,7 @@ * */ +#include "config.h" #include #include #include diff --git a/test/pcm.c b/test/pcm.c index 0484ea39..223edafc 100644 --- a/test/pcm.c +++ b/test/pcm.c @@ -2,6 +2,8 @@ * This small demo sends a simple sinusoidal wave to your speakers. */ +#include "config.h" + #include #include #include diff --git a/test/playmidi1.c b/test/playmidi1.c index f178279a..831e9578 100644 --- a/test/playmidi1.c +++ b/test/playmidi1.c @@ -34,6 +34,8 @@ * */ +#include "config.h" + #include #include #include diff --git a/test/seq.c b/test/seq.c index 34b000fd..04f7e5d4 100644 --- a/test/seq.c +++ b/test/seq.c @@ -1,3 +1,5 @@ +#include "config.h" + #include #include #include diff --git a/test/timer.c b/test/timer.c index b05eb2f0..63ea4a54 100644 --- a/test/timer.c +++ b/test/timer.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/test/user-ctl-element-set.c b/test/user-ctl-element-set.c index fee130e2..ceccdba7 100644 --- a/test/user-ctl-element-set.c +++ b/test/user-ctl-element-set.c @@ -7,6 +7,7 @@ * Licensed under the terms of the GNU General Public License, version 2. */ +#include "config.h" #include "../include/asoundlib.h" #include #include From f39256a9b03bd58a7f7f3d72ce06c1d1d03ab513 Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sat, 29 Jul 2023 10:49:18 +0100 Subject: [PATCH 088/267] doxygen: Fix missing group end markers From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- include/asoundef.h | 6 ++++-- include/topology.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/asoundef.h b/include/asoundef.h index f4dfa920..261db40b 100644 --- a/include/asoundef.h +++ b/include/asoundef.h @@ -215,6 +215,8 @@ extern "C" { #define CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED (1<<7) /**< stereo downmis prohibited */ #define CEA861_AUDIO_INFOFRAME_DB5_LSV (0xf<<3) /**< mask - level-shift values */ +/** \} */ + /** * \defgroup MIDI_Interface Constants for MIDI v1.0 * Constants for MIDI v1.0. @@ -224,6 +226,8 @@ extern "C" { #define MIDI_CHANNELS 16 /**< Number of channels per port/cable. */ #define MIDI_GM_DRUM_CHANNEL (10-1) /**< Channel number for GM drums. */ +/** \} */ + /** * \defgroup MIDI_Commands MIDI Commands * MIDI command codes. @@ -335,8 +339,6 @@ extern "C" { /** \} */ -/** \} */ - #ifdef __cplusplus } #endif diff --git a/include/topology.h b/include/topology.h index d1feee4d..4c708ef4 100644 --- a/include/topology.h +++ b/include/topology.h @@ -1167,7 +1167,7 @@ int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags); */ int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags); -/* \} */ +/** \} */ #ifdef __cplusplus } From 9505cfdb4603269684725c9c7514dd5a937f6d6b Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sat, 29 Jul 2023 11:08:25 +0100 Subject: [PATCH 089/267] doxygen: fix broken parameter name tags From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- include/mixer.h | 2 +- include/use-case.h | 2 +- src/conf.c | 8 ++++---- src/pcm/pcm.c | 10 +++++----- src/rawmidi/ump.c | 8 ++++---- src/ucm/main.c | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/mixer.h b/include/mixer.h index 51b6f04a..735dfdd8 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -47,7 +47,7 @@ typedef struct _snd_mixer_elem snd_mixer_elem_t; /** * \brief Mixer callback function - * \param mixer Mixer handle + * \param ctl Mixer handle * \param mask event mask * \param elem related mixer element (if any) * \return 0 on success otherwise a negative error code diff --git a/include/use-case.h b/include/use-case.h index e5d4fd68..95f792d8 100644 --- a/include/use-case.h +++ b/include/use-case.h @@ -534,7 +534,7 @@ static __inline__ int snd_use_case_verb_list(snd_use_case_mgr_t *uc_mgr, /** * \brief Parse control element identifier - * \param elem_id Element identifier + * \param dst Element identifier * \param ucm_id Use case identifier * \param value String value to be parsed * \return Zero if success, otherwise a negative error code diff --git a/src/conf.c b/src/conf.c index da51182e..9887d0e9 100644 --- a/src/conf.c +++ b/src/conf.c @@ -2055,7 +2055,7 @@ int snd_config_load(snd_config_t *config, snd_input_t *in) /** * \brief Loads a configuration tree from a string. - * \param[out] The function puts the handle to the configuration + * \param[out] config The function puts the handle to the configuration * node loaded from the file(s) at the address specified * by \a config. * \param[in] s String with the ASCII configuration @@ -2260,9 +2260,9 @@ static int _snd_config_array_merge(snd_config_t *dst, snd_config_t *src, int ind /** * \brief In-place merge of two config handles - * \param dst[out] Config handle for the merged contents - * \param src[in] Config handle to merge into dst (may be NULL) - * \param override[in] Override flag + * \param[out] dst Config handle for the merged contents + * \param[in] src Config handle to merge into dst (may be NULL) + * \param[in] override Override flag * \return Zero if successful, otherwise a negative error code. * * This function merges all fields from the source compound to the destination compound. diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 75ceab6e..16cfe493 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -2234,7 +2234,7 @@ const char *snd_pcm_tstamp_mode_name(const snd_pcm_tstamp_t mode) /** * \brief get name of PCM tstamp type setting - * \param mode PCM tstamp type + * \param type PCM tstamp type * \return ascii name of PCM tstamp type setting */ const char *snd_pcm_tstamp_type_name(snd_pcm_tstamp_type_t type) @@ -3439,10 +3439,10 @@ int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_ /** * \brief Copy one or more areas - * \param dst_areas destination areas specification (one for each channel) + * \param dst_channels destination areas specification (one for each channel) * \param dst_offset offset in frames inside destination area * \param dst_size size in frames of the destination buffer - * \param src_areas source areas specification (one for each channel) + * \param src_channels source areas specification (one for each channel) * \param src_offset offset in frames inside source area * \param dst_size size in frames of the source buffer * \param channels channels count @@ -7022,7 +7022,7 @@ void snd_pcm_status_get_driver_htstamp(const snd_pcm_status_t *obj, snd_htimesta /** * \brief Get audio_tstamp_report from a PCM status container * \param obj pointer to #snd_pcm_status_t - * \param ptr Pointer to returned report (valid fields are accuracy and type) + * \param audio_tstamp_report Pointer to returned report (valid fields are accuracy and type) */ void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj, snd_pcm_audio_tstamp_report_t *audio_tstamp_report) @@ -7036,7 +7036,7 @@ void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj, /** * \brief set audio_tstamp_config from a PCM status container * \param obj pointer to #snd_pcm_status_t - * \param ptr Pointer to config (valid fields are type and report_analog_delay) + * \param audio_tstamp_config Pointer to config (valid fields are type and report_analog_delay) */ void snd_pcm_status_set_audio_htstamp_config(snd_pcm_status_t *obj, snd_pcm_audio_tstamp_config_t *audio_tstamp_config) diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index 80fa5140..119b58f6 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -315,7 +315,7 @@ size_t snd_ump_endpoint_info_sizeof(void) /** * \brief allocate the snd_ump_endpoint_info_t structure - * \param ptr returned pointer + * \param info returned pointer * \return 0 on success otherwise a negative error code if fails * * Allocates a new snd_rawmidi_status_t structure using the standard @@ -331,7 +331,7 @@ int snd_ump_endpoint_info_malloc(snd_ump_endpoint_info_t **info) /** * \brief frees the snd_ump_endpoint_info_t structure - * \param status pointer to the snd_ump_endpoint_info_t structure to free + * \param info pointer to the snd_ump_endpoint_info_t structure to free * * Frees the given snd_ump_endpoint_info_t structure using the standard * free C library function. @@ -504,7 +504,7 @@ size_t snd_ump_block_info_sizeof(void) /** * \brief allocate the snd_ump_block_info_t structure - * \param ptr returned pointer + * \param info returned pointer * \return 0 on success otherwise a negative error code if fails * * Allocates a new snd_ump_block_info_t structure using the standard @@ -520,7 +520,7 @@ int snd_ump_block_info_malloc(snd_ump_block_info_t **info) /** * \brief frees the snd_ump_block_info_t structure - * \param status pointer to the snd_ump_block_info_t structure to free + * \param info pointer to the snd_ump_block_info_t structure to free * * Frees the given snd_ump_block_info_t structure using the standard * free C library function. diff --git a/src/ucm/main.c b/src/ucm/main.c index 66ffd00d..7675398b 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -2791,7 +2791,7 @@ int snd_use_case_set(snd_use_case_mgr_t *uc_mgr, /** * \brief Parse control element identifier - * \param elem_id Element identifier + * \param dst Element identifier * \param ucm_id Use case identifier * \param value String value to be parsed * \return Zero if success, otherwise a negative error code From df0feab38f47cbb0b0f3235ea0f83331a2351d1b Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sat, 29 Jul 2023 11:22:49 +0100 Subject: [PATCH 090/267] doxygen: fix image path From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- doc/doxygen.cfg.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in index c5ebdfab..8132cd16 100644 --- a/doc/doxygen.cfg.in +++ b/doc/doxygen.cfg.in @@ -110,7 +110,7 @@ EXCLUDE = @top_srcdir@/src/control/control_local.h \ RECURSIVE = YES FILE_PATTERNS = *.c *.h EXAMPLE_PATH = @top_srcdir@/test -IMAGE_PATH = pictures +IMAGE_PATH = @top_srcdir@/doc/pictures QUIET = YES EXTRACT_ALL = NO From e72c605179c5bbc86cc4c9837c53f498c0dc7c3a Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sat, 29 Jul 2023 12:00:11 +0100 Subject: [PATCH 091/267] doxygen: escape xml tags From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- include/topology.h | 2 -- src/ucm/ucm_confdoc.h | 14 +++++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/include/topology.h b/include/topology.h index 4c708ef4..ad7c66de 100644 --- a/include/topology.h +++ b/include/topology.h @@ -254,8 +254,6 @@ extern "C" { * And data of these sections will be merged in the same order as they are * in the list, as the element's private data for kernel. * - * - * *
Vendor Tokens
* A vendor token list is defined as a new section. Each token element is * a pair of string ID and integer value. And both the ID and value are diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index d8948b80..7a907934 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -397,18 +397,18 @@ ${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 +${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 #### Special whole string substitution Substituted string | Value ---------------------|--------------------- -${evali:} | Evaluate expression like *($var+2)/3* [**Syntax 6**]; target node will be integer; substituted only in the LibraryConfig subtree +${evali:\} | Evaluate expression like *($var+2)/3* [**Syntax 6**]; target node will be integer; substituted only in the LibraryConfig subtree #### Find card substitution From 57959cef8fe29ba8aca1a437d254779f6b46df0a Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sat, 29 Jul 2023 14:30:30 +0100 Subject: [PATCH 092/267] doxygen: fix list indentation errors From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- include/use-case.h | 56 +++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/include/use-case.h b/include/use-case.h index 95f792d8..297664ee 100644 --- a/include/use-case.h +++ b/include/use-case.h @@ -325,10 +325,10 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, * - playback control device name * - PlaybackVolume * - playback control volume identifier string - * - can be parsed using snd_use_case_parse_ctl_elem_id() + * - can be parsed using #snd_use_case_parse_ctl_elem_id() * - PlaybackSwitch * - playback control switch identifier string - * - can be parsed using snd_use_case_parse_ctl_elem_id() + * - can be parsed using #snd_use_case_parse_ctl_elem_id() * - PlaybackPriority * - priority value (1-10000), higher value means higher priority * - CaptureRate @@ -345,20 +345,20 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, * - capture control device name * - CaptureVolume * - capture control volume identifier string - * - can be parsed using snd_use_case_parse_ctl_elem_id() + * - can be parsed using #snd_use_case_parse_ctl_elem_id() * - CaptureSwitch * - capture control switch identifier string - * - can be parsed using snd_use_case_parse_ctl_elem_id() + * - can be parsed using #snd_use_case_parse_ctl_elem_id() * - CapturePriority * - priority value (1-10000), higher value means higher priority * - PlaybackMixer * - name of playback mixer * - PlaybackMixerElem * - mixer element playback identifier - * - can be parsed using snd_use_case_parse_selem_id() + * - can be parsed using #snd_use_case_parse_selem_id() * - PlaybackMasterElem * - mixer element playback identifier for the master control - * - can be parsed using snd_use_case_parse_selem_id() + * - can be parsed using #snd_use_case_parse_selem_id() * - PlaybackMasterType * - type of the master volume control * - Valid values: "soft" (software attenuation) @@ -366,10 +366,10 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, * - name of capture mixer * - CaptureMixerElem * - mixer element capture identifier - * - can be parsed using snd_use_case_parse_selem_id() + * - can be parsed using #snd_use_case_parse_selem_id() * - CaptureMasterElem * - mixer element playback identifier for the master control - * - can be parsed using snd_use_case_parse_selem_id() + * - can be parsed using #snd_use_case_parse_selem_id() * - CaptureMasterType * - type of the master volume control * - Valid values: "soft" (software attenuation) @@ -405,9 +405,9 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, * but that's application policy configuration that doesn't belong * to UCM configuration files. * - MinBufferLevel - * - This is used on platform where reported buffer level is not accurate. - * E.g. "512", which holds 512 samples in device buffer. Note: this will - * increase latency. + * - This is used on platform where reported buffer level is not accurate. + * E.g. "512", which holds 512 samples in device buffer. Note: this will + * increase latency. */ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, const char *identifier, @@ -436,24 +436,24 @@ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, * \return Zero if success, otherwise a negative error code * * Known identifiers: - * - _fboot - execute the fixed boot sequence (value = NULL) - * - _boot - execute the boot sequence (value = NULL) - * - only when driver controls identifiers are changed - * (otherwise the old control values are restored) - * - _defaults - execute the 'defaults' sequence (value = NULL) - * - _verb - set current verb = value - * - _enadev - enable given device = value - * - _disdev - disable given device = value - * - _swdev/{old_device} - new_device = value - * - disable old_device and then enable new_device - * - if old_device is not enabled just return - * - check transmit sequence firstly - * - _enamod - enable given modifier = value - * - _dismod - disable given modifier = value + * - _fboot - execute the fixed boot sequence (value = NULL) + * - _boot - execute the boot sequence (value = NULL) + * - only when driver controls identifiers are changed + * (otherwise the old control values are restored) + * - _defaults - execute the 'defaults' sequence (value = NULL) + * - _verb - set current verb = value + * - _enadev - enable given device = value + * - _disdev - disable given device = value + * - _swdev/{old_device} - new_device = value + * - disable old_device and then enable new_device + * - if old_device is not enabled just return + * - check transmit sequence firstly + * - _enamod - enable given modifier = value + * - _dismod - disable given modifier = value * - _swmod/{old_modifier} - new_modifier = value - * - disable old_modifier and then enable new_modifier - * - if old_modifier is not enabled just return - * - check transmit sequence firstly + * - disable old_modifier and then enable new_modifier + * - if old_modifier is not enabled just return + * - check transmit sequence firstly */ int snd_use_case_set(snd_use_case_mgr_t *uc_mgr, const char *identifier, From f53fd880f04962d4a9857375b9be7f52165cc22c Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sat, 29 Jul 2023 14:58:42 +0100 Subject: [PATCH 093/267] doxygen: fix inadvertent link requests From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- include/topology.h | 4 ++-- src/pcm/pcm_extplug.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/topology.h b/include/topology.h index ad7c66de..d452f29b 100644 --- a/include/topology.h +++ b/include/topology.h @@ -219,7 +219,7 @@ extern "C" { * *
  * SectionData."data element name" {
- *	index "1"	#Index number
+ *	index "1"	# Index number
  *	tuples [
  *		"id of the 1st vendor tuples section"
  *		"id of the 2nd vendor tuples section"
@@ -637,7 +637,7 @@ extern "C" {
  *		...
  *	]
  *
- *	default_hw_conf_id "1"		#default HW config ID for init
+ *	default_hw_conf_id "1"		# default HW config ID for init
  *
  *	# Optional boolean flags
  *	symmetric_rates			"true"
diff --git a/src/pcm/pcm_extplug.c b/src/pcm/pcm_extplug.c
index a0ff003c..feb32b99 100644
--- a/src/pcm/pcm_extplug.c
+++ b/src/pcm/pcm_extplug.c
@@ -650,8 +650,8 @@ parameter linked #snd_pcm_extplug_set_param_link() can be used for the
 corresponding parameter. For example if the extplug does not support channel nor
 format conversion the supported client parameters can be limited with
 snd_pcm_extplug_set_param_*() and afterwards
-#snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and
-#snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be
+snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and
+snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be
 called to keep the client and slave parameters the same.
 */
 

From 6a0a316bfb0ab4f0549e06c5e496360613488bf6 Mon Sep 17 00:00:00 2001
From: borine <32966433+borine@users.noreply.github.com>
Date: Sat, 29 Jul 2023 14:45:22 +0100
Subject: [PATCH 094/267] doxygen: include external control docs

From: borine@github
Link: https://github.com/alsa-project/alsa-lib/pull/340
Signed-off-by: Jaroslav Kysela 
---
 doc/doxygen.cfg.in        | 1 +
 src/control/control_ext.c | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in
index 8132cd16..1a5dbf56 100644
--- a/doc/doxygen.cfg.in
+++ b/doc/doxygen.cfg.in
@@ -42,6 +42,7 @@ INPUT            = @top_srcdir@/doc/index.doxygen \
 		   @top_srcdir@/src/userfile.c \
 		   @top_srcdir@/src/control/cards.c \
 		   @top_srcdir@/src/control/control.c \
+		   @top_srcdir@/src/control/control_ext.c \
 		   @top_srcdir@/src/control/control_plugin.c \
 		   @top_srcdir@/src/control/control_hw.c \
 		   @top_srcdir@/src/control/control_remap.c \
diff --git a/src/control/control_ext.c b/src/control/control_ext.c
index 09106df1..cf3da3d8 100644
--- a/src/control/control_ext.c
+++ b/src/control/control_ext.c
@@ -622,7 +622,7 @@ The rest fields are filled by #snd_ctl_ext_create().  The handle field
 is the resultant PCM handle.  The others are the current status of the
 PCM.
 
-\section ctl_ext_impl Callback Functions of External Control Plugins
+\section ctl_ext_impl_cb Callback Functions of External Control Plugins
 
 The callback functions in #snd_ctl_ext_callback_t define the real
 behavior of the driver.  There are many callbacks but many of them are optional. 

From 23931800dad0cda0242179957a6f2915a4da2b5d Mon Sep 17 00:00:00 2001
From: borine <32966433+borine@users.noreply.github.com>
Date: Sat, 29 Jul 2023 16:01:33 +0100
Subject: [PATCH 095/267] doxygen: pcm: silence 'not documented' warnings

From: borine@github
Link: https://github.com/alsa-project/alsa-lib/pull/340
Signed-off-by: Jaroslav Kysela 
---
 include/pcm.h       | 20 ++++++++++----------
 src/pcm/pcm.c       |  6 +++---
 src/pcm/pcm_meter.c |  2 ++
 src/pcm/pcm_route.c |  2 ++
 4 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/include/pcm.h b/include/pcm.h
index a29ea980..e7853313 100644
--- a/include/pcm.h
+++ b/include/pcm.h
@@ -343,6 +343,7 @@ typedef enum _snd_pcm_tstamp {
 	SND_PCM_TSTAMP_LAST = SND_PCM_TSTAMP_ENABLE
 } snd_pcm_tstamp_t;
 
+/** PCM timestamp type */
 typedef enum _snd_pcm_tstamp_type {
 	SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0,	/**< gettimeofday equivalent */
 	SND_PCM_TSTAMP_TYPE_MONOTONIC,	/**< posix_clock_monotonic equivalent */
@@ -350,6 +351,7 @@ typedef enum _snd_pcm_tstamp_type {
 	SND_PCM_TSTAMP_TYPE_LAST = SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW,
 } snd_pcm_tstamp_type_t;
 
+/** PCM audio timestamp type */
 typedef enum _snd_pcm_audio_tstamp_type {
 	/**
 	 * first definition for backwards compatibility only,
@@ -364,24 +366,22 @@ typedef enum _snd_pcm_audio_tstamp_type {
 	SND_PCM_AUDIO_TSTAMP_TYPE_LAST = SND_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED
 } snd_pcm_audio_tstamp_type_t;
 
+/** PCM audio timestamp config */
 typedef struct _snd_pcm_audio_tstamp_config {
 	/* 5 of max 16 bits used */
-	unsigned int type_requested:4;
-	unsigned int report_delay:1; /* add total delay to A/D or D/A */
+	unsigned int type_requested:4; /**< requested audio tstamp type */
+	unsigned int report_delay:1; /**< add total delay to A/D or D/A */
 } snd_pcm_audio_tstamp_config_t;
 
+/** PCM audio timestamp report */
 typedef struct _snd_pcm_audio_tstamp_report {
 	/* 6 of max 16 bits used for bit-fields */
 
-	/* for backwards compatibility */
-	unsigned int valid:1;
+	unsigned int valid:1; /**< for backwards compatibility */
+	unsigned int actual_type:4; /**< actual type if hardware could not support requested timestamp */
 
-	/* actual type if hardware could not support requested timestamp */
-	unsigned int actual_type:4;
-
-	/* accuracy represented in ns units */
-	unsigned int accuracy_report:1; /* 0 if accuracy unknown, 1 if accuracy field is valid */
-	unsigned int accuracy; /* up to 4.29s, will be packed in separate field  */
+	unsigned int accuracy_report:1; /**< 0 if accuracy unknown, 1 if accuracy field is valid */
+	unsigned int accuracy; /**< up to 4.29s in ns units, will be packed in separate field  */
 } snd_pcm_audio_tstamp_report_t;
 
 /** Unsigned frames quantity */
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
index 16cfe493..2077d6fa 100644
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -3444,7 +3444,7 @@ int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_
  * \param dst_size size in frames of the destination buffer
  * \param src_channels source areas specification (one for each channel)
  * \param src_offset offset in frames inside source area
- * \param dst_size size in frames of the source buffer
+ * \param src_size size in frames of the source buffer
  * \param channels channels count
  * \param frames frames to copy
  * \param format PCM sample format
@@ -7022,7 +7022,7 @@ void snd_pcm_status_get_driver_htstamp(const snd_pcm_status_t *obj, snd_htimesta
 /**
  * \brief Get audio_tstamp_report from a PCM status container
  * \param obj pointer to #snd_pcm_status_t
- * \param audio_tstamp_report Pointer to returned report (valid fields are accuracy and type)
+ * \param audio_tstamp_report Pointer to returned report
  */
 void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj,
 					     snd_pcm_audio_tstamp_report_t *audio_tstamp_report)
@@ -7036,7 +7036,7 @@ void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj,
 /**
  * \brief set audio_tstamp_config from a PCM status container
  * \param obj pointer to #snd_pcm_status_t
- * \param audio_tstamp_config Pointer to config (valid fields are type and report_analog_delay)
+ * \param audio_tstamp_config Pointer to config (valid fields are type_requested and report_delay)
  */
 void snd_pcm_status_set_audio_htstamp_config(snd_pcm_status_t *obj,
 					     snd_pcm_audio_tstamp_config_t *audio_tstamp_config)
diff --git a/src/pcm/pcm_meter.c b/src/pcm/pcm_meter.c
index 18d921cc..68c369de 100644
--- a/src/pcm/pcm_meter.c
+++ b/src/pcm/pcm_meter.c
@@ -34,9 +34,11 @@
 #include 
 #include 
 
+#ifndef DOC_HIDDEN
 #define atomic_read(ptr)    __atomic_load_n(ptr, __ATOMIC_SEQ_CST )
 #define atomic_add(ptr, n)  __atomic_add_fetch(ptr, n, __ATOMIC_SEQ_CST)
 #define atomic_dec(ptr)     __atomic_sub_fetch(ptr, 1, __ATOMIC_SEQ_CST)
+#endif
 
 #ifndef PIC
 /* entry for static linking */
diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c
index 94292fc7..38057cb2 100644
--- a/src/pcm/pcm_route.c
+++ b/src/pcm/pcm_route.c
@@ -765,7 +765,9 @@ static int strtochannel(const char *id, snd_pcm_chmap_t *chmap,
 	}
 }
 
+#ifndef DOC_HIDDEN
 #define MAX_CHMAP_CHANNELS 256
+#endif
 
 static int determine_chmap(snd_config_t *tt, snd_pcm_chmap_t **tt_chmap)
 {

From 473a2eaba8c6aa632398bc777e475857b8db006b Mon Sep 17 00:00:00 2001
From: borine <32966433+borine@users.noreply.github.com>
Date: Sat, 29 Jul 2023 16:02:58 +0100
Subject: [PATCH 096/267] doxygen: control: silence 'not documented' item
 warnings

From: borine@github
Link: https://github.com/alsa-project/alsa-lib/pull/340
Signed-off-by: Jaroslav Kysela 
---
 src/control/control.c       | 3 +++
 src/control/control_hw.c    | 3 ++-
 src/control/control_remap.c | 7 +++++++
 3 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/control/control.c b/src/control/control.c
index fe23dfd9..d77ab24c 100644
--- a/src/control/control.c
+++ b/src/control/control.c
@@ -428,6 +428,7 @@ int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info)
 	return ctl->ops->element_info(ctl, info);
 }
 
+#ifndef DOC_HIDDEN
 #if 0 /* deprecated */
 static bool validate_element_member_dimension(snd_ctl_elem_info_t *info)
 {
@@ -503,6 +504,8 @@ int __snd_ctl_add_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
 	return ctl->ops->element_add(ctl, info);
 }
 
+#endif /* DOC_HIDDEN */
+
 /**
  * \brief Create and add some user-defined control elements of integer type.
  * \param ctl A handle of backend module for control interface.
diff --git a/src/control/control_hw.c b/src/control/control_hw.c
index d21ef226..a353767d 100644
--- a/src/control/control_hw.c
+++ b/src/control/control_hw.c
@@ -40,11 +40,12 @@
 const char *_snd_module_control_hw = "";
 #endif
 
+#ifndef DOC_HIDDEN
+
 #ifndef F_SETSIG
 #define F_SETSIG 10
 #endif
 
-#ifndef DOC_HIDDEN
 #define SNDRV_FILE_CONTROL	ALSA_DEVICE_DIRECTORY "controlC%i"
 #define SNDRV_CTL_VERSION_MAX	SNDRV_PROTOCOL_VERSION(2, 0, 4)
 
diff --git a/src/control/control_remap.c b/src/control/control_remap.c
index faf9a739..6fcb01f1 100644
--- a/src/control/control_remap.c
+++ b/src/control/control_remap.c
@@ -33,6 +33,7 @@
 #include 
 #include 
 
+#ifndef DOC_HIDDEN
 #if 0
 #define REMAP_DEBUG 1
 #define debug(format, args...) fprintf(stderr, format, ##args)
@@ -48,6 +49,7 @@
 #endif
 
 #define EREMAPNOTFOUND (888899)
+#endif /* DOC_HIDDEN */
 
 #ifndef PIC
 /* entry for static linking */
@@ -379,10 +381,12 @@ static int snd_ctl_remap_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list)
 	return 0;
 }
 
+#ifndef DOC_HIDDEN
 #define ACCESS_BITS(bits) \
 	(bits & (SNDRV_CTL_ELEM_ACCESS_READWRITE|\
 		 SNDRV_CTL_ELEM_ACCESS_VOLATILE|\
 		 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE))
+#endif /* DOC_HIDDEN */
 
 static int remap_map_elem_info(snd_ctl_remap_t *priv, snd_ctl_elem_info_t *info)
 {
@@ -1141,6 +1145,7 @@ static int parse_map(snd_ctl_remap_t *priv, snd_config_t *conf)
  * \param name Name of control device
  * \param remap Remap configuration
  * \param map Map configuration
+ * \param child child configuration root
  * \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
@@ -1326,4 +1331,6 @@ int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd
 		snd_ctl_close(cctl);
 	return err;
 }
+#ifndef DOC_HIDDEN
 SND_DLSYM_BUILD_VERSION(_snd_ctl_remap_open, SND_CONTROL_DLSYM_VERSION);
+#endif

From 72e72ed029993b45985f3842569f2dbe18054291 Mon Sep 17 00:00:00 2001
From: borine <32966433+borine@users.noreply.github.com>
Date: Sat, 29 Jul 2023 19:47:26 +0100
Subject: [PATCH 097/267] doxygen: rawmidi: silence 'not documented' warnings

From: borine@github
Link: https://github.com/alsa-project/alsa-lib/pull/340
Signed-off-by: Jaroslav Kysela 
---
 src/rawmidi/ump.c       | 1 +
 src/rawmidi/ump_local.h | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c
index 119b58f6..25fbaff2 100644
--- a/src/rawmidi/ump.c
+++ b/src/rawmidi/ump.c
@@ -674,6 +674,7 @@ const char *snd_ump_block_info_get_name(const snd_ump_block_info_t *info)
 
 /**
  * \brief get UMP block information
+ * \param ump UMP handle
  * \param info pointer to a snd_ump_block_info_t structure
  * \return 0 on success otherwise a negative error code
  *
diff --git a/src/rawmidi/ump_local.h b/src/rawmidi/ump_local.h
index 53ce9a66..53ea9701 100644
--- a/src/rawmidi/ump_local.h
+++ b/src/rawmidi/ump_local.h
@@ -3,8 +3,10 @@
 #include "ump.h"
 #include "ump_msg.h"
 
+#ifndef DOC_HIDDEN
 struct _snd_ump {
 	snd_rawmidi_t *rawmidi;
 	unsigned int flags;
 	int is_input;
 };
+#endif /* DOC_HIDDEN */

From 9cb4414e3bb977443bcf5303f77c99b31af09e86 Mon Sep 17 00:00:00 2001
From: borine <32966433+borine@users.noreply.github.com>
Date: Sun, 30 Jul 2023 09:48:53 +0100
Subject: [PATCH 098/267] doxygen: conf: silence 'not documented' warnings

From: borine@github
Link: https://github.com/alsa-project/alsa-lib/pull/340
Signed-off-by: Jaroslav Kysela 
---
 doc/doxygen.cfg.in |  1 +
 include/conf.h     | 12 +++++++++++-
 src/conf.c         | 33 +++++++++++++++++++++++++++++----
 src/confeval.c     |  6 +++++-
 src/confmisc.c     |  2 +-
 5 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in
index 1a5dbf56..5333f6da 100644
--- a/doc/doxygen.cfg.in
+++ b/doc/doxygen.cfg.in
@@ -36,6 +36,7 @@ INPUT            = @top_srcdir@/doc/index.doxygen \
 		   @top_srcdir@/src/input.c \
 		   @top_srcdir@/src/output.c \
 		   @top_srcdir@/src/conf.c \
+		   @top_srcdir@/src/confeval.c \
 		   @top_srcdir@/src/confmisc.c \
 		   @top_srcdir@/src/names.c \
 		   @top_srcdir@/src/shmarea.c \
diff --git a/include/conf.h b/include/conf.h
index 800707d9..09da0e9a 100644
--- a/include/conf.h
+++ b/include/conf.h
@@ -33,7 +33,7 @@ extern "C" {
 #endif
 
 /**
- *  \defgroup Config Configuration Interface
+ *  \defgroup Configuration Configuration Interface
  *  The configuration functions and types allow you to read, enumerate,
  *  modify and write the contents of ALSA configuration files.
  *  \{
@@ -109,6 +109,16 @@ int snd_config_search_definition(snd_config_t *config,
 				 const char *base, const char *key,
 				 snd_config_t **result);
 
+/**
+ * \brief custom expansion callback
+ * \param[out] dst The function puts the handle to the new configuration
+ *                 node at the address specified by \a dst.
+ * \param[in] s string the string to be expanded
+ * \param[in] private_data Handle to the \c private_data node.
+ * \return A non-negative value if successful, otherwise a negative error code.
+ *
+ * Use a function of this type to define a custom expansion 
+ */
 typedef int (*snd_config_expand_fcn_t)(snd_config_t **dst, const char *s, void *private_data);
 
 int snd_config_expand_custom(snd_config_t *config, snd_config_t *root,
diff --git a/src/conf.c b/src/conf.c
index 9887d0e9..eca44c03 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -527,7 +527,7 @@ static inline void snd_config_unlock(void) { }
 #endif
 
 /*
- * Add a diretory to the paths to search included files.
+ * Add a directory to the paths to search included files.
  * param fd -  File object that owns these paths to search files included by it.
  * param dir - Path of the directory to add. Allocated externally and need to
 *              be freed manually later.
@@ -584,6 +584,8 @@ static void free_include_paths(struct filedesc *fd)
 	}
 }
 
+#endif /* DOC_HIDDEN */
+
 /**
  * \brief Returns the default top-level config directory
  * \return The top-level config directory path string
@@ -605,6 +607,8 @@ const char *snd_config_topdir(void)
 	return topdir;
 }
 
+#ifndef DOC_HIDDEN
+
 static char *_snd_config_path(const char *name)
 {
 	const char *root = snd_config_topdir();
@@ -1700,7 +1704,7 @@ static int _snd_config_save_children(snd_config_t *config, snd_output_t *out,
 	}
 	return 0;
 }
-#endif
+#endif /* DOC_HIDDEN */
 
 
 /**
@@ -2873,6 +2877,26 @@ int snd_config_imake_string(snd_config_t **config, const char *id, const char *v
 	return 0;
 }
 
+/**
+ * \brief Creates a string configuration node with the given initial value.
+ * \param[out] config The function puts the handle to the new node at
+ *                    the address specified by \a config.
+ * \param[in] id The id of the new node.
+ * \param[in] value The initial value of the new node.  May be \c NULL.
+ * \return Zero if successful, otherwise a negative error code.
+ *
+ * This function creates a new node of type #SND_CONFIG_TYPE_STRING. The node
+ * contains with a copy of the string \c value, replacing any character other
+ * than alphanumeric, space, or '-' with the character '_'.
+ *
+ * \par Errors:
+ * 
+ *
-ENOMEM
Out of memory. + *
+ * + * \par Conforming to: + * LSB 3.2 + */ int snd_config_imake_safe_string(snd_config_t **config, const char *id, const char *value) { int err; @@ -3894,7 +3918,6 @@ int snd_config_search_alias_hooks(snd_config_t *config, #define ALSA_CONFIG_PATH_VAR "ALSA_CONFIG_PATH" /** - * \ingroup Config * \brief Configuration top-level node (the global configuration). * * This variable contains a handle to the top-level configuration node, @@ -4295,7 +4318,7 @@ SND_DLSYM_BUILD_VERSION(snd_config_hook_load, SND_CONFIG_DLSYM_VERSION_HOOK); int snd_determine_driver(int card, char **driver); #endif -snd_config_t *_snd_config_hook_private_data(int card, const char *driver) +static snd_config_t *_snd_config_hook_private_data(int card, const char *driver) { snd_config_t *private_data, *v; int err; @@ -5810,6 +5833,7 @@ static void _snd_config_end(void) } #endif +#ifndef DOC_HIDDEN size_t page_size(void) { long s = sysconf(_SC_PAGE_SIZE); @@ -5845,3 +5869,4 @@ size_t page_ptr(size_t object_offset, size_t object_size, size_t *offset, size_t *offset = object_offset; return r; } +#endif /* DOC_HIDDEN */ diff --git a/src/confeval.c b/src/confeval.c index a27ee1d1..56b6ebec 100644 --- a/src/confeval.c +++ b/src/confeval.c @@ -38,7 +38,9 @@ #include #include +#ifndef DOC_HIDDEN typedef long long value_type_t; +#endif /* DOC_HIDDEN */ static const char *_find_end_of_expression(const char *s, char begin, char end) { @@ -119,6 +121,7 @@ static int _to_integer(value_type_t *val, snd_config_t *c) return err; } +#ifndef DOC_HIDDEN int _snd_eval_string(snd_config_t **dst, const char *s, snd_config_expand_fcn_t fcn, void *private_data) { @@ -244,6 +247,7 @@ int _snd_eval_string(snd_config_t **dst, const char *s, else return snd_config_imake_integer(dst, NULL, left); } +#endif /* DOC_HIDDEN */ /** * \brief Evaluate an math expression in the string @@ -251,7 +255,7 @@ int _snd_eval_string(snd_config_t **dst, const char *s, * node at the address specified by \a dst. * \param[in] s A string to evaluate * \param[in] fcn A function to get the variable contents - * \param[in] private_value A private value for the variable contents function + * \param[in] private_data A private value for the variable contents function * \return 0 if successful, otherwise a negative error code. */ int snd_config_evaluate_string(snd_config_t **dst, const char *s, diff --git a/src/confmisc.c b/src/confmisc.c index b55d86f7..0559fb66 100644 --- a/src/confmisc.c +++ b/src/confmisc.c @@ -645,7 +645,7 @@ static int string_from_integer(char **dst, long v) } #endif -int _snd_func_private_data(snd_config_t **dst, snd_config_t *src, +static int _snd_func_private_data(snd_config_t **dst, snd_config_t *src, snd_config_t **private_data, const char *id) { int err; From 47228e4e6e8482ea8a30df04ad5cdc3bf68b40b3 Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sun, 30 Jul 2023 10:22:53 +0100 Subject: [PATCH 099/267] doxygen: seq: silence 'not documented' warnings From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- doc/doxygen.cfg.in | 3 ++- src/seq/seq.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in index 5333f6da..0604c8b2 100644 --- a/doc/doxygen.cfg.in +++ b/doc/doxygen.cfg.in @@ -128,7 +128,8 @@ PREDEFINED = DOXYGEN PIC "DOC_HIDDEN" \ ALSA_PCM_NEW_HW_PARAMS_API \ _POSIX_C_SOURCE \ "use_default_symbol_version(x,y,z)=" \ - "link_warning(x,y)=" + "link_warning(x,y)=" \ + __attribute__((x))= OPTIMIZE_OUTPUT_FOR_C = YES # doxygen 1.2.6 option TYPEDEF_HIDES_STRUCT = YES # needed in doxygen >= 1.5.4 diff --git a/src/seq/seq.c b/src/seq/seq.c index c4f218df..899dfe9f 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -1763,7 +1763,9 @@ int snd_seq_client_info_get_ump_group_enabled(const snd_seq_client_info_t *info, return !(info->group_filter & (1U << group)); } +#ifndef DOC_HIDDEN #define UMP_GROUPLESS_FILTER (1U << 0) +#endif /* DOC_HIDDEN */ /** * \brief Get the UMP groupless message handling status From 382c700dfece324128d7a60ae0cdf07f3e250e90 Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sun, 30 Jul 2023 10:58:23 +0100 Subject: [PATCH 100/267] doxygen: namehint: silence 'not documented' warnings Also creates a new module within the Configuration interface documentation in which to present the name hint docs. From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- doc/doxygen.cfg.in | 1 + include/control.h | 18 ++++++++++++++---- src/control/namehint.c | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in index 0604c8b2..d9292fa8 100644 --- a/doc/doxygen.cfg.in +++ b/doc/doxygen.cfg.in @@ -50,6 +50,7 @@ INPUT = @top_srcdir@/doc/index.doxygen \ @top_srcdir@/src/control/control_shm.c \ @top_srcdir@/src/control/ctlparse.c \ @top_srcdir@/src/control/hcontrol.c \ + @top_srcdir@/src/control/namehint.c \ @top_srcdir@/src/control/setup.c \ @top_srcdir@/src/control/tlv.c \ @top_srcdir@/src/mixer \ diff --git a/include/control.h b/include/control.h index 41892de2..ab482ba4 100644 --- a/include/control.h +++ b/include/control.h @@ -374,10 +374,6 @@ int snd_card_get_index(const char *name); int snd_card_get_name(int card, char **name); int snd_card_get_longname(int card, char **name); -int snd_device_name_hint(int card, const char *iface, void ***hints); -int snd_device_name_free_hint(void **hints); -char *snd_device_name_get_hint(const void *hint, const char *id); - int snd_ctl_open(snd_ctl_t **ctl, const char *name, int mode); int snd_ctl_open_lconf(snd_ctl_t **ctl, const char *name, int mode, snd_config_t *lconf); int snd_ctl_open_fallback(snd_ctl_t **ctl, snd_config_t *root, const char *name, const char *orig_name, int mode); @@ -797,6 +793,20 @@ int snd_sctl_remove(snd_sctl_t *handle); /** \} */ +/** + * \defgroup Hint Name Hint Interface + * \ingroup Configuration + * The name hint interface - get descriptive information about a device + * (name, description, input/output). + * \{ + */ + +int snd_device_name_hint(int card, const char *iface, void ***hints); +int snd_device_name_free_hint(void **hints); +char *snd_device_name_get_hint(const void *hint, const char *id); + +/** \} */ + #ifdef __cplusplus } #endif diff --git a/src/control/namehint.c b/src/control/namehint.c index 64ecafa5..11783c0c 100644 --- a/src/control/namehint.c +++ b/src/control/namehint.c @@ -1,5 +1,6 @@ /** * \file control/namehint.c + * \ingroup Configuration * \brief Give device name hints * \author Jaroslav Kysela * \date 2006 From c4680ed458dcf5851f1c05bfee43e271472c203f Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sun, 30 Jul 2023 11:27:28 +0100 Subject: [PATCH 101/267] doxygen: global: silence 'not documented' warnings From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- include/global.h | 2 ++ src/async.c | 2 ++ src/dlmisc.c | 2 ++ src/shmarea.c | 12 ++++++++++++ 4 files changed, 18 insertions(+) diff --git a/include/global.h b/include/global.h index dfe9bc2b..f8124769 100644 --- a/include/global.h +++ b/include/global.h @@ -128,9 +128,11 @@ int snd_async_handler_get_fd(snd_async_handler_t *handler); int snd_async_handler_get_signo(snd_async_handler_t *handler); void *snd_async_handler_get_callback_private(snd_async_handler_t *handler); +#ifdef HAVE_SYS_SHM_H struct snd_shm_area *snd_shm_area_create(int shmid, void *ptr); struct snd_shm_area *snd_shm_area_share(struct snd_shm_area *area); int snd_shm_area_destroy(struct snd_shm_area *area); +#endif int snd_user_file(const char *file, char **result); diff --git a/src/async.c b/src/async.c index 8e8481b5..76c6fb9e 100644 --- a/src/async.c +++ b/src/async.c @@ -29,7 +29,9 @@ #include static struct sigaction previous_action; +#ifndef DOC_HIDDEN #define MAX_SIG_FUNCTION_CODE 10 /* i.e. SIG_DFL SIG_IGN SIG_HOLD et al */ +#endif /* DOC_HIDDEN */ #ifdef SND_ASYNC_RT_SIGNAL /** async signal number */ diff --git a/src/dlmisc.c b/src/dlmisc.c index bc527f83..d7aff456 100644 --- a/src/dlmisc.c +++ b/src/dlmisc.c @@ -170,8 +170,10 @@ EXPORT_SYMBOL void *INTERNAL(snd_dlopen_old)(const char *name, int mode) } #endif +#ifndef DOC_HIDDEN use_symbol_version(__snd_dlopen_old, snd_dlopen, ALSA_0.9); use_default_symbol_version(__snd_dlopen, snd_dlopen, ALSA_1.1.6); +#endif /* DOC_HIDDEN */ /** * \brief Closes a dynamic library - ALSA wrapper for \c dlclose. diff --git a/src/shmarea.c b/src/shmarea.c index b566c79e..4e621f7c 100644 --- a/src/shmarea.c +++ b/src/shmarea.c @@ -18,6 +18,16 @@ * */ +/** + * \file shmarea.c + * \ingroup Global + * \brief shared memory helpers + * \author Jaroslav Kysela + * \date 2001 + * + * Shared memory helpers + */ + #include "config.h" /* These funcs are only used by pcm_mmap when sys/shm.h is available. */ @@ -102,6 +112,7 @@ int snd_shm_area_destroy(struct snd_shm_area *area) return 0; } +#ifndef DOC_HIDDEN void snd_shm_area_destructor(void) __attribute__ ((destructor)); void snd_shm_area_destructor(void) @@ -114,5 +125,6 @@ void snd_shm_area_destructor(void) shmdt(area->ptr); } } +#endif /* DOC_HIDDEN */ #endif From 220377a2dd9910f78d0e7691b3d5d9cad9998796 Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sun, 30 Jul 2023 12:05:13 +0100 Subject: [PATCH 102/267] doxygen: topology: silence 'not documented' warnings From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- include/topology.h | 59 +++++++++++++++++++++++++-------------------- src/topology/ctl.c | 2 ++ src/topology/data.c | 2 ++ src/topology/elem.c | 2 ++ src/topology/pcm.c | 2 ++ 5 files changed, 41 insertions(+), 26 deletions(-) diff --git a/include/topology.h b/include/topology.h index d452f29b..2b83bbc8 100644 --- a/include/topology.h +++ b/include/topology.h @@ -840,7 +840,7 @@ struct snd_tplg_tlv_dbscale_template { int mute; /*!< is min dB value mute ? */ }; -/** \struct snd_tplg_channel_template +/** \struct snd_tplg_channel_elem * \brief Template type for single channel mapping. */ struct snd_tplg_channel_elem { @@ -1021,26 +1021,26 @@ struct snd_tplg_pcm_template { * hardware config, i.e. hardware audio formats. */ struct snd_tplg_hw_config_template { - int id; /* unique ID - - used to match */ - unsigned int fmt; /* SND_SOC_DAI_FORMAT_ format value */ - unsigned char clock_gated; /* SND_SOC_TPLG_DAI_CLK_GATE_ value */ - unsigned char invert_bclk; /* 1 for inverted BCLK, 0 for normal */ - unsigned char invert_fsync; /* 1 for inverted frame clock, 0 for normal */ - unsigned char bclk_provider; /* SND_SOC_TPLG_BCLK_ value */ - unsigned char fsync_provider; /* SND_SOC_TPLG_FSYNC_ value */ - unsigned char mclk_direction; /* SND_SOC_TPLG_MCLK_ value */ - unsigned short reserved; /* for 32bit alignment */ - unsigned int mclk_rate; /* MCLK or SYSCLK freqency in Hz */ - unsigned int bclk_rate; /* BCLK freqency in Hz */ - unsigned int fsync_rate; /* frame clock in Hz */ - unsigned int tdm_slots; /* number of TDM slots in use */ - unsigned int tdm_slot_width; /* width in bits for each slot */ - unsigned int tx_slots; /* bit mask for active Tx slots */ - unsigned int rx_slots; /* bit mask for active Rx slots */ - unsigned int tx_channels; /* number of Tx channels */ - unsigned int *tx_chanmap; /* array of slot number */ - unsigned int rx_channels; /* number of Rx channels */ - unsigned int *rx_chanmap; /* array of slot number */ + int id; /*!< unique ID - - used to match */ + unsigned int fmt; /*!< SND_SOC_DAI_FORMAT_ format value */ + unsigned char clock_gated; /*!< SND_SOC_TPLG_DAI_CLK_GATE_ value */ + unsigned char invert_bclk; /*!< 1 for inverted BCLK, 0 for normal */ + unsigned char invert_fsync; /*!< 1 for inverted frame clock, 0 for normal */ + unsigned char bclk_provider; /*!< SND_SOC_TPLG_BCLK_ value */ + unsigned char fsync_provider; /*!< SND_SOC_TPLG_FSYNC_ value */ + unsigned char mclk_direction; /*!< SND_SOC_TPLG_MCLK_ value */ + unsigned short reserved; /*!< for 32bit alignment */ + unsigned int mclk_rate; /*!< MCLK or SYSCLK freqency in Hz */ + unsigned int bclk_rate; /*!< BCLK freqency in Hz */ + unsigned int fsync_rate; /*!< frame clock in Hz */ + unsigned int tdm_slots; /*!< number of TDM slots in use */ + unsigned int tdm_slot_width; /*!< width in bits for each slot */ + unsigned int tx_slots; /*!< bit mask for active Tx slots */ + unsigned int rx_slots; /*!< bit mask for active Rx slots */ + unsigned int tx_channels; /*!< number of Tx channels */ + unsigned int *tx_chanmap; /*!< array of slot number */ + unsigned int rx_channels; /*!< number of Rx channels */ + unsigned int *rx_chanmap; /*!< array of slot number */ }; /** \struct snd_tplg_dai_template @@ -1071,15 +1071,15 @@ struct snd_tplg_link_template { struct snd_tplg_stream_template *stream; /*!< supported configs */ struct snd_tplg_hw_config_template *hw_config; /*!< supported HW configs */ - int num_hw_configs; /* number of hw configs */ - int default_hw_config_id; /* default hw config ID for init */ + int num_hw_configs; /*!< number of hw configs */ + int default_hw_config_id; /*!< default hw config ID for init */ - unsigned int flag_mask; /* bitmask of flags to configure */ - unsigned int flags; /* SND_SOC_TPLG_LNK_FLGBIT_* flag value */ + unsigned int flag_mask; /*!< bitmask of flags to configure */ + unsigned int flags; /*!< SND_SOC_TPLG_LNK_FLGBIT_* flag value */ struct snd_soc_tplg_private *priv; /*!< private data */ }; -/** \struct snd_tplg_obj_template +/** \struct snd_tplg_obj_template_t * \brief Generic Template Object */ typedef struct snd_tplg_obj_template { @@ -1152,7 +1152,13 @@ int snd_tplg_set_version(snd_tplg_t *tplg, unsigned int version); * \brief Save the topology to the text configuration string. * \param tplg Topology instance. * \param dst A pointer to string with result (malloc). + * \param flags save mode * \return Zero on success, otherwise a negative error code + * + * Valid flags are + * - SND_TPLG_SAVE_SORT + * - SND_TPLG_SAVE_GROUPS + * - SND_TPLG_SAVE_NOCHECK */ int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags); @@ -1161,6 +1167,7 @@ int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags); * \param tplg Topology instance. * \param bin Binary topology input buffer. * \param size Binary topology input buffer size. + * \param dflags - not used, must be set to 0. * \return Zero on success, otherwise a negative error code */ int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags); diff --git a/src/topology/ctl.c b/src/topology/ctl.c index 04f39d77..67622957 100644 --- a/src/topology/ctl.c +++ b/src/topology/ctl.c @@ -19,12 +19,14 @@ #include "tplg_local.h" +#ifndef DOC_HIDDEN #define ENUM_VAL_SIZE (SNDRV_CTL_ELEM_ID_NAME_MAXLEN >> 2) struct ctl_access_elem { const char *name; unsigned int value; }; +#endif /* DOC_HIDDEN */ /* CTL access strings and codes */ /* place the multi-bit values on top - like read_write - for save */ diff --git a/src/topology/data.c b/src/topology/data.c index 2f077d36..e1611aea 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -712,11 +712,13 @@ static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem) return 0; } +#ifndef DOC_HIDDEN struct tuple_type { unsigned int type; const char *name; unsigned int size; }; +#endif /* DOC_HIDDEN */ static struct tuple_type tuple_types[] = { { diff --git a/src/topology/elem.c b/src/topology/elem.c index a7a7241d..2e31da5d 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -481,10 +481,12 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, return elem; } +#ifndef DOC_HIDDEN struct tplg_alloc { struct list_head list; void *data[0]; }; +#endif /* DOC_HIDDEN */ void *tplg_calloc(struct list_head *heap, size_t size) { diff --git a/src/topology/pcm.c b/src/topology/pcm.c index 539f258d..acec27f9 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -1364,10 +1364,12 @@ int tplg_save_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED, return err; } +#ifndef DOC_HIDDEN struct audio_hw_format { unsigned int type; const char *name; }; +#endif /* DOC_HIDDEN */ static struct audio_hw_format audio_hw_formats[] = { { From cde0e819f7cef78877d07f0afc2cb2a556980391 Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sun, 30 Jul 2023 13:23:04 +0100 Subject: [PATCH 103/267] doxygen: ucm: silence warnings Removes duplicate documentation blocks and fixes 'not documented' warnings From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 51 ---------------------------------------------- src/ucm/ucm_subs.c | 2 ++ 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index 7675398b..b6679c1d 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -1505,12 +1505,6 @@ skip: return end + 3; } -/** - * \brief Init sound card use case manager. - * \param uc_mgr Returned use case manager pointer - * \param card_name name of card to open - * \return zero on success, otherwise a negative error code - */ int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr, const char *card_name) { @@ -1577,11 +1571,6 @@ _err: return err; } -/** - * \brief Reload and reparse all use case files. - * \param uc_mgr Use case manager - * \return zero on success, otherwise a negative error code - */ int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr) { int err; @@ -1606,11 +1595,6 @@ int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr) return err; } -/** - * \brief Close use case manager. - * \param uc_mgr Use case manager - * \return zero on success, otherwise a negative error code - */ int snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr) { uc_mgr_card_close(uc_mgr); @@ -1659,11 +1643,6 @@ static int dismantle_use_case(snd_use_case_mgr_t *uc_mgr) return err; } -/** - * \brief Reset sound card controls to default values. - * \param uc_mgr Use case manager - * \return zero on success, otherwise a negative error code - */ int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr) { int err; @@ -2095,13 +2074,6 @@ static int get_enabled_modifier_list(snd_use_case_mgr_t *uc_mgr, name); } -/** - * \brief Obtain a list of entries - * \param uc_mgr Use case manager (may be NULL - card list) - * \param identifier (may be NULL - card list) - * \param list Returned allocated list - * \return Number of list entries if success, otherwise a negative error code - */ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, const char *identifier, const char **list[]) @@ -2312,16 +2284,6 @@ static int get_alibpref(snd_use_case_mgr_t *uc_mgr, char **str) return 0; } -/** - * \brief Get current - string - * \param uc_mgr Use case manager - * \param identifier - * \param value Value pointer - * \return Zero if success, otherwise a negative error code - * - * Note: String is dynamically allocated, use free() to - * deallocate this string. - */ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, const char *identifier, const char **value) @@ -2434,12 +2396,6 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, ; val; /* return value */ \ }) -/** - * \brief Get current - integer - * \param uc_mgr Use case manager - * \param identifier - * \return Value if success, otherwise a negative error code - */ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, const char *identifier, long *value) @@ -2732,13 +2688,6 @@ static int switch_modifier(snd_use_case_mgr_t *uc_mgr, return err; } -/** - * \brief Set new - * \param uc_mgr Use case manager - * \param identifier - * \param value Value - * \return Zero if success, otherwise a negative error code - */ int snd_use_case_set(snd_use_case_mgr_t *uc_mgr, const char *identifier, const char *value) diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c index e62290ea..2b010338 100644 --- a/src/ucm/ucm_subs.c +++ b/src/ucm/ucm_subs.c @@ -191,6 +191,7 @@ static char *rval_card_id_by_name(snd_use_case_mgr_t *uc_mgr, const char *id) return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info)); } +#ifndef DOC_HIDDEN typedef struct lookup_iterate *(*lookup_iter_fcn_t) (snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter); typedef const char *(*lookup_fcn_t)(void *); @@ -212,6 +213,7 @@ struct lookup_iterate { struct ctl_list *ctl_list; void *info; }; +#endif /* DOC_HIDDEN */ static char *rval_lookup_main(snd_use_case_mgr_t *uc_mgr, const char *query, From bbe38d055fd34968a70788ecd66ff47553f5fac9 Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sun, 30 Jul 2023 15:51:46 +0100 Subject: [PATCH 104/267] doxygen: silence warning from asoundlib.h From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index ac8e15c3..3d917f82 100644 --- a/configure.ac +++ b/configure.ac @@ -784,9 +784,11 @@ EOF fi cat >> include/asoundlib.h < #include From 731253e82a041c558a19e269444cd56a611f857a Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Sun, 30 Jul 2023 16:26:56 +0100 Subject: [PATCH 105/267] doxygen: conf: do not hide PCM specific function From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- src/confmisc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/confmisc.c b/src/confmisc.c index 0559fb66..9b30d6c1 100644 --- a/src/confmisc.c +++ b/src/confmisc.c @@ -1031,6 +1031,14 @@ int snd_func_card_name(snd_config_t **dst, snd_config_t *root, SND_DLSYM_BUILD_VERSION(snd_func_card_name, SND_CONFIG_DLSYM_VERSION_EVALUATE); #endif +#ifdef DOXYGEN +/* For consistency with the PCM Interface module, include documentation even + * when PCM module is not included in the build. */ +#ifndef BUILD_PCM +#define BUILD_PCM +#endif +#endif /* DOXYGEN */ + #ifdef BUILD_PCM /** From 08e79fc5a39aac374d5e44e3ef2c37001b7f2bd5 Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Wed, 2 Aug 2023 14:40:29 +0100 Subject: [PATCH 106/267] doxygen: include docs for shmarea functions From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- doc/doxygen.cfg.in | 1 + include/global.h | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in index d9292fa8..8bf29b21 100644 --- a/doc/doxygen.cfg.in +++ b/doc/doxygen.cfg.in @@ -112,6 +112,7 @@ EXCLUDE = @top_srcdir@/src/control/control_local.h \ @top_srcdir@/src/topology/tplg_local.h RECURSIVE = YES FILE_PATTERNS = *.c *.h +INCLUDE_PATH = @top_builddir@/include EXAMPLE_PATH = @top_srcdir@/test IMAGE_PATH = @top_srcdir@/doc/pictures QUIET = YES diff --git a/include/global.h b/include/global.h index f8124769..dfe9bc2b 100644 --- a/include/global.h +++ b/include/global.h @@ -128,11 +128,9 @@ int snd_async_handler_get_fd(snd_async_handler_t *handler); int snd_async_handler_get_signo(snd_async_handler_t *handler); void *snd_async_handler_get_callback_private(snd_async_handler_t *handler); -#ifdef HAVE_SYS_SHM_H struct snd_shm_area *snd_shm_area_create(int shmid, void *ptr); struct snd_shm_area *snd_shm_area_share(struct snd_shm_area *area); int snd_shm_area_destroy(struct snd_shm_area *area); -#endif int snd_user_file(const char *file, char **result); From 2714f438cb50ba1796d34637f35dad21e1158641 Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Wed, 2 Aug 2023 16:11:50 +0100 Subject: [PATCH 107/267] doxygen: fix broken examples links From: borine@github Link: https://github.com/alsa-project/alsa-lib/pull/340 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 21 +++++++++++++++------ src/rawmidi/rawmidi.c | 3 ++- src/timer/timer.c | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 2077d6fa..ad9e8956 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -223,7 +223,7 @@ There are two methods to transfer samples in application. The first method is the standard read / write one. The second method, uses the direct audio buffer to communicate with the device while ALSA library manages this space itself. You can find examples of all communication schemes for playback -in \ref example_test_pcm "Sine-wave generator example". To complete the +in \link example_test_pcm Sine-wave generator example \endlink. To complete the list, we should note that #snd_pcm_wait() function contains embedded poll waiting implementation. @@ -632,30 +632,39 @@ The null device is null plugin. This device has not any arguments. The full featured examples with cross-links can be found in Examples section (see top of page): -\anchor example_test_pcm \par Sine-wave generator \par -alsa-lib/test/pcm.c example shows various transfer methods for the playback direction. +\link example_test_pcm alsa-lib/test/pcm.c \endlink +example shows various transfer methods for the playback direction. \par Minimalistic PCM playback code \par -alsa-lib/test/pcm_min.c example shows the minimal code to produce a sound. +\link example_test_minimal alsa-lib/test/pcm_min.c \endlink +example shows the minimal code to produce a sound. \par Latency measuring tool \par -alsa-lib/test/latency.c example shows the measuring of minimal latency between capture and +\link example_test_latency alsa-lib/test/latency.c \endlink +example shows the measuring of minimal latency between capture and playback devices. */ /** \example ../../test/pcm.c +\anchor example_test_pcm +Shows various transfer methods for the playback direction. */ /** \example ../../test/pcm_min.c +\anchor example_test_minimal +Shows the minimal code to produce a sound. */ /** \example ../../test/latency.c +\anchor example_test_latency +Shows the measuring of minimal latency between capture and +playback devices. */ #include "pcm_local.h" @@ -7431,7 +7440,7 @@ int __snd_pcm_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, _skip: \endcode * - * Look to the \ref example_test_pcm "Sine-wave generator" example + * Look to the \link example_test_pcm Sine-wave generator \endlink example * for more details about the generate_sine function. * * The function is thread-safe when built with the proper option. diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index 8dca9312..316f524b 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -132,7 +132,7 @@ The timestamping is available only on input streams. The full featured examples with cross-links: \par Simple input/output test program -\ref example_test_rawmidi "example code" +\link example_test_rawmidi example code \endlink \par This example shows open and read/write rawmidi operations. @@ -141,6 +141,7 @@ This example shows open and read/write rawmidi operations. /** * \example ../test/rawmidi.c * \anchor example_test_rawmidi + * Shows open and read/write rawmidi operations. */ #include diff --git a/src/timer/timer.c b/src/timer/timer.c index 6fc710b9..0f8491b8 100644 --- a/src/timer/timer.c +++ b/src/timer/timer.c @@ -56,7 +56,7 @@ Events are read via snd_timer_read() function. The full featured examples with cross-links: \par Simple timer test program -\ref example_test_timer "example code" +\link example_test_timer example code \endlink \par This example shows opening a timer device and reading of timer events. From 70d4e042dc449c05d3eadae353824bf090fe1632 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 1 Sep 2023 16:27:15 +0200 Subject: [PATCH 108/267] dogyxen: fix topology.h warnings Signed-off-by: Jaroslav Kysela --- include/topology.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/topology.h b/include/topology.h index 2b83bbc8..a590d1a8 100644 --- a/include/topology.h +++ b/include/topology.h @@ -417,7 +417,7 @@ extern "C" { * Values [ * "value1" * "value2" - "value3" + * "value3" * ] * } *
@@ -502,7 +502,7 @@ extern "C" { * no_pm "true" # No PM control bit. * reg "20" # PM bit register offset * shift "0" # PM bit register shift - * invert "1 # PM bit is inverted + * invert "1" # PM bit is inverted * subseq "8" # subsequence number * * event_type "1" # DAPM widget event type From 9ed626d472da7a7009110a15ce9ca1a969ef9551 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 1 Sep 2023 16:27:43 +0200 Subject: [PATCH 109/267] topology: fix src/mixer/mixer.c return value warning Signed-off-by: Jaroslav Kysela --- src/mixer/mixer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c index d6456278..7dd5c47a 100644 --- a/src/mixer/mixer.c +++ b/src/mixer/mixer.c @@ -482,7 +482,6 @@ int snd_mixer_elem_remove(snd_mixer_elem_t *elem) /** * \brief Free a mixer element * \param elem Mixer element - * \return 0 on success otherwise a negative error code * * For use by mixer element class specific code. */ From 00da3feacead90f321c31a52ba10434a3dec2614 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 1 Jul 2023 19:09:47 +0200 Subject: [PATCH 110/267] ucm: mark internal functions static From: Tony Wasserka /neobrain@github/ Fixes: https://github.com/alsa-project/alsa-lib/pull/335 Signed-off-by: Jaroslav Kysela --- src/ucm/parser.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ucm/parser.c b/src/ucm/parser.c index c111661f..98acd97e 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -94,7 +94,7 @@ static char *replace_string(char **dst, const char *value) /* * Parse string */ -int parse_string(snd_config_t *n, char **res) +static int parse_string(snd_config_t *n, char **res) { int err; @@ -110,7 +110,7 @@ int parse_string(snd_config_t *n, char **res) /* * Parse string and substitute */ -int parse_string_substitute(snd_use_case_mgr_t *uc_mgr, +static int parse_string_substitute(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, char **res) { const char *str; @@ -129,7 +129,7 @@ int parse_string_substitute(snd_use_case_mgr_t *uc_mgr, /* * Parse string and substitute */ -int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr, +static int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, char **res) { if (uc_mgr->conf_format < 3) @@ -140,7 +140,7 @@ int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr, /* * Parse integer with substitution */ -int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr, +static int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, long *res) { char *s1, *s2; @@ -160,7 +160,7 @@ int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr, /* * Parse integer with substitution */ -int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr, +static int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, long *res) { char *s1, *s2; @@ -184,7 +184,7 @@ int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr, /* * Parse safe ID */ -int parse_is_name_safe(const char *name) +static int parse_is_name_safe(const char *name) { if (strchr(name, '.')) { uc_error("char '.' not allowed in '%s'", name); @@ -193,7 +193,7 @@ int parse_is_name_safe(const char *name) return 1; } -int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s) +static int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s) { if (uc_mgr->conf_format < 3) { *s = strdup(s1); @@ -204,7 +204,7 @@ int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s) return uc_mgr_get_substituted_value(uc_mgr, s, s1); } -int parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, +static int parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, const char *alt, char **name) { const char *id; From 8883943995d428fcae5926ec9a4275e76e880b1a Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 1 Sep 2023 16:50:03 +0200 Subject: [PATCH 111/267] ucm: main - remove cast to pointer from integer of different size warning Signed-off-by: Jaroslav Kysela --- src/ucm/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index b6679c1d..3a3c9c15 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -507,7 +507,7 @@ static int execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type) free(value); if (info2) { if (info2->type == SND_CTL_ELEM_TYPE_ENUMERATED) - free((void *)info2->value.enumerated.names_ptr); + free((void *)(size_t)info2->value.enumerated.names_ptr); free(info2); } free(info); From c72bbed82513074fb5f0d474c7e88e2b4b945905 Mon Sep 17 00:00:00 2001 From: Bernd Kuhls Date: Thu, 29 Jun 2023 07:57:21 +0200 Subject: [PATCH 112/267] Fix symver build error on non-ELF platforms The following error is observed on Microblaze [1] build: error: symver is only supported on ELF platforms due to using __attribute__((symver)) on non-ELF platform. [1] http://autobuild.buildroot.net/results/1e9/1e965d83d75615f35308440c5db044314a349357/build-end.log ac_check_attribute_symver.m4 was downloaded from https://github.com/smuellerDD/libkcapi/blob/master/m4/ac_check_attribute_symver.m4 Fixes: https://github.com/alsa-project/alsa-lib/pull/334 Signed-off-by: Tan En De Signed-off-by: Stephan Mueller Signed-off-by: Bernd Kuhls Signed-off-by: Jaroslav Kysela --- configure.ac | 1 + include/alsa-symbols.h | 2 +- m4/ac_check_attribute_symver.m4 | 24 ++++++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 m4/ac_check_attribute_symver.m4 diff --git a/configure.ac b/configure.ac index 3d917f82..04541790 100644 --- a/configure.ac +++ b/configure.ac @@ -46,6 +46,7 @@ dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_HEADER_TIME +AC_CHECK_ATTRIBUTE_SYMVER dnl Checks for library functions. AC_PROG_GCC_TRADITIONAL diff --git a/include/alsa-symbols.h b/include/alsa-symbols.h index f8c49103..2298cb50 100644 --- a/include/alsa-symbols.h +++ b/include/alsa-symbols.h @@ -29,7 +29,7 @@ #define INTERNAL_CONCAT2_2(Pre, Post) Pre##Post #define INTERNAL(Name) INTERNAL_CONCAT2_2(__, Name) -#if __GNUC__ > 10 +#if HAVE_ATTRIBUTE_SYMVER && __GNUC__ > 10 #define symbol_version(real, name, version) \ extern __typeof (real) real __attribute__((symver (#name "@" #version))) #define default_symbol_version(real, name, version) \ diff --git a/m4/ac_check_attribute_symver.m4 b/m4/ac_check_attribute_symver.m4 new file mode 100644 index 00000000..ac62bf36 --- /dev/null +++ b/m4/ac_check_attribute_symver.m4 @@ -0,0 +1,24 @@ +dnl Check compiler support for symver function attribute +AC_DEFUN([AC_CHECK_ATTRIBUTE_SYMVER], [ + saved_CFLAGS=$CFLAGS + CFLAGS="-O0 -Werror" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[ + void _test_attribute_symver(void); + __attribute__((__symver__("sym@VER_1.2.3"))) void _test_attribute_symver(void) {} + ]], + [[ + _test_attribute_symver() + ]] + )], + [ + AC_DEFINE([HAVE_ATTRIBUTE_SYMVER], 1, [Define to 1 if __attribute__((symver)) is supported]) + ], + [ + AC_DEFINE([HAVE_ATTRIBUTE_SYMVER], 0, [Define to 0 if __attribute__((symver)) is not supported]) + ] + ) + CFLAGS=$saved_CFLAGS +]) + From f70653fda79742cd1ff93e7a521ae3a333d21882 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 1 Sep 2023 17:35:48 +0200 Subject: [PATCH 113/267] Release v1.2.10 Signed-off-by: Jaroslav Kysela --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 04541790..225d86ec 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.9) +AC_INIT(alsa-lib, 1.2.10) AC_CONFIG_SRCDIR([src/control/control.c]) AC_CONFIG_MACRO_DIR([m4]) From 9685505bf438467d9b11919b332b066d3a1c94d0 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 4 Sep 2023 09:37:29 +0200 Subject: [PATCH 114/267] gitcompile: Add static build Signed-off-by: Jaroslav Kysela --- gitcompile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gitcompile b/gitcompile index 0fe092ce..c70448f7 100755 --- a/gitcompile +++ b/gitcompile @@ -32,6 +32,10 @@ if [ $# -ne 0 ]; then lto="-flto -flto-partition=none" echo "Forced lto build..." shift ;; + static) + static=yes + echo "Selected static build..." + shift ;; *) endloop=yes ;; @@ -75,6 +79,12 @@ if [ "$python2" = "yes" ]; then args="$args --enable-python2" fi +if [ "$static" = "yes" ]; then + #args="$args --enable-shared=no --enable-static=yes" + args="$args --disable-shared" +fi + + touch ltconfig libtoolize --force --copy --automake aclocal $ACLOCAL_FLAGS From fcce13a6726c52882bd8b7131c61c4eba308792c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 4 Sep 2023 09:38:26 +0200 Subject: [PATCH 115/267] control.h: Fix ump header file detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently, the control.h is used from apps separately (outside asoundlib.h). Avoid errors like: /usr/include/alsa/control.h:417:47: error: ‘snd_ump_endpoint_info_t’ has not been declared 417 | int snd_ctl_ump_endpoint_info(snd_ctl_t *ctl, snd_ump_endpoint_info_t *info); | ^~~~~~~~~~~~~~~~~~~~~~~ /usr/include/alsa/control.h:418:44: error: ‘snd_ump_block_info_t’ has not been declared 418 | int snd_ctl_ump_block_info(snd_ctl_t *ctl, snd_ump_block_info_t *info); | ^~~~~~~~~~~~~~~~~~~~ Fixes: https://github.com/alsa-project/alsa-lib/issues/348 Signed-off-by: Jaroslav Kysela --- include/control.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/control.h b/include/control.h index ab482ba4..e7541d56 100644 --- a/include/control.h +++ b/include/control.h @@ -413,6 +413,8 @@ int snd_ctl_pcm_prefer_subdevice(snd_ctl_t *ctl, int subdev); int snd_ctl_rawmidi_next_device(snd_ctl_t *ctl, int * device); int snd_ctl_rawmidi_info(snd_ctl_t *ctl, snd_rawmidi_info_t * info); int snd_ctl_rawmidi_prefer_subdevice(snd_ctl_t *ctl, int subdev); +#endif +#ifdef __ALSA_UMP_H 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); int snd_ctl_ump_block_info(snd_ctl_t *ctl, snd_ump_block_info_t *info); From 10bd599970acc71c92f85eb08943eb8d3d702a9c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 6 Sep 2023 15:16:44 +0200 Subject: [PATCH 116/267] global.h: move __STRING() macro outside !PIC ifdef block It solves the musl libc compilation issue. control.c: In function 'snd_ctl_open_conf': ../../include/global.h:98:36: warning: implicit declaration of function '__STRING' [-Wimplicit-function-declaratio] 98 | #define SND_DLSYM_VERSION(version) __STRING(version) | ^~~~~~~~ Fixes: https://github.com/alsa-project/alsa-lib/issues/350 Signed-off-by: Jaroslav Kysela --- include/global.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/global.h b/include/global.h index dfe9bc2b..3ecaeee8 100644 --- a/include/global.h +++ b/include/global.h @@ -51,6 +51,11 @@ const char *snd_asoundlib_version(void); #define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) #endif +#ifndef __STRING +/** \brief Return 'x' argument as string */ +#define __STRING(x) #x +#endif + #ifdef PIC /* dynamic build */ /** \hideinitializer \brief Helper macro for #SND_DLSYM_BUILD_VERSION. */ @@ -71,11 +76,6 @@ struct snd_dlsym_link { extern struct snd_dlsym_link *snd_dlsym_start; -#ifndef __STRING -/** \brief Return 'x' argument as string */ -#define __STRING(x) #x -#endif - /** \hideinitializer \brief Helper macro for #SND_DLSYM_BUILD_VERSION. */ #define __SND_DLSYM_VERSION(prefix, name, version) _ ## prefix ## name ## version /** From 0e3dfb9f705ca78be34cd70fd59d67c431e29cc7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 9 Sep 2023 17:42:03 +0200 Subject: [PATCH 117/267] pcm: Fix segfault with 32bit libs The recent rearrangement of header inclusion order caused a regression showing segfaults on 32bit Arm. The primary reason is the inconsistent compile condition depending on the inclusion of config.h; while most of other code include pcm_local.h (that implicitly includes config.h) at first, pcm_direct.c doesn't do it, hence the access with direct plugins crashes. For fixing it, we need to include config.h at the beginning. But, it's better to include pcm_local.h for all relevant code for consistency. The patch does it, and also it adds the guard in pcm_local.h for double inclusions. Fixes: ad3a8b8b314e ("reshuffle included files to include config.h as first") Link: https://github.com/alsa-project/alsa-lib/issues/352 Signed-off-by: Takashi Iwai --- src/pcm/pcm_direct.c | 1 + src/pcm/pcm_dmix.c | 2 +- src/pcm/pcm_dshare.c | 1 + src/pcm/pcm_dsnoop.c | 1 + src/pcm/pcm_local.h | 5 +++++ src/pcm/pcm_shm.c | 1 + 6 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 040fc160..e53e5923 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -19,6 +19,7 @@ * */ +#include "pcm_local.h" #include #include #include diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c index 7cd3c508..55cae3e7 100644 --- a/src/pcm/pcm_dmix.c +++ b/src/pcm/pcm_dmix.c @@ -26,7 +26,7 @@ * */ -#include "config.h" +#include "pcm_local.h" #include #include #include diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c index 454b39a9..c0329098 100644 --- a/src/pcm/pcm_dshare.c +++ b/src/pcm/pcm_dshare.c @@ -26,6 +26,7 @@ * */ +#include "pcm_local.h" #include #include #include diff --git a/src/pcm/pcm_dsnoop.c b/src/pcm/pcm_dsnoop.c index d3ce300c..bf67c68a 100644 --- a/src/pcm/pcm_dsnoop.c +++ b/src/pcm/pcm_dsnoop.c @@ -26,6 +26,7 @@ * */ +#include "pcm_local.h" #include #include #include diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 6a0e71e7..152c92c3 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -20,6 +20,9 @@ * */ +#ifndef __PCM_LOCAL_H +#define __PCM_LOCAL_H + #include "config.h" #include @@ -1223,3 +1226,5 @@ static inline void snd_pcm_unlock(snd_pcm_t *pcm) #define snd_pcm_lock(pcm) do {} while (0) #define snd_pcm_unlock(pcm) do {} while (0) #endif /* THREAD_SAFE_API */ + +#endif /* __PCM_LOCAL_H */ diff --git a/src/pcm/pcm_shm.c b/src/pcm/pcm_shm.c index f0bfd934..d9596547 100644 --- a/src/pcm/pcm_shm.c +++ b/src/pcm/pcm_shm.c @@ -26,6 +26,7 @@ * */ +#include "pcm_local.h" #include #include #include From 81a7a93636d9472fcb0c2ff32d9bfdf6ed10763d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 13 Sep 2023 12:27:21 +0200 Subject: [PATCH 118/267] reshuffle included files to include config.h as first - v2 config.h may contain defines like _FILE_OFFSET_BITS which influence the system wide include files (off_t types, open -> open64 function usage etc.). Fixes: ad3a8b8b ("reshuffle included files to include config.h as first") Related: https://github.com/alsa-project/alsa-lib/pull/333 Signed-off-by: Jaroslav Kysela --- src/control/setup.c | 2 +- src/rawmidi/rawmidi.c | 2 +- src/rawmidi/rawmidi_local.h | 2 +- src/rawmidi/rawmidi_virt.c | 4 +--- src/rawmidi/ump.c | 4 ---- src/seq/seq.c | 2 +- src/seq/seq_hw.c | 2 +- src/seq/seq_local.h | 2 +- src/seq/seq_midi_event.c | 2 +- src/seq/seqmid.c | 4 +--- src/userfile.c | 2 +- 11 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/control/setup.c b/src/control/setup.c index 88635e42..fb096117 100644 --- a/src/control/setup.c +++ b/src/control/setup.c @@ -29,13 +29,13 @@ * */ +#include "local.h" #include #include #include #include #include #include -#include "local.h" #ifndef DOC_HIDDEN typedef struct { diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index 316f524b..c4b45fa2 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -144,12 +144,12 @@ This example shows open and read/write rawmidi operations. * Shows open and read/write rawmidi operations. */ +#include "rawmidi_local.h" #include #include #include #include #include -#include "rawmidi_local.h" /** * \brief setup the default parameters diff --git a/src/rawmidi/rawmidi_local.h b/src/rawmidi/rawmidi_local.h index 19dbf725..f0bb06a7 100644 --- a/src/rawmidi/rawmidi_local.h +++ b/src/rawmidi/rawmidi_local.h @@ -19,10 +19,10 @@ * */ +#include "local.h" #include #include #include -#include "local.h" typedef struct { int (*close)(snd_rawmidi_t *rawmidi); diff --git a/src/rawmidi/rawmidi_virt.c b/src/rawmidi/rawmidi_virt.c index 884b8ff8..04c485d3 100644 --- a/src/rawmidi/rawmidi_virt.c +++ b/src/rawmidi/rawmidi_virt.c @@ -19,13 +19,11 @@ * */ -#include -#include +#include "rawmidi_local.h" #include #include #include #include -#include "rawmidi_local.h" #include "seq.h" #include "seq_midi_event.h" diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index 25fbaff2..39c1c4a9 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -4,10 +4,6 @@ * \brief Universal MIDI Protocol (UMP) Interface */ -#include -#include -#include -#include "local.h" #include "rawmidi_local.h" #include "ump_local.h" diff --git a/src/seq/seq.c b/src/seq/seq.c index 899dfe9f..fd8ca30e 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -777,8 +777,8 @@ void event_filter(snd_seq_t *seq, snd_seq_event_t *ev) */ -#include #include "seq_local.h" +#include /**************************************************************************** * * diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c index a51ebfb6..b74948c8 100644 --- a/src/seq/seq_hw.c +++ b/src/seq/seq_hw.c @@ -20,9 +20,9 @@ * */ +#include "seq_local.h" #include #include -#include "seq_local.h" #ifndef PIC /* entry for static linking */ diff --git a/src/seq/seq_local.h b/src/seq/seq_local.h index 9b4a6545..46824806 100644 --- a/src/seq/seq_local.h +++ b/src/seq/seq_local.h @@ -23,10 +23,10 @@ #ifndef __SEQ_LOCAL_H #define __SEQ_LOCAL_H +#include "local.h" #include #include #include -#include "local.h" #define SND_SEQ_OBUF_SIZE (16*1024) /* default size */ #define SND_SEQ_IBUF_SIZE 500 /* in event_size aligned */ diff --git a/src/seq/seq_midi_event.c b/src/seq/seq_midi_event.c index df09bde3..95a44e9b 100644 --- a/src/seq/seq_midi_event.c +++ b/src/seq/seq_midi_event.c @@ -28,10 +28,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "local.h" #if HAVE_MALLOC_H #include #endif -#include "local.h" #ifndef DOC_HIDDEN diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 55651f38..9ec93ee8 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -20,14 +20,12 @@ * */ -#include -#include +#include "seq_local.h" #include #include #include #include #include -#include "seq_local.h" /** * \brief queue controls - start/stop/continue diff --git a/src/userfile.c b/src/userfile.c index 4a740834..492ea9cb 100644 --- a/src/userfile.c +++ b/src/userfile.c @@ -18,7 +18,7 @@ * */ -#include +#include "config.h" #include #include #include From 96f60d829f2c9dc9ad9eda46410adaa41b4b0da0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Oct 2023 08:20:15 +0200 Subject: [PATCH 119/267] seq: Fix typos in symbol version definitions There were obvious typos in src/Versions.in that resulted in the undefined symbols. Correct those entries. Fixes: 2aefb5c41cc0 ("seq: Add UMP support") Closes: https://github.com/alsa-project/alsa-lib/issues/356 Signed-off-by: Takashi Iwai --- src/Versions.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Versions.in b/src/Versions.in index c8ac1c82..c53a99fa 100644 --- a/src/Versions.in +++ b/src/Versions.in @@ -158,13 +158,13 @@ ALSA_1.2.10 { @SYMBOL_PREFIX@snd_ctl_ump_block_info; @SYMBOL_PREFIX@snd_seq_ump_*; @SYMBOL_PREFIX@snd_seq_client_info_get_midi_version; - @SYMBOL_PREFIX@snd_seq_seq_client_info_get_ump_group_enabled; + @SYMBOL_PREFIX@snd_seq_client_info_get_ump_group_enabled; @SYMBOL_PREFIX@snd_seq_client_info_get_ump_groupless_enabled; - @SYMBOL_PREFIX@snd_seq_seq_client_get_ump_conversion; + @SYMBOL_PREFIX@snd_seq_client_info_get_ump_conversion; @SYMBOL_PREFIX@snd_seq_client_info_set_midi_version; - @SYMBOL_PREFIX@snd_seq_seq_client_info_set_ump_group_enabled; + @SYMBOL_PREFIX@snd_seq_client_info_set_ump_group_enabled; @SYMBOL_PREFIX@snd_seq_client_info_set_ump_groupless_enabled; - @SYMBOL_PREFIX@snd_seq_seq_client_set_ump_conversion; + @SYMBOL_PREFIX@snd_seq_client_info_set_ump_conversion; @SYMBOL_PREFIX@snd_seq_get_ump_endpoint_info; @SYMBOL_PREFIX@snd_seq_get_ump_block_info; @SYMBOL_PREFIX@snd_seq_set_ump_endpoint_info; From 915a71a2cdf6361f0fc77fa367a67910dc0288db Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 4 Nov 2023 10:05:39 +0100 Subject: [PATCH 120/267] seq: Fix invalid sanity-check in snd_seq_set_input_buffer_size() snd_seq_set_input_buffer_size() has an assert() call with packet_size, but it's still uninitialized at that point. Fix it with the real packet size. 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 fd8ca30e..5ec737a7 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -1269,9 +1269,9 @@ int snd_seq_set_input_buffer_size(snd_seq_t *seq, size_t size) size_t packet_size; assert(seq && seq->ibuf); + packet_size = get_packet_size(seq); assert(size >= packet_size); snd_seq_drop_input(seq); - packet_size = get_packet_size(seq); size = (size + packet_size - 1) / packet_size; if (size != seq->ibufsize) { char *newbuf; From f202ec3c23abf16a2382acc0de35900173e32160 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 28 Oct 2023 21:33:29 +0900 Subject: [PATCH 121/267] mixer: simple: Support dB TLVs for CTL_SINGLE controls dB mappings do not work for controls not named "* Volume", since we do not fall back to CTL_SINGLE in get_selem_ctl. Add that branch to make it work. Fixes dB ranges for e.g. controls named "* Gain". Closes: https://github.com/alsa-project/alsa-lib/pull/358 Signed-off-by: Hector Martin Signed-off-by: Jaroslav Kysela --- src/mixer/simple_none.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mixer/simple_none.c b/src/mixer/simple_none.c index 846b0ca9..dd03fcf1 100644 --- a/src/mixer/simple_none.c +++ b/src/mixer/simple_none.c @@ -1155,11 +1155,12 @@ static selem_ctl_t *get_selem_ctl(selem_none_t *s, int dir) c = &s->ctls[CTL_CAPTURE_VOLUME]; else return NULL; - if (! c->elem) { + if (! c->elem) c = &s->ctls[CTL_GLOBAL_VOLUME]; - if (! c->elem) - return NULL; - } + if (! c->elem) + c = &s->ctls[CTL_SINGLE]; + if (! c->elem) + return NULL; if (c->type != SND_CTL_ELEM_TYPE_INTEGER) return NULL; return c; From 2fca03e792ef1b740e8a7370fdd360d0b627c84c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 6 Nov 2023 16:27:11 +0100 Subject: [PATCH 122/267] seq: Clear UMP event flag for legacy apps It seems that some applications (at least Chrome WebMIDI) set random bits to the flags of event packet, and this confuses as if they were UMP-events, which are eventually filtered out. Although it's a bug of applications, it's better to avoid the regressions. So this patch forcibly clears the UMP flag of the incoming and outgoing events when the application is running in the legacy mode (i.e. midi_version = 0). Fixes: 2aefb5c41cc0 ("seq: Add UMP support") Closes: https://github.com/alsa-project/alsa-lib/issues/360 Signed-off-by: Takashi Iwai --- src/seq/seq.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/seq/seq.c b/src/seq/seq.c index 5ec737a7..643cf159 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -4161,6 +4161,13 @@ int snd_seq_event_output(snd_seq_t *seq, snd_seq_event_t *ev) return result; } +/* workaround for broken legacy apps that set UMP event bit unexpectedly */ +static void clear_ump_for_legacy_apps(snd_seq_t *seq, snd_seq_event_t *ev) +{ + if (!seq->midi_version && snd_seq_ev_is_ump(ev)) + ev->flags &= ~SNDRV_SEQ_EVENT_UMP; +} + /** * \brief output an event onto the lib buffer without draining buffer * \param seq sequencer handle @@ -4178,6 +4185,7 @@ int snd_seq_event_output_buffer(snd_seq_t *seq, snd_seq_event_t *ev) { int len; assert(seq && ev); + clear_ump_for_legacy_apps(seq, ev); len = snd_seq_event_length(ev); if (len < 0) return -EINVAL; @@ -4238,6 +4246,7 @@ int snd_seq_event_output_direct(snd_seq_t *seq, snd_seq_event_t *ev) ssize_t len; void *buf; + clear_ump_for_legacy_apps(seq, ev); len = snd_seq_event_length(ev); if (len < 0) return len; @@ -4374,6 +4383,7 @@ static int snd_seq_event_retrieve_buffer(snd_seq_t *seq, snd_seq_event_t **retp) snd_seq_event_t *ev; *retp = ev = (snd_seq_event_t *)(seq->ibuf + seq->ibufptr * packet_size); + clear_ump_for_legacy_apps(seq, ev); seq->ibufptr++; seq->ibuflen--; if (! snd_seq_ev_is_variable(ev)) From 94a5ddff9d5d85104755ee17b301c289a060cebf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 6 Nov 2023 16:33:59 +0100 Subject: [PATCH 123/267] seq: Simplify snd_seq_extract_output() Now that we never put UMP events on the output buffer in the legacy mode, the check and skip of UMP events are no longer necessary. It means that ump_allowed argument is meaningless in extract_output(), too. Let's drop the unnecessary check and move the code extract_output() into snd_seq_extract_output() again, and call this directly from snd_seq_ump_extract_output() for simplification. Signed-off-by: Takashi Iwai --- src/seq/seq.c | 52 ++++++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/src/seq/seq.c b/src/seq/seq.c index 643cf159..5eac4848 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -4308,36 +4308,6 @@ int snd_seq_drain_output(snd_seq_t *seq) return 0; } -static int extract_output(snd_seq_t *seq, snd_seq_event_t **ev_res, int ump_allowed) -{ - size_t len, olen; - assert(seq); - if (ev_res) - *ev_res = NULL; - repeat: - if ((olen = seq->obufused) < sizeof(snd_seq_event_t)) - return -ENOENT; - len = snd_seq_event_length((snd_seq_event_t *)seq->obuf); - if (olen < len) - return -ENOENT; - /* skip invalid UMP events */ - if (snd_seq_ev_is_ump((snd_seq_event_t *)seq->obuf) && !ump_allowed) { - seq->obufused -= len; - memmove(seq->obuf, seq->obuf + len, seq->obufused); - goto repeat; - } - if (ev_res) { - /* extract the event */ - if (alloc_tmpbuf(seq, len) < 0) - return -ENOMEM; - memcpy(seq->tmpbuf, seq->obuf, len); - *ev_res = (snd_seq_event_t *)seq->tmpbuf; - } - seq->obufused = olen - len; - memmove(seq->obuf, seq->obuf + len, seq->obufused); - return 0; -} - /** * \brief extract the first event in output buffer * \param seq sequencer handle @@ -4351,7 +4321,25 @@ static int extract_output(snd_seq_t *seq, snd_seq_event_t **ev_res, int ump_allo */ int snd_seq_extract_output(snd_seq_t *seq, snd_seq_event_t **ev_res) { - return extract_output(seq, ev_res, 0); + size_t len, olen; + assert(seq); + if (ev_res) + *ev_res = NULL; + if ((olen = seq->obufused) < sizeof(snd_seq_event_t)) + return -ENOENT; + len = snd_seq_event_length((snd_seq_event_t *)seq->obuf); + if (olen < len) + return -ENOENT; + if (ev_res) { + /* extract the event */ + if (alloc_tmpbuf(seq, len) < 0) + return -ENOMEM; + memcpy(seq->tmpbuf, seq->obuf, len); + *ev_res = (snd_seq_event_t *)seq->tmpbuf; + } + seq->obufused = olen - len; + memmove(seq->obuf, seq->obuf + len, seq->obufused); + return 0; } /*----------------------------------------------------------------*/ @@ -4547,7 +4535,7 @@ int snd_seq_ump_extract_output(snd_seq_t *seq, snd_seq_ump_event_t **ev_res) { if (!seq->midi_version) return -EBADFD; - return extract_output(seq, (snd_seq_event_t **)ev_res, 1); + return snd_seq_extract_output(seq, (snd_seq_event_t **)ev_res); } /** From ed6b07084bfea4155bbc98bcf38508ab81bdd008 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 6 Nov 2023 16:36:55 +0100 Subject: [PATCH 124/267] seq: Check protocol compatibility with the current version There is no need for checking the protocol compatibility with another version, but we just need to check for the current version. Signed-off-by: Takashi Iwai --- src/seq/seq_hw.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c index b74948c8..eeaf26e1 100644 --- a/src/seq/seq_hw.c +++ b/src/seq/seq_hw.c @@ -32,7 +32,6 @@ const char *_snd_module_seq_hw = ""; #ifndef DOC_HIDDEN #define SNDRV_FILE_SEQ ALSA_DEVICE_DIRECTORY "seq" #define SNDRV_FILE_ALOADSEQ ALOAD_DEVICE_DIRECTORY "aloadSEQ" -#define SNDRV_SEQ_VERSION_MAX SNDRV_PROTOCOL_VERSION(1, 0, 2) typedef struct { int fd; @@ -535,7 +534,7 @@ int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode) close(fd); return ret; } - if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_SEQ_VERSION_MAX)) { + if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_SEQ_VERSION)) { close(fd); return -SND_ERROR_INCOMPATIBLE_VERSION; } From f33284e85e57857199b873ff4e6b4e1eeeb71162 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 4 Aug 2023 10:07:19 +0200 Subject: [PATCH 125/267] pcm: Introduce snd_pcm_subformat_value() Allow userspace applications to select subformats easily just like in snd_pcm_format_t case - by string conversion. Closes: https://github.com/alsa-project/alsa-lib/pull/342 Signed-off-by: Cezary Rojewski Signed-off-by: Jaroslav Kysela --- include/pcm.h | 3 +++ src/pcm/pcm.c | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/include/pcm.h b/include/pcm.h index e7853313..f18620be 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -282,6 +282,8 @@ typedef enum _snd_pcm_format { /** PCM sample subformat */ typedef enum _snd_pcm_subformat { + /** Unknown */ + SND_PCM_SUBFORMAT_UNKNOWN = -1, /** Standard */ SND_PCM_SUBFORMAT_STD = 0, SND_PCM_SUBFORMAT_LAST = SND_PCM_SUBFORMAT_STD @@ -1092,6 +1094,7 @@ const char *snd_pcm_format_name(const snd_pcm_format_t format); const char *snd_pcm_format_description(const snd_pcm_format_t format); const char *snd_pcm_subformat_name(const snd_pcm_subformat_t subformat); const char *snd_pcm_subformat_description(const snd_pcm_subformat_t subformat); +snd_pcm_subformat_t snd_pcm_subformat_value(const char* name); snd_pcm_format_t snd_pcm_format_value(const char* name); const char *snd_pcm_tstamp_mode_name(const snd_pcm_tstamp_t mode); const char *snd_pcm_state_name(const snd_pcm_state_t state); diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index ad9e8956..8aae5afd 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -2197,6 +2197,30 @@ const char *snd_pcm_subformat_description(const snd_pcm_subformat_t subformat) return snd_pcm_subformat_descriptions[subformat]; } +/** + * \brief get PCM sample subformat from name + * \param name PCM sample subformat name (case insensitive) + * \return PCM sample subformat + */ +snd_pcm_subformat_t snd_pcm_subformat_value(const char* name) +{ + snd_pcm_subformat_t subformat; + + for (subformat = 0; subformat <= SND_PCM_SUBFORMAT_LAST; subformat++) { + if (snd_pcm_subformat_names[subformat] && + !strcasecmp(name, snd_pcm_subformat_names[subformat])) + return subformat; + } + + for (subformat = 0; subformat <= SND_PCM_SUBFORMAT_LAST; subformat++) { + if (snd_pcm_subformat_descriptions[subformat] && + !strcasecmp(name, snd_pcm_subformat_descriptions[subformat])) + return subformat; + } + + return SND_PCM_SUBFORMAT_UNKNOWN; +} + /** * \brief (DEPRECATED) get name of PCM start mode setting * \param mode PCM start mode From d8000f57fc458a46d1bdb5f6cedafd44fd52691f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 4 Aug 2023 10:13:40 +0200 Subject: [PATCH 126/267] pcm: Add MSBITS subformat options Improve granularity of format selection for S32/U32 formats by adding masks representing 20, 24 and 32 most significant bits. Closes: https://github.com/alsa-project/alsa-lib/pull/342 Signed-off-by: Cezary Rojewski Signed-off-by: Jaroslav Kysela --- include/pcm.h | 8 +++++++- include/sound/uapi/asound.h | 5 ++++- src/pcm/pcm.c | 6 ++++++ src/pcm/pcm_params.c | 5 ++++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/pcm.h b/include/pcm.h index f18620be..102ff812 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -286,7 +286,13 @@ typedef enum _snd_pcm_subformat { SND_PCM_SUBFORMAT_UNKNOWN = -1, /** Standard */ SND_PCM_SUBFORMAT_STD = 0, - SND_PCM_SUBFORMAT_LAST = SND_PCM_SUBFORMAT_STD + /** Maximum bits based on PCM format */ + SND_PCM_SUBFORMAT_MSBITS_MAX = 1, + /** 20 most significant bits */ + SND_PCM_SUBFORMAT_MSBITS_20 = 2, + /** 24 most significant bits */ + SND_PCM_SUBFORMAT_MSBITS_24 = 3, + SND_PCM_SUBFORMAT_LAST = SND_PCM_SUBFORMAT_MSBITS_24 } snd_pcm_subformat_t; /** PCM state */ diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index 32095ef7..f3b2b94d 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -274,7 +274,10 @@ typedef int __bitwise snd_pcm_format_t; typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_SUBFORMAT_STD ((snd_pcm_subformat_t) 0) -#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_STD +#define SNDRV_PCM_SUBFORMAT_MSBITS_MAX ((snd_pcm_subformat_t) 1) +#define SNDRV_PCM_SUBFORMAT_MSBITS_20 ((snd_pcm_subformat_t) 2) +#define SNDRV_PCM_SUBFORMAT_MSBITS_24 ((snd_pcm_subformat_t) 3) +#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_MSBITS_24 #define SNDRV_PCM_INFO_MMAP 0x00000001 /* hardware supports mmap */ #define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 8aae5afd..4bb5c759 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -2070,10 +2070,16 @@ static const char *const snd_pcm_type_names[] = { static const char *const snd_pcm_subformat_names[] = { SUBFORMAT(STD), + SUBFORMAT(MSBITS_MAX), + SUBFORMAT(MSBITS_20), + SUBFORMAT(MSBITS_24), }; static const char *const snd_pcm_subformat_descriptions[] = { SUBFORMATD(STD, "Standard"), + SUBFORMATD(MSBITS_MAX, "Maximum based on PCM format"), + SUBFORMATD(MSBITS_20, "20 most significant bits"), + SUBFORMATD(MSBITS_24, "24 most significant bits"), }; static const char *const snd_pcm_start_mode_names[] = { diff --git a/src/pcm/pcm_params.c b/src/pcm/pcm_params.c index 26c70132..c2f78634 100644 --- a/src/pcm/pcm_params.c +++ b/src/pcm/pcm_params.c @@ -2008,7 +2008,10 @@ static const snd_mask_t refine_masks[SND_PCM_HW_PARAM_LAST_MASK - SND_PCM_HW_PAR }, [SND_PCM_HW_PARAM_SUBFORMAT - SND_PCM_HW_PARAM_FIRST_MASK] = { .bits = { - PCM_BIT(SNDRV_PCM_SUBFORMAT_STD) + PCM_BIT(SNDRV_PCM_SUBFORMAT_STD) | + PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_MAX) | + PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_20) | + PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_24), }, }, }; From caab680d7efb2d2e8e77f48f104e789bb41cbd6d Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 4 Aug 2023 10:20:33 +0200 Subject: [PATCH 127/267] pcm: Fix incompatible-pointer-type warnings Update snd_pcm_subformat_t getters and setters so they no longer throw warnings when building. Closes: https://github.com/alsa-project/alsa-lib/pull/342 Signed-off-by: Cezary Rojewski Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 4bb5c759..dc59c50d 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -4495,7 +4495,7 @@ EXPORT_SYMBOL int INTERNAL(snd_pcm_hw_params_get_subformat)(const snd_pcm_hw_par int snd_pcm_hw_params_get_subformat(const snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat) #endif { - return snd_pcm_hw_param_get(params, SND_PCM_HW_PARAM_SUBFORMAT, subformat, NULL); + return snd_pcm_hw_param_get(params, SND_PCM_HW_PARAM_SUBFORMAT, (unsigned int *)subformat, NULL); } /** @@ -4535,7 +4535,7 @@ EXPORT_SYMBOL int INTERNAL(snd_pcm_hw_params_set_subformat_first)(snd_pcm_t *pcm int snd_pcm_hw_params_set_subformat_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat) #endif { - return snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, subformat, NULL); + return snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, (unsigned int *)subformat, NULL); } /** @@ -4551,7 +4551,7 @@ EXPORT_SYMBOL int INTERNAL(snd_pcm_hw_params_set_subformat_last)(snd_pcm_t *pcm, int snd_pcm_hw_params_set_subformat_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat) #endif { - return snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, subformat, NULL); + return snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, (unsigned int *)subformat, NULL); } /** From 454638ef30b06a28ed6e42d5d4608498a8874ac1 Mon Sep 17 00:00:00 2001 From: Zeno Endemann Date: Fri, 24 Nov 2023 13:49:49 +0100 Subject: [PATCH 128/267] pcm: clarify documentation on some hw params related functions Closes: https://github.com/alsa-project/alsa-lib/pull/369 Signed-off-by: Zeno Endemann Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index dc59c50d..0cce108f 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -894,6 +894,7 @@ int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info) * \param pcm PCM handle * \param params Configuration space definition container * \return 0 on success otherwise a negative error code + * \retval -EBADFD no hardware configuration is set */ int snd_pcm_hw_params_current(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { @@ -960,6 +961,8 @@ int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) /** \brief Remove PCM hardware configuration and free associated resources * \param pcm PCM handle * \return 0 on success otherwise a negative error code + * + * The function will also report success if no configuration is set. */ int snd_pcm_hw_free(snd_pcm_t *pcm) { @@ -3929,6 +3932,11 @@ int snd_pcm_hw_params_get_fifo_size(const snd_pcm_hw_params_t *params) * * The configuration space will be filled with all possible ranges * for the PCM device. + * + * Note that the configuration space may be constrained by the + * currently installed configuration on the PCM device. To remove + * any constrains, free the configuration with #snd_pcm_hw_free + * first. */ int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { From cd04da2bcfe147b08b8db2930b2294aead0a8968 Mon Sep 17 00:00:00 2001 From: Zeno Endemann Date: Fri, 24 Nov 2023 14:33:47 +0100 Subject: [PATCH 129/267] pcm: clarify documentation of poll descriptor usage This is based on my understanding of the intended behavior, the test/pcm.c example code, as well as the github pull request discussion (#370). There needs to be more clarifiaction regarding the exact semantics of the value of the revents output parameter of snd_pcm_poll_descriptors_revents, since there are events that do not necessarily correspond to POLLIN or POLLOUT (such as period events), but I believe this is a lot less obvious and needs confirmation first. Closes: https://github.com/alsa-project/alsa-lib/pull/370 Signed-off-by: Zeno Endemann Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 0cce108f..bc7e26fd 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -1811,6 +1811,11 @@ static int __snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, * corresponding FD_SET arrays and demangle events using * \link ::snd_pcm_poll_descriptors_revents() \endlink . * + * It is guaranteed that for the given PCM handle, the output poll + * descriptor structs (and their count) will never change, thus it is + * valid to call the function once and reuse its output for the + * lifetime of the PCM device. + * * The function is thread-safe when built with the proper option. */ int snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space) @@ -1846,6 +1851,13 @@ static int __snd_pcm_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, * Note: Even if multiple poll descriptors are used (i.e. pfds > 1), * this function returns only a single event. * + * The passed in count of poll descriptors must be equal to + * \link ::snd_pcm_poll_descriptors_count() \endlink and the passed in array + * must match the array returned by \link ::snd_pcm_poll_descriptors() \endlink + * (in its full length and original order) with the revent fields updated + * according to the poll() result. This function will not modify the file + * descriptor or event field of any element of the given poll descriptor array. + * * The function is thread-safe when built with the proper option. */ int snd_pcm_poll_descriptors_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) From 0e0a92bde2a70142aef9e9921d08a11d9d4a59a9 Mon Sep 17 00:00:00 2001 From: Mingjie Shen Date: Wed, 15 Nov 2023 16:40:07 -0500 Subject: [PATCH 130/267] aserver: fix buffer overwriting name array should allocate space for the null terminator. Also, need to check if client->name has enough space for strcpy. Closes: https://github.com/alsa-project/alsa-lib/pull/364 Signed-off-by: Mingjie Shen Signed-off-by: Jaroslav Kysela --- aserver/aserver.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aserver/aserver.c b/aserver/aserver.c index 1742f628..50ee3992 100644 --- a/aserver/aserver.c +++ b/aserver/aserver.c @@ -738,7 +738,7 @@ static int snd_client_open(client_t *client) ans.result = -EINVAL; goto _answer; } - name = alloca(req.namelen); + name = alloca(req.namelen + 1); err = read(client->ctrl_fd, name, req.namelen); if (err < 0) { SYSERROR("read failed"); @@ -775,6 +775,10 @@ static int snd_client_open(client_t *client) name[req.namelen] = '\0'; client->transport_type = req.transport_type; + if (sizeof(client->name) < (size_t)(req.namelen + 1)) { + ans.result = -ENOMEM; + goto _answer; + } strcpy(client->name, name); client->stream = req.stream; client->mode = req.mode; From 3d4230aa8c74e4f8282c3fca17c408d0074931ac Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 12 Dec 2023 13:41:30 +0100 Subject: [PATCH 131/267] configure: bumb version to 1.2.11pre1 (for aplay/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 225d86ec..f86011f1 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.10) +AC_INIT(alsa-lib, 1.2.11pre1) AC_CONFIG_SRCDIR([src/control/control.c]) AC_CONFIG_MACRO_DIR([m4]) From 60d6fad711dc1083ce4021173f02a33ffe5030df Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 19 Dec 2023 18:37:56 +0100 Subject: [PATCH 132/267] control: remap - fix the endless loop in remap_numid_child_new() Avoid the new numid search for the maped only controls. Link: https://lore.kernel.org/alsa-devel/20231219164539.GB14858@ediswmail.ad.cirrus.com/ Reported-by: Charles Keepax Signed-off-by: Jaroslav Kysela --- src/control/control_remap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/control/control_remap.c b/src/control/control_remap.c index 6fcb01f1..81cf38e3 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -148,7 +148,7 @@ static snd_ctl_numid_t *remap_numid_child_new(snd_ctl_remap_t *priv, unsigned in if (numid_child == 0) return NULL; - if (remap_find_numid_app(priv, numid_child)) { + if (priv->numid_remap_active && remap_find_numid_app(priv, numid_child)) { while (remap_find_numid_app(priv, priv->numid_app_last)) priv->numid_app_last++; numid_app = priv->numid_app_last; @@ -222,6 +222,7 @@ static snd_ctl_map_t *remap_find_map_numid(snd_ctl_remap_t *priv, unsigned int n } return NULL; } + static snd_ctl_map_t *remap_find_map_id(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id) { size_t count; From d4992c36cc962bd2b3cef7158af173a95177ba2e Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 2 Jan 2024 10:52:33 +0100 Subject: [PATCH 133/267] pcm: fix the documentation for snd_pcm_poll_descriptors again As noted in the github issue, the poll descriptors may change depending on the stream parameters. Fixes: cd04da2b ("pcm: clarify documentation of poll descriptor usage") Fixes: https://github.com/alsa-project/alsa-lib/issues/372 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index bc7e26fd..75e81c03 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -1812,9 +1812,10 @@ static int __snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, * \link ::snd_pcm_poll_descriptors_revents() \endlink . * * It is guaranteed that for the given PCM handle, the output poll - * descriptor structs (and their count) will never change, thus it is - * valid to call the function once and reuse its output for the - * lifetime of the PCM device. + * descriptor structs (and their count) will not change after + * hardware and software parameters setup. Thus it is valid to call + * the function once when all parameters are set and reuse its output + * for the lifetime of the stream parameters. * * The function is thread-safe when built with the proper option. */ From 19e63491821d755e491fe0e6d579bac455c3e8a5 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 2 Jan 2024 11:03:30 +0100 Subject: [PATCH 134/267] pcm: plug plugin - fast_ops may be changed when sw_params are set Link: https://github.com/alsa-project/alsa-lib/issues/372 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_plug.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index abf6f1ab..e5a3a189 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -1065,6 +1065,18 @@ static int snd_pcm_plug_hw_free(snd_pcm_t *pcm) return err; } +static int snd_pcm_plug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) +{ + snd_pcm_plug_t *plug = pcm->private_data; + snd_pcm_t *slave = plug->gen.slave; + int err = snd_pcm_sw_params(slave, params); + if (err >= 0) { + pcm->fast_ops = slave->fast_ops; + pcm->fast_op_arg = slave->fast_op_arg; + } + return err; +} + static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out) { snd_pcm_plug_t *plug = pcm->private_data; @@ -1078,7 +1090,7 @@ static const snd_pcm_ops_t snd_pcm_plug_ops = { .hw_refine = snd_pcm_plug_hw_refine, .hw_params = snd_pcm_plug_hw_params, .hw_free = snd_pcm_plug_hw_free, - .sw_params = snd_pcm_generic_sw_params, + .sw_params = snd_pcm_plug_sw_params, .channel_info = snd_pcm_generic_channel_info, .dump = snd_pcm_plug_dump, .nonblock = snd_pcm_generic_nonblock, From c3fec78dddc09d82cbceb0dfa1d09d25f05aa92d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 2 Jan 2024 11:16:26 +0100 Subject: [PATCH 135/267] pcm: handle start_treshold in snd_pcm_write_areas more robustly The start_treshold is defined as unsigned, so fix the condition to allow using values avobe LONG_MAX. Also, fix the documentation for the 64-bit platforms (INTMAX -> LONG_MAX). Fixes: https://github.com/alsa-project/alsa-lib/issues/374 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 75e81c03..e8986ad8 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -378,7 +378,7 @@ be started automatically from the device. For capture, if the application wants to read count of samples equal or greater then the stream will be started. If you want to use explicit start (#snd_pcm_start), you can set this value greater than ring buffer size (in samples), but use the -constant MAXINT is not a bad idea. +constant LONG_MAX or the boundary value is not a bad idea. \par Stop threshold @@ -7721,7 +7721,8 @@ snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area /* some plugins might automatically start the stream */ state = __snd_pcm_state(pcm); if (state == SND_PCM_STATE_PREPARED && - hw_avail >= (snd_pcm_sframes_t) pcm->start_threshold) { + hw_avail >= 0 && + (snd_pcm_uframes_t) hw_avail >= pcm->start_threshold) { err = __snd_pcm_start(pcm); if (err < 0) goto _end; From 31f2ab57e2ba350ece69696b00a48bceca22d7e7 Mon Sep 17 00:00:00 2001 From: Zeno Endemann Date: Mon, 1 Jan 2024 17:59:46 +0100 Subject: [PATCH 136/267] pcm: documentation improvement mostly regarding samples and frames In some places samples and frames are used interchangeably, which may be a little confusing, so update it to say 'frames' where it could matter. Closes: https://github.com/alsa-project/alsa-lib/pull/375 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 52 +++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index e8986ad8..bd819a00 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -96,7 +96,7 @@ standard C open function - see 'man 2 open'). In non-blocked behaviour, these I/O functions never stops, they return -EAGAIN error code, when no data can be transferred (the ring buffer is full in our case). In blocked behaviour, these I/O functions stop and wait until there is a room in the -ring buffer (playback) or until there are a new samples (capture). The ALSA +ring buffer (playback) or until there are new samples (capture). The ALSA implementation can be found in the \ref alsa_pcm_rw section. \subsection pcm_transfer_event Event waiting routines @@ -351,9 +351,9 @@ enumeration. These parameters - #snd_pcm_sw_params_t can be modified at any time including the running state. -\par Minimum available count of samples +\par Minimum available count of frames -This parameter controls the wakeup point. If the count of available samples +This parameter controls the wakeup point. If the count of available frames is equal or greater than this value, then application will be activated. \par Timestamp mode @@ -372,29 +372,29 @@ is ignored by device. Usually, this value is set to one (no align). \par Start threshold The start threshold parameter is used to determine the start point in -stream. For playback, if samples in ring buffer is equal or greater than -the start threshold parameters and the stream is not running, the stream will -be started automatically from the device. For capture, if the application wants -to read count of samples equal or greater then the stream will be started. -If you want to use explicit start (#snd_pcm_start), you can -set this value greater than ring buffer size (in samples), but use the -constant LONG_MAX or the boundary value is not a bad idea. +stream. For playback, if the frame count in the ring buffer is equal or greater +than the start threshold parameter and the stream is not running, the stream +will be started automatically from the device. For capture, if the application +wants to read count of frames equal or greater then the stream will be started. +If you want to use explicit start (#snd_pcm_start), you can set this value +greater than the ring buffer size (in frames). For that simply using a large +constant such as LONG_MAX or the boundary value is not a bad idea. \par Stop threshold Similarly, the stop threshold parameter is used to automatically stop -the running stream, when the available samples crosses this boundary. +the running stream, when the available frames crosses this boundary. It means, for playback, the empty samples in ring buffer and for capture, the filled (used) samples in ring buffer. \par Silence threshold -The silence threshold specifies count of samples filled with silence -ahead of the current application pointer for playback. It is usable -for applications when an overrun is possible (like tasks depending on -network I/O etc.). If application wants to manage the ahead samples itself, -the #snd_pcm_rewind() function allows to forget the last -samples in the stream. +The silence threshold specifies the count of frames before an underrun when the +buffer gets filled with frames of silence according to the silence size parameter +ahead of the current application pointer for playback. It is usable for applications +when an underrun is possible (like tasks depending on network I/O etc.). If +application wants to manage the ahead samples itself, the #snd_pcm_rewind() function +allows to forget the last samples in the stream. \section pcm_status Obtaining stream status @@ -402,11 +402,11 @@ The stream status is stored in #snd_pcm_status_t structure. These parameters can be obtained: the current stream state - #snd_pcm_status_get_state(), timestamp of trigger - #snd_pcm_status_get_trigger_tstamp(), timestamp of last -pointer update #snd_pcm_status_get_tstamp(), delay in samples - -#snd_pcm_status_get_delay(), available count in samples - -#snd_pcm_status_get_avail(), maximum available samples - +pointer update #snd_pcm_status_get_tstamp(), delay in frames - +#snd_pcm_status_get_delay(), available count in frames - +#snd_pcm_status_get_avail(), maximum available frames - #snd_pcm_status_get_avail_max(), ADC over-range count in -samples - #snd_pcm_status_get_overrange(). The last two +frames - #snd_pcm_status_get_overrange(). The last two parameters - avail_max and overrange are reset to zero after the status call. @@ -414,7 +414,7 @@ call.

The function #snd_pcm_avail_update() updates the current -available count of samples for writing (playback) or filled samples for +available count of frames for writing (playback) or filled frames for reading (capture). This call is mandatory for updating actual r/w pointer. Using standalone, it is a light method to obtain current stream position, because it does not require the user <-> kernel context switch, but the value @@ -427,10 +427,10 @@ The function #snd_pcm_avail() reads the current hardware pointer in the ring buffer from hardware and calls #snd_pcm_avail_update() then.

-The function #snd_pcm_delay() returns the delay in samples. -For playback, it means count of samples in the ring buffer before -the next sample will be sent to DAC. For capture, it means count of samples -in the ring buffer before the next sample will be captured from ADC. It works +The function #snd_pcm_delay() returns the delay in frames. +For playback, it means count of frames in the ring buffer before +the next frames will be sent to DAC. For capture, it means count of frames +in the ring buffer before the next frames will be captured from ADC. It works only when the stream is in the running or draining (playback only) state. Note that this function does not update the current r/w pointer for applications, so the function #snd_pcm_avail_update() must be called afterwards From 55c633829073dd4c53a8ef984b609d0e50316a68 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 13 Nov 2023 12:51:56 +0100 Subject: [PATCH 137/267] seq: Add snd_seq_ump_ev_clear() Like snd_seq_ev_clear(), let's add a helper to clear the event record for a UMP-capable sequencer event. While we're at it, change snd_seq_ev_clear() to be a static inline function, so that we make sure that the proper pointer type is passed. Signed-off-by: Takashi Iwai --- include/seqmid.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/include/seqmid.h b/include/seqmid.h index da346999..4464c2d3 100644 --- a/include/seqmid.h +++ b/include/seqmid.h @@ -45,8 +45,21 @@ extern "C" { * * This macro clears the given event record pointer to the default status. */ -#define snd_seq_ev_clear(ev) \ - memset(ev, 0, sizeof(snd_seq_event_t)) +static inline void snd_seq_ev_clear(snd_seq_event_t *ev) +{ + memset(ev, 0, sizeof(*ev)); +} + +/** + * \brief initialize event record for UMP + * \param ev event record pointer + * + * This macro clears the given UMP event record pointer to the default status. + */ +static inline void snd_seq_ump_ev_clear(snd_seq_ump_event_t *ev) +{ + memset(ev, 0, sizeof(*ev)); +} /** * \brief set the tag for given event From dc7da761f3a2b09f5249c26019e9572d5d0200e1 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 23 Jan 2024 16:16:41 +0100 Subject: [PATCH 138/267] topology: separate Versions linker script A slightly different solution is preferred than PR recommends (bellow). Only symbols with snd_ prefix should be public. Create a minimal Versions file for libatopology. Link: https://github.com/alsa-project/alsa-lib/pull/376 Signed-off-by: Jaroslav Kysela --- src/topology/Makefile.am | 2 +- src/topology/Versions | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/topology/Versions diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index 9f48891f..f4dab76d 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -1,7 +1,7 @@ COMPATNUM=@LIBTOOL_VERSION_INFO@ if VERSIONED_SYMBOLS -VSYMS = -Wl,--version-script=../Versions +VSYMS = -Wl,--version-script=Versions else VSYMS = endif diff --git a/src/topology/Versions b/src/topology/Versions new file mode 100644 index 00000000..84c67f19 --- /dev/null +++ b/src/topology/Versions @@ -0,0 +1,7 @@ +ALSA_1.2.1 { + global: + snd_*; + + local: + *; +}; From c9055730033ebdc96475e0b2fd8896496a9733ff Mon Sep 17 00:00:00 2001 From: Nicholas Vinson Date: Sat, 6 Jan 2024 22:54:56 -0500 Subject: [PATCH 139/267] configure.ac: Update AC_OUTPUT() function Modern autoconf practice says AC_OUTPUT() should be called with no arguments and generated configuration files should be specified by calling AC_CONFIG_FILES() before AC_OUTPUT(). Update configure.ac to follow this practice. Closes: https://github.com/alsa-project/alsa-lib/pull/376 Signed-off-by: Nicholas Vinson Signed-off-by: Jaroslav Kysela --- configure.ac | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/configure.ac b/configure.ac index f86011f1..9db527c3 100644 --- a/configure.ac +++ b/configure.ac @@ -747,22 +747,24 @@ if test ! -L "$srcdir"/include/alsa ; then ln -sf . "$srcdir"/include/alsa fi -AC_OUTPUT(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \ - include/Makefile include/sound/Makefile include/sound/uapi/Makefile \ - src/Versions src/Makefile \ - src/control/Makefile src/mixer/Makefile \ - 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/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 \ - test/Makefile test/lsb/Makefile \ - utils/Makefile utils/alsa-lib.spec utils/alsa.pc utils/alsa-topology.pc) +AC_CONFIG_FILES(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \ + include/Makefile include/sound/Makefile include/sound/uapi/Makefile \ + src/Versions src/Makefile \ + src/control/Makefile src/mixer/Makefile \ + 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/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 \ + test/Makefile test/lsb/Makefile \ + utils/Makefile utils/alsa-lib.spec utils/alsa.pc utils/alsa-topology.pc) + +AC_OUTPUT() dnl Create asoundlib.h dynamically according to configure options echo "Creating asoundlib.h..." From 10ff172202a664f3668d74276d1c2aaf6ac54f72 Mon Sep 17 00:00:00 2001 From: Nicholas Vinson Date: Mon, 1 Jan 2024 16:10:47 -0500 Subject: [PATCH 140/267] src/Versions.in: Add guards for opt. alisp symbols Add guards for optional alisp symbols. This ensures that the Versions file does not contain undefined symbols when building alisp support is disabled. Fixes Gentoo bugs 914511 (https://bugs.gentoo.org/914511), 914643 (https://bugs.gentoo.org/914643), and 919417 (https://bugs.gentoo.org/919417). Fixes bug #305 Fixes alsa_lisp symbol error reported in bug #356 Closes: https://github.com/alsa-project/alsa-lib/pull/376 Fixes: https://github.com/alsa-project/alsa-lib/issues/305 Link: https://github.com/alsa-project/alsa-lib/issues/356 Signed-off-by: Nicholas Vinson Signed-off-by: Jaroslav Kysela --- configure.ac | 2 +- src/Makefile.am | 11 ++++++++++- src/{Versions.in => Versions.in.in} | 4 ++++ 3 files changed, 15 insertions(+), 2 deletions(-) rename src/{Versions.in => Versions.in.in} (99%) diff --git a/configure.ac b/configure.ac index 9db527c3..024e70b0 100644 --- a/configure.ac +++ b/configure.ac @@ -749,7 +749,7 @@ fi AC_CONFIG_FILES(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \ include/Makefile include/sound/Makefile include/sound/uapi/Makefile \ - src/Versions src/Makefile \ + src/Versions.in src/Makefile \ src/control/Makefile src/mixer/Makefile \ src/pcm/Makefile src/pcm/scopes/Makefile \ src/rawmidi/Makefile src/timer/Makefile \ diff --git a/src/Makefile.am b/src/Makefile.am index df46dbc4..b4a0d5d9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,10 +1,11 @@ -EXTRA_DIST=Versions COMPATNUM=@LIBTOOL_VERSION_INFO@ if VERSIONED_SYMBOLS VSYMS = -Wl,--version-script=Versions +BUILT_SOURCES = $(top_builddir)/src/Versions else VSYMS = +BUILT_SOURCES = endif if SYMBOLIC_FUNCTIONS @@ -13,6 +14,8 @@ else SYMFUNCS = endif +VERSION_CPPFLAGS = + lib_LTLIBRARIES = libasound.la libasound_la_SOURCES = conf.c confeval.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c @@ -43,6 +46,9 @@ 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 @@ -51,6 +57,9 @@ libasound_la_LIBADD += @ALSA_DEPLIBS@ libasound_la_LDFLAGS = -version-info $(COMPATNUM) $(VSYMS) $(SYMFUNCS) $(LDFLAGS_NOUNDEFINED) +$(top_builddir)/src/Versions: $(top_builddir)/src/Versions.in + $(COMPILE) -E $(VERSION_CPPFLAGS) -x assembler-with-cpp -o $@ $< + control/libcontrol.la: $(MAKE) -C control libcontrol.la diff --git a/src/Versions.in b/src/Versions.in.in similarity index 99% rename from src/Versions.in rename to src/Versions.in.in index c53a99fa..041eff08 100644 --- a/src/Versions.in +++ b/src/Versions.in.in @@ -118,15 +118,19 @@ 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 { From 14308adb0dddda73fe102c8eb92d84b5a4a5cd5c Mon Sep 17 00:00:00 2001 From: Nicholas Vinson Date: Wed, 3 Jan 2024 20:24:36 -0500 Subject: [PATCH 141/267] src/Versions.in: Add guards for pcm and timer syms Configure script allows for the disabling of the pcm module. When disabled, the pcm symbols will not be available. Add guards to remove symbols from version map when not present. Closes: https://github.com/alsa-project/alsa-lib/pull/376 Signed-off-by: Nicholas Vinson Signed-off-by: Jaroslav Kysela --- src/Makefile.am | 3 +++ src/Versions.in.in | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/Makefile.am b/src/Makefile.am index b4a0d5d9..ad5bf8ef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,6 +26,9 @@ SUBDIRS += mixer libasound_la_LIBADD += mixer/libmixer.la endif if BUILD_PCM +if VERSIONED_SYMBOLS +VERSION_CPPFLAGS += -DHAVE_PCM_SYMS -DHAVE_TIMER_SYMS +endif SUBDIRS += pcm timer libasound_la_LIBADD += pcm/libpcm.la timer/libtimer.la endif diff --git a/src/Versions.in.in b/src/Versions.in.in index 041eff08..98f36ded 100644 --- a/src/Versions.in.in +++ b/src/Versions.in.in @@ -16,6 +16,7 @@ ALSA_0.9 { }; ALSA_0.9.0rc4 { +#ifdef HAVE_PCM_SYMS global: @SYMBOL_PREFIX@snd_pcm_hw_params_get_access; @@ -42,6 +43,7 @@ ALSA_0.9.0rc4 { @SYMBOL_PREFIX@snd_pcm_hw_params_get_rate_max; @SYMBOL_PREFIX@snd_pcm_hw_params_set_rate_near; @SYMBOL_PREFIX@snd_pcm_hw_params_set_rate_first; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_rate_last; @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_time; @@ -85,22 +87,29 @@ ALSA_0.9.0rc4 { @SYMBOL_PREFIX@snd_pcm_hw_params_set_tick_time_near; @SYMBOL_PREFIX@snd_pcm_hw_params_set_tick_time_first; @SYMBOL_PREFIX@snd_pcm_hw_params_set_tick_time_last; +#endif } ALSA_0.9; ALSA_0.9.0rc8 { +#ifdef HAVE_PCM_SYMS global: @SYMBOL_PREFIX@snd_pcm_forward; @SYMBOL_PREFIX@snd_pcm_status_get_trigger_htstamp; @SYMBOL_PREFIX@snd_pcm_status_get_htstamp; +#endif } ALSA_0.9.0rc4; ALSA_0.9.0 { +#if defined HAVE_PCM_SYMS || defined HAVE_TIMER_SYMS global: +#if defined HAVE_PCM_SYMS @SYMBOL_PREFIX@snd_pcm_type_name; +#endif +#ifdef HAVE_TIMER_SYMS @SYMBOL_PREFIX@snd_timer_query_info; @SYMBOL_PREFIX@snd_timer_query_params; @SYMBOL_PREFIX@snd_timer_query_status; @@ -108,6 +117,8 @@ ALSA_0.9.0 { @SYMBOL_PREFIX@snd_timer_params_get_exclusive; @SYMBOL_PREFIX@snd_timer_params_set_filter; @SYMBOL_PREFIX@snd_timer_params_get_filter; +#endif +#endif } ALSA_0.9.0rc8; ALSA_0.9.3 { @@ -146,11 +157,13 @@ ALSA_1.2.6 { } ALSA_1.1.6; ALSA_1.2.9 { +#ifdef HAVE_PCM_SYMS global: @SYMBOL_PREFIX@snd_pcm_hw_params_is_perfect_drain; @SYMBOL_PREFIX@snd_pcm_hw_params_set_drain_silence; @SYMBOL_PREFIX@snd_pcm_hw_params_get_drain_silence; +#endif } ALSA_1.2.6; ALSA_1.2.10 { From 5801f1b861c49e68651b49e8fd0a4fbe559c178e Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 24 Jan 2024 17:58:48 +0100 Subject: [PATCH 142/267] .gitignore: add src/Versions.in to ignore file list Signed-off-by: Jaroslav Kysela --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 88d6b6ad..a74b5195 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 src/Versions +src/Versions.in src/conf/topology/sklrt286/data/pvt_data src/control/ctl_symbols_list.c src/pcm/pcm_symbols_list.c From b44eff91c9bce380f69ae4e3f52548e72ee52265 Mon Sep 17 00:00:00 2001 From: Ryan Underwood Date: Sat, 13 May 2023 17:16:34 -0500 Subject: [PATCH 143/267] conf: pcm: Set C-Media USB 7.1 sound card (ICUSBAUDIO7D) to six_channel for surround40 This 7.1 analog sound card identifies as the following: 0d8c:0102 C-Media Electronics, Inc. CM106 Like Sound Device (lsusb) 1 [ICUSBAUDIO7D ]: USB-Audio - ICUSBAUDIO7D (/proc/asound/cards) It has the following four-channel analog stream layout: Interface 1 Altset 3 Format: S16_LE Channels: 4 Endpoint: 6 OUT (ADAPTIVE) Rates: 44100, 48000 Bits: 16 Channel map: FL FR FC LFE (/proc/asound/card1/stream0) This layout makes it impossible to play audio to the surround (SL/SR) speakers plugged into the "Surround" jack in 4 channel mode. Instead, set six_channel so that the six-channel layout will be used to access those speakers: Interface 1 Altset 4 Format: S16_LE Channels: 6 Endpoint: 6 OUT (ADAPTIVE) Rates: 44100, 48000 Bits: 16 Channel map: FL FR FC LFE SL SR Tested with speaker-test -Dsurround40:CARD=ICUSBAUDIO7D,DEV=0 -c4 The speaker system that required surround40 in order to downmix LFE into the front/rear mix is a Klipsch ProMedia v.2-400, which has 4 speakers and a subwoofer with an internal crossover, connected via front and rear stereo analog cables. Closes: https://github.com/alsa-project/alsa-lib/pull/319 Signed-off-by: Ryan Underwood 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 839e61cb..80631b2e 100644 --- a/src/conf/cards/USB-Audio.conf +++ b/src/conf/cards/USB-Audio.conf @@ -27,6 +27,7 @@ USB-Audio.pcm.use_dmix { USB-Audio.pcm.surround40_type { "AudioPhile" two_stereo_devices "Audiophile USB (tm)" two_stereo_devices + "ICUSBAUDIO7D" six_channels "OmniStudio" two_stereo_devices "Quattro" two_stereo_devices "SB Audigy 2 NX" six_channels From f0658e6dbd94b887a6a86e47831734b5ee900dbc Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 24 Jan 2024 18:08:09 +0100 Subject: [PATCH 144/267] add back Versions source file to EXTRA_DIST to fix packaging Signed-off-by: Jaroslav Kysela --- src/Makefile.am | 1 + src/topology/Makefile.am | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Makefile.am b/src/Makefile.am index ad5bf8ef..74a108dc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,3 +1,4 @@ +EXTRA_DIST = Versions.in.in COMPATNUM=@LIBTOOL_VERSION_INFO@ if VERSIONED_SYMBOLS diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index f4dab76d..04299588 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -1,3 +1,4 @@ +EXTRA_DIST = Versions COMPATNUM=@LIBTOOL_VERSION_INFO@ if VERSIONED_SYMBOLS From 55a758a964d430faaa6c9cff112ee6227775df4b Mon Sep 17 00:00:00 2001 From: q66 Date: Thu, 7 Sep 2023 23:13:01 +0200 Subject: [PATCH 145/267] pcm: sofvol plugin: fix signed overflow This fixes functionality on specific setups with UBSan. Closes: https://github.com/alsa-project/alsa-lib/pull/351 Signed-off-by: q66 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_softvol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pcm/pcm_softvol.c b/src/pcm/pcm_softvol.c index 75f2aa0a..38c63679 100644 --- a/src/pcm/pcm_softvol.c +++ b/src/pcm/pcm_softvol.c @@ -113,7 +113,7 @@ static inline int MULTI_DIV_32x16(int a, unsigned short b) y.i = 0; #if __BYTE_ORDER == __LITTLE_ENDIAN x.i = (unsigned short)v.s[0]; - x.i *= b; + x.i *= (unsigned int)b; y.s[0] = x.s[1]; y.i += (int)v.s[1] * b; #else From a69ac97e4d5f3416158e12d97778f6fb098fbb52 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 24 Jan 2024 18:20:44 +0100 Subject: [PATCH 146/267] pcm: route plugin: allocate temporary array on stack only one time Fixes: https://github.com/alsa-project/alsa-lib/pull/363 Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_route.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c index 38057cb2..affb929f 100644 --- a/src/pcm/pcm_route.c +++ b/src/pcm/pcm_route.c @@ -1151,6 +1151,11 @@ static int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_ent snd_config_iterator_t i, inext; unsigned int k; int err; + + long *scha = alloca(tt_ssize * sizeof(long)); + if (scha == NULL) + return -ENOMEM; + for (k = 0; k < tt_csize * tt_ssize; ++k) ttable[k] = 0.0; snd_config_for_each(i, inext, tt) { @@ -1172,7 +1177,6 @@ static int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_ent snd_config_t *jnode = snd_config_iterator_entry(j); double value; int ss; - long *scha = alloca(tt_ssize * sizeof(long)); const char *id; if (snd_config_get_id(jnode, &id) < 0) continue; From 84768748431fa550897a0f90af7dbb09b1817152 Mon Sep 17 00:00:00 2001 From: Tim Blechmann Date: Mon, 5 Dec 2022 14:42:58 +0800 Subject: [PATCH 147/267] headers: avoid c++ keyword `class` is a c++ keyword, let's try not to use them in public headers Closes: https://github.com/alsa-project/alsa-lib/pull/286 Signed-off-by: Tim Blechmann Signed-off-by: Jaroslav Kysela --- include/mixer_abst.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/mixer_abst.h b/include/mixer_abst.h index ea8c096e..ec23e0dd 100644 --- a/include/mixer_abst.h +++ b/include/mixer_abst.h @@ -97,10 +97,10 @@ struct sm_elem_ops { int snd_mixer_selem_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2); -int snd_mixer_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info); -void *snd_mixer_sbasic_get_private(const snd_mixer_class_t *class); -void snd_mixer_sbasic_set_private(const snd_mixer_class_t *class, void *private_data); -void snd_mixer_sbasic_set_private_free(const snd_mixer_class_t *class, void (*private_free)(snd_mixer_class_t *class)); +int snd_mixer_sbasic_info(const snd_mixer_class_t *mixer_class, sm_class_basic_t *info); +void *snd_mixer_sbasic_get_private(const snd_mixer_class_t *mixer_class); +void snd_mixer_sbasic_set_private(const snd_mixer_class_t *mixer_class, void *private_data); +void snd_mixer_sbasic_set_private_free(const snd_mixer_class_t *mixer_class, void (*private_free)(snd_mixer_class_t *mixer_class)); /** \} */ From f48dd3a751fa875fe675b6a47d54ed05beed6c9c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 29 Jan 2024 17:12:32 +0100 Subject: [PATCH 148/267] topology: fix Versions file The exported symbols were tagged with ALSA_0.9 version previously. We cannot use a different version (regressions). Signed-off-by: Jaroslav Kysela --- src/topology/Versions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/topology/Versions b/src/topology/Versions index 84c67f19..9c2ed843 100644 --- a/src/topology/Versions +++ b/src/topology/Versions @@ -1,4 +1,4 @@ -ALSA_1.2.1 { +ALSA_0.9 { global: snd_*; From ffed4f342678c31bf0b9edfe184be5f3de41603a Mon Sep 17 00:00:00 2001 From: Zeno Endemann Date: Mon, 29 Jan 2024 15:02:44 +0100 Subject: [PATCH 149/267] pcm: document interaction of drain silence and sw silence I'm not sure this is the best way to describe the interaction, but I think this needs to be mentioned in some form at least. Closes: https://github.com/alsa-project/alsa-lib/pull/381 Signed-off-by: Zeno Endemann Signed-off-by: Jaroslav Kysela --- src/pcm/pcm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index bd819a00..ab3bbda7 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -6895,6 +6895,10 @@ int snd_pcm_sw_params_get_silence_threshold(const snd_pcm_sw_params_t *params, s * underrun is nearer than silence threshold (see * #snd_pcm_sw_params_set_silence_threshold) * + * When drain silence (see #snd_pcm_hw_params_get_drain_silence) is disabled, + * this will also apply for draining, i.e. silence is written also when the + * drain end is nearer than the silence threshold. + * * The special case is when silence size value is equal or greater than * boundary. The unused portion of the ring buffer (initial written samples * are untouched) is filled with silence at start. Later, only just processed From 7e3a3c2b0a092d0f568ba3c98365030dd91cc877 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 29 Jan 2024 17:16:07 +0100 Subject: [PATCH 150/267] Release v1.2.11 Signed-off-by: Jaroslav Kysela --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 024e70b0..7a152a4a 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.11pre1) +AC_INIT(alsa-lib, 1.2.11) AC_CONFIG_SRCDIR([src/control/control.c]) AC_CONFIG_MACRO_DIR([m4]) From 2a736a0d2543f206fd2653aaae8a08a4c42eb917 Mon Sep 17 00:00:00 2001 From: Jan Palus Date: Tue, 30 Jan 2024 14:24:25 +0100 Subject: [PATCH 151/267] 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 152/267] 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 153/267] 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 154/267] 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 155/267] 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 156/267] 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 157/267] 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 158/267] 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 159/267] 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 160/267] 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 161/267] 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 162/267] 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 163/267] 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 164/267] 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 165/267] 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 166/267] 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 167/267] 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 168/267] 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 169/267] 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 170/267] 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 171/267] 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 172/267] 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 173/267] 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 174/267] 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 175/267] 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 176/267] 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 177/267] 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 178/267] 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 179/267] 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 180/267] 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 181/267] 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 182/267] 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 183/267] 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 184/267] 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 185/267] 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 186/267] 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 187/267] 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 188/267] 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 189/267] 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 190/267] 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 191/267] 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 192/267] 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 193/267] 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 194/267] 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 195/267] 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 196/267] 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 197/267] .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 198/267] 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 199/267] 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 200/267] 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 201/267] 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 202/267] 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 203/267] 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 204/267] 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 205/267] 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 206/267] 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 207/267] 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 208/267] 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 209/267] 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 210/267] 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 211/267] 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 212/267] 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 213/267] 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 214/267] 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 215/267] 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 216/267] 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 217/267] 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 218/267] 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 219/267] 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 220/267] 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 221/267] 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 222/267] 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 223/267] 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 224/267] 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 225/267] 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 226/267] 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 227/267] 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 228/267] 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 229/267] 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 230/267] 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 231/267] 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 232/267] 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 233/267] 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 234/267] 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 235/267] 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 236/267] 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 237/267] 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 238/267] 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 239/267] 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 240/267] 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 241/267] 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 242/267] 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 243/267] 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 244/267] 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 245/267] 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 246/267] 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 247/267] 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 248/267] 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 249/267] 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 250/267] 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 251/267] 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 252/267] 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 253/267] 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 254/267] 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 255/267] 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 256/267] 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 257/267] 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 258/267] 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 259/267] 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 260/267] 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 261/267] 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 262/267] 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 263/267] 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 264/267] 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 265/267] 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 266/267] 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 267/267] 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)++;