Compare commits

...

97 commits

Author SHA1 Message Date
Jaroslav Kysela
d62b1d5407 conf: fix parse_array_def override code path
Some checks failed
Build alsa-lib / fedora_latest_build (push) Has been cancelled
Build alsa-lib / ubuntu_last_build (push) Has been cancelled
The error may cause segmentation fault and incorrect behaviour.

Closes: https://github.com/alsa-project/alsa-lib/issues/477
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-09-18 11:00:47 +02:00
Adam Sampson
3a97718124 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 <ats@offog.org>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-07-31 15:31:40 +02:00
Jochen Sprickerhof
782b0597c2 ucm: use close_range on _GNU_SOURCE
Closes: https://github.com/alsa-project/alsa-lib/pull/459
Signed-off-by: Jochen Sprickerhof <git@jochen.sprickerhof.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-07-31 15:30:51 +02:00
wyjstrong
4ad4d9590a Force to use alphasort64() sorting function for Harmony OS
Closes: https://github.com/alsa-project/alsa-lib/pull/467
Signed-off-by: wyjstrong <wyjstrong@163.com>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-07-31 15:29:21 +02:00
Kai Vehmanen
5f524e3004 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 <kai.vehmanen@linux.intel.com>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-07-31 15:25:18 +02:00
Jaroslav Kysela
ee5a58f48e ucm: regex: fix the error message (missing argument)
Link: https://github.com/alsa-project/alsa-ucm-conf/pull/580
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-06-26 11:33:15 +02:00
Daniel Dadap
07ec2ad34c 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 <ddadap@nvidia.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2025-05-22 09:04:55 +02:00
Jaroslav Kysela
8df60992b7 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 <perex@perex.cz>
2025-05-09 09:55:00 +02:00
Jaroslav Kysela
368ea9af82 github: fix Fedora workflow (awk package dependency)
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-04-24 15:16:12 +02:00
Jaroslav Kysela
8ab0228f51 Revert "ucm: do not bump syntax version to 8"
This reverts commit e51cba0973.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-04-24 13:25:38 +02:00
Jaroslav Kysela
81d70a2713 Release v1.2.14
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-04-14 18:42:04 +02:00
Jaroslav Kysela
c748afe123 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 <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-04-14 18:41:42 +02:00
Jaroslav Kysela
8291d2c601 add snd_strlcat() function
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-04-14 18:41:42 +02:00
Jaroslav Kysela
e51cba0973 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 <perex@perex.cz>
2025-04-14 09:44:42 +02:00
Jaroslav Kysela
6073f53051 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 <perex@perex.cz>
2025-04-08 16:28:38 +02:00
Jaroslav Kysela
8b75db9676 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 <kgroeneveld@lenbrook.com>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-04-08 11:20:04 +02:00
Jaroslav Kysela
15f2b27612 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 <perex@perex.cz>
2025-04-07 18:53:02 +02:00
Kevin Groeneveld
7e01443ecc 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 <kgroeneveld@lenbrook.com>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-04-07 18:49:52 +02:00
Jaroslav Kysela
a4e47461ec seq: update_group_ports - rewrite blknames update
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 <perex@perex.cz>
2025-04-07 18:46:32 +02:00
Simon Howard
647c001321 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 <fraggle@soulsphere.org>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-04-07 18:36:54 +02:00
Felix Wolfsteller
0d2acc2084 pcm: fix minor typos in doc
Closes: https://github.com/alsa-project/alsa-lib/pull/446
Signed-off-by: Felix Wolfsteller <felix.wolfsteller@gmail.com>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-04-07 18:36:32 +02:00
Takashi Iwai
90eb3c1e96 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: 6167b8ce3e ("seq: Add API helper functions for creating UMP Endpoint and Blocks")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2025-04-01 08:10:27 +02:00
Takashi Iwai
95d0274b60 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: 6167b8ce3e ("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 <tiwai@suse.de>
2025-04-01 08:10:27 +02:00
Jaroslav Kysela
e088d08c76 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 <perex@perex.cz>
2025-03-24 11:50:52 +01:00
Jaroslav Kysela
d8300e5cb7 ucm: add '${LibCaps}' substitution
It is a preparation for future checking of alsa-lib's extensions.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-03-21 11:41:09 +01:00
Jaroslav Kysela
70f4c95df5 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 <perex@perex.cz>
2025-03-21 11:26:32 +01:00
Jaroslav Kysela
d5f19bcabc 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 <perex@perex.cz>
2025-03-21 10:39:09 +01:00
Jaroslav Kysela
42b8f1299f control: remap - separate event handling from map (preparation for sync)
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-03-21 09:43:04 +01:00
Jaroslav Kysela
fb285b366f seq: shuffle calloc arguments in snd_seq_hw_open (gcc warning)
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-03-21 09:42:35 +01:00
Jaroslav Kysela
1b9a073e6a 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 <perex@perex.cz>
2025-03-20 21:55:54 +01:00
Jaroslav Kysela
6855cb838d 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 <perex@perex.cz>
2025-03-19 17:55:33 +01:00
Jaroslav Kysela
e9e3c01ff7 ucm: format @@SyntaxVersion to 4 digits
It is better for regex matching.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-03-19 12:20:27 +01:00
Jaroslav Kysela
8f6fef8b1a ucm: enhance documentation (sys-card + ranges + more)
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-03-19 12:05:34 +01:00
Jaroslav Kysela
12f6790910 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 <perex@perex.cz>
2025-03-19 11:54:50 +01:00
Jaroslav Kysela
7fbd47ce79 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 <perex@perex.cz>
2025-03-13 18:28:05 +01:00
Takashi Iwai
0c7086777a 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: 6167b8ce3e ("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 <tiwai@suse.de>
2025-03-01 11:00:15 +01:00
Takashi Iwai
df8f1cc1ec 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: 2aefb5c41c ("seq: Add UMP support")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2025-02-17 12:06:09 +01:00
Jaroslav Kysela
e550dcdd90 utils: add missing alsa-topology.pc.in to EXTRA_DIST
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-02-03 11:54:12 +01:00
Jaroslav Kysela
305168fd22 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 <perex@perex.cz>
2025-02-02 22:28:17 +01:00
Jaroslav Kysela
0bb35d4980 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 <perex@perex.cz>
2025-02-02 22:26:42 +01:00
Jaroslav Kysela
995e279081 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 <perex@perex.cz>
2025-02-02 22:00:54 +01:00
Jaroslav Kysela
1101c397aa doc: fix permissions
Doxygen create also subdirectories. Make sure that files
in those directories have required permissions.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2025-02-02 21:51:25 +01:00
Jaroslav Kysela
c8bc54a9ca 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 <perex@perex.cz>
2025-02-02 19:22:05 +01:00
Jaroslav Kysela
a7de48692f 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 <perex@perex.cz>
2025-02-02 18:59:06 +01:00
Jaroslav Kysela
ea8972c83b 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 <perex@perex.cz>
2025-02-02 18:56:47 +01:00
Takashi Iwai
35d2efefa9 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 <tiwai@suse.de>
2025-01-14 16:47:37 +01:00
Takashi Iwai
cdd5a9fa10 rawmidi: Make rawmidi flag bits doxygen-style comments
We forgot to put the markers in rawmidi info bit flags.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2025-01-14 16:47:34 +01:00
Takashi Iwai
137eca7720 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 <tiwai@suse.de>
2025-01-14 16:46:50 +01:00
Takashi Iwai
e1cf4d3f68 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 <tiwai@suse.de>
2025-01-14 16:44:47 +01:00
Takashi Iwai
3fe7d9a49b 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 <tiwai@suse.de>
2025-01-14 16:44:43 +01:00
Jaroslav Kysela
352cbc5eb9 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 <perex@perex.cz>
2024-11-25 16:18:42 +01:00
Jaroslav Kysela
76d2d285c1 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 <perex@perex.cz>
2024-11-25 15:14:42 +01:00
Takashi Iwai
07cee0ba05 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 <tiwai@suse.de>
2024-11-24 09:37:03 +01:00
Takashi Iwai
6880219ad4 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 <tiwai@suse.de>
2024-11-24 09:32:29 +01:00
Nicholas Vinson
76edab4e59 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 769d1db1b0 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 <nvinson234@gmail.com>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2024-11-15 09:27:10 +01:00
Jaroslav Kysela
785fd327ad Release v1.2.13
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2024-11-12 10:36:52 +01:00
Jaroslav Kysela
49295a4e17 control: remap - clarify comments and docs
Fix copy-n-paste errors.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2024-11-12 10:32:56 +01:00
Jaroslav Kysela
4e7a510095 control: remap - fix copy-n-paste in _snd_ctl_remap_open's comment
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2024-10-24 11:08:00 +02:00
Jaroslav Kysela
a3865b2439 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 <perex@perex.cz>
2024-10-21 09:31:16 +02:00
Jaroslav Kysela
073dc7577a configure: bumb version to 1.2.13pre1 (for alsa-utils)
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2024-09-19 11:55:58 +02:00
Jaroslav Kysela
93d7645d84 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 <perex@perex.cz>
2024-09-06 12:58:14 +02:00
Jaroslav Kysela
513ef7ace4 github: use upload-artifacts@v4
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2024-09-06 12:53:03 +02:00
Arkadiusz Bokowy
9ac93d1252 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 <arkadiusz.bokowy@gmail.com>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2024-09-06 10:51:06 +02:00
Takashi Iwai
fc58f8fcc3 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: 982786e9eb ("Fix bogus pause flag on dmix")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-09-06 10:15:46 +02:00
Takashi Iwai
2adc30e983 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: 6d1d620ead ("pcm: dmix: resume workaround for buggy driver")
Reported-and-tested-by: Chancel Liu <chancel.liu@nxp.com>
Closes: https://lore.kernel.org/DB9PR04MB94988752ED7C43B399E0BC00E3942@DB9PR04MB9498.eurprd04.prod.outlook.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-09-06 10:13:29 +02:00
Jaroslav Kysela
3b9f3b9431 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 <perex@perex.cz>
2024-09-06 09:58:47 +02:00
Takashi Iwai
645668dca2 src/Versions.in: Add the new snd_pcm_hw_params_get_sync for 1.2.13
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-08-20 16:21:33 +02:00
Takashi Iwai
f81236cf7e 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 <tiwai@suse.de>
2024-08-20 16:16:47 +02:00
Jaroslav Kysela
b154d9145f 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 <perex@perex.cz>
2024-08-20 15:43:30 +02:00
Takashi Iwai
9b6dfb3eb6 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 <tiwai@suse.de>
2024-08-20 09:41:53 +02:00
Takashi Iwai
f08f4aceec .gitignore: Add test/umpinfo
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-08-15 16:43:10 +02:00
Takashi Iwai
3e38164ee5 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 <tiwai@suse.de>
2024-08-15 16:41:53 +02:00
Takashi Iwai
fa673b719c 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: 81b0cf46d1 ("control: Add UMP Endpoint and Block info query support")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-08-15 16:26:33 +02:00
Takashi Iwai
769d1db1b0 seq: Correct a typo in documentation
The right function name is snd_seq_has_queue_tempo_base().

Fixes: 24c7f42733 ("seq: Add API functions to set different tempo base values")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-08-07 15:50:21 +02:00
Takashi Iwai
ddc4c668ba 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 <tiwai@suse.de>
2024-08-07 15:48:26 +02:00
Takashi Iwai
d969439813 seq: Avoid strlcat()
strlcat() isn't available in every system, so better to avoid it.
Rewrite the code without strlcat().

Fixes: 6167b8ce3e ("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 <tiwai@suse.de>
2024-07-31 11:20:16 +02:00
Takashi Iwai
f090a93ea8 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 <tiwai@suse.de>
2024-07-28 10:55:00 +02:00
Takashi Iwai
28948f2fcc 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 <tiwai@suse.de>
2024-07-28 10:54:53 +02:00
Takashi Iwai
530e2f8131 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: 30f8ba74c5 ("ump_msg: Add definitions for Utility, Stream and Flex Data messages")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-19 14:30:12 +02:00
Takashi Iwai
48101de6fa 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 <tiwai@suse.de>
2024-07-09 07:23:23 +02:00
Takashi Iwai
24c7f42733 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 <tiwai@suse.de>
2024-07-05 17:12:44 +02:00
Takashi Iwai
568b2ac1db 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 <tiwai@suse.de>
2024-07-04 14:44:00 +02:00
Takashi Iwai
ade099fab7 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 <tiwai@suse.de>
2024-07-04 14:42:25 +02:00
Takashi Iwai
30f8ba74c5 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 <tiwai@suse.de>
2024-07-04 14:42:03 +02:00
Takashi Iwai
8734673c21 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: 040356ecf0 ("ump: Add helpers to parse / set UMP packet data")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-06-26 16:53:17 +02:00
Takashi Iwai
32e2c8d8a2 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 <tiwai@suse.de>
2024-06-26 11:46:35 +02:00
Takashi Iwai
2071eb8a44 src/Versions.in: Add new seq / rawmidi functions for UMP
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-06-26 11:39:06 +02:00
Takashi Iwai
f8df023597 ump: Add descriptions for UMP RawMidi interface
Also update doxygen config accordingly.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-06-26 11:25:32 +02:00
Takashi Iwai
18579adfbc ump_msg.h: Fix doxygen comments
Correct the lack of markers and missing comments for some unions.

Fixes: 040356ecf0 ("ump: Add helpers to parse / set UMP packet data")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-06-26 11:22:06 +02:00
Takashi Iwai
8da704ef4f seq: Correct section descriptions for UMP
The usage of \section and \subsection were wrong in the previous
commit, where the tags were missing.

Fixes: 3390f31664 ("seq: Add description about MIDI 2.0 and UMP handling")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-06-26 11:20:18 +02:00
Takashi Iwai
3390f31664 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 <tiwai@suse.de>
2024-06-25 11:00:29 +02:00
Takashi Iwai
6da898cf40 ump: Fix doxygen error for snd_ump_endpoint_info_set_protocol()
Fix a typo that caused a doxygen error.

Fixes: 6767f623ca ("ump: Add missing *_set variants for snd_ump_endpoint_info and snd_ump_block_info")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-06-25 11:00:29 +02:00
Jaroslav Kysela
ef38bff00e 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 <perex@perex.cz>
2024-06-21 16:32:10 +02:00
Takashi Iwai
cacc5bd5c1 Add test/seq-ump-example to .gitignore
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-06-19 17:25:57 +02:00
Takashi Iwai
f784035a90 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 <tiwai@suse.de>
2024-06-19 17:24:50 +02:00
Takashi Iwai
6167b8ce3e 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 <tiwai@suse.de>
2024-06-19 17:12:24 +02:00
Takashi Iwai
6767f623ca 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 <tiwai@suse.de>
2024-06-18 16:01:04 +02:00
85 changed files with 3097 additions and 5988 deletions

View file

@ -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:
@ -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/

3
.gitignore vendored
View file

@ -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
@ -65,6 +64,8 @@ test/playmidi1
test/queue_timer
test/rawmidi
test/seq
test/seq-ump-example
test/timer
test/umpinfo
test/lsb/config
test/lsb/midi_event

View file

@ -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

View file

@ -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

View file

@ -1,110 +0,0 @@
/*
* ALSA lisp implementation
* Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
*
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#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;
}

View file

@ -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")

View file

@ -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)

View file

@ -1 +0,0 @@
(princ "itest.lisp file included!\n")

View file

@ -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 <perex@perex.cz>
; 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)

View file

@ -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.14)
AC_CONFIG_SRCDIR([src/control/control.c])
AC_CONFIG_MACRO_DIR([m4])
@ -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 <<EOF
Please, do not enable PIC (--with-pic) for static library. Consider
to remove this option or use PIE flags.
EOF
exit 1
fi
dnl ALSA configuration directory
AC_ARG_WITH(configdir,
AS_HELP_STRING([--with-configdir=dir],
@ -410,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"])
@ -474,6 +479,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])
@ -481,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])
@ -619,6 +629,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])
@ -757,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)
@ -811,6 +822,7 @@ test "$build_pcm" = "yes" && echo "#include <alsa/timer.h>" >> include/asoundlib
test "$build_hwdep" = "yes" && echo "#include <alsa/hwdep.h>" >> include/asoundlib.h
echo "#include <alsa/control.h>" >> include/asoundlib.h
test "$build_mixer" = "yes" && echo "#include <alsa/mixer.h>" >> include/asoundlib.h
test "$build_seq" = "yes" && echo "#include <alsa/ump_msg.h>" >> include/asoundlib.h
test "$build_seq" = "yes" && echo "#include <alsa/seq_event.h>" >> include/asoundlib.h
test "$build_seq" = "yes" && echo "#include <alsa/seq.h>" >> include/asoundlib.h
test "$build_seq" = "yes" && echo "#include <alsa/seqmid.h>" >> include/asoundlib.h

View file

@ -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/*

View file

@ -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 \

View file

@ -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

View file

@ -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
@ -57,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

View file

@ -1,55 +0,0 @@
/*
* ALSA lisp implementation
* Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
*
*
* 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);

View file

@ -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 <alsa/asoundlib.h>, <alsa/asoundef.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_ASOUNDEF_H
#define __ALSA_ASOUNDEF_H

View file

@ -38,3 +38,5 @@
#include <poll.h>
#include <errno.h>
#include <stdarg.h>
#include <stdint.h>
#include <time.h>

View file

@ -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 <alsa/asoundlib.h>
#endif
#ifndef __ALSA_CONF_H
#define __ALSA_CONF_H

View file

@ -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 <alsa/asoundlib.h>, <alsa/control.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_CONTROL_H
#define __ALSA_CONTROL_H

View file

@ -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 <alsa/asoundlib.h>
#endif
#ifdef __cplusplus
extern "C" {

View file

@ -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 <alsa/asoundlib.h>, <alsa/error.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_ERROR_H
#define __ALSA_ERROR_H
@ -40,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);

View file

@ -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 <alsa/asoundlib.h>, <alsa/global.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_GLOBAL_H_
#define __ALSA_GLOBAL_H_
/* for timeval and timespec */
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif

View file

@ -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 <alsa/asoundlib.h>, <alsa/hwdep.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_HWDEP_H
#define __ALSA_HWDEP_H

View file

@ -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 <alsa/asoundlib.h>, <alsa/input.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_INPUT_H
#define __ALSA_INPUT_H

View file

@ -30,6 +30,9 @@
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <stdint.h>
/* for timeval and timespec */
#include <time.h>
#ifdef HAVE_ENDIAN_H
#include <endian.h>
#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"
@ -208,6 +212,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
@ -262,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

View file

@ -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 <alsa/asoundlib.h>, <alsa/mixer.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_MIXER_H
#define __ALSA_MIXER_H

View file

@ -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 <alsa/asoundlib.h>, <alsa/input.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_OUTPUT_H
#define __ALSA_OUTPUT_H
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif

View file

@ -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 <alsa/asoundlib.h>, <alsa/pcm.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_PCM_H
#define __ALSA_PCM_H
@ -33,8 +39,6 @@
extern "C" {
#endif
#include <stdint.h>
/**
* \defgroup PCM PCM Interface
* See the \ref pcm page for more details.
@ -508,6 +512,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 +754,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;

View file

@ -26,7 +26,9 @@
#ifndef __ALSA_PCM_EXTERNAL_H
#define __ALSA_PCM_EXTERNAL_H
#include "pcm.h"
#ifndef __ASOUNDLIB_LOCAL
#include <alsa/asoundlib.h>
#endif
#ifdef __cplusplus
extern "C" {

View file

@ -28,6 +28,11 @@
*
*/
#if !defined(__ALSA_PCM_EXTERNAL_H) && !defined(ALSA_LIBRARY_BUILD)
#warning "use #include <alsa/pcm_external.h>, <alsa/pcm_extplug.h> should not be used directly"
#include <alsa/pcm_external.h>
#endif
#ifndef __ALSA_PCM_EXTPLUG_H
#define __ALSA_PCM_EXTPLUG_H

View file

@ -28,6 +28,11 @@
*
*/
#if !defined(__ALSA_PCM_EXTERNAL_H) && !defined(ALSA_LIBRARY_BUILD)
#warning "use #include <alsa/pcm_external.h>, <alsa/pcm_ioplug.h> should not be used directly"
#include <alsa/pcm_external.h>
#endif
#ifndef __ALSA_PCM_IOPLUG_H
#define __ALSA_PCM_IOPLUG_H

View file

@ -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 <alsa/asoundlib.h>, <alsa/pcm_old.h> should not be used directly"
#endif
/*
* Old ALSA 0.9.x API
*/

View file

@ -31,6 +31,10 @@
#ifndef __ALSA_PCM_RATE_H
#define __ALSA_PCM_RATE_H
#ifndef __ASOUNDLIB_LOCAL
#include <alsa/asoundlib.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif

View file

@ -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 <alsa/asoundlib.h>, <alsa/rawmidi.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_RAWMIDI_H
#define __ALSA_RAWMIDI_H
@ -94,7 +100,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 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 +131,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);

View file

@ -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 <alsa/asoundlib.h>, <alsa/seq.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_SEQ_H
#define __ALSA_SEQ_H
@ -250,8 +256,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 */
@ -318,6 +324,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 +341,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);
@ -506,13 +514,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);
/*
*/

View file

@ -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 <alsa/asoundlib.h>, <alsa/seq_event.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_SEQ_EVENT_H
#define __ALSA_SEQ_EVENT_H
@ -133,6 +139,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) */
@ -292,6 +303,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 */
@ -304,6 +321,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 */

View file

@ -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 <alsa/asoundlib.h>, <alsa/seq_midi_event.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_SEQ_MIDI_EVENT_H
#define __ALSA_SEQ_MIDI_EVENT_H

View file

@ -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 <alsa/asoundlib.h>, <alsa/seqmid.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_SEQMID_H
#define __ALSA_SEQMID_H
@ -520,6 +526,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

View file

@ -3,22 +3,6 @@
* Main header file for the ALSA sequencer
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
* (c) 1998-1999 by Jaroslav Kysela <perex@perex.cz>
*
*
* 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
@ -26,7 +10,7 @@
#include <sound/asound.h>
/** version of the sequencer */
#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 3)
#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 5)
/**
* definition of sequencer event types
@ -108,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
@ -269,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;
@ -281,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 */
@ -477,6 +471,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
@ -539,11 +535,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 */
};
@ -616,7 +613,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

View file

@ -3,22 +3,6 @@
* Advanced Linux Sound Architecture - ALSA - Driver
* Copyright (c) 1994-2003 by Jaroslav Kysela <perex@perex.cz>,
* Abramo Bagnara <abramo@alsa-project.org>
*
*
* 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 <linux/types.h>
#include <asm/byteorder.h>
#else
#include <sys/endian.h>
#include <endian.h>
#include <sys/ioctl.h>
#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,11 +414,12 @@ 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 */
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 {
@ -442,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) */
@ -577,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 */
@ -709,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,
@ -721,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 */
@ -733,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)
@ -759,7 +762,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;
@ -807,7 +810,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
@ -843,7 +846,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)
@ -860,7 +863,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,
@ -885,6 +888,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 */
@ -963,6 +967,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)
@ -979,6 +995,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
@ -1064,7 +1082,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)
@ -1161,7 +1179,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)

View file

@ -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 <alsa/asoundlib.h>, <alsa/timer.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_TIMER_H
#define __ALSA_TIMER_H

View file

@ -6,6 +6,12 @@
* 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 <alsa/asoundlib.h>, <alsa/ump.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_UMP_H
#define __ALSA_UMP_H
@ -13,6 +19,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 */
@ -69,6 +81,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
@ -77,6 +92,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 +109,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 */
@ -110,6 +140,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
@ -118,11 +151,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 +167,21 @@ 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

View file

@ -6,18 +6,26 @@
* 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 <alsa/asoundlib.h>, <alsa/ump_msg.h> should not be used directly"
#include <alsa/asoundlib.h>
#endif
#ifndef __ALSA_UMP_MSG_H
#define __ALSA_UMP_MSG_H
#include <stdint.h>
#ifdef __cplusplus
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 */
@ -36,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 */
@ -55,16 +63,16 @@ 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 */
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 */
@ -74,16 +82,16 @@ 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 */
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 */
@ -93,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 */
@ -112,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 */
@ -131,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 */
@ -150,15 +158,15 @@ 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 */
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 */
@ -167,16 +175,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,9 +195,9 @@ 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
#if __BYTE_ORDER == __BIG_ENDIAN
uint8_t type:4; /**< UMP packet type */
uint8_t group:4; /**< UMP Group */
uint8_t status:4; /**< Status */
@ -214,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 */
@ -237,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 */
@ -266,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 */
@ -289,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 */
@ -312,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 */
@ -335,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 */
@ -364,14 +372,14 @@ 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 */
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,20 +387,20 @@ 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
#if __BYTE_ORDER == __BIG_ENDIAN
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) */
uint32_t data; /**< Data (32bit) */
#else
uint16_t reserved; /**< Unused */
uint8_t channel:4; /**< Channel */
@ -400,11 +408,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 */
@ -416,7 +424,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,27 +435,417 @@ 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;
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;
/** Stream Message (generic) (128bit) */
typedef struct _snd_ump_msg_stream_gen {
#if __BYTE_ORDER == __BIG_ENDIAN
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 {
#if __BYTE_ORDER == __BIG_ENDIAN
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 {
#if __BYTE_ORDER == __BIG_ENDIAN
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 {
#if __BYTE_ORDER == __BIG_ENDIAN
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 {
#if __BYTE_ORDER == __BIG_ENDIAN
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 Key Signature Message (128bit) */
typedef struct _snd_ump_msg_set_key_sig {
#if __BYTE_ORDER == __BIG_ENDIAN
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 {
#if __BYTE_ORDER == __BIG_ENDIAN
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_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 */
} 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 {
#if __BYTE_ORDER == __BIG_ENDIAN
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 {
#if __BYTE_ORDER == __BIG_ENDIAN
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 {
#if __BYTE_ORDER == __BIG_ENDIAN
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
*/
@ -511,6 +908,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,
@ -567,6 +970,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
*/
@ -655,8 +1112,21 @@ 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)
{
#if __BYTE_ORDER == __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);
#ifdef __cplusplus
}

View file

@ -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,18 +45,12 @@ endif
if BUILD_SEQ
SUBDIRS += seq
libasound_la_LIBADD += seq/libseq.la
VERSION_CPPFLAGS += -DHAVE_SEQ_SYMS
endif
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@
@ -100,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

View file

@ -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 {
@ -169,10 +159,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,4 +185,32 @@ 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_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;
@SYMBOL_PREFIX@snd_seq_queue_tempo_get_tempo_base;
@SYMBOL_PREFIX@snd_seq_queue_tempo_set_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
#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_*;
@SYMBOL_PREFIX@snd_ump_packet_length;
#endif
} ALSA_1.2.10;

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -1,151 +0,0 @@
/*
* ALSA lisp implementation
* Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
*
* 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<<ALISP_OBJ_PAIR_HASH_SHIFT)
#define ALISP_OBJ_PAIR_HASH_MASK (ALISP_OBJ_PAIR_HASH_SIZE-1)
#define ALISP_FREE_OBJ_POOL 512 /* free objects above this pool */
struct alisp_instance {
int verbose: 1,
warning: 1,
debug: 1;
/* i/o */
snd_input_t *in;
snd_output_t *out;
snd_output_t *eout; /* error output */
snd_output_t *vout; /* verbose output */
snd_output_t *wout; /* warning output */
snd_output_t *dout; /* debug output */
/* lexer */
int charno;
int lineno;
int lex_buf[ALISP_LEX_BUF_MAX];
int *lex_bufp;
char *token_buffer;
int token_buffer_max;
int thistoken;
/* object allocator / storage */
long free_objs;
long used_objs;
long max_objs;
struct list_head free_objs_list;
struct list_head used_objs_list[ALISP_OBJ_PAIR_HASH_SIZE][ALISP_OBJ_LAST_SEARCH + 1];
/* set object */
struct list_head setobjs_list[ALISP_OBJ_PAIR_HASH_SIZE];
};

View file

@ -1,936 +0,0 @@
/*
* ALSA lisp implementation - sound related commands
* Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
*
*
* 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 }
};

View file

@ -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

View file

@ -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)++;
@ -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;
@ -4139,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

View file

@ -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

View file

@ -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)

View file

@ -1,11 +0,0 @@
;
; SiS SI7018 mixer abstract layer
;
; Copyright (c) 2003 Jaroslav Kysela <perex@perex.cz>
; 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
)

View file

@ -1,11 +0,0 @@
;
; SiS SI7018 mixer abstract layer
;
; Copyright (c) 2003 Jaroslav Kysela <perex@perex.cz>
; 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
)

View file

@ -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)
)

View file

@ -57,6 +57,7 @@ CMI8786 cards.CMI8788
CMI8787 cards.CMI8788
pistachio cards.pistachio-card
VC4-HDMI cards.vc4-hdmi
hda-acpi cards.HDA-Intel
<confdir:ctl/default.conf>
<confdir:pcm/default.conf>

View file

@ -1,115 +0,0 @@
;
; Toplevel configuration for the ALSA Ordinary Mixer Interface
;
; Copyright (c) 2003 Jaroslav Kysela <perex@perex.cz>
; 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"))

View file

@ -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;
}
/**

View file

@ -2,11 +2,11 @@
* \file control/control_remap.c
* \brief CTL Remap Plugin Interface
* \author Jaroslav Kysela <perex@perex.cz>
* \date 2021
* \date 2021-2025
*/
/*
* Control - Remap Controls
* Copyright (c) 2021 by Jaroslav Kysela <perex@perex.cz>
* Copyright (c) 2021-2025 by Jaroslav Kysela <perex@perex.cz>
*
*
* This library is free software; you can redistribute it and/or modify
@ -29,6 +29,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
@ -76,28 +77,51 @@ 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;
} 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 {
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 {
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 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;
size_t event_queue_tail;
snd_ctl_map_event_t *event_queue;
} snd_ctl_remap_t;
#endif
@ -172,6 +196,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;
@ -291,18 +332,102 @@ 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 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;
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++)
free(map->controls[idx2].channel_map);
free(map->controls);
}
free(priv->map_read_queue);
free(priv->event_queue);
free(priv->sync);
free(priv->map);
free(priv->remap);
free(priv->numid);
@ -347,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;
@ -367,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;
}
@ -459,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;
@ -467,12 +613,17 @@ 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);
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);
}
@ -481,7 +632,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 +652,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];
}
@ -522,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;
@ -530,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);
@ -544,7 +726,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 +746,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)) {
@ -597,6 +798,37 @@ 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_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++) {
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;
@ -605,6 +837,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);
@ -794,19 +1029,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) {
@ -817,20 +1078,51 @@ 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)
changed = 1;
}
if (changed)
event_add(priv, &map->map_id, map->map_id.numid, event_mask);
}
}
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(&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);
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);
}
}
}
@ -840,18 +1132,19 @@ 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;
unsigned int numid_child;
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);
@ -861,11 +1154,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;
@ -873,11 +1168,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;
}
@ -914,6 +1211,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)
@ -1017,43 +1315,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 +1400,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;
@ -1140,12 +1475,141 @@ 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->switch_state = true;
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_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;
int count, err;
if (conf == NULL)
return 0;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
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);
}
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
@ -1154,14 +1618,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));
@ -1180,8 +1645,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);
@ -1192,13 +1663,16 @@ 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;
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);
@ -1228,10 +1702,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
...
}
@ -1264,6 +1738,27 @@ 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
}
}
sync {
# synchronize multiple controls without any translations
sample_group_1 [
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
@ -1282,7 +1777,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
@ -1295,6 +1790,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;
@ -1313,6 +1809,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;
@ -1327,7 +1827,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;

View file

@ -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;
}
/**

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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().
@ -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
@ -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.
*/
@ -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);
@ -3948,6 +3958,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 +7361,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 +7369,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)
@ -8731,7 +8763,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)

View file

@ -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 | SND_PCM_INFO_PAUSE);
#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 | SND_PCM_INFO_PAUSE);
params->rate_num = dmix->shmptr->s.rate;
params->rate_den = 1;
params->fifo_size = 0;
@ -1154,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);
@ -1183,8 +1183,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

View file

@ -1669,17 +1669,19 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd,
if (SNDRV_PROTOCOL_VERSION(2, 0, 9) <= ver) {
struct timespec timespec;
if (clock_gettime(CLOCK_MONOTONIC, &timespec) == 0) {
int on = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
if (ioctl(fd, SNDRV_PCM_IOCTL_TTSTAMP, &on) < 0) {
ret = -errno;
SNDMSG("TTSTAMP failed");
return ret;
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
#endif
if (SNDRV_PROTOCOL_VERSION(2, 0, 5) <= ver) {
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;

View file

@ -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

View file

@ -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;
@ -337,6 +360,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 +512,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 protocol 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 +703,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 +755,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 +845,152 @@ 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.
*/
#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 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_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 */
/**
* \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
@ -767,3 +1090,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];
}

View file

@ -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_queue_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
@ -775,6 +790,67 @@ void event_filter(snd_seq_t *seq, snd_seq_event_t *ev)
}
\endcode
\section seq_midi2 MIDI 2.0 and 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.
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 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
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"
@ -1042,7 +1118,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 +1128,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;
}
@ -1276,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);
@ -1790,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);
}
/**
@ -2351,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
@ -2555,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
@ -3813,6 +3922,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
@ -3868,6 +3990,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
@ -3897,10 +4034,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;
}
/*----------------------------------------------------------------*/

View file

@ -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;
@ -275,12 +284,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;
@ -567,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);
@ -587,6 +599,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);

View file

@ -94,6 +94,11 @@ 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 */
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);

View file

@ -493,3 +493,244 @@ 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[0]) {
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,
SND_SEQ_PORT_CAP_UMP_ENDPOINT |
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 |
SND_SEQ_PORT_TYPE_MIDI_UMP |
SND_SEQ_PORT_TYPE_APPLICATION |
SND_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");
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 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 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 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;
}
if (bp->name[0] == '\0')
continue;
if (blknames[0])
snd_strlcat(blknames, ", ", sizeof(blknames));
snd_strlcat(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;
}

View file

@ -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

View file

@ -386,26 +386,58 @@ 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
-----------------------|---------------------
${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)
${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:\<str\>} | Environment variable \<str\>
${sys:\<str\>} | Contents of sysfs file \<str\>
${sys-card:\<str\>} | Contents of sysfs file in /sys/class/sound/card? tree [**Syntax 8**]
${var:\<str\>} | UCM parser variable (set using a _Define_ block)
${eval:\<str\>} | Evaluate expression like *($var+2)/3* [**Syntax 5**]
${find-card:\<str\>} | Find a card - see _Find card substitution_ section
${find-device:\<str\>} | Find a device - see _Find device substitution_ section
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.
#### Library capabilities
None at the moment. The list will grow after *Syntax 8* (library 1.2.14).
#### Special whole string substitution
Substituted string | Value

View file

@ -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);

View file

@ -40,7 +40,7 @@
#include <pthread.h>
#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

View file

@ -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;
}

View file

@ -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 <perex@perex.cz>
*/
@ -30,6 +30,19 @@
#include <limits.h>
#include <regex.h>
/* 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;
@ -504,20 +517,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 +652,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;
@ -738,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);
@ -751,6 +908,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);

View file

@ -1,7 +1,7 @@
SUBDIRS=. lsb
check_PROGRAMS=control pcm pcm_min latency seq \
playmidi1 timer rawmidi midiloop \
check_PROGRAMS=control pcm pcm_min latency seq seq-ump-example \
playmidi1 timer rawmidi midiloop umpinfo \
oldapi queue_timer namehint client_event_filter \
chmap audio_time user-ctl-element-set pcm-multi-thread
@ -12,10 +12,12 @@ 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
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

View file

@ -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;

View file

@ -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

View file

@ -45,10 +45,11 @@
#include <errno.h>
#include <string.h>
#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;
@ -242,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 */

187
test/seq-ump-example.c Normal file
View file

@ -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 <stdio.h>
#include <getopt.h>
#include <alsa/asoundlib.h>
#include <alsa/ump_msg.h>
/* 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;
}

147
test/umpinfo.c Normal file
View file

@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Inquire UMP Endpoint and Block info in various APIs
#include <stdio.h>
#include <alsa/asoundlib.h>
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;
}

View file

@ -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)