diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d8a025b4..0de93aa4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,9 +11,9 @@ 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@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Safe git directory @@ -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/ @@ -75,7 +75,7 @@ jobs: image: ubuntu:latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Prepare environment run: | export DEBIAN_FRONTEND=noninteractive diff --git a/.gitignore b/.gitignore index a74b5195..80d3f8b7 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,6 @@ include/version.h include/alsa include/asoundlib.h utils/alsa-lib.spec -alsalisp/alsalisp aserver/aserver m4/libtool.m4 m4/ltoptions.m4 @@ -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 diff --git a/Makefile.am b/Makefile.am index ff4c963a..5bcef999 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,11 +10,6 @@ endif if BUILD_PCM_PLUGIN_SHM SUBDIRS += aserver endif -if BUILD_MIXER -if BUILD_ALISP -SUBDIRS += alsalisp -endif -endif SUBDIRS += test utils EXTRA_DIST=README.md ChangeLog INSTALL TODO NOTES configure gitcompile libtool \ depcomp version MEMORY-LEAK m4/attributes.m4 diff --git a/alsalisp/Makefile.am b/alsalisp/Makefile.am deleted file mode 100644 index 8e3e0159..00000000 --- a/alsalisp/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -noinst_PROGRAMS = alsalisp - -alsalisp_SOURCES = alsalisp.c -alsalisp_LDADD = ../src/libasound.la - -all: alsalisp - -AM_CPPFLAGS=-I$(top_srcdir)/include -I$(top_srcdir)/src/alisp diff --git a/alsalisp/alsalisp.c b/alsalisp/alsalisp.c deleted file mode 100644 index d1e1bce2..00000000 --- a/alsalisp/alsalisp.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * ALSA lisp implementation - * Copyright (c) 2003 by Jaroslav Kysela - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include - -#include "asoundlib.h" -#include "alisp.h" - -static int verbose = 0; -static int warning = 0; -static int debug = 0; - -static void interpret_filename(const char *file) -{ - struct alisp_cfg cfg; - snd_input_t *in; - snd_output_t *out; - int err; - - memset(&cfg, 0, sizeof(cfg)); - if (file != NULL && strcmp(file, "-") != 0) { - if ((err = snd_input_stdio_open(&in, file, "r")) < 0) { - fprintf(stderr, "unable to open filename '%s' (%s)\n", file, snd_strerror(err)); - return; - } - } else { - if ((err = snd_input_stdio_attach(&in, stdin, 0)) < 0) { - fprintf(stderr, "unable to attach stdin '%s' (%s)\n", file, snd_strerror(err)); - return; - } - } - if (snd_output_stdio_attach(&out, stdout, 0) < 0) { - snd_input_close(in); - fprintf(stderr, "unable to attach stdout (%s)\n", strerror(errno)); - return; - } - cfg.verbose = verbose; - cfg.warning = warning; - cfg.debug = debug; - cfg.in = in; - cfg.out = cfg.eout = cfg.vout = cfg.wout = cfg.dout = out; - err = alsa_lisp(&cfg, NULL); - if (err < 0) - fprintf(stderr, "alsa lisp returned error %i (%s)\n", err, strerror(err)); - else if (verbose) - printf("file %s passed ok via alsa lisp interpreter\n", file); - snd_output_close(out); - snd_input_close(in); -} - -static void usage(void) -{ - fprintf(stderr, "usage: alsalisp [-vdw] [file...]\n"); - exit(1); -} - -int main(int argc, char **argv) -{ - int c; - - while ((c = getopt(argc, argv, "vdw")) != -1) { - switch (c) { - case 'v': - verbose = 1; - break; - case 'd': - debug = 1; - break; - case 'w': - warning = 1; - break; - case '?': - default: - usage(); - /* NOTREACHED */ - } - } - argc -= optind; - argv += optind; - - if (argc < 1) - interpret_filename(NULL); - else - while (*argv) - interpret_filename(*argv++); - - return 0; -} diff --git a/alsalisp/hctl.lisp b/alsalisp/hctl.lisp deleted file mode 100644 index 504050f6..00000000 --- a/alsalisp/hctl.lisp +++ /dev/null @@ -1,91 +0,0 @@ -(setq card (Acall 'card_next -1)) -(setq card (Aresult card)) -(while (>= card 0) - (progn - (princ "found card: " card "\n") - (princ " name : " (Aresult (Acall 'card_get_name card)) "\n") - (princ " longname: " (Aresult (Acall 'card_get_longname card)) "\n") - (setq card (Acall 'card_next card)) - (setq card (Aresult card)) - ) -) -(unsetq card) - -(princ "card_get_index test (SI7018): " (Acall 'card_get_index "SI7018") "\n") -(princ "card_get_index test (ABCD): " (Acall 'card_get_index "ABCD") "\n") - -(setq hctl (Acall 'hctl_open 'default nil)) -(if (= (Aerror hctl) 0) - (progn - (princ "open success: " hctl "\n") - (setq hctl (Ahandle hctl)) - (princ "open hctl: " hctl "\n") - (setq hctl (Acall 'hctl_close hctl)) - (if (= hctl 0) - (princ "close success\n") - (princ "close failed: " hctl "\n") - ) - ) - (progn - (princ "open failed: " hctl "\n") - ) -) -(unsetq hctl) - -(setq ctl (Acall 'ctl_open 'default nil)) -(if (= (Aerror ctl) 0) - (progn - (princ "ctl open success: " ctl "\n") - (setq ctl (Ahandle ctl)) - (setq info (Aresult (Acall 'ctl_card_info ctl))) - (princ "ctl card info: " info "\n") - (princ "ctl card info (mixername): " (cdr (assq "mixername" info)) "\n") - (unsetq info) - (setq hctl (Acall 'hctl_open_ctl ctl)) - (if (= (Aerror hctl) 0) - (progn - (princ "hctl open success: " hctl "\n") - (setq hctl (Ahandle hctl)) - (princ "open hctl: " hctl "\n") - (princ "load hctl: " (Acall 'hctl_load hctl) "\n") - (princ "first : " (Acall 'hctl_first_elem hctl) "\n") - (princ "last : " (Acall 'hctl_last_elem hctl) "\n") - (princ "next (first): " (Acall 'hctl_elem_next (Acall 'hctl_first_elem hctl)) "\n") - (princ "prev (last) : " (Acall 'hctl_elem_prev (Acall 'hctl_last_elem hctl)) "\n") - (setq elem (Acall 'hctl_first_elem hctl)) - (while elem - (progn - (setq info (Acall 'hctl_elem_info elem)) - (princ info "\n") - (setq value (Acall 'hctl_elem_read elem)) - (princ value "\n") - (when (equal (cdr (assq "name" (car (cdr (assq "id" (Aresult info)))))) "Master Playback Volume") - (princ "write Master: " (Acall 'hctl_elem_write elem (20 20)) "\n") - ) - (unsetq info value) - (gc) - (setq elem (Acall 'hctl_elem_next elem)) - ) - ) - (unsetq elem) - (setq hctl (Acall 'hctl_close hctl)) - (if (= hctl 0) - (princ "hctl close success\n") - (princ "hctl close failed: " hctl "\n") - ) - ) - (progn - (princ "hctl open failed: " hctl "\n") - (Acall 'ctl_close ctl) - ) - ) - (unsetq hctl) - ) - (progn - (princ "ctl open failed: " ctl "\n") - ) -) -(unsetq ctl) - -(&stat-memory) -(&dump-memory "memory.dump") diff --git a/alsalisp/hello.lisp b/alsalisp/hello.lisp deleted file mode 100644 index f04fc381..00000000 --- a/alsalisp/hello.lisp +++ /dev/null @@ -1,26 +0,0 @@ -(princ "Hello ALSA world\n") -(princ "One " 1 "\n") -(princ "Two " (+ 1 1) "\n") - -(defun myprinc (o) (progn (princ o))) -(myprinc "Printed via myprinc function!\n") -(unsetq myprinc) - -(defun printnum (from to) (while (<= from to) (princ " " from) (setq from (+ from 1)))) -(princ "Numbers 1-10: ") (printnum 1 10) (princ "\n") -(unsetq printnum) - -(defun factorial (n) (if (> n 1) (* n (factorial (- n 1))) 1)) -(princ "Factorial of 10: " (factorial 10) "\n") -(princ "Float test 1.1 + 1.35 = " (+ 1.1 1.35) "\n") -(princ "Factorial of 10.0: " (factorial 10.0) "\n") -(princ "Factorial of 20.0: " (factorial 20.0) "\n") -(unsetq factorial) - -(setq alist '((one . first) (two . second) (three . third))) -(princ "alist = " alist "\n") -(princ "alist assoc one = " (assoc 'one alist) "\n") -(princ "alist rassoc third = " (rassoc 'third alist) "\n") -(unsetq alist) - -(&stat-memory) diff --git a/alsalisp/itest.lisp b/alsalisp/itest.lisp deleted file mode 100644 index decd9ae7..00000000 --- a/alsalisp/itest.lisp +++ /dev/null @@ -1 +0,0 @@ -(princ "itest.lisp file included!\n") diff --git a/alsalisp/test.lisp b/alsalisp/test.lisp deleted file mode 100644 index 5e3820f4..00000000 --- a/alsalisp/test.lisp +++ /dev/null @@ -1,382 +0,0 @@ -; -; Test code for all basic alsa lisp commands. -; The test is indended to find memory leaks. -; -; Copyright (c) 2003 Jaroslav Kysela -; License: GPL v2 (http://www.gnu.org/licenses/gpl.html) -; - -; -; Basic commands -; - -(!=) (&check-memory) -(!= 0) (&check-memory) -(!= 0 1) (&check-memory) -(!= 1 1) (&check-memory) -(!= 0 1 2) (&check-memory) -(!= 'aaaa 'bbbb) (&check-memory) - -(%) (&check-memory) -(% 11) (&check-memory) -(% 11 5) (&check-memory) -(% 11.5 5.1) (&check-memory) -(% 11.5 5.1 2.2) (&check-memory) -(% 'aaaa 'bbbb) (&check-memory) - -(&check-memory) (&check-memory) -(&check-memory "abcd") (&check-memory) -(&dump-memory "-") (&check-memory) -(&dump-memory) (&check-memory) -(&dump-objects "-") (&check-memory) -(&dump-objects) (&check-memory) -(&stat-memory) (&check-memory) -(&stat-memory "abcd") (&check-memory) - -(*) (&check-memory) -(* 1) (&check-memory) -(* 1 2) (&check-memory) -(* 1.1 2.2) (&check-memory) -(* 1.1 2.2 3.3) (&check-memory) -(* 'aaaa) (&check-memory) - -(+) (&check-memory) -(+ 1) (&check-memory) -(+ 1 2) (&check-memory) -(+ 1.1 2.2) (&check-memory) -(+ 1.1 2.2 3.3) (&check-memory) -(+ 'aaaa) (&check-memory) -(+ 'aaaa 'bbbb) (&check-memory) -(+ "aaaa") (&check-memory) -(+ "aaaa" "bbbb") (&check-memory) -(+ "aaaa" "bbbb" "cccc") (&check-memory) - -(-) (&check-memory) -(- 1) (&check-memory) -(- 1 2) (&check-memory) -(- 1.1 2.2) (&check-memory) -(- 1.1 2.2 3.3) (&check-memory) -(- 'aaaa) (&check-memory) -(- 'aaaa 'bbbb) (&check-memory) - -(/) (&check-memory) -(/ 1) (&check-memory) -(/ 1 2) (&check-memory) -(/ 1.1 2.2) (&check-memory) -(/ 1.1 2.2 3.3) (&check-memory) -(/ 'aaaa) (&check-memory) -(/ 'aaaa 'bbbb) (&check-memory) - -(<) (&check-memory) -(< 0) (&check-memory) -(< 0 1) (&check-memory) -(< 1 0) (&check-memory) -(< 0 1 2) (&check-memory) - -(<=) (&check-memory) -(<= 0) (&check-memory) -(<= 0 1) (&check-memory) -(<= 1 0) (&check-memory) -(<= 0 1 2) (&check-memory) - -(=) (&check-memory) -(= 0) (&check-memory) -(= 0 1) (&check-memory) -(= 1 1) (&check-memory) -(= 0 1 2) (&check-memory) - -(>) (&check-memory) -(> 0) (&check-memory) -(> 0 1) (&check-memory) -(> 1 0) (&check-memory) -(> 0 1 2) (&check-memory) - -(>= 0) (&check-memory) -(>= 0 1) (&check-memory) -(>= 1 0) (&check-memory) -(>= 0 1 2) (&check-memory) - -(and) (&check-memory) -(and 0) (&check-memory) -(and 1) (&check-memory) -(and 0 0 0) (&check-memory) - -(quote a) (&check-memory) - -(assoc) (&check-memory) -(assoc 'one) (&check-memory) -(assoc 'one '((one . first))) (&check-memory) -(assoc 'one '((two . second))) (&check-memory) -(assoc 'one '((one . first) (two . second))) (&check-memory) - -(assq) (&check-memory) -(assq 'one) (&check-memory) -(assq "one" '(("one" . "first"))) (&check-memory) -(assq "one" '(("two" . "second"))) (&check-memory) -(assq "one" '(("one" . "first") ("two" . "second"))) (&check-memory) - -(atom) (&check-memory) -(atom 'one) (&check-memory) -(atom "one") (&check-memory) -(atom "one" 'two) (&check-memory) - -(funcall) (&check-memory) - -(car) (&check-memory) -(car '(one . two)) (&check-memory) - -(cdr) (&check-memory) -(cdr '(one . two)) (&check-memory) - -(concat) (&check-memory) -(concat 'aaaa) (&check-memory) -(concat 'aaaa 'bbbb) (&check-memory) -(concat "aaaa") (&check-memory) -(concat "aaaa" "bbbb") (&check-memory) -(concat "aaaa" "bbbb" "cccc") (&check-memory) - -(cond) (&check-memory) -(cond 0) (&check-memory) -(cond 0 1) (&check-memory) -(cond 0 1 2) (&check-memory) -(cond 0 1 2 3) (&check-memory) -(cond (0 'a) (1 'b) (0 'd)) (&check-memory) -(cond 1) (&check-memory) -(cond 1 1) (&check-memory) -(cond 1 1 2) (&check-memory) -(cond 1 1 2 3) (&check-memory) - -(cons) (&check-memory) -(cons "a") (&check-memory) -(cons "a" "b") (&check-memory) -(cons "a" "b" "c") (&check-memory) - -(eq) (&check-memory) -(eq 1) (&check-memory) -(eq 0 0) (&check-memory) -(eq "a" "b") (&check-memory) -(eq "a" "b" "c") (&check-memory) - -(equal) (&check-memory) -(equal 1) (&check-memory) -(equal 0 0) (&check-memory) -(equal "a" "b") (&check-memory) -(equal "a" "b" "c") (&check-memory) - -(exfun) (&check-memory) -(exfun 'abcd) (&check-memory) -(exfun 'abcd 'ijkl) (&check-memory) - -(format) (&check-memory) -(format 1) (&check-memory) -(format 'a) (&check-memory) -(format "a" "b" "c") (&check-memory) -(format "1.2") (&check-memory) -(format "%c" 43) (&check-memory) -(format "%d" 12) (&check-memory) -(format "%i" 12) (&check-memory) -(format "%f" 12.1) (&check-memory) -(format "%s" "abcd") (&check-memory) -(format "%s %i %i" "abcd" 1 2) (&check-memory) - -(garbage-collect) (&check-memory) -(gc) (&check-memory) - -(if) (&check-memory) -(if t) (&check-memory) -(if t 'a) (&check-memory) -(if t 'a 'b) (&check-memory) -(if nil) (&check-memory) -(if nil 'a) (&check-memory) -(if nil 'a 'b) (&check-memory) - -(include "itest.lisp") (&check-memory) - -(list) (&check-memory) -(list "a") (&check-memory) -(list "a" "b") (&check-memory) -(list "a" "b" "c") (&check-memory) - -(not) (&check-memory) -(not 0) (&check-memory) -(not nil) (&check-memory) -(not t) (&check-memory) -(not 'a) (&check-memory) -(not 'a 'b 'c 'd) (&check-memory) - -(nth) (&check-memory) -(nth 2) (&check-memory) -(nth 2 nil) (&check-memory) -(nth 2 '(('one 'two 'three))) (&check-memory) - -(null) (&check-memory) -(null 0) (&check-memory) -(null nil) (&check-memory) -(null t) (&check-memory) -(null 'a) (&check-memory) -(null 'a 'b 'c 'd) (&check-memory) - -(or) (&check-memory) -(or 0) (&check-memory) -(or 1) (&check-memory) -(or 0 0 0) (&check-memory) - -(path) (&check-memory) -(path 0) (&check-memory) -(path 1) (&check-memory) -(path 0 0 0) (&check-memory) -(path "data") (&check-memory) - -(princ) (&check-memory) -(princ "\nabcd\n") (&check-memory) -(princ "a" "b" "c\n") (&check-memory) - -(prog1) (&check-memory) -(prog1 1) (&check-memory) -(prog1 1 2 3 4) (&check-memory) - -(prog2) (&check-memory) -(prog2 1) (&check-memory) -(prog2 1 2 3 4) (&check-memory) - -(progn) (&check-memory) -(progn 1) (&check-memory) -(progn 1 2 3 4) (&check-memory) - -(quote) (&check-memory) -(quote a) (&check-memory) - -(rassoc) (&check-memory) -(rassoc 'first) (&check-memory) -(rassoc 'first '((one . first))) (&check-memory) -(rassoc 'first '((two . second))) (&check-memory) -(rassoc 'first '((one . first) (two . second))) (&check-memory) - -(rassq) (&check-memory) -(rassq "first") (&check-memory) -(rassq "first" '(("one" . "first"))) (&check-memory) -(rassq "first" '(("two" . "second"))) (&check-memory) -(rassq "first" '(("one" . "first") ("two" . "second"))) (&check-memory) - -(set) (&check-memory) -(set "a") (unset "a") (&check-memory) -(set "a" 1) (unset "a") (&check-memory) -(set a 1) (unset a) (&check-memory) -(set "a" 1 2) (unset "a") (&check-memory) - -(setf) (&check-memory) -(setf a) (unsetf a) (&check-memory) -(setf a 1) (unsetf a) (&check-memory) -(setf a 1 2) (unsetf a) (&check-memory) - -(setq) (&check-memory) -(setq a) (unsetq a) (&check-memory) -(setq a 1) (unsetq a) (&check-memory) -(setq a 1 2) (unsetq a) (&check-memory) - -(string-equal) (&check-memory) -(string-equal 1) (&check-memory) -(string-equal "a") (&check-memory) -(string-equal "a" "a") (&check-memory) -(string-equal "a" "b") (&check-memory) -(string-equal "a" "b" "c") (&check-memory) - -(string-to-integer) (&check-memory) -(string-to-integer 1) (&check-memory) -(string-to-integer 1.5) (&check-memory) -(string-to-integer "a") (&check-memory) -(string-to-integer "a" "a") (&check-memory) -(string-to-integer "a" "b") (&check-memory) -(string-to-integer "a" "b" "c") (&check-memory) - -(string-to-float) (&check-memory) -(string-to-float 1) (&check-memory) -(string-to-float 1.5) (&check-memory) -(string-to-float "a") (&check-memory) -(string-to-float "a" "a") (&check-memory) -(string-to-float "a" "b") (&check-memory) -(string-to-float "a" "b" "c") (&check-memory) - -(string=) (&check-memory) -(string= 1) (&check-memory) -(string= "a") (&check-memory) -(string= "a" "a") (&check-memory) -(string= "a" "b") (&check-memory) -(string= "a" "b" "c") (&check-memory) - -(unless) (&check-memory) -(unless 1) (&check-memory) -(unless 0 1 2) (&check-memory) -(unless t 2 3 4) (&check-memory) -(unless nil 2 3 4) (&check-memory) - -(unset) (&check-memory) -(unset "a") (&check-memory) - -(unsetf) (&check-memory) -(unsetf a) (&check-memory) -(unsetf a b) (&check-memory) - -(unsetq) (&check-memory) -(unsetq a) (&check-memory) -(unsetq a b) (&check-memory) - -(when) (&check-memory) -(when 0) (&check-memory) -(when 0 1) (&check-memory) -(when t 1) (&check-memory) -(when nil 1) (&check-memory) - -(while) (&check-memory) -(while nil) (&check-memory) -(while nil 1) (&check-memory) -(while nil 1 2 3 4) (&check-memory) - -; -; more complex command sequences -; - -(setq abcd "abcd") -(unsetq abcd) -(&check-memory) - -(setq abcd (("abcd" . "efgh") ("1234" . "5678"))) -(unsetq abcd) -(&check-memory) - -(defun myfun () (princ "a\n")) -(exfun 'myfun) -(unsetq myfun) -(&check-memory) - -(defun myfun () (princ "a\n")) -(funcall 'myfun) -(funcall 'myfun 'aaaaa) -(unsetq myfun) -(&check-memory) - -(defun myfun (o) (princ o "a\n")) -(funcall 'myfun) -(funcall 'myfun 'aaaaa) -(unsetq myfun) -(&check-memory) - -(defun myfun (o p) (princ o p "\n")) -(funcall 'myfun) -(funcall 'myfun 'aaaaa) -(funcall 'myfun 'aaaaa 'bbbbb) -(unsetq myfun) -(&check-memory) - -(defun printnum (from to) (while (<= from to) (princ " " from) (setq from (+ from 1)))) -(princ "Numbers 1-10:") (printnum 1 10) (princ "\n") -(unsetq printnum) - -; -; game over -; - -(princ "*********************\n") -(princ "OK, all tests passed!\n") -(princ "*********************\n") -(&stat-memory) diff --git a/configure.ac b/configure.ac index 7a152a4a..2a808fa8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(alsa-lib, 1.2.11) +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 <" >> include/asoundlib test "$build_hwdep" = "yes" && echo "#include " >> include/asoundlib.h echo "#include " >> include/asoundlib.h test "$build_mixer" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h diff --git a/doc/Makefile.am b/doc/Makefile.am index e087f424..77174cf3 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -9,14 +9,16 @@ doc: doxygen doxygen.cfg doc-pack: doc - -chmod a+r $(top_srcdir)/doc/doxygen/html/* - -chmod a-w $(top_srcdir)/doc/doxygen/html/* + -chmod -R a+r $(top_srcdir)/doc/doxygen/html/* + -chmod -R a-w $(top_srcdir)/doc/doxygen/html/* if ! test -z "$(AMTAR)"; then \ $(AMTAR) --create --directory=$(top_srcdir)/doc/doxygen/html --verbose --file=- . | bzip2 -c -9 > $(top_srcdir)/../alsa-lib-doc.tar.bz2 ; \ else \ $(TAR) --create --directory=$(top_srcdir)/doc/doxygen/html --verbose --file=- . | bzip2 -c -9 > $(top_srcdir)/../alsa-lib-doc.tar.bz2 ; \ fi + -chmod -R u+w $(top_srcdir)/doc/doxygen/html/* rm -rf $(top_srcdir)/doc/doxygen/html/* doc-clean: + -chmod -R u+w $(top_srcdir)/doc/doxygen/html/* rm -rf $(top_srcdir)/doc/doxygen/html/* diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in index 8bf29b21..bcc3a33e 100644 --- a/doc/doxygen.cfg.in +++ b/doc/doxygen.cfg.in @@ -17,6 +17,8 @@ INPUT = @top_srcdir@/doc/index.doxygen \ @top_srcdir@/include/control.h \ @top_srcdir@/include/pcm.h \ @top_srcdir@/include/rawmidi.h \ + @top_srcdir@/include/ump.h \ + @top_srcdir@/include/ump_msg.h \ @top_srcdir@/include/timer.h \ @top_srcdir@/include/hwdep.h \ @top_srcdir@/include/seq.h \ diff --git a/gitcompile b/gitcompile index c70448f7..783d0fd3 100755 --- a/gitcompile +++ b/gitcompile @@ -5,7 +5,6 @@ set -e bits32= cbits32= modules= -alisp= lto= if [ $# -ne 0 ]; then endloop= @@ -20,10 +19,6 @@ if [ $# -ne 0 ]; then modules=yes echo "Forced mixer modules build..." shift ;; - alisp) - alisp=yes - echo "Forced alisp code build..." - shift ;; python2) python2=yes echo "Forced python2 interpreter build..." @@ -71,10 +66,6 @@ if [ "$modules" = "yes" ]; then args="$args --enable-mixer-pymods" fi -if [ "$alisp" = "yes" ]; then - args="$args --enable-alisp" -fi - if [ "$python2" = "yes" ]; then args="$args --enable-python2" fi diff --git a/include/Makefile.am b/include/Makefile.am index 16d68b51..bd8a4b97 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -22,15 +22,16 @@ endif if BUILD_PCM_PLUGIN_RATE alsainclude_HEADERS += pcm_rate.h endif -if BUILD_PCM_PLUGIN_EXTPLUG -alsainclude_HEADERS += pcm_external.h pcm_extplug.h -endif -if BUILD_PCM_PLUGIN_IOPLUG -if !BUILD_PCM_PLUGIN_EXTPLUG -alsainclude_HEADERS += pcm_external.h -endif -alsainclude_HEADERS += pcm_ioplug.h +if BUILD_PCM_PLUGIN_EXTERNAL +# FIXME: pcm_external.h includes both pcm_extplug.h and pcm_ioplug.h +alsainclude_HEADERS += pcm_external.h pcm_extplug.h pcm_ioplug.h endif +#if BUILD_PCM_PLUGIN_EXTPLUG +#alsainclude_HEADERS += pcm_extplug.h +#endif +#if BUILD_PCM_PLUGIN_IOPLUG +#alsainclude_HEADERS += pcm_ioplug.h +#endif endif if BUILD_RAWMIDI @@ -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 diff --git a/include/alisp.h b/include/alisp.h deleted file mode 100644 index 11d7adf4..00000000 --- a/include/alisp.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * ALSA lisp implementation - * Copyright (c) 2003 by Jaroslav Kysela - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -struct alisp_cfg { - int verbose: 1, - warning: 1, - debug: 1; - snd_input_t *in; /* program code */ - snd_output_t *out; /* program output */ - snd_output_t *eout; /* error output */ - snd_output_t *vout; /* verbose output */ - snd_output_t *wout; /* warning output */ - snd_output_t *dout; /* debug output */ -}; - -struct alisp_instance; -struct alisp_object; -struct alisp_seq_iterator; - -struct alisp_cfg *alsa_lisp_default_cfg(snd_input_t *input); -void alsa_lisp_default_cfg_free(struct alisp_cfg *cfg); -int alsa_lisp(struct alisp_cfg *cfg, struct alisp_instance **instance); -void alsa_lisp_free(struct alisp_instance *instance); -int alsa_lisp_function(struct alisp_instance *instance, struct alisp_seq_iterator **result, - const char *id, const char *args, ...) -#ifndef DOC_HIDDEN - __attribute__ ((format (printf, 4, 5))) -#endif - ; -void alsa_lisp_result_free(struct alisp_instance *instance, - struct alisp_seq_iterator *result); -int alsa_lisp_seq_first(struct alisp_instance *instance, const char *id, - struct alisp_seq_iterator **seq); -int alsa_lisp_seq_next(struct alisp_seq_iterator **seq); -int alsa_lisp_seq_count(struct alisp_seq_iterator *seq); -int alsa_lisp_seq_integer(struct alisp_seq_iterator *seq, long *val); -int alsa_lisp_seq_pointer(struct alisp_seq_iterator *seq, const char *ptr_id, void **ptr); diff --git a/include/asoundef.h b/include/asoundef.h index 261db40b..5e158b35 100644 --- a/include/asoundef.h +++ b/include/asoundef.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_ASOUNDEF_H #define __ALSA_ASOUNDEF_H diff --git a/include/asoundlib-head.h b/include/asoundlib-head.h index 7cfa440d..382a0a58 100644 --- a/include/asoundlib-head.h +++ b/include/asoundlib-head.h @@ -38,3 +38,5 @@ #include #include #include +#include +#include diff --git a/include/conf.h b/include/conf.h index 09da0e9a..327659c9 100644 --- a/include/conf.h +++ b/include/conf.h @@ -25,6 +25,11 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#include +#endif + #ifndef __ALSA_CONF_H #define __ALSA_CONF_H diff --git a/include/control.h b/include/control.h index e7541d56..b206fbb0 100644 --- a/include/control.h +++ b/include/control.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_CONTROL_H #define __ALSA_CONTROL_H diff --git a/include/control_external.h b/include/control_external.h index 488fa6e2..2baaea26 100644 --- a/include/control_external.h +++ b/include/control_external.h @@ -23,10 +23,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ + #ifndef __ALSA_CONTROL_EXTERNAL_H #define __ALSA_CONTROL_EXTERNAL_H -#include "control.h" +#ifndef __ASOUNDLIB_LOCAL +#include +#endif #ifdef __cplusplus extern "C" { diff --git a/include/error.h b/include/error.h index 7239db85..13f59d55 100644 --- a/include/error.h +++ b/include/error.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_ERROR_H #define __ALSA_ERROR_H @@ -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); diff --git a/include/global.h b/include/global.h index 3ecaeee8..84a40557 100644 --- a/include/global.h +++ b/include/global.h @@ -25,12 +25,15 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_GLOBAL_H_ #define __ALSA_GLOBAL_H_ -/* for timeval and timespec */ -#include - #ifdef __cplusplus extern "C" { #endif diff --git a/include/hwdep.h b/include/hwdep.h index 46ed1f78..174aa899 100644 --- a/include/hwdep.h +++ b/include/hwdep.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_HWDEP_H #define __ALSA_HWDEP_H diff --git a/include/input.h b/include/input.h index f84954a9..80fc8624 100644 --- a/include/input.h +++ b/include/input.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_INPUT_H #define __ALSA_INPUT_H diff --git a/include/local.h b/include/local.h index 512e4455..2498cd26 100644 --- a/include/local.h +++ b/include/local.h @@ -30,6 +30,9 @@ #include #include #include +#include +/* for timeval and timespec */ +#include #ifdef HAVE_ENDIAN_H #include #elif defined(HAVE_SYS_ENDIAN_H) @@ -187,6 +190,7 @@ #include "hwdep.h" #include "control.h" #include "mixer.h" +#include "ump_msg.h" #include "seq_event.h" #include "seq.h" @@ -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 diff --git a/include/mixer.h b/include/mixer.h index 735dfdd8..93dd70cd 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_MIXER_H #define __ALSA_MIXER_H diff --git a/include/output.h b/include/output.h index 5e16420b..14089c05 100644 --- a/include/output.h +++ b/include/output.h @@ -25,11 +25,15 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_OUTPUT_H #define __ALSA_OUTPUT_H -#include - #ifdef __cplusplus extern "C" { #endif diff --git a/include/pcm.h b/include/pcm.h index 102ff812..3fa9203e 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -26,6 +26,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_PCM_H #define __ALSA_PCM_H @@ -33,8 +39,6 @@ extern "C" { #endif -#include - /** * \defgroup PCM PCM Interface * See the \ref pcm page for more details. @@ -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; diff --git a/include/pcm_external.h b/include/pcm_external.h index a6005b34..27e53fc0 100644 --- a/include/pcm_external.h +++ b/include/pcm_external.h @@ -26,7 +26,9 @@ #ifndef __ALSA_PCM_EXTERNAL_H #define __ALSA_PCM_EXTERNAL_H -#include "pcm.h" +#ifndef __ASOUNDLIB_LOCAL +#include +#endif #ifdef __cplusplus extern "C" { diff --git a/include/pcm_extplug.h b/include/pcm_extplug.h index e5c02d4d..1294a59e 100644 --- a/include/pcm_extplug.h +++ b/include/pcm_extplug.h @@ -28,6 +28,11 @@ * */ +#if !defined(__ALSA_PCM_EXTERNAL_H) && !defined(ALSA_LIBRARY_BUILD) +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_PCM_EXTPLUG_H #define __ALSA_PCM_EXTPLUG_H diff --git a/include/pcm_ioplug.h b/include/pcm_ioplug.h index 81ac8613..722dc9d7 100644 --- a/include/pcm_ioplug.h +++ b/include/pcm_ioplug.h @@ -28,6 +28,11 @@ * */ +#if !defined(__ALSA_PCM_EXTERNAL_H) && !defined(ALSA_LIBRARY_BUILD) +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_PCM_IOPLUG_H #define __ALSA_PCM_IOPLUG_H diff --git a/include/pcm_old.h b/include/pcm_old.h index a9f5308f..3ff5f82c 100644 --- a/include/pcm_old.h +++ b/include/pcm_old.h @@ -1,3 +1,8 @@ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#error "use #include , should not be used directly" +#endif + /* * Old ALSA 0.9.x API */ diff --git a/include/pcm_plugin.h b/include/pcm_plugin.h index 2d014394..f3e8f3b5 100644 --- a/include/pcm_plugin.h +++ b/include/pcm_plugin.h @@ -133,6 +133,19 @@ int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, const char *name, snd_config_t *root, snd_config_t *conf, snd_pcm_stream_t stream, int mode); +/* + * IEC958 subframe conversion plugin + */ +int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, + snd_pcm_format_t sformat, snd_pcm_t *slave, + int close_slave, + const unsigned char *status_bits, + const unsigned char *preamble_vals, + int hdmi_mode); +int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, + snd_config_t *root, snd_config_t *conf, + snd_pcm_stream_t stream, int mode); + /* * Route plugin for linear formats */ diff --git a/include/pcm_rate.h b/include/pcm_rate.h index 48473ed4..0a914d86 100644 --- a/include/pcm_rate.h +++ b/include/pcm_rate.h @@ -31,6 +31,10 @@ #ifndef __ALSA_PCM_RATE_H #define __ALSA_PCM_RATE_H +#ifndef __ASOUNDLIB_LOCAL +#include +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/include/rawmidi.h b/include/rawmidi.h index 2630d1e6..a144f659 100644 --- a/include/rawmidi.h +++ b/include/rawmidi.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_RAWMIDI_H #define __ALSA_RAWMIDI_H @@ -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); diff --git a/include/seq.h b/include/seq.h index e55f5c16..17bcfa53 100644 --- a/include/seq.h +++ b/include/seq.h @@ -26,6 +26,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_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); /* */ diff --git a/include/seq_event.h b/include/seq_event.h index 9ca384ee..b27ce36c 100644 --- a/include/seq_event.h +++ b/include/seq_event.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_SEQ_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 */ diff --git a/include/seq_midi_event.h b/include/seq_midi_event.h index 4cfb15c4..6fbbf400 100644 --- a/include/seq_midi_event.h +++ b/include/seq_midi_event.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_SEQ_MIDI_EVENT_H #define __ALSA_SEQ_MIDI_EVENT_H diff --git a/include/seqmid.h b/include/seqmid.h index 4464c2d3..6d22692f 100644 --- a/include/seqmid.h +++ b/include/seqmid.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_SEQMID_H #define __ALSA_SEQMID_H @@ -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 diff --git a/include/sound/uapi/asequencer.h b/include/sound/uapi/asequencer.h index b913f31d..9682795b 100644 --- a/include/sound/uapi/asequencer.h +++ b/include/sound/uapi/asequencer.h @@ -3,22 +3,6 @@ * Main header file for the ALSA sequencer * Copyright (c) 1998-1999 by Frank van de Pol * (c) 1998-1999 by Jaroslav Kysela - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef __SOUND_ASEQUENCER_H #define __SOUND_ASEQUENCER_H @@ -26,7 +10,7 @@ #include /** version of the sequencer */ -#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 3) +#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 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 diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index f3b2b94d..1f91d994 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -3,22 +3,6 @@ * Advanced Linux Sound Architecture - ALSA - Driver * Copyright (c) 1994-2003 by Jaroslav Kysela , * Abramo Bagnara - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef __SOUND_ASOUND_H @@ -28,7 +12,7 @@ #include #include #else -#include +#include #include #endif @@ -54,8 +38,10 @@ * * ****************************************************************************/ +#define AES_IEC958_STATUS_SIZE 24 + struct snd_aes_iec958 { - unsigned char status[24]; /* AES/IEC958 channel status bits */ + unsigned char status[AES_IEC958_STATUS_SIZE]; /* AES/IEC958 channel status bits */ unsigned char subcode[147]; /* AES/IEC958 subcode bits */ unsigned char pad; /* nothing */ unsigned char dig_subframe[4]; /* AES/IEC958 subframe bits */ @@ -154,7 +140,7 @@ struct snd_hwdep_dsp_image { * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 15) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 18) typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; @@ -200,6 +186,11 @@ typedef int __bitwise snd_pcm_format_t; #define SNDRV_PCM_FORMAT_S24_BE ((snd_pcm_format_t) 7) /* low three bytes */ #define SNDRV_PCM_FORMAT_U24_LE ((snd_pcm_format_t) 8) /* low three bytes */ #define SNDRV_PCM_FORMAT_U24_BE ((snd_pcm_format_t) 9) /* low three bytes */ +/* + * For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the + * available bit count in most significant bit. It's for the case of so-called 'left-justified' or + * `right-padding` sample which has less width than 32 bit. + */ #define SNDRV_PCM_FORMAT_S32_LE ((snd_pcm_format_t) 10) #define SNDRV_PCM_FORMAT_S32_BE ((snd_pcm_format_t) 11) #define SNDRV_PCM_FORMAT_U32_LE ((snd_pcm_format_t) 12) @@ -301,7 +292,8 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME 0x02000000 /* report absolute hardware link audio time, not reset on startup */ #define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */ #define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */ - +#define SNDRV_PCM_INFO_EXPLICIT_SYNC 0x10000000 /* needs explicit sync of pointers and data */ +#define SNDRV_PCM_INFO_NO_REWINDS 0x20000000 /* hardware can only support monotonic changes of appl_ptr */ #define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ @@ -340,7 +332,7 @@ union snd_pcm_sync_id { unsigned char id[16]; unsigned short id16[8]; unsigned int id32[4]; -}; +} __attribute__((deprecated)); struct snd_pcm_info { unsigned int device; /* RO/WR (control): device number */ @@ -354,7 +346,7 @@ struct snd_pcm_info { int dev_subclass; /* SNDRV_PCM_SUBCLASS_* */ unsigned int subdevices_count; unsigned int subdevices_avail; - union snd_pcm_sync_id sync; /* hardware synchronization ID */ + unsigned char pad1[16]; /* was: hardware synchronization ID */ unsigned char reserved[64]; /* reserved for future... */ }; @@ -393,8 +385,8 @@ typedef int snd_pcm_hw_param_t; #define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) /* avoid rate resampling */ #define SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER (1<<1) /* export buffer */ #define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2) /* disable period wakeups */ -#define SNDRV_PCM_HW_PARAMS_NO_DRAIN_SILENCE (1<<3) /* suppress the silence fill - * for draining +#define SNDRV_PCM_HW_PARAMS_NO_DRAIN_SILENCE (1<<3) /* suppress drain with the filling + * of the silence samples */ struct snd_interval { @@ -422,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) diff --git a/include/timer.h b/include/timer.h index dc5ca69e..09283ce0 100644 --- a/include/timer.h +++ b/include/timer.h @@ -25,6 +25,12 @@ * */ +#if !defined(__ASOUNDLIB_H) && !defined(ALSA_LIBRARY_BUILD) +/* don't use ALSA_LIBRARY_BUILD define in sources outside alsa-lib */ +#warning "use #include , should not be used directly" +#include +#endif + #ifndef __ALSA_TIMER_H #define __ALSA_TIMER_H diff --git a/include/ump.h b/include/ump.h index 1e5e0534..d19ab557 100644 --- a/include/ump.h +++ b/include/ump.h @@ -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 , should not be used directly" +#include +#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 diff --git a/include/ump_msg.h b/include/ump_msg.h index e0264f6e..f61cee04 100644 --- a/include/ump_msg.h +++ b/include/ump_msg.h @@ -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 , should not be used directly" +#include +#endif + #ifndef __ALSA_UMP_MSG_H #define __ALSA_UMP_MSG_H -#include - #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 } diff --git a/include/use-case.h b/include/use-case.h index 297664ee..b54f2124 100644 --- a/include/use-case.h +++ b/include/use-case.h @@ -139,7 +139,7 @@ extern "C" { #define SND_USE_CASE_DEV_BLUETOOTH "Bluetooth" /**< Bluetooth Device */ #define SND_USE_CASE_DEV_EARPIECE "Earpiece" /**< Earpiece Device */ #define SND_USE_CASE_DEV_SPDIF "SPDIF" /**< SPDIF Device */ -#define SND_USE_CASE_DEV_HDMI "HDMI" /**< HDMI Device */ +#define SND_USE_CASE_DEV_HDMI "HDMI" /**< HDMI / DisplayPort Device */ #define SND_USE_CASE_DEV_USB "USB" /**< USB Device (multifunctional) */ #define SND_USE_CASE_DEV_DIRECT "Direct" /**< Direct Device (no channel remapping), (e.g. ProAudio usage) */ /* add new devices to end of list */ diff --git a/src/Makefile.am b/src/Makefile.am index 74a108dc..679c626c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,6 +36,7 @@ endif if BUILD_RAWMIDI SUBDIRS += rawmidi libasound_la_LIBADD += rawmidi/librawmidi.la +VERSION_CPPFLAGS += -DHAVE_RAWMIDI_SYMS endif if BUILD_HWDEP SUBDIRS += hwdep @@ -44,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 diff --git a/src/Versions.in.in b/src/Versions.in.in index 98f36ded..4aa2e13b 100644 --- a/src/Versions.in.in +++ b/src/Versions.in.in @@ -129,19 +129,9 @@ ALSA_0.9.3 { } ALSA_0.9.0; ALSA_0.9.5 { -#ifdef HAVE_ALISP_SYMS - global: - - @SYMBOL_PREFIX@alsa_lisp; -#endif } ALSA_0.9.3; ALSA_0.9.7 { -#ifdef HAVE_ALISP_SYMS - global: - - @SYMBOL_PREFIX@alsa_lisp_*; -#endif } ALSA_0.9.5; ALSA_1.1.6 { @@ -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; diff --git a/src/alisp/Makefile.am b/src/alisp/Makefile.am deleted file mode 100644 index 1234e111..00000000 --- a/src/alisp/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ -EXTRA_LTLIBRARIES = libalisp.la - -EXTRA_DIST = alisp_snd.c - -libalisp_la_SOURCES = alisp.c - -noinst_HEADERS = alisp_local.h - -all: libalisp.la - -AM_CPPFLAGS=-I$(top_srcdir)/include diff --git a/src/alisp/alisp.c b/src/alisp/alisp.c deleted file mode 100644 index bb841119..00000000 --- a/src/alisp/alisp.c +++ /dev/null @@ -1,3495 +0,0 @@ -/* - * ALSA lisp implementation - * Copyright (c) 2003 by Jaroslav Kysela - * - * Based on work of Sandro Sigala (slisp-1.2) - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define alisp_seq_iterator alisp_object - -#include "local.h" -#include "alisp.h" -#include "alisp_local.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - - -struct alisp_object alsa_lisp_nil; -struct alisp_object alsa_lisp_t; - -/* parser prototypes */ -static struct alisp_object * parse_object(struct alisp_instance *instance, int havetoken); -static void princ_cons(snd_output_t *out, struct alisp_object * p); -static void princ_object(snd_output_t *out, struct alisp_object * p); -static struct alisp_object * eval(struct alisp_instance *instance, struct alisp_object * p); - -/* functions */ -static struct alisp_object *F_eval(struct alisp_instance *instance, struct alisp_object *); -static struct alisp_object *F_progn(struct alisp_instance *instance, struct alisp_object *); -static struct alisp_object *F_funcall(struct alisp_instance *instance, struct alisp_object *); - -/* others */ -static int alisp_include_file(struct alisp_instance *instance, const char *filename); - -/* - * object handling - */ - -static int get_string_hash(const char *s) -{ - int val = 0; - if (s == NULL) - return val; - while (*s) - val += *s++; - return val & ALISP_OBJ_PAIR_HASH_MASK; -} - -static void nomem(void) -{ - SNDERR("alisp: no enough memory"); -} - -static void lisp_verbose(struct alisp_instance *instance, const char *fmt, ...) -{ - va_list ap; - - if (!instance->verbose) - return; - va_start(ap, fmt); - snd_output_printf(instance->vout, "alisp: "); - snd_output_vprintf(instance->vout, fmt, ap); - snd_output_putc(instance->vout, '\n'); - va_end(ap); -} - -static void lisp_error(struct alisp_instance *instance, const char *fmt, ...) -{ - va_list ap; - - if (!instance->warning) - return; - va_start(ap, fmt); - snd_output_printf(instance->eout, "alisp error: "); - snd_output_vprintf(instance->eout, fmt, ap); - snd_output_putc(instance->eout, '\n'); - va_end(ap); -} - -static void lisp_warn(struct alisp_instance *instance, const char *fmt, ...) -{ - va_list ap; - - if (!instance->warning) - return; - va_start(ap, fmt); - snd_output_printf(instance->wout, "alisp warning: "); - snd_output_vprintf(instance->wout, fmt, ap); - snd_output_putc(instance->wout, '\n'); - va_end(ap); -} - -static void lisp_debug(struct alisp_instance *instance, const char *fmt, ...) -{ - va_list ap; - - if (!instance->debug) - return; - va_start(ap, fmt); - snd_output_printf(instance->dout, "alisp debug: "); - snd_output_vprintf(instance->dout, fmt, ap); - snd_output_putc(instance->dout, '\n'); - va_end(ap); -} - -static struct alisp_object * new_object(struct alisp_instance *instance, int type) -{ - struct alisp_object * p; - - if (list_empty(&instance->free_objs_list)) { - p = (struct alisp_object *)malloc(sizeof(struct alisp_object)); - if (p == NULL) { - nomem(); - return NULL; - } - lisp_debug(instance, "allocating cons %p", p); - } else { - p = (struct alisp_object *)instance->free_objs_list.next; - list_del(&p->list); - instance->free_objs--; - lisp_debug(instance, "recycling cons %p", p); - } - - instance->used_objs++; - - alisp_set_type(p, type); - alisp_set_refs(p, 1); - if (type == ALISP_OBJ_CONS) { - p->value.c.car = &alsa_lisp_nil; - p->value.c.cdr = &alsa_lisp_nil; - list_add(&p->list, &instance->used_objs_list[0][ALISP_OBJ_CONS]); - } - - if (instance->used_objs + instance->free_objs > instance->max_objs) - instance->max_objs = instance->used_objs + instance->free_objs; - - return p; -} - -static void free_object(struct alisp_object * p) -{ - switch (alisp_get_type(p)) { - case ALISP_OBJ_STRING: - case ALISP_OBJ_IDENTIFIER: - free(p->value.s); - alisp_set_type(p, ALISP_OBJ_INTEGER); - break; - default: - break; - } -} - -static void delete_object(struct alisp_instance *instance, struct alisp_object * p) -{ - if (p == NULL || p == &alsa_lisp_nil || p == &alsa_lisp_t) - return; - if (alisp_compare_type(p, ALISP_OBJ_NIL) || - alisp_compare_type(p, ALISP_OBJ_T)) - return; - assert(alisp_get_refs(p) > 0); - lisp_debug(instance, "delete cons %p (type = %i, refs = %i) (s = '%s')", p, alisp_get_type(p), alisp_get_refs(p), - alisp_compare_type(p, ALISP_OBJ_STRING) || - alisp_compare_type(p, ALISP_OBJ_IDENTIFIER) ? p->value.s : "???"); - if (alisp_dec_refs(p)) - return; - list_del(&p->list); - instance->used_objs--; - free_object(p); - if (instance->free_objs >= ALISP_FREE_OBJ_POOL) { - lisp_debug(instance, "freed cons %p", p); - free(p); - return; - } - lisp_debug(instance, "moved cons %p to free list", p); - list_add(&p->list, &instance->free_objs_list); - instance->free_objs++; -} - -static void delete_tree(struct alisp_instance *instance, struct alisp_object * p) -{ - if (p == NULL) - return; - if (alisp_compare_type(p, ALISP_OBJ_CONS)) { - delete_tree(instance, p->value.c.car); - delete_tree(instance, p->value.c.cdr); - } - delete_object(instance, p); -} - -static struct alisp_object * incref_object(struct alisp_instance *instance ATTRIBUTE_UNUSED, struct alisp_object * p) -{ - if (p == NULL || p == &alsa_lisp_nil || p == &alsa_lisp_t) - return p; - if (alisp_get_refs(p) == ALISP_MAX_REFS) { - assert(0); - fprintf(stderr, "OOPS: alsa lisp: incref fatal error\n"); - exit(EXIT_FAILURE); - } - alisp_inc_refs(p); - return p; -} - -static struct alisp_object * incref_tree(struct alisp_instance *instance, struct alisp_object * p) -{ - if (p == NULL) - return NULL; - if (alisp_compare_type(p, ALISP_OBJ_CONS)) { - incref_tree(instance, p->value.c.car); - incref_tree(instance, p->value.c.cdr); - } - return incref_object(instance, p); -} - -/* Function not used yet. Leave it commented out until we actually use it to - * avoid compiler complaints */ -#if 0 -static struct alisp_object * incref_tree_explicit(struct alisp_instance *instance, struct alisp_object * p, struct alisp_object * e) -{ - if (p == NULL) - return NULL; - if (alisp_compare_type(p, ALISP_OBJ_CONS)) { - if (e == p) { - incref_tree(instance, p->value.c.car); - incref_tree(instance, p->value.c.cdr); - } else { - incref_tree_explicit(instance, p->value.c.car, e); - incref_tree_explicit(instance, p->value.c.cdr, e); - } - } - if (e == p) - return incref_object(instance, p); - return p; -} -#endif - -static void free_objects(struct alisp_instance *instance) -{ - struct list_head *pos, *pos1; - struct alisp_object * p; - struct alisp_object_pair * pair; - int i, j; - - for (i = 0; i < ALISP_OBJ_PAIR_HASH_SIZE; i++) { - list_for_each_safe(pos, pos1, &instance->setobjs_list[i]) { - pair = list_entry(pos, struct alisp_object_pair, list); - lisp_debug(instance, "freeing pair: '%s' -> %p", pair->name, pair->value); - delete_tree(instance, pair->value); - free((void *)pair->name); - free(pair); - } - } - for (i = 0; i < ALISP_OBJ_PAIR_HASH_SIZE; i++) - for (j = 0; j <= ALISP_OBJ_LAST_SEARCH; j++) { - list_for_each_safe(pos, pos1, &instance->used_objs_list[i][j]) { - p = list_entry(pos, struct alisp_object, list); - lisp_warn(instance, "object %p is still referenced %i times!", p, alisp_get_refs(p)); -#if 0 - snd_output_printf(instance->wout, ">>>> "); - princ_object(instance->wout, p); - snd_output_printf(instance->wout, " <<<<\n"); -#endif - if (alisp_get_refs(p) > 0) - alisp_set_refs(p, 1); - delete_object(instance, p); - } - } - list_for_each_safe(pos, pos1, &instance->free_objs_list) { - p = list_entry(pos, struct alisp_object, list); - list_del(&p->list); - free(p); - lisp_debug(instance, "freed (all) cons %p", p); - } -} - -static struct alisp_object * search_object_identifier(struct alisp_instance *instance, const char *s) -{ - struct list_head * pos; - struct alisp_object * p; - - list_for_each(pos, &instance->used_objs_list[get_string_hash(s)][ALISP_OBJ_IDENTIFIER]) { - p = list_entry(pos, struct alisp_object, list); - if (alisp_get_refs(p) > ALISP_MAX_REFS_LIMIT) - continue; - if (!strcmp(p->value.s, s)) - return incref_object(instance, p); - } - - return NULL; -} - -static struct alisp_object * search_object_string(struct alisp_instance *instance, const char *s) -{ - struct list_head * pos; - struct alisp_object * p; - - list_for_each(pos, &instance->used_objs_list[get_string_hash(s)][ALISP_OBJ_STRING]) { - p = list_entry(pos, struct alisp_object, list); - if (!strcmp(p->value.s, s)) { - if (alisp_get_refs(p) > ALISP_MAX_REFS_LIMIT) - continue; - return incref_object(instance, p); - } - } - - return NULL; -} - -static struct alisp_object * search_object_integer(struct alisp_instance *instance, long in) -{ - struct list_head * pos; - struct alisp_object * p; - - list_for_each(pos, &instance->used_objs_list[in & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_INTEGER]) { - p = list_entry(pos, struct alisp_object, list); - if (p->value.i == in) { - if (alisp_get_refs(p) > ALISP_MAX_REFS_LIMIT) - continue; - return incref_object(instance, p); - } - } - - return NULL; -} - -static struct alisp_object * search_object_float(struct alisp_instance *instance, double in) -{ - struct list_head * pos; - struct alisp_object * p; - - list_for_each(pos, &instance->used_objs_list[(long)in & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_FLOAT]) { - p = list_entry(pos, struct alisp_object, list); - if (p->value.i == in) { - if (alisp_get_refs(p) > ALISP_MAX_REFS_LIMIT) - continue; - return incref_object(instance, p); - } - } - - return NULL; -} - -static struct alisp_object * search_object_pointer(struct alisp_instance *instance, const void *ptr) -{ - struct list_head * pos; - struct alisp_object * p; - - list_for_each(pos, &instance->used_objs_list[(long)ptr & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_POINTER]) { - p = list_entry(pos, struct alisp_object, list); - if (p->value.ptr == ptr) { - if (alisp_get_refs(p) > ALISP_MAX_REFS_LIMIT) - continue; - return incref_object(instance, p); - } - } - - return NULL; -} - -static struct alisp_object * new_integer(struct alisp_instance *instance, long value) -{ - struct alisp_object * obj; - - obj = search_object_integer(instance, value); - if (obj != NULL) - return obj; - obj = new_object(instance, ALISP_OBJ_INTEGER); - if (obj) { - list_add(&obj->list, &instance->used_objs_list[value & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_INTEGER]); - obj->value.i = value; - } - return obj; -} - -static struct alisp_object * new_float(struct alisp_instance *instance, double value) -{ - struct alisp_object * obj; - - obj = search_object_float(instance, value); - if (obj != NULL) - return obj; - obj = new_object(instance, ALISP_OBJ_FLOAT); - if (obj) { - list_add(&obj->list, &instance->used_objs_list[(long)value & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_FLOAT]); - obj->value.f = value; - } - return obj; -} - -static struct alisp_object * new_string(struct alisp_instance *instance, const char *str) -{ - struct alisp_object * obj; - - obj = search_object_string(instance, str); - if (obj != NULL) - return obj; - obj = new_object(instance, ALISP_OBJ_STRING); - if (obj) - list_add(&obj->list, &instance->used_objs_list[get_string_hash(str)][ALISP_OBJ_STRING]); - if (obj && (obj->value.s = strdup(str)) == NULL) { - delete_object(instance, obj); - nomem(); - return NULL; - } - return obj; -} - -static struct alisp_object * new_identifier(struct alisp_instance *instance, const char *id) -{ - struct alisp_object * obj; - - obj = search_object_identifier(instance, id); - if (obj != NULL) - return obj; - obj = new_object(instance, ALISP_OBJ_IDENTIFIER); - if (obj) - list_add(&obj->list, &instance->used_objs_list[get_string_hash(id)][ALISP_OBJ_IDENTIFIER]); - if (obj && (obj->value.s = strdup(id)) == NULL) { - delete_object(instance, obj); - nomem(); - return NULL; - } - return obj; -} - -static struct alisp_object * new_pointer(struct alisp_instance *instance, const void *ptr) -{ - struct alisp_object * obj; - - obj = search_object_pointer(instance, ptr); - if (obj != NULL) - return obj; - obj = new_object(instance, ALISP_OBJ_POINTER); - if (obj) { - list_add(&obj->list, &instance->used_objs_list[(long)ptr & ALISP_OBJ_PAIR_HASH_MASK][ALISP_OBJ_POINTER]); - obj->value.ptr = ptr; - } - return obj; -} - -static struct alisp_object * new_cons_pointer(struct alisp_instance * instance, const char *ptr_id, void *ptr) -{ - struct alisp_object * lexpr; - - if (ptr == NULL) - return &alsa_lisp_nil; - lexpr = new_object(instance, ALISP_OBJ_CONS); - if (lexpr == NULL) - return NULL; - lexpr->value.c.car = new_string(instance, ptr_id); - if (lexpr->value.c.car == NULL) - goto __end; - lexpr->value.c.cdr = new_pointer(instance, ptr); - if (lexpr->value.c.cdr == NULL) { - delete_object(instance, lexpr->value.c.car); - __end: - delete_object(instance, lexpr); - return NULL; - } - return lexpr; -} - -void alsa_lisp_init_objects(void) __attribute__ ((constructor)); - -void alsa_lisp_init_objects(void) -{ - memset(&alsa_lisp_nil, 0, sizeof(alsa_lisp_nil)); - alisp_set_type(&alsa_lisp_nil, ALISP_OBJ_NIL); - INIT_LIST_HEAD(&alsa_lisp_nil.list); - memset(&alsa_lisp_t, 0, sizeof(alsa_lisp_t)); - alisp_set_type(&alsa_lisp_t, ALISP_OBJ_T); - INIT_LIST_HEAD(&alsa_lisp_t.list); -} - -/* - * lexer - */ - -static int xgetc(struct alisp_instance *instance) -{ - instance->charno++; - if (instance->lex_bufp > instance->lex_buf) - return *--(instance->lex_bufp); - return snd_input_getc(instance->in); -} - -static inline void xungetc(struct alisp_instance *instance, int c) -{ - *(instance->lex_bufp)++ = c; - instance->charno--; -} - -static int init_lex(struct alisp_instance *instance) -{ - instance->charno = instance->lineno = 1; - instance->token_buffer_max = 10; - if ((instance->token_buffer = (char *)malloc(instance->token_buffer_max)) == NULL) { - nomem(); - return -ENOMEM; - } - instance->lex_bufp = instance->lex_buf; - return 0; -} - -static void done_lex(struct alisp_instance *instance) -{ - free(instance->token_buffer); -} - -static char * extend_buf(struct alisp_instance *instance, char *p) -{ - int off = p - instance->token_buffer; - - instance->token_buffer_max += 10; - instance->token_buffer = (char *)realloc(instance->token_buffer, instance->token_buffer_max); - if (instance->token_buffer == NULL) { - nomem(); - return NULL; - } - - return instance->token_buffer + off; -} - -static int gettoken(struct alisp_instance *instance) -{ - char *p; - int c; - - for (;;) { - c = xgetc(instance); - switch (c) { - case '\n': - ++instance->lineno; - break; - - case ' ': case '\f': case '\t': case '\v': case '\r': - break; - - case ';': - /* Comment: ";".*"\n" */ - while ((c = xgetc(instance)) != '\n' && c != EOF) - ; - if (c != EOF) - ++instance->lineno; - break; - - case '?': - /* Character: "?". */ - c = xgetc(instance); - sprintf(instance->token_buffer, "%d", c); - return instance->thistoken = ALISP_INTEGER; - - case '-': - /* Minus sign: "-". */ - c = xgetc(instance); - if (!isdigit(c)) { - xungetc(instance, c); - c = '-'; - goto got_id; - } - xungetc(instance, c); - c = '-'; - /* FALLTRHU */ - - case '0': - case '1': case '2': case '3': - case '4': case '5': case '6': - case '7': case '8': case '9': - /* Integer: [0-9]+ */ - p = instance->token_buffer; - instance->thistoken = ALISP_INTEGER; - do { - __ok: - if (p - instance->token_buffer >= instance->token_buffer_max - 1) { - p = extend_buf(instance, p); - if (p == NULL) - return instance->thistoken = EOF; - } - *p++ = c; - c = xgetc(instance); - if (c == '.' && instance->thistoken == ALISP_INTEGER) { - c = xgetc(instance); - xungetc(instance, c); - if (isdigit(c)) { - instance->thistoken = ALISP_FLOAT; - c = '.'; - goto __ok; - } else { - c = '.'; - } - } else if (c == 'e' && instance->thistoken == ALISP_FLOAT) { - c = xgetc(instance); - if (isdigit(c)) { - instance->thistoken = ALISP_FLOATE; - goto __ok; - } - } - } while (isdigit(c)); - xungetc(instance, c); - *p = '\0'; - return instance->thistoken; - - got_id: - case '!': case '_': case '+': case '*': case '/': case '%': - case '<': case '>': case '=': case '&': - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': - case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': - case 's': case 't': case 'u': case 'v': case 'w': case 'x': - case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': - case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': - case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': - case 'Y': case 'Z': - /* Identifier: [!-/+*%<>=&a-zA-Z_][-/+*%<>=&a-zA-Z_0-9]* */ - p = instance->token_buffer; - do { - if (p - instance->token_buffer >= instance->token_buffer_max - 1) { - p = extend_buf(instance, p); - if (p == NULL) - return instance->thistoken = EOF; - } - *p++ = c; - c = xgetc(instance); - } while (isalnum(c) || strchr("!_-+*/%<>=&", c) != NULL); - xungetc(instance, c); - *p = '\0'; - return instance->thistoken = ALISP_IDENTIFIER; - - case '"': - /* String: "\""([^"]|"\\".)*"\"" */ - p = instance->token_buffer; - while ((c = xgetc(instance)) != '"' && c != EOF) { - if (p - instance->token_buffer >= instance->token_buffer_max - 1) { - p = extend_buf(instance, p); - if (p == NULL) - return instance->thistoken = EOF; - } - if (c == '\\') { - c = xgetc(instance); - switch (c) { - case '\n': ++instance->lineno; break; - case 'a': *p++ = '\a'; break; - case 'b': *p++ = '\b'; break; - case 'f': *p++ = '\f'; break; - case 'n': *p++ = '\n'; break; - case 'r': *p++ = '\r'; break; - case 't': *p++ = '\t'; break; - case 'v': *p++ = '\v'; break; - default: *p++ = c; - } - } else { - if (c == '\n') - ++instance->lineno; - *p++ = c; - } - } - *p = '\0'; - return instance->thistoken = ALISP_STRING; - - default: - return instance->thistoken = c; - } - } -} - -/* - * parser - */ - -static struct alisp_object * parse_form(struct alisp_instance *instance) -{ - int thistoken; - struct alisp_object * p, * first = NULL, * prev = NULL; - - while ((thistoken = gettoken(instance)) != ')' && thistoken != EOF) { - /* - * Parse a dotted pair notation. - */ - if (thistoken == '.') { - gettoken(instance); - if (prev == NULL) { - lisp_error(instance, "unexpected '.'"); - __err: - delete_tree(instance, first); - return NULL; - } - prev->value.c.cdr = parse_object(instance, 1); - if (prev->value.c.cdr == NULL) - goto __err; - if ((thistoken = gettoken(instance)) != ')') { - lisp_error(instance, "expected ')'"); - goto __err; - } - break; - } - - p = new_object(instance, ALISP_OBJ_CONS); - if (p == NULL) - goto __err; - - if (first == NULL) - first = p; - if (prev != NULL) - prev->value.c.cdr = p; - - p->value.c.car = parse_object(instance, 1); - if (p->value.c.car == NULL) - goto __err; - - prev = p; - } - - if (first == NULL) - return &alsa_lisp_nil; - else - return first; -} - -static struct alisp_object * quote_object(struct alisp_instance *instance, struct alisp_object * obj) -{ - struct alisp_object * p; - - if (obj == NULL) - goto __end1; - - p = new_object(instance, ALISP_OBJ_CONS); - if (p == NULL) - goto __end1; - - p->value.c.car = new_identifier(instance, "quote"); - if (p->value.c.car == NULL) - goto __end; - p->value.c.cdr = new_object(instance, ALISP_OBJ_CONS); - if (p->value.c.cdr == NULL) { - delete_object(instance, p->value.c.car); - __end: - delete_object(instance, p); - __end1: - delete_tree(instance, obj); - return NULL; - } - - p->value.c.cdr->value.c.car = obj; - return p; -} - -static inline struct alisp_object * parse_quote(struct alisp_instance *instance) -{ - return quote_object(instance, parse_object(instance, 0)); -} - -static struct alisp_object * parse_object(struct alisp_instance *instance, int havetoken) -{ - int thistoken; - struct alisp_object * p = NULL; - - if (!havetoken) - thistoken = gettoken(instance); - else - thistoken = instance->thistoken; - - switch (thistoken) { - case EOF: - break; - case '(': - p = parse_form(instance); - break; - case '\'': - p = parse_quote(instance); - break; - case ALISP_IDENTIFIER: - if (!strcmp(instance->token_buffer, "t")) - p = &alsa_lisp_t; - else if (!strcmp(instance->token_buffer, "nil")) - p = &alsa_lisp_nil; - else { - p = new_identifier(instance, instance->token_buffer); - } - break; - case ALISP_INTEGER: { - p = new_integer(instance, atol(instance->token_buffer)); - break; - } - case ALISP_FLOAT: - case ALISP_FLOATE: { - p = new_float(instance, atof(instance->token_buffer)); - break; - } - case ALISP_STRING: - p = new_string(instance, instance->token_buffer); - break; - default: - lisp_warn(instance, "%d:%d: unexpected character `%c'", instance->lineno, instance->charno, thistoken); - break; - } - - return p; -} - -/* - * object manipulation - */ - -static struct alisp_object_pair * set_object_direct(struct alisp_instance *instance, struct alisp_object * name, struct alisp_object * value) -{ - struct alisp_object_pair *p; - const char *id; - - id = name->value.s; - p = (struct alisp_object_pair *)malloc(sizeof(struct alisp_object_pair)); - if (p == NULL) { - nomem(); - return NULL; - } - p->name = strdup(id); - if (p->name == NULL) { - delete_tree(instance, value); - free(p); - return NULL; - } - list_add(&p->list, &instance->setobjs_list[get_string_hash(id)]); - p->value = value; - return p; -} - -static int check_set_object(struct alisp_instance * instance, struct alisp_object * name) -{ - if (name == &alsa_lisp_nil) { - lisp_warn(instance, "setting the value of a nil object"); - return 0; - } - if (name == &alsa_lisp_t) { - lisp_warn(instance, "setting the value of a t object"); - return 0; - } - if (!alisp_compare_type(name, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(name, ALISP_OBJ_STRING)) { - lisp_warn(instance, "setting the value of an object with non-indentifier"); - return 0; - } - return 1; -} - -static struct alisp_object_pair * set_object(struct alisp_instance *instance, struct alisp_object * name, struct alisp_object * value) -{ - struct list_head *pos; - struct alisp_object_pair *p; - const char *id; - - if (name == NULL || value == NULL) - return NULL; - - id = name->value.s; - - list_for_each(pos, &instance->setobjs_list[get_string_hash(id)]) { - p = list_entry(pos, struct alisp_object_pair, list); - if (!strcmp(p->name, id)) { - delete_tree(instance, p->value); - p->value = value; - return p; - } - } - - p = (struct alisp_object_pair *)malloc(sizeof(struct alisp_object_pair)); - if (p == NULL) { - nomem(); - return NULL; - } - p->name = strdup(id); - if (p->name == NULL) { - delete_tree(instance, value); - free(p); - return NULL; - } - list_add(&p->list, &instance->setobjs_list[get_string_hash(id)]); - p->value = value; - return p; -} - -static struct alisp_object * unset_object(struct alisp_instance *instance, struct alisp_object * name) -{ - struct list_head *pos; - struct alisp_object *res; - struct alisp_object_pair *p; - const char *id; - - if (!alisp_compare_type(name, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(name, ALISP_OBJ_STRING)) { - lisp_warn(instance, "unset object with a non-indentifier"); - return &alsa_lisp_nil; - } - id = name->value.s; - - list_for_each(pos, &instance->setobjs_list[get_string_hash(id)]) { - p = list_entry(pos, struct alisp_object_pair, list); - if (!strcmp(p->name, id)) { - list_del(&p->list); - res = p->value; - free((void *)p->name); - free(p); - return res; - } - } - - return &alsa_lisp_nil; -} - -static struct alisp_object * get_object1(struct alisp_instance *instance, const char *id) -{ - struct alisp_object_pair *p; - struct list_head *pos; - - list_for_each(pos, &instance->setobjs_list[get_string_hash(id)]) { - p = list_entry(pos, struct alisp_object_pair, list); - if (!strcmp(p->name, id)) - return p->value; - } - - return &alsa_lisp_nil; -} - -static struct alisp_object * get_object(struct alisp_instance *instance, struct alisp_object * name) -{ - if (!alisp_compare_type(name, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(name, ALISP_OBJ_STRING)) { - delete_tree(instance, name); - return &alsa_lisp_nil; - } - return get_object1(instance, name->value.s); -} - -static struct alisp_object * replace_object(struct alisp_instance *instance, struct alisp_object * name, struct alisp_object * onew) -{ - struct alisp_object_pair *p; - struct alisp_object *r; - struct list_head *pos; - const char *id; - - if (!alisp_compare_type(name, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(name, ALISP_OBJ_STRING)) { - delete_tree(instance, name); - return &alsa_lisp_nil; - } - id = name->value.s; - list_for_each(pos, &instance->setobjs_list[get_string_hash(id)]) { - p = list_entry(pos, struct alisp_object_pair, list); - if (!strcmp(p->name, id)) { - r = p->value; - p->value = onew; - return r; - } - } - - return NULL; -} - -static void dump_objects(struct alisp_instance *instance, const char *fname) -{ - struct alisp_object_pair *p; - snd_output_t *out; - struct list_head *pos; - int i, err; - - if (!strcmp(fname, "-")) - err = snd_output_stdio_attach(&out, stdout, 0); - else - err = snd_output_stdio_open(&out, fname, "w+"); - if (err < 0) { - SNDERR("alisp: cannot open file '%s' for writing (%s)", fname, snd_strerror(errno)); - return; - } - - for (i = 0; i < ALISP_OBJ_PAIR_HASH_SIZE; i++) { - list_for_each(pos, &instance->setobjs_list[i]) { - p = list_entry(pos, struct alisp_object_pair, list); - if (alisp_compare_type(p->value, ALISP_OBJ_CONS) && - alisp_compare_type(p->value->value.c.car, ALISP_OBJ_IDENTIFIER) && - !strcmp(p->value->value.c.car->value.s, "lambda")) { - snd_output_printf(out, "(defun %s ", p->name); - princ_cons(out, p->value->value.c.cdr); - snd_output_printf(out, ")\n"); - continue; - } - snd_output_printf(out, "(setq %s '", p->name); - princ_object(out, p->value); - snd_output_printf(out, ")\n"); - } - } - snd_output_close(out); -} - -static const char *obj_type_str(struct alisp_object * p) -{ - switch (alisp_get_type(p)) { - case ALISP_OBJ_NIL: return "nil"; - case ALISP_OBJ_T: return "t"; - case ALISP_OBJ_INTEGER: return "integer"; - case ALISP_OBJ_FLOAT: return "float"; - case ALISP_OBJ_IDENTIFIER: return "identifier"; - case ALISP_OBJ_STRING: return "string"; - case ALISP_OBJ_POINTER: return "pointer"; - case ALISP_OBJ_CONS: return "cons"; - default: assert(0); - } -} - -static void print_obj_lists(struct alisp_instance *instance, snd_output_t *out) -{ - struct list_head *pos; - struct alisp_object * p; - int i, j; - - snd_output_printf(out, "** used objects\n"); - for (i = 0; i < ALISP_OBJ_PAIR_HASH_SIZE; i++) - for (j = 0; j <= ALISP_OBJ_LAST_SEARCH; j++) - list_for_each(pos, &instance->used_objs_list[i][j]) { - p = list_entry(pos, struct alisp_object, list); - snd_output_printf(out, "** %p (%s) (", p, obj_type_str(p)); - if (!alisp_compare_type(p, ALISP_OBJ_CONS)) - princ_object(out, p); - else - snd_output_printf(out, "cons"); - snd_output_printf(out, ") refs=%i\n", alisp_get_refs(p)); - } - snd_output_printf(out, "** free objects\n"); - list_for_each(pos, &instance->free_objs_list) { - p = list_entry(pos, struct alisp_object, list); - snd_output_printf(out, "** %p\n", p); - } -} - -static void dump_obj_lists(struct alisp_instance *instance, const char *fname) -{ - snd_output_t *out; - int err; - - if (!strcmp(fname, "-")) - err = snd_output_stdio_attach(&out, stdout, 0); - else - err = snd_output_stdio_open(&out, fname, "w+"); - if (err < 0) { - SNDERR("alisp: cannot open file '%s' for writing (%s)", fname, snd_strerror(errno)); - return; - } - - print_obj_lists(instance, out); - - snd_output_close(out); -} - -/* - * functions - */ - -static int count_list(struct alisp_object * p) -{ - int i = 0; - - while (p != &alsa_lisp_nil && alisp_compare_type(p, ALISP_OBJ_CONS)) { - p = p->value.c.cdr; - ++i; - } - - return i; -} - -static inline struct alisp_object * car(struct alisp_object * p) -{ - if (alisp_compare_type(p, ALISP_OBJ_CONS)) - return p->value.c.car; - - return &alsa_lisp_nil; -} - -static inline struct alisp_object * cdr(struct alisp_object * p) -{ - if (alisp_compare_type(p, ALISP_OBJ_CONS)) - return p->value.c.cdr; - - return &alsa_lisp_nil; -} - -/* - * Syntax: (car expr) - */ -static struct alisp_object * F_car(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object *p1 = car(args), *p2; - delete_tree(instance, cdr(args)); - delete_object(instance, args); - p1 = eval(instance, p1); - delete_tree(instance, cdr(p1)); - p2 = car(p1); - delete_object(instance, p1); - return p2; -} - -/* - * Syntax: (cdr expr) - */ -static struct alisp_object * F_cdr(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object *p1 = car(args), *p2; - delete_tree(instance, cdr(args)); - delete_object(instance, args); - p1 = eval(instance, p1); - delete_tree(instance, car(p1)); - p2 = cdr(p1); - delete_object(instance, p1); - return p2; -} - -/* - * Syntax: (+ expr...) - */ -static struct alisp_object * F_add(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * n; - long v = 0; - double f = 0; - int type = ALISP_OBJ_INTEGER; - - p1 = eval(instance, car(p)); - for (;;) { - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - if (type == ALISP_OBJ_FLOAT) - f += p1->value.i; - else - v += p1->value.i; - } else if (alisp_compare_type(p1, ALISP_OBJ_FLOAT)) { - f += p1->value.f + v; - v = 0; - type = ALISP_OBJ_FLOAT; - } else { - lisp_warn(instance, "sum with a non integer or float operand"); - } - delete_tree(instance, p1); - p = cdr(n = p); - delete_object(instance, n); - if (p == &alsa_lisp_nil) - break; - p1 = eval(instance, car(p)); - } - if (type == ALISP_OBJ_INTEGER) { - return new_integer(instance, v); - } else { - return new_float(instance, f); - } -} - -/* - * Syntax: (concat expr...) - */ -static struct alisp_object * F_concat(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * n; - char *str = NULL, *str1; - - p1 = eval(instance, car(p)); - for (;;) { - if (alisp_compare_type(p1, ALISP_OBJ_STRING)) { - str1 = realloc(str, (str ? strlen(str) : 0) + strlen(p1->value.s) + 1); - if (str1 == NULL) { - nomem(); - free(str); - return NULL; - } - if (str == NULL) - strcpy(str1, p1->value.s); - else - strcat(str1, p1->value.s); - str = str1; - } else { - lisp_warn(instance, "concat with a non string or identifier operand"); - } - delete_tree(instance, p1); - p = cdr(n = p); - delete_object(instance, n); - if (p == &alsa_lisp_nil) - break; - p1 = eval(instance, car(p)); - } - if (str) { - p = new_string(instance, str); - free(str); - } else { - p = &alsa_lisp_nil; - } - return p; -} - -/* - * Syntax: (- expr...) - */ -static struct alisp_object * F_sub(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * n; - long v = 0; - double f = 0; - int type = ALISP_OBJ_INTEGER; - - do { - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - if (p == args && cdr(p) != &alsa_lisp_nil) { - v = p1->value.i; - } else { - if (type == ALISP_OBJ_FLOAT) - f -= p1->value.i; - else - v -= p1->value.i; - } - } else if (alisp_compare_type(p1, ALISP_OBJ_FLOAT)) { - if (type == ALISP_OBJ_INTEGER) { - f = v; - type = ALISP_OBJ_FLOAT; - } - if (p == args && cdr(p) != &alsa_lisp_nil) - f = p1->value.f; - else { - f -= p1->value.f; - } - } else - lisp_warn(instance, "difference with a non integer or float operand"); - delete_tree(instance, p1); - n = cdr(p); - delete_object(instance, p); - p = n; - } while (p != &alsa_lisp_nil); - - if (type == ALISP_OBJ_INTEGER) { - return new_integer(instance, v); - } else { - return new_float(instance, f); - } -} - -/* - * Syntax: (* expr...) - */ -static struct alisp_object * F_mul(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * n; - long v = 1; - double f = 1; - int type = ALISP_OBJ_INTEGER; - - do { - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - if (type == ALISP_OBJ_FLOAT) - f *= p1->value.i; - else - v *= p1->value.i; - } else if (alisp_compare_type(p1, ALISP_OBJ_FLOAT)) { - f *= p1->value.f * v; v = 1; - type = ALISP_OBJ_FLOAT; - } else { - lisp_warn(instance, "product with a non integer or float operand"); - } - delete_tree(instance, p1); - n = cdr(p); - delete_object(instance, p); - p = n; - } while (p != &alsa_lisp_nil); - - if (type == ALISP_OBJ_INTEGER) { - return new_integer(instance, v); - } else { - return new_float(instance, f); - } -} - -/* - * Syntax: (/ expr...) - */ -static struct alisp_object * F_div(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * n; - long v = 0; - double f = 0; - int type = ALISP_OBJ_INTEGER; - - do { - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - if (p == args && cdr(p) != &alsa_lisp_nil) { - v = p1->value.i; - } else { - if (p1->value.i == 0) { - lisp_warn(instance, "division by zero"); - v = 0; - f = 0; - break; - } else { - if (type == ALISP_OBJ_FLOAT) - f /= p1->value.i; - else - v /= p1->value.i; - } - } - } else if (alisp_compare_type(p1, ALISP_OBJ_FLOAT)) { - if (type == ALISP_OBJ_INTEGER) { - f = v; - type = ALISP_OBJ_FLOAT; - } - if (p == args && cdr(p) != &alsa_lisp_nil) { - f = p1->value.f; - } else { - if (p1->value.f == 0) { - lisp_warn(instance, "division by zero"); - f = 0; - break; - } else { - f /= p1->value.i; - } - } - } else - lisp_warn(instance, "quotient with a non integer or float operand"); - delete_tree(instance, p1); - n = cdr(p); - delete_object(instance, p); - p = n; - } while (p != &alsa_lisp_nil); - - if (type == ALISP_OBJ_INTEGER) { - return new_integer(instance, v); - } else { - return new_float(instance, f); - } -} - -/* - * Syntax: (% expr1 expr2) - */ -static struct alisp_object * F_mod(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * p3; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p2->value.i == 0) { - lisp_warn(instance, "module by zero"); - p3 = new_integer(instance, 0); - } else { - p3 = new_integer(instance, p1->value.i % p2->value.i); - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - f1 = fmod(f1, f2); - if (f1 == EDOM) { - lisp_warn(instance, "module by zero"); - p3 = new_float(instance, 0); - } else { - p3 = new_float(instance, f1); - } - } else { - lisp_warn(instance, "module with a non integer or float operand"); - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return p3; -} - -/* - * Syntax: (< expr1 expr2) - */ -static struct alisp_object * F_lt(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p1->value.i < p2->value.i) { - __true: - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - if (f1 < f2) - goto __true; - } else { - lisp_warn(instance, "comparison with a non integer or float operand"); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (> expr1 expr2) - */ -static struct alisp_object * F_gt(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p1->value.i > p2->value.i) { - __true: - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - if (f1 > f2) - goto __true; - } else { - lisp_warn(instance, "comparison with a non integer or float operand"); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (<= expr1 expr2) - */ -static struct alisp_object * F_le(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p1->value.i <= p2->value.i) { - __true: - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - if (f1 <= f2) - goto __true; - } else { - lisp_warn(instance, "comparison with a non integer or float operand"); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (>= expr1 expr2) - */ -static struct alisp_object * F_ge(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p1->value.i >= p2->value.i) { - __true: - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - if (f1 >= f2) - goto __true; - } else { - lisp_warn(instance, "comparison with a non integer or float operand"); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (= expr1 expr2) - */ -static struct alisp_object * F_numeq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (alisp_compare_type(p1, ALISP_OBJ_INTEGER) && - alisp_compare_type(p2, ALISP_OBJ_INTEGER)) { - if (p1->value.i == p2->value.i) { - __true: - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - } else if ((alisp_compare_type(p1, ALISP_OBJ_INTEGER) || - alisp_compare_type(p1, ALISP_OBJ_FLOAT)) && - (alisp_compare_type(p2, ALISP_OBJ_INTEGER) || - alisp_compare_type(p2, ALISP_OBJ_FLOAT))) { - double f1, f2; - f1 = alisp_compare_type(p1, ALISP_OBJ_INTEGER) ? p1->value.i : p1->value.f; - f2 = alisp_compare_type(p2, ALISP_OBJ_INTEGER) ? p2->value.i : p2->value.f; - if (f1 == f2) - goto __true; - } else { - lisp_warn(instance, "comparison with a non integer or float operand"); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (!= expr1 expr2) - */ -static struct alisp_object * F_numneq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p; - - p = F_numeq(instance, args); - if (p == &alsa_lisp_nil) - return &alsa_lisp_t; - return &alsa_lisp_nil; -} - -/* - * Syntax: (exfun name) - * Test, if a function exists - */ -static struct alisp_object * F_exfun(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - p2 = get_object(instance, p1); - if (p2 == &alsa_lisp_nil) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - p2 = car(p2); - if (alisp_compare_type(p2, ALISP_OBJ_IDENTIFIER) && - !strcmp(p2->value.s, "lambda")) { - delete_tree(instance, p1); - return &alsa_lisp_t; - } - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -static void princ_string(snd_output_t *out, char *s) -{ - char *p; - - snd_output_putc(out, '"'); - for (p = s; *p != '\0'; ++p) - switch (*p) { - case '\a': snd_output_putc(out, '\\'); snd_output_putc(out, 'a'); break; - case '\b': snd_output_putc(out, '\\'); snd_output_putc(out, 'b'); break; - case '\f': snd_output_putc(out, '\\'); snd_output_putc(out, 'f'); break; - case '\n': snd_output_putc(out, '\\'); snd_output_putc(out, 'n'); break; - case '\r': snd_output_putc(out, '\\'); snd_output_putc(out, 'r'); break; - case '\t': snd_output_putc(out, '\\'); snd_output_putc(out, 't'); break; - case '\v': snd_output_putc(out, '\\'); snd_output_putc(out, 'v'); break; - case '"': snd_output_putc(out, '\\'); snd_output_putc(out, '"'); break; - default: snd_output_putc(out, *p); - } - snd_output_putc(out, '"'); -} - -static void princ_cons(snd_output_t *out, struct alisp_object * p) -{ - do { - princ_object(out, p->value.c.car); - p = p->value.c.cdr; - if (p != &alsa_lisp_nil) { - snd_output_putc(out, ' '); - if (!alisp_compare_type(p, ALISP_OBJ_CONS)) { - snd_output_printf(out, ". "); - princ_object(out, p); - } - } - } while (p != &alsa_lisp_nil && alisp_compare_type(p, ALISP_OBJ_CONS)); -} - -static void princ_object(snd_output_t *out, struct alisp_object * p) -{ - switch (alisp_get_type(p)) { - case ALISP_OBJ_NIL: - snd_output_printf(out, "nil"); - break; - case ALISP_OBJ_T: - snd_output_putc(out, 't'); - break; - case ALISP_OBJ_IDENTIFIER: - snd_output_printf(out, "%s", p->value.s); - break; - case ALISP_OBJ_STRING: - princ_string(out, p->value.s); - break; - case ALISP_OBJ_INTEGER: - snd_output_printf(out, "%ld", p->value.i); - break; - case ALISP_OBJ_FLOAT: - snd_output_printf(out, "%f", p->value.f); - break; - case ALISP_OBJ_POINTER: - snd_output_printf(out, "<%p>", p->value.ptr); - break; - case ALISP_OBJ_CONS: - snd_output_putc(out, '('); - princ_cons(out, p); - snd_output_putc(out, ')'); - } -} - -/* - * Syntax: (princ expr...) - */ -static struct alisp_object * F_princ(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1 = NULL, * n; - - do { - if (p1) - delete_tree(instance, p1); - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_STRING)) - snd_output_printf(instance->out, "%s", p1->value.s); - else - princ_object(instance->out, p1); - n = cdr(p); - delete_object(instance, p); - p = n; - } while (p != &alsa_lisp_nil); - - return p1; -} - -/* - * Syntax: (atom expr) - */ -static struct alisp_object * F_atom(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p; - - p = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (p == NULL) - return NULL; - - switch (alisp_get_type(p)) { - case ALISP_OBJ_T: - case ALISP_OBJ_NIL: - case ALISP_OBJ_INTEGER: - case ALISP_OBJ_FLOAT: - case ALISP_OBJ_STRING: - case ALISP_OBJ_IDENTIFIER: - case ALISP_OBJ_POINTER: - delete_tree(instance, p); - return &alsa_lisp_t; - default: - break; - } - - delete_tree(instance, p); - return &alsa_lisp_nil; -} - -/* - * Syntax: (cons expr1 expr2) - */ -static struct alisp_object * F_cons(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p; - - p = new_object(instance, ALISP_OBJ_CONS); - if (p) { - p->value.c.car = eval(instance, car(args)); - p->value.c.cdr = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - } else { - delete_tree(instance, args); - } - - return p; -} - -/* - * Syntax: (list expr1...) - */ -static struct alisp_object * F_list(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * first = NULL, * prev = NULL, * p1; - - if (p == &alsa_lisp_nil) - return &alsa_lisp_nil; - - do { - p1 = new_object(instance, ALISP_OBJ_CONS); - if (p1 == NULL) { - delete_tree(instance, p); - delete_tree(instance, first); - return NULL; - } - p1->value.c.car = eval(instance, car(p)); - if (p1->value.c.car == NULL) { - delete_tree(instance, first); - delete_tree(instance, cdr(p)); - delete_object(instance, p); - return NULL; - } - if (first == NULL) - first = p1; - if (prev != NULL) - prev->value.c.cdr = p1; - prev = p1; - p = cdr(p1 = p); - delete_object(instance, p1); - } while (p != &alsa_lisp_nil); - - return first; -} - -static inline int eq(struct alisp_object * p1, struct alisp_object * p2) -{ - return p1 == p2; -} - -static int equal(struct alisp_object * p1, struct alisp_object * p2) -{ - int type1, type2; - - if (eq(p1, p2)) - return 1; - - type1 = alisp_get_type(p1); - type2 = alisp_get_type(p2); - - if (type1 == ALISP_OBJ_CONS || type2 == ALISP_OBJ_CONS) - return 0; - - if (type1 == type2) { - switch (type1) { - case ALISP_OBJ_STRING: - return !strcmp(p1->value.s, p2->value.s); - case ALISP_OBJ_INTEGER: - return p1->value.i == p2->value.i; - case ALISP_OBJ_FLOAT: - return p1->value.i == p2->value.i; - } - } - - return 0; -} - -/* - * Syntax: (eq expr1 expr2) - */ -static struct alisp_object * F_eq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (eq(p1, p2)) { - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (equal expr1 expr2) - */ -static struct alisp_object * F_equal(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (equal(p1, p2)) { - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_t; - } - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (quote expr) - */ -static struct alisp_object * F_quote(struct alisp_instance *instance ATTRIBUTE_UNUSED, struct alisp_object * args) -{ - struct alisp_object *p = car(args); - - delete_tree(instance, cdr(args)); - delete_object(instance, args); - return p; -} - -/* - * Syntax: (and expr...) - */ -static struct alisp_object * F_and(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1 = NULL, * n; - - do { - if (p1) - delete_tree(instance, p1); - p1 = eval(instance, car(p)); - if (p1 == &alsa_lisp_nil) { - delete_tree(instance, p1); - delete_tree(instance, cdr(p)); - delete_object(instance, p); - return &alsa_lisp_nil; - } - p = cdr(n = p); - delete_object(instance, n); - } while (p != &alsa_lisp_nil); - - return p1; -} - -/* - * Syntax: (or expr...) - */ -static struct alisp_object * F_or(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1 = NULL, * n; - - do { - if (p1) - delete_tree(instance, p1); - p1 = eval(instance, car(p)); - if (p1 != &alsa_lisp_nil) { - delete_tree(instance, cdr(p)); - delete_object(instance, p); - return p1; - } - p = cdr(n = p); - delete_object(instance, n); - } while (p != &alsa_lisp_nil); - - return &alsa_lisp_nil; -} - -/* - * Syntax: (not expr) - * Syntax: (null expr) - */ -static struct alisp_object * F_not(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = eval(instance, car(args)); - - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (p != &alsa_lisp_nil) { - delete_tree(instance, p); - return &alsa_lisp_nil; - } - - delete_tree(instance, p); - return &alsa_lisp_t; -} - -/* - * Syntax: (cond (expr1 [expr2])...) - */ -static struct alisp_object * F_cond(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * p2, * p3; - - do { - p1 = car(p); - if ((p2 = eval(instance, car(p1))) != &alsa_lisp_nil) { - p3 = cdr(p1); - delete_object(instance, p1); - delete_tree(instance, cdr(p)); - delete_object(instance, p); - if (p3 != &alsa_lisp_nil) { - delete_tree(instance, p2); - return F_progn(instance, p3); - } else { - delete_tree(instance, p3); - return p2; - } - } else { - delete_tree(instance, p2); - delete_tree(instance, cdr(p1)); - delete_object(instance, p1); - } - p = cdr(p2 = p); - delete_object(instance, p2); - } while (p != &alsa_lisp_nil); - - return &alsa_lisp_nil; -} - -/* - * Syntax: (if expr then-expr else-expr...) - */ -static struct alisp_object * F_if(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * p3; - - p1 = car(args); - p2 = car(cdr(args)); - p3 = cdr(cdr(args)); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - p1 = eval(instance, p1); - if (p1 != &alsa_lisp_nil) { - delete_tree(instance, p1); - delete_tree(instance, p3); - return eval(instance, p2); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return F_progn(instance, p3); -} - -/* - * Syntax: (when expr then-expr...) - */ -static struct alisp_object * F_when(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = car(args); - p2 = cdr(args); - delete_object(instance, args); - if ((p1 = eval(instance, p1)) != &alsa_lisp_nil) { - delete_tree(instance, p1); - return F_progn(instance, p2); - } else { - delete_tree(instance, p1); - delete_tree(instance, p2); - } - - return &alsa_lisp_nil; -} - -/* - * Syntax: (unless expr else-expr...) - */ -static struct alisp_object * F_unless(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2; - - p1 = car(args); - p2 = cdr(args); - delete_object(instance, args); - if ((p1 = eval(instance, p1)) == &alsa_lisp_nil) { - return F_progn(instance, p2); - } else { - delete_tree(instance, p1); - delete_tree(instance, p2); - } - - return &alsa_lisp_nil; -} - -/* - * Syntax: (while expr exprs...) - */ -static struct alisp_object * F_while(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * p3; - - p1 = car(args); - p2 = cdr(args); - - delete_object(instance, args); - while (1) { - incref_tree(instance, p1); - if ((p3 = eval(instance, p1)) == &alsa_lisp_nil) - break; - delete_tree(instance, p3); - incref_tree(instance, p2); - delete_tree(instance, F_progn(instance, p2)); - } - - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; -} - -/* - * Syntax: (progn expr...) - */ -static struct alisp_object * F_progn(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1 = NULL, * n; - - do { - if (p1) - delete_tree(instance, p1); - p1 = eval(instance, car(p)); - n = cdr(p); - delete_object(instance, p); - p = n; - } while (p != &alsa_lisp_nil); - - return p1; -} - -/* - * Syntax: (prog1 expr...) - */ -static struct alisp_object * F_prog1(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * first = NULL, * p1; - - do { - p1 = eval(instance, car(p)); - if (first == NULL) - first = p1; - else - delete_tree(instance, p1); - p1 = cdr(p); - delete_object(instance, p); - p = p1; - } while (p != &alsa_lisp_nil); - - if (first == NULL) - first = &alsa_lisp_nil; - - return first; -} - -/* - * Syntax: (prog2 expr...) - */ -static struct alisp_object * F_prog2(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * second = NULL, * p1; - int i = 0; - - do { - ++i; - p1 = eval(instance, car(p)); - if (i == 2) - second = p1; - else - delete_tree(instance, p1); - p1 = cdr(p); - delete_object(instance, p); - p = p1; - } while (p != &alsa_lisp_nil); - - if (second == NULL) - second = &alsa_lisp_nil; - - return second; -} - -/* - * Syntax: (set name value) - */ -static struct alisp_object * F_set(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1 = eval(instance, car(args)), - * p2 = eval(instance, car(cdr(args))); - - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - if (!check_set_object(instance, p1)) { - delete_tree(instance, p2); - p2 = &alsa_lisp_nil; - } else { - if (set_object(instance, p1, p2) == NULL) { - delete_tree(instance, p1); - delete_tree(instance, p2); - return NULL; - } - } - delete_tree(instance, p1); - return incref_tree(instance, p2); -} - -/* - * Syntax: (unset name) - */ -static struct alisp_object * F_unset(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1 = eval(instance, car(args)); - - delete_tree(instance, unset_object(instance, p1)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - return p1; -} - -/* - * Syntax: (setq name value...) - * Syntax: (setf name value...) - * `name' is not evalled - */ -static struct alisp_object * F_setq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1, * p2 = NULL, *n; - - do { - p1 = car(p); - p2 = eval(instance, car(cdr(p))); - n = cdr(cdr(p)); - delete_object(instance, cdr(p)); - delete_object(instance, p); - if (!check_set_object(instance, p1)) { - delete_tree(instance, p2); - p2 = &alsa_lisp_nil; - } else { - if (set_object(instance, p1, p2) == NULL) { - delete_tree(instance, p1); - delete_tree(instance, p2); - return NULL; - } - } - delete_tree(instance, p1); - p = n; - } while (p != &alsa_lisp_nil); - - return incref_tree(instance, p2); -} - -/* - * Syntax: (unsetq name...) - * Syntax: (unsetf name...) - * `name' is not evalled - */ -static struct alisp_object * F_unsetq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1 = NULL, * n; - - do { - if (p1) - delete_tree(instance, p1); - p1 = unset_object(instance, car(p)); - delete_tree(instance, car(p)); - p = cdr(n = p); - delete_object(instance, n); - } while (p != &alsa_lisp_nil); - - return p1; -} - -/* - * Syntax: (defun name arglist expr...) - * `name' is not evalled - * `arglist' is not evalled - */ -static struct alisp_object * F_defun(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1 = car(args), - * p2 = car(cdr(args)), - * p3 = cdr(cdr(args)); - struct alisp_object * lexpr; - - lexpr = new_object(instance, ALISP_OBJ_CONS); - if (lexpr) { - lexpr->value.c.car = new_identifier(instance, "lambda"); - if (lexpr->value.c.car == NULL) { - delete_object(instance, lexpr); - delete_tree(instance, args); - return NULL; - } - if ((lexpr->value.c.cdr = new_object(instance, ALISP_OBJ_CONS)) == NULL) { - delete_object(instance, lexpr->value.c.car); - delete_object(instance, lexpr); - delete_tree(instance, args); - return NULL; - } - lexpr->value.c.cdr->value.c.car = p2; - lexpr->value.c.cdr->value.c.cdr = p3; - delete_object(instance, cdr(args)); - delete_object(instance, args); - if (set_object(instance, p1, lexpr) == NULL) { - delete_tree(instance, p1); - delete_tree(instance, lexpr); - return NULL; - } - delete_tree(instance, p1); - } else { - delete_tree(instance, args); - } - return &alsa_lisp_nil; -} - -static struct alisp_object * eval_func(struct alisp_instance *instance, struct alisp_object * p, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * p3, * p4; - struct alisp_object ** eval_objs, ** save_objs; - int i; - - p1 = car(p); - if (alisp_compare_type(p1, ALISP_OBJ_IDENTIFIER) && - !strcmp(p1->value.s, "lambda")) { - p2 = car(cdr(p)); - p3 = args; - - if ((i = count_list(p2)) != count_list(p3)) { - lisp_warn(instance, "wrong number of parameters"); - goto _delete; - } - - eval_objs = malloc(2 * i * sizeof(struct alisp_object *)); - if (eval_objs == NULL) { - nomem(); - goto _delete; - } - save_objs = eval_objs + i; - - /* - * Save the new variable values. - */ - i = 0; - while (p3 != &alsa_lisp_nil) { - eval_objs[i++] = eval(instance, car(p3)); - p3 = cdr(p4 = p3); - delete_object(instance, p4); - } - - /* - * Save the old variable values and set the new ones. - */ - i = 0; - while (p2 != &alsa_lisp_nil) { - p3 = car(p2); - save_objs[i] = replace_object(instance, p3, eval_objs[i]); - if (save_objs[i] == NULL && - set_object_direct(instance, p3, eval_objs[i]) == NULL) { - p4 = NULL; - goto _end; - } - p2 = cdr(p2); - ++i; - } - - p4 = F_progn(instance, cdr(incref_tree(instance, p3 = cdr(p)))); - - /* - * Restore the old variable values. - */ - p2 = car(p3); - delete_object(instance, p3); - i = 0; - while (p2 != &alsa_lisp_nil) { - p3 = car(p2); - if (save_objs[i] == NULL) { - p3 = unset_object(instance, p3); - } else { - p3 = replace_object(instance, p3, save_objs[i]); - } - i++; - delete_tree(instance, p3); - delete_tree(instance, car(p2)); - p2 = cdr(p3 = p2); - delete_object(instance, p3); - } - - _end: - free(eval_objs); - - return p4; - } else { - _delete: - delete_tree(instance, args); - } - return &alsa_lisp_nil; -} - -struct alisp_object * F_gc(struct alisp_instance *instance ATTRIBUTE_UNUSED, struct alisp_object * args ATTRIBUTE_UNUSED) -{ - /* improved: no more traditional gc */ - return &alsa_lisp_t; -} - -/* - * Syntax: (path what) - * what is string ('data') - */ -struct alisp_object * F_path(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (!alisp_compare_type(p1, ALISP_OBJ_STRING)) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - if (!strcmp(p1->value.s, "data")) { - delete_tree(instance, p1); - return new_string(instance, snd_config_topdir()); - } - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -/* - * Syntax: (include filename...) - */ -struct alisp_object * F_include(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1; - int res = -ENOENT; - - do { - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_STRING)) - res = alisp_include_file(instance, p1->value.s); - delete_tree(instance, p1); - p = cdr(p1 = p); - delete_object(instance, p1); - } while (p != &alsa_lisp_nil); - - return new_integer(instance, res); -} - -/* - * Syntax: (string-to-integer value) - * 'value' can be integer or float type - */ -struct alisp_object * F_string_to_integer(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = eval(instance, car(args)), * p1; - - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (alisp_compare_type(p, ALISP_OBJ_INTEGER)) - return p; - if (alisp_compare_type(p, ALISP_OBJ_FLOAT)) { - p1 = new_integer(instance, floor(p->value.f)); - } else { - lisp_warn(instance, "expected an integer or float for integer conversion"); - p1 = &alsa_lisp_nil; - } - delete_tree(instance, p); - return p1; -} - -/* - * Syntax: (string-to-float value) - * 'value' can be integer or float type - */ -struct alisp_object * F_string_to_float(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = eval(instance, car(args)), * p1; - - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (alisp_compare_type(p, ALISP_OBJ_FLOAT)) - return p; - if (alisp_compare_type(p, ALISP_OBJ_INTEGER)) { - p1 = new_float(instance, p->value.i); - } else { - lisp_warn(instance, "expected an integer or float for integer conversion"); - p1 = &alsa_lisp_nil; - } - delete_tree(instance, p); - return p1; -} - -static int append_to_string(char **s, int *len, char *from, int size) -{ - if (*len == 0) { - *s = malloc(*len = size + 1); - if (*s == NULL) { - nomem(); - return -ENOMEM; - } - memcpy(*s, from, size); - } else { - *len += size; - *s = realloc(*s, *len); - if (*s == NULL) { - nomem(); - return -ENOMEM; - } - memcpy(*s + strlen(*s), from, size); - } - (*s)[*len - 1] = '\0'; - return 0; -} - -static int format_parse_char(struct alisp_instance *instance, char **s, int *len, struct alisp_object *p) -{ - char b; - - if (!alisp_compare_type(p, ALISP_OBJ_INTEGER)) { - lisp_warn(instance, "format: expected integer\n"); - return 0; - } - b = p->value.i; - return append_to_string(s, len, &b, 1); -} - -static int format_parse_integer(struct alisp_instance *instance, char **s, int *len, struct alisp_object *p) -{ - int res; - char *s1; - - if (!alisp_compare_type(p, ALISP_OBJ_INTEGER) && - !alisp_compare_type(p, ALISP_OBJ_FLOAT)) { - lisp_warn(instance, "format: expected integer or float\n"); - return 0; - } - s1 = malloc(64); - if (s1 == NULL) { - nomem(); - return -ENOMEM; - } - sprintf(s1, "%li", alisp_compare_type(p, ALISP_OBJ_FLOAT) ? (long)floor(p->value.f) : p->value.i); - res = append_to_string(s, len, s1, strlen(s1)); - free(s1); - return res; -} - -static int format_parse_float(struct alisp_instance *instance, char **s, int *len, struct alisp_object *p) -{ - int res; - char *s1; - - if (!alisp_compare_type(p, ALISP_OBJ_INTEGER) && - !alisp_compare_type(p, ALISP_OBJ_FLOAT)) { - lisp_warn(instance, "format: expected integer or float\n"); - return 0; - } - s1 = malloc(64); - if (s1 == NULL) { - nomem(); - return -ENOMEM; - } - sprintf(s1, "%f", alisp_compare_type(p, ALISP_OBJ_FLOAT) ? p->value.f : (double)p->value.i); - res = append_to_string(s, len, s1, strlen(s1)); - free(s1); - return res; -} - -static int format_parse_string(struct alisp_instance *instance, char **s, int *len, struct alisp_object *p) -{ - if (!alisp_compare_type(p, ALISP_OBJ_STRING)) { - lisp_warn(instance, "format: expected string\n"); - return 0; - } - return append_to_string(s, len, p->value.s, strlen(p->value.s)); -} - -/* - * Syntax: (format format value...) - * 'format' is C-like format string - */ -struct alisp_object * F_format(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = eval(instance, car(args)), * p1 = cdr(args), * n; - char *s, *s1, *s2; - int len; - - delete_object(instance, args); - if (!alisp_compare_type(p, ALISP_OBJ_STRING)) { - delete_tree(instance, p1); - delete_tree(instance, p); - lisp_warn(instance, "format: expected an format string"); - return &alsa_lisp_nil; - } - s = p->value.s; - s1 = NULL; - len = 0; - n = eval(instance, car(p1)); - do { - while (1) { - s2 = s; - while (*s2 && *s2 != '%') - s2++; - if (s2 != s) { - if (append_to_string(&s1, &len, s, s2 - s) < 0) { - __error: - delete_tree(instance, n); - delete_tree(instance, cdr(p1)); - delete_object(instance, p1); - delete_tree(instance, p); - return NULL; - } - } - if (*s2 == '%') - s2++; - switch (*s2) { - case '%': - if (append_to_string(&s1, &len, s2, 1) < 0) - goto __error; - s = s2 + 1; - break; - case 'c': - if (format_parse_char(instance, &s1, &len, n) < 0) - goto __error; - s = s2 + 1; - goto __next; - case 'd': - case 'i': - if (format_parse_integer(instance, &s1, &len, n) < 0) - goto __error; - s = s2 + 1; - goto __next; - case 'f': - if (format_parse_float(instance, &s1, &len, n) < 0) - goto __error; - s = s2 + 1; - goto __next; - case 's': - if (format_parse_string(instance, &s1, &len, n) < 0) - goto __error; - s = s2 + 1; - goto __next; - case '\0': - goto __end; - default: - lisp_warn(instance, "unknown format char '%c'", *s2); - s = s2 + 1; - goto __next; - } - } - __next: - delete_tree(instance, n); - p1 = cdr(n = p1); - delete_object(instance, n); - n = eval(instance, car(p1)); - } while (*s); - __end: - delete_tree(instance, n); - delete_tree(instance, cdr(p1)); - delete_object(instance, p1); - delete_tree(instance, p); - if (len > 0) { - p1 = new_string(instance, s1); - free(s1); - } else { - p1 = &alsa_lisp_nil; - } - return p1; -} - -/* - * Syntax: (compare-strings str1 start1 end1 str2 start2 end2 /opt-case-insensitive) - * 'str1' is first compared string - * 'start1' is first char (0..) - * 'end1' is last char (0..) - * 'str2' is second compared string - * 'start2' is first char (0..) - * 'end2' is last char (0..) - * /opt-case-insensitive true - case insensitive match - */ -struct alisp_object * F_compare_strings(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1 = args, * n, * p[7]; - char *s1, *s2; - int start1, end1, start2, end2; - - for (start1 = 0; start1 < 7; start1++) { - p[start1] = eval(instance, car(p1)); - p1 = cdr(n = p1); - delete_object(instance, n); - } - delete_tree(instance, p1); - if (alisp_compare_type(p[0], ALISP_OBJ_STRING)) { - lisp_warn(instance, "compare-strings: first argument must be string\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - if (alisp_compare_type(p[1], ALISP_OBJ_INTEGER)) { - lisp_warn(instance, "compare-strings: second argument must be integer\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - if (alisp_compare_type(p[2], ALISP_OBJ_INTEGER)) { - lisp_warn(instance, "compare-strings: third argument must be integer\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - if (alisp_compare_type(p[3], ALISP_OBJ_STRING)) { - lisp_warn(instance, "compare-strings: fifth argument must be string\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - if (!alisp_compare_type(p[4], ALISP_OBJ_NIL) && - !alisp_compare_type(p[4], ALISP_OBJ_INTEGER)) { - lisp_warn(instance, "compare-strings: fourth argument must be integer\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - if (!alisp_compare_type(p[5], ALISP_OBJ_NIL) && - !alisp_compare_type(p[5], ALISP_OBJ_INTEGER)) { - lisp_warn(instance, "compare-strings: sixth argument must be integer\n"); - p1 = &alsa_lisp_nil; - goto __err; - } - s1 = p[0]->value.s; - start1 = p[1]->value.i; - end1 = p[2]->value.i; - s2 = p[3]->value.s; - start2 = alisp_compare_type(p[4], ALISP_OBJ_NIL) ? 0 : p[4]->value.i; - end2 = alisp_compare_type(p[5], ALISP_OBJ_NIL) ? start2 + (end1 - start1) : p[5]->value.i; - if (start1 < 0 || start2 < 0 || end1 < 0 || end2 < 0 || - start1 >= (int)strlen(s1) || start2 >= (int)strlen(s2) || - (end1 - start1) != (end2 - start2)) { - p1 = &alsa_lisp_nil; - goto __err; - } - if (p[6] != &alsa_lisp_nil) { - while (start1 < end1) { - if (s1[start1] == '\0' || - s2[start2] == '\0' || - tolower(s1[start1]) != tolower(s2[start2])) { - p1 = &alsa_lisp_nil; - goto __err; - } - start1++; - start2++; - } - } else { - while (start1 < end1) { - if (s1[start1] == '\0' || - s2[start2] == '\0' || - s1[start1] != s2[start2]) { - p1 = &alsa_lisp_nil; - goto __err; - } - start1++; - start2++; - } - } - p1 = &alsa_lisp_t; - - __err: - for (start1 = 0; start1 < 7; start1++) - delete_tree(instance, p[start1]); - return p1; -} - -/* - * Syntax: (assoc key alist) - */ -struct alisp_object * F_assoc(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * n; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - do { - if (eq(p1, car(car(p2)))) { - n = car(p2); - delete_tree(instance, p1); - delete_tree(instance, cdr(p2)); - delete_object(instance, p2); - return n; - } - delete_tree(instance, car(p2)); - p2 = cdr(n = p2); - delete_object(instance, n); - } while (p2 != &alsa_lisp_nil); - - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -/* - * Syntax: (rassoc value alist) - */ -struct alisp_object * F_rassoc(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, *p2, * n; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - do { - if (eq(p1, cdr(car(p2)))) { - n = car(p2); - delete_tree(instance, p1); - delete_tree(instance, cdr(p2)); - delete_object(instance, p2); - return n; - } - delete_tree(instance, car(p2)); - p2 = cdr(n = p2); - delete_object(instance, n); - } while (p2 != &alsa_lisp_nil); - - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -/* - * Syntax: (assq key alist) - */ -struct alisp_object * F_assq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * n; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - do { - if (equal(p1, car(car(p2)))) { - n = car(p2); - delete_tree(instance, p1); - delete_tree(instance, cdr(p2)); - delete_object(instance, p2); - return n; - } - delete_tree(instance, car(p2)); - p2 = cdr(n = p2); - delete_object(instance, n); - } while (p2 != &alsa_lisp_nil); - - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -/* - * Syntax: (nth index alist) - */ -struct alisp_object * F_nth(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * n; - long idx; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - if (!alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - delete_tree(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; - } - if (!alisp_compare_type(p2, ALISP_OBJ_CONS)) { - delete_object(instance, p1); - delete_tree(instance, p2); - return &alsa_lisp_nil; - } - idx = p1->value.i; - delete_object(instance, p1); - while (idx-- > 0) { - delete_tree(instance, car(p2)); - p2 = cdr(n = p2); - delete_object(instance, n); - } - n = car(p2); - delete_tree(instance, cdr(p2)); - delete_object(instance, p2); - return n; -} - -/* - * Syntax: (rassq value alist) - */ -struct alisp_object * F_rassq(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, * p2, * n; - - p1 = eval(instance, car(args)); - p2 = eval(instance, car(cdr(args))); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - - do { - if (equal(p1, cdr(car(p2)))) { - n = car(p2); - delete_tree(instance, p1); - delete_tree(instance, cdr(p2)); - delete_object(instance, p2); - return n; - } - delete_tree(instance, car(p2)); - p2 = cdr(n = p2); - delete_object(instance, n); - } while (p2 != &alsa_lisp_nil); - - delete_tree(instance, p1); - return &alsa_lisp_nil; -} - -static struct alisp_object * F_dump_memory(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = car(args); - - if (p != &alsa_lisp_nil && cdr(args) == &alsa_lisp_nil && - alisp_compare_type(p, ALISP_OBJ_STRING)) { - if (strlen(p->value.s) > 0) { - dump_objects(instance, p->value.s); - delete_tree(instance, args); - return &alsa_lisp_t; - } else - lisp_warn(instance, "expected filename"); - } else - lisp_warn(instance, "wrong number of parameters (expected string)"); - - delete_tree(instance, args); - return &alsa_lisp_nil; -} - -static struct alisp_object * F_stat_memory(struct alisp_instance *instance, struct alisp_object * args) -{ - snd_output_printf(instance->out, "*** Memory stats\n"); - snd_output_printf(instance->out, " used_objs = %li, free_objs = %li, max_objs = %li, obj_size = %i (total bytes = %li, max bytes = %li)\n", - instance->used_objs, - instance->free_objs, - instance->max_objs, - (int)sizeof(struct alisp_object), - (long)((instance->used_objs + instance->free_objs) * sizeof(struct alisp_object)), - (long)(instance->max_objs * sizeof(struct alisp_object))); - delete_tree(instance, args); - return &alsa_lisp_nil; -} - -static struct alisp_object * F_check_memory(struct alisp_instance *instance, struct alisp_object * args) -{ - delete_tree(instance, args); - if (instance->used_objs > 0) { - fprintf(stderr, "!!!alsa lisp - check memory failed!!!\n"); - F_stat_memory(instance, &alsa_lisp_nil); - exit(EXIT_FAILURE); - } - return &alsa_lisp_t; -} - -static struct alisp_object * F_dump_objects(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = car(args); - - if (p != &alsa_lisp_nil && cdr(args) == &alsa_lisp_nil && - alisp_compare_type(p, ALISP_OBJ_STRING)) { - if (strlen(p->value.s) > 0) { - dump_obj_lists(instance, p->value.s); - delete_tree(instance, args); - return &alsa_lisp_t; - } else - lisp_warn(instance, "expected filename"); - } else - lisp_warn(instance, "wrong number of parameters (expected string)"); - - delete_tree(instance, args); - return &alsa_lisp_nil; -} - -struct intrinsic { - const char *name; - struct alisp_object * (*func)(struct alisp_instance *instance, struct alisp_object * args); -}; - -static const struct intrinsic intrinsics[] = { - { "!=", F_numneq }, - { "%", F_mod }, - { "&check-memory", F_check_memory }, - { "&dump-memory", F_dump_memory }, - { "&dump-objects", F_dump_objects }, - { "&stat-memory", F_stat_memory }, - { "*", F_mul }, - { "+", F_add }, - { "-", F_sub }, - { "/", F_div }, - { "<", F_lt }, - { "<=", F_le }, - { "=", F_numeq }, - { ">", F_gt }, - { ">=", F_ge }, - { "and", F_and }, - { "assoc", F_assoc }, - { "assq", F_assq }, - { "atom", F_atom }, - { "car", F_car }, - { "cdr", F_cdr }, - { "compare-strings", F_compare_strings }, - { "concat", F_concat }, - { "cond", F_cond }, - { "cons", F_cons }, - { "defun", F_defun }, - { "eq", F_eq }, - { "equal", F_equal }, - { "eval", F_eval }, - { "exfun", F_exfun }, - { "format", F_format }, - { "funcall", F_funcall }, - { "garbage-collect", F_gc }, - { "gc", F_gc }, - { "if", F_if }, - { "include", F_include }, - { "list", F_list }, - { "not", F_not }, - { "nth", F_nth }, - { "null", F_not }, - { "or", F_or }, - { "path", F_path }, - { "princ", F_princ }, - { "prog1", F_prog1 }, - { "prog2", F_prog2 }, - { "progn", F_progn }, - { "quote", F_quote }, - { "rassoc", F_rassoc }, - { "rassq", F_rassq }, - { "set", F_set }, - { "setf", F_setq }, - { "setq", F_setq }, - { "string-equal", F_equal }, - { "string-to-float", F_string_to_float }, - { "string-to-integer", F_string_to_integer }, - { "string-to-number", F_string_to_float }, - { "string=", F_equal }, - { "unless", F_unless }, - { "unset", F_unset }, - { "unsetf", F_unsetq }, - { "unsetq", F_unsetq }, - { "when", F_when }, - { "while", F_while }, -}; - -#include "alisp_snd.c" - -static int compar(const void *p1, const void *p2) -{ - return strcmp(((struct intrinsic *)p1)->name, - ((struct intrinsic *)p2)->name); -} - -static inline struct alisp_object * eval_cons1(struct alisp_instance *instance, struct alisp_object * p1, struct alisp_object * p2) -{ - struct alisp_object * p3; - struct intrinsic key, *item; - - key.name = p1->value.s; - - if ((item = bsearch(&key, intrinsics, - sizeof intrinsics / sizeof intrinsics[0], - sizeof intrinsics[0], compar)) != NULL) { - delete_object(instance, p1); - return item->func(instance, p2); - } - - if ((item = bsearch(&key, snd_intrinsics, - sizeof snd_intrinsics / sizeof snd_intrinsics[0], - sizeof snd_intrinsics[0], compar)) != NULL) { - delete_object(instance, p1); - return item->func(instance, p2); - } - - if ((p3 = get_object(instance, p1)) != &alsa_lisp_nil) { - delete_object(instance, p1); - return eval_func(instance, p3, p2); - } else { - lisp_warn(instance, "function `%s' is undefined", p1->value.s); - delete_object(instance, p1); - delete_tree(instance, p2); - } - - return &alsa_lisp_nil; -} - -/* - * Syntax: (funcall function args...) - */ -static struct alisp_object * F_funcall(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = eval(instance, car(args)), * p1; - - if (!alisp_compare_type(p, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(p, ALISP_OBJ_STRING)) { - lisp_warn(instance, "expected an function name"); - delete_tree(instance, p); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - return &alsa_lisp_nil; - } - p1 = cdr(args); - delete_object(instance, args); - return eval_cons1(instance, p, p1); -} - -static inline struct alisp_object * eval_cons(struct alisp_instance *instance, struct alisp_object * p) -{ - struct alisp_object * p1 = car(p), * p2; - - if (p1 != &alsa_lisp_nil && alisp_compare_type(p1, ALISP_OBJ_IDENTIFIER)) { - if (!strcmp(p1->value.s, "lambda")) - return p; - - p2 = cdr(p); - delete_object(instance, p); - return eval_cons1(instance, p1, p2); - } else { - delete_tree(instance, p); - } - - return &alsa_lisp_nil; -} - -static struct alisp_object * eval(struct alisp_instance *instance, struct alisp_object * p) -{ - switch (alisp_get_type(p)) { - case ALISP_OBJ_IDENTIFIER: { - struct alisp_object *r = incref_tree(instance, get_object(instance, p)); - delete_object(instance, p); - return r; - } - case ALISP_OBJ_INTEGER: - case ALISP_OBJ_FLOAT: - case ALISP_OBJ_STRING: - case ALISP_OBJ_POINTER: - return p; - case ALISP_OBJ_CONS: - return eval_cons(instance, p); - default: - break; - } - - return p; -} - -static struct alisp_object * F_eval(struct alisp_instance *instance, struct alisp_object * args) -{ - return eval(instance, eval(instance, car(args))); -} - -/* - * main routine - */ - -static int alisp_include_file(struct alisp_instance *instance, const char *filename) -{ - snd_input_t *old_in; - struct alisp_object *p, *p1; - char *name; - int retval = 0, err; - - err = snd_user_file(filename, &name); - if (err < 0) - return err; - old_in = instance->in; - err = snd_input_stdio_open(&instance->in, name, "r"); - if (err < 0) { - retval = err; - goto _err; - } - if (instance->verbose) - lisp_verbose(instance, "** include filename '%s'", name); - - for (;;) { - if ((p = parse_object(instance, 0)) == NULL) - break; - if (instance->verbose) { - lisp_verbose(instance, "** code"); - princ_object(instance->vout, p); - snd_output_putc(instance->vout, '\n'); - } - p1 = eval(instance, p); - if (p1 == NULL) { - retval = -ENOMEM; - break; - } - if (instance->verbose) { - lisp_verbose(instance, "** result"); - princ_object(instance->vout, p1); - snd_output_putc(instance->vout, '\n'); - } - delete_tree(instance, p1); - if (instance->debug) { - lisp_debug(instance, "** objects after operation"); - print_obj_lists(instance, instance->dout); - } - } - - snd_input_close(instance->in); - _err: - free(name); - instance->in = old_in; - return retval; -} - -int alsa_lisp(struct alisp_cfg *cfg, struct alisp_instance **_instance) -{ - struct alisp_instance *instance; - struct alisp_object *p, *p1; - int i, j, retval = 0; - - instance = (struct alisp_instance *)calloc(1, sizeof(struct alisp_instance)); - if (instance == NULL) { - nomem(); - return -ENOMEM; - } - instance->verbose = cfg->verbose && cfg->vout; - instance->warning = cfg->warning && cfg->wout; - instance->debug = cfg->debug && cfg->dout; - instance->in = cfg->in; - instance->out = cfg->out; - instance->vout = cfg->vout; - instance->eout = cfg->eout; - instance->wout = cfg->wout; - instance->dout = cfg->dout; - INIT_LIST_HEAD(&instance->free_objs_list); - for (i = 0; i < ALISP_OBJ_PAIR_HASH_SIZE; i++) { - for (j = 0; j <= ALISP_OBJ_LAST_SEARCH; j++) - INIT_LIST_HEAD(&instance->used_objs_list[i][j]); - INIT_LIST_HEAD(&instance->setobjs_list[i]); - } - - init_lex(instance); - - for (;;) { - if ((p = parse_object(instance, 0)) == NULL) - break; - if (instance->verbose) { - lisp_verbose(instance, "** code"); - princ_object(instance->vout, p); - snd_output_putc(instance->vout, '\n'); - } - p1 = eval(instance, p); - if (p1 == NULL) { - retval = -ENOMEM; - break; - } - if (instance->verbose) { - lisp_verbose(instance, "** result"); - princ_object(instance->vout, p1); - snd_output_putc(instance->vout, '\n'); - } - delete_tree(instance, p1); - if (instance->debug) { - lisp_debug(instance, "** objects after operation"); - print_obj_lists(instance, instance->dout); - } - } - - if (_instance) - *_instance = instance; - else - alsa_lisp_free(instance); - - return retval; -} - -void alsa_lisp_free(struct alisp_instance *instance) -{ - if (instance == NULL) - return; - done_lex(instance); - free_objects(instance); - free(instance); -} - -struct alisp_cfg *alsa_lisp_default_cfg(snd_input_t *input) -{ - snd_output_t *output, *eoutput; - struct alisp_cfg *cfg; - int err; - - err = snd_output_stdio_attach(&output, stdout, 0); - if (err < 0) - return NULL; - err = snd_output_stdio_attach(&eoutput, stderr, 0); - if (err < 0) { - snd_output_close(output); - return NULL; - } - cfg = calloc(1, sizeof(struct alisp_cfg)); - if (cfg == NULL) { - snd_output_close(eoutput); - snd_output_close(output); - return NULL; - } - cfg->out = output; - cfg->wout = eoutput; - cfg->eout = eoutput; - cfg->dout = eoutput; - cfg->in = input; - return cfg; -} - -void alsa_lisp_default_cfg_free(struct alisp_cfg *cfg) -{ - snd_input_close(cfg->in); - snd_output_close(cfg->out); - snd_output_close(cfg->dout); - free(cfg); -} - -int alsa_lisp_function(struct alisp_instance *instance, struct alisp_seq_iterator **result, - const char *id, const char *args, ...) -{ - int err = 0; - struct alisp_object *aargs = NULL, *obj, *res; - - if (args && *args != 'n') { - va_list ap; - struct alisp_object *p; - p = NULL; - va_start(ap, args); - while (*args) { - if (*args++ != '%') { - err = -EINVAL; - break; - } - if (*args == '\0') { - err = -EINVAL; - break; - } - obj = NULL; - err = 0; - switch (*args++) { - case 's': - obj = new_string(instance, va_arg(ap, char *)); - break; - case 'i': - obj = new_integer(instance, va_arg(ap, int)); - break; - case 'l': - obj = new_integer(instance, va_arg(ap, long)); - break; - case 'f': - case 'd': - obj = new_integer(instance, va_arg(ap, double)); - break; - case 'p': { - char _ptrid[24]; - char *ptrid = _ptrid; - while (*args && *args != '%') - *ptrid++ = *args++; - *ptrid = 0; - if (ptrid == _ptrid) { - err = -EINVAL; - break; - } - obj = new_cons_pointer(instance, _ptrid, va_arg(ap, void *)); - obj = quote_object(instance, obj); - break; - } - default: - err = -EINVAL; - break; - } - if (err < 0) - goto __args_end; - if (obj == NULL) { - err = -ENOMEM; - goto __args_end; - } - if (p == NULL) { - p = aargs = new_object(instance, ALISP_OBJ_CONS); - } else { - p->value.c.cdr = new_object(instance, ALISP_OBJ_CONS); - p = p->value.c.cdr; - } - if (p == NULL) { - err = -ENOMEM; - goto __args_end; - } - p->value.c.car = obj; - } - __args_end: - va_end(ap); - if (err < 0) - return err; -#if 0 - snd_output_printf(instance->wout, ">>>"); - princ_object(instance->wout, aargs); - snd_output_printf(instance->wout, "<<<\n"); -#endif - } - - err = -ENOENT; - if (aargs == NULL) - aargs = &alsa_lisp_nil; - if ((obj = get_object1(instance, id)) != &alsa_lisp_nil) { - res = eval_func(instance, obj, aargs); - err = 0; - } else { - struct intrinsic key, *item; - key.name = id; - if ((item = bsearch(&key, intrinsics, - sizeof intrinsics / sizeof intrinsics[0], - sizeof intrinsics[0], compar)) != NULL) { - res = item->func(instance, aargs); - err = 0; - } else if ((item = bsearch(&key, snd_intrinsics, - sizeof snd_intrinsics / sizeof snd_intrinsics[0], - sizeof snd_intrinsics[0], compar)) != NULL) { - res = item->func(instance, aargs); - err = 0; - } else { - res = &alsa_lisp_nil; - } - } - if (res == NULL) - err = -ENOMEM; - if (err == 0 && result) { - *result = res; - } else { - delete_tree(instance, res); - } - - return 0; -} - -void alsa_lisp_result_free(struct alisp_instance *instance, - struct alisp_seq_iterator *result) -{ - delete_tree(instance, result); -} - -int alsa_lisp_seq_first(struct alisp_instance *instance, const char *id, - struct alisp_seq_iterator **seq) -{ - struct alisp_object * p1; - - p1 = get_object1(instance, id); - if (p1 == NULL) - return -ENOMEM; - *seq = p1; - return 0; -} - -int alsa_lisp_seq_next(struct alisp_seq_iterator **seq) -{ - struct alisp_object * p1 = *seq; - - p1 = cdr(p1); - if (p1 == &alsa_lisp_nil) - return -ENOENT; - *seq = p1; - return 0; -} - -int alsa_lisp_seq_count(struct alisp_seq_iterator *seq) -{ - int count = 0; - - while (seq != &alsa_lisp_nil) { - count++; - seq = cdr(seq); - } - return count; -} - -int alsa_lisp_seq_integer(struct alisp_seq_iterator *seq, long *val) -{ - if (alisp_compare_type(seq, ALISP_OBJ_CONS)) - seq = seq->value.c.cdr; - if (alisp_compare_type(seq, ALISP_OBJ_INTEGER)) - *val = seq->value.i; - else - return -EINVAL; - return 0; -} - -int alsa_lisp_seq_pointer(struct alisp_seq_iterator *seq, const char *ptr_id, void **ptr) -{ - struct alisp_object * p2; - - if (alisp_compare_type(seq, ALISP_OBJ_CONS) && - alisp_compare_type(seq->value.c.car, ALISP_OBJ_CONS)) - seq = seq->value.c.car; - if (alisp_compare_type(seq, ALISP_OBJ_CONS)) { - p2 = seq->value.c.car; - if (!alisp_compare_type(p2, ALISP_OBJ_STRING)) - return -EINVAL; - if (strcmp(p2->value.s, ptr_id)) - return -EINVAL; - p2 = seq->value.c.cdr; - if (!alisp_compare_type(p2, ALISP_OBJ_POINTER)) - return -EINVAL; - *ptr = (void *)seq->value.ptr; - } else - return -EINVAL; - return 0; -} diff --git a/src/alisp/alisp_local.h b/src/alisp/alisp_local.h deleted file mode 100644 index af638843..00000000 --- a/src/alisp/alisp_local.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * ALSA lisp implementation - * Copyright (c) 2003 by Jaroslav Kysela - * - * Based on work of Sandro Sigala (slisp-1.2) - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "list.h" - -enum alisp_tokens { - ALISP_IDENTIFIER, - ALISP_INTEGER, - ALISP_FLOAT, - ALISP_FLOATE, - ALISP_STRING -}; - -enum alisp_objects { - ALISP_OBJ_INTEGER, - ALISP_OBJ_FLOAT, - ALISP_OBJ_IDENTIFIER, - ALISP_OBJ_STRING, - ALISP_OBJ_POINTER, - ALISP_OBJ_CONS, - ALISP_OBJ_LAST_SEARCH = ALISP_OBJ_CONS, - ALISP_OBJ_NIL, - ALISP_OBJ_T, -}; - -struct alisp_object; - -#define ALISP_TYPE_MASK 0xf0000000 -#define ALISP_TYPE_SHIFT 28 -#define ALISP_REFS_MASK 0x0fffffff -#define ALISP_REFS_SHIFT 0 -#define ALISP_MAX_REFS (ALISP_REFS_MASK>>ALISP_REFS_SHIFT) -#define ALISP_MAX_REFS_LIMIT ((ALISP_MAX_REFS + 1) / 2) - -struct alisp_object { - struct list_head list; - unsigned int type_refs; /* type and count of references */ - union { - char *s; - long i; - double f; - const void *ptr; - struct { - struct alisp_object *car; - struct alisp_object *cdr; - } c; - } value; -}; - -static inline enum alisp_objects alisp_get_type(struct alisp_object *p) -{ - return (p->type_refs >> ALISP_TYPE_SHIFT); -} - -static inline void alisp_set_type(struct alisp_object *p, enum alisp_objects type) -{ - p->type_refs &= ~ALISP_TYPE_MASK; - p->type_refs |= (unsigned int)type << ALISP_TYPE_SHIFT; -} - -static inline int alisp_compare_type(struct alisp_object *p, enum alisp_objects type) -{ - return ((unsigned int)type << ALISP_TYPE_SHIFT) == - (p->type_refs & ALISP_TYPE_MASK); -} - -static inline void alisp_set_refs(struct alisp_object *p, unsigned int refs) -{ - p->type_refs &= ~ALISP_REFS_MASK; - p->type_refs |= refs & ALISP_REFS_MASK; -} - -static inline unsigned int alisp_get_refs(struct alisp_object *p) -{ - return p->type_refs & ALISP_REFS_MASK; -} - -static inline unsigned int alisp_inc_refs(struct alisp_object *p) -{ - unsigned r = alisp_get_refs(p) + 1; - alisp_set_refs(p, r); - return r; -} - -static inline unsigned int alisp_dec_refs(struct alisp_object *p) -{ - unsigned r = alisp_get_refs(p) - 1; - alisp_set_refs(p, r); - return r; -} - -struct alisp_object_pair { - struct list_head list; - const char *name; - struct alisp_object *value; -}; - -#define ALISP_LEX_BUF_MAX 16 -#define ALISP_OBJ_PAIR_HASH_SHIFT 4 -#define ALISP_OBJ_PAIR_HASH_SIZE (1< - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "../control/control_local.h" - -struct acall_table { - const char *name; - struct alisp_object * (*func) (struct alisp_instance *instance, struct acall_table * item, struct alisp_object * args); - void * xfunc; - const char *prefix; -}; - -/* - * helper functions - */ - -static inline int get_integer(struct alisp_object * obj) -{ - if (alisp_compare_type(obj, ALISP_OBJ_INTEGER)) - return obj->value.i; - return 0; -} - -static inline const void *get_pointer(struct alisp_object * obj) -{ - if (alisp_compare_type(obj, ALISP_OBJ_POINTER)) - return obj->value.ptr; - return NULL; -} - -static const char *get_string(struct alisp_object * obj, const char * deflt) -{ - if (obj == &alsa_lisp_t) - return "true"; - if (alisp_compare_type(obj, ALISP_OBJ_STRING) || - alisp_compare_type(obj, ALISP_OBJ_IDENTIFIER)) - return obj->value.s; - return deflt; -} - -struct flags { - const char *key; - unsigned int mask; -}; - -static unsigned int get_flags(struct alisp_instance * instance, - struct alisp_object * obj, - const struct flags * flags, - unsigned int deflt) -{ - const char *key; - int invert; - unsigned int result; - const struct flags *ptr; - struct alisp_object *n; - - if (obj == &alsa_lisp_nil) - return deflt; - result = deflt; - do { - key = get_string(obj, NULL); - if (key) { - invert = key[0] == '!'; - key += invert; - ptr = flags; - while (ptr->key) { - if (!strcmp(ptr->key, key)) { - if (invert) - result &= ~ptr->mask; - else - result |= ptr->mask; - break; - } - ptr++; - } - } - delete_tree(instance, car(obj)); - obj = cdr(n = obj); - delete_object(instance, n); - } while (obj != &alsa_lisp_nil); - return result; -} - -static const void *get_ptr(struct alisp_instance * instance, - struct alisp_object * obj, - const char *_ptr_id) -{ - const char *ptr_id; - const void *ptr; - - ptr_id = get_string(car(obj), NULL); - if (ptr_id == NULL) { - delete_tree(instance, obj); - return NULL; - } - if (strcmp(ptr_id, _ptr_id)) { - delete_tree(instance, obj); - return NULL; - } - ptr = get_pointer(cdr(obj)); - delete_tree(instance, obj); - return ptr; -} - -static struct alisp_object * new_lexpr(struct alisp_instance * instance, int err) -{ - struct alisp_object * lexpr; - - lexpr = new_object(instance, ALISP_OBJ_CONS); - if (lexpr == NULL) - return NULL; - lexpr->value.c.car = new_integer(instance, err); - if (lexpr->value.c.car == NULL) { - delete_object(instance, lexpr); - return NULL; - } - lexpr->value.c.cdr = new_object(instance, ALISP_OBJ_CONS); - if (lexpr->value.c.cdr == NULL) { - delete_object(instance, lexpr->value.c.car); - delete_object(instance, lexpr); - return NULL; - } - return lexpr; -} - -static struct alisp_object * add_cons(struct alisp_instance * instance, - struct alisp_object *lexpr, - int cdr, const char *id, - struct alisp_object *obj) -{ - struct alisp_object * p1, * p2; - - if (lexpr == NULL || obj == NULL) { - delete_tree(instance, obj); - return NULL; - } - if (cdr) { - p1 = lexpr->value.c.cdr = new_object(instance, ALISP_OBJ_CONS); - } else { - p1 = lexpr->value.c.car = new_object(instance, ALISP_OBJ_CONS); - } - lexpr = p1; - if (p1 == NULL) { - delete_tree(instance, obj); - return NULL; - } - p1->value.c.car = new_object(instance, ALISP_OBJ_CONS); - if ((p2 = p1->value.c.car) == NULL) - goto __err; - p2->value.c.car = new_string(instance, id); - if (p2->value.c.car == NULL) { - __err: - if (cdr) - lexpr->value.c.cdr = NULL; - else - lexpr->value.c.car = NULL; - delete_tree(instance, p1); - delete_tree(instance, obj); - return NULL; - } - p2->value.c.cdr = obj; - return lexpr; -} - -static struct alisp_object * add_cons2(struct alisp_instance * instance, - struct alisp_object *lexpr, - int cdr, struct alisp_object *obj) -{ - struct alisp_object * p1; - - if (lexpr == NULL || obj == NULL) { - delete_tree(instance, obj); - return NULL; - } - if (cdr) { - p1 = lexpr->value.c.cdr = new_object(instance, ALISP_OBJ_CONS); - } else { - p1 = lexpr->value.c.car = new_object(instance, ALISP_OBJ_CONS); - } - lexpr = p1; - if (p1 == NULL) { - delete_tree(instance, obj); - return NULL; - } - p1->value.c.car = obj; - return lexpr; -} - -static struct alisp_object * new_result1(struct alisp_instance * instance, - int err, const char *ptr_id, void *ptr) -{ - struct alisp_object * lexpr, * p1; - - if (err < 0) - ptr = NULL; - lexpr = new_object(instance, ALISP_OBJ_CONS); - if (lexpr == NULL) - return NULL; - lexpr->value.c.car = new_integer(instance, err); - if (lexpr->value.c.car == NULL) { - delete_object(instance, lexpr); - return NULL; - } - p1 = add_cons(instance, lexpr, 1, ptr_id, new_pointer(instance, ptr)); - if (p1 == NULL) { - delete_object(instance, lexpr); - return NULL; - } - return lexpr; -} - -static struct alisp_object * new_result2(struct alisp_instance * instance, - int err, int val) -{ - struct alisp_object * lexpr, * p1; - - if (err < 0) - val = 0; - lexpr = new_lexpr(instance, err); - if (lexpr == NULL) - return NULL; - p1 = lexpr->value.c.cdr; - p1->value.c.car = new_integer(instance, val); - if (p1->value.c.car == NULL) { - delete_object(instance, lexpr); - return NULL; - } - return lexpr; -} - -static struct alisp_object * new_result3(struct alisp_instance * instance, - int err, const char *str) -{ - struct alisp_object * lexpr, * p1; - - if (err < 0) - str = ""; - lexpr = new_lexpr(instance, err); - if (lexpr == NULL) - return NULL; - p1 = lexpr->value.c.cdr; - p1->value.c.car = new_string(instance, str); - if (p1->value.c.car == NULL) { - delete_object(instance, lexpr); - return NULL; - } - return lexpr; -} - -/* - * macros - */ - -/* - * HCTL functions - */ - -typedef int (*snd_int_pp_strp_int_t)(void **rctl, const char *name, int mode); -typedef int (*snd_int_pp_p_t)(void **rctl, void *handle); -typedef int (*snd_int_p_t)(void *rctl); -typedef char * (*snd_str_p_t)(void *rctl); -typedef int (*snd_int_intp_t)(int *val); -typedef int (*snd_int_str_t)(const char *str); -typedef int (*snd_int_int_strp_t)(int val, char **str); -typedef void *(*snd_p_p_t)(void *handle); - -static struct alisp_object * FA_int_pp_strp_int(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - const char *name; - int err, mode; - void *handle; - struct alisp_object *p1, *p2; - static const struct flags flags[] = { - { "nonblock", SND_CTL_NONBLOCK }, - { "async", SND_CTL_ASYNC }, - { "readonly", SND_CTL_READONLY }, - { NULL, 0 } - }; - - name = get_string(p1 = eval(instance, car(args)), NULL); - if (name == NULL) - return &alsa_lisp_nil; - mode = get_flags(instance, p2 = eval(instance, car(cdr(args))), flags, 0); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - delete_tree(instance, p2); - err = ((snd_int_pp_strp_int_t)item->xfunc)(&handle, name, mode); - delete_tree(instance, p1); - return new_result1(instance, err, item->prefix, handle); -} - -static struct alisp_object * FA_int_pp_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - int err; - void *handle; - const char *prefix1; - struct alisp_object *p1; - - if (item->xfunc == &snd_hctl_open_ctl) - prefix1 = "ctl"; - else { - delete_tree(instance, args); - return &alsa_lisp_nil; - } - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (void *)get_ptr(instance, p1, prefix1); - if (handle == NULL) - return &alsa_lisp_nil; - err = ((snd_int_pp_p_t)item->xfunc)(&handle, handle); - return new_result1(instance, err, item->prefix, handle); -} - -static struct alisp_object * FA_p_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - void *handle; - const char *prefix1; - struct alisp_object * p1; - - if (item->xfunc == &snd_hctl_first_elem || - item->xfunc == &snd_hctl_last_elem || - item->xfunc == &snd_hctl_elem_next || - item->xfunc == &snd_hctl_elem_prev) - prefix1 = "hctl_elem"; - else if (item->xfunc == &snd_hctl_ctl) - prefix1 = "ctl"; - else { - delete_tree(instance, args); - return &alsa_lisp_nil; - } - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (void *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - handle = ((snd_p_p_t)item->xfunc)(handle); - return new_cons_pointer(instance, prefix1, handle); -} - -static struct alisp_object * FA_int_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - void *handle; - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (void *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - return new_integer(instance, ((snd_int_p_t)item->xfunc)(handle)); -} - -static struct alisp_object * FA_str_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - void *handle; - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (void *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - return new_string(instance, ((snd_str_p_t)item->xfunc)(handle)); -} - -static struct alisp_object * FA_int_intp(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - int val, err; - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (!alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - val = p1->value.i; - delete_tree(instance, p1); - err = ((snd_int_intp_t)item->xfunc)(&val); - return new_result2(instance, err, val); -} - -static struct alisp_object * FA_int_str(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - int err; - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (!alisp_compare_type(p1, ALISP_OBJ_STRING) && - !alisp_compare_type(p1, ALISP_OBJ_IDENTIFIER)) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - err = ((snd_int_str_t)item->xfunc)(p1->value.s); - delete_tree(instance, p1); - return new_integer(instance, err); -} - -static struct alisp_object * FA_int_int_strp(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - int err; - char *str; - long val; - struct alisp_object * p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - if (!alisp_compare_type(p1, ALISP_OBJ_INTEGER)) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - val = p1->value.i; - delete_tree(instance, p1); - err = ((snd_int_int_strp_t)item->xfunc)(val, &str); - return new_result3(instance, err, str); -} - -static struct alisp_object * FA_card_info(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_ctl_t *handle; - struct alisp_object * lexpr, * p1; - snd_ctl_card_info_t info = {0}; - int err; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (snd_ctl_t *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - err = snd_ctl_card_info(handle, &info); - lexpr = new_lexpr(instance, err); - if (err < 0) - return lexpr; - p1 = add_cons(instance, lexpr->value.c.cdr, 0, "id", new_string(instance, snd_ctl_card_info_get_id(&info))); - p1 = add_cons(instance, p1, 1, "driver", new_string(instance, snd_ctl_card_info_get_driver(&info))); - p1 = add_cons(instance, p1, 1, "name", new_string(instance, snd_ctl_card_info_get_name(&info))); - p1 = add_cons(instance, p1, 1, "longname", new_string(instance, snd_ctl_card_info_get_longname(&info))); - p1 = add_cons(instance, p1, 1, "mixername", new_string(instance, snd_ctl_card_info_get_mixername(&info))); - p1 = add_cons(instance, p1, 1, "components", new_string(instance, snd_ctl_card_info_get_components(&info))); - if (p1 == NULL) { - delete_tree(instance, lexpr); - return NULL; - } - return lexpr; -} - -static struct alisp_object * create_ctl_elem_id(struct alisp_instance * instance, snd_ctl_elem_id_t * id, struct alisp_object * cons) -{ - cons = add_cons(instance, cons, 0, "numid", new_integer(instance, snd_ctl_elem_id_get_numid(id))); - cons = add_cons(instance, cons, 1, "iface", new_string(instance, snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(id)))); - cons = add_cons(instance, cons, 1, "dev", new_integer(instance, snd_ctl_elem_id_get_device(id))); - cons = add_cons(instance, cons, 1, "subdev", new_integer(instance, snd_ctl_elem_id_get_subdevice(id))); - cons = add_cons(instance, cons, 1, "name", new_string(instance, snd_ctl_elem_id_get_name(id))); - cons = add_cons(instance, cons, 1, "index", new_integer(instance, snd_ctl_elem_id_get_index(id))); - return cons; -} - -static int parse_ctl_elem_id(struct alisp_instance * instance, - struct alisp_object * cons, - snd_ctl_elem_id_t * id) -{ - struct alisp_object *p1; - const char *xid; - - if (cons == NULL) - return -ENOMEM; - snd_ctl_elem_id_clear(id); - id->numid = 0; - do { - p1 = car(cons); - if (alisp_compare_type(p1, ALISP_OBJ_CONS)) { - xid = get_string(p1->value.c.car, NULL); - if (xid == NULL) { - /* noop */ - } else if (!strcmp(xid, "numid")) { - snd_ctl_elem_id_set_numid(id, get_integer(p1->value.c.cdr)); - } else if (!strcmp(xid, "iface")) { - snd_ctl_elem_id_set_interface(id, snd_config_get_ctl_iface_ascii(get_string(p1->value.c.cdr, "0"))); - } else if (!strcmp(xid, "dev")) { - snd_ctl_elem_id_set_device(id, get_integer(p1->value.c.cdr)); - } else if (!strcmp(xid, "subdev")) { - snd_ctl_elem_id_set_subdevice(id, get_integer(p1->value.c.cdr)); - } else if (!strcmp(xid, "name")) { - snd_ctl_elem_id_set_name(id, get_string(p1->value.c.cdr, "?")); - } else if (!strcmp(xid, "index")) { - snd_ctl_elem_id_set_index(id, get_integer(p1->value.c.cdr)); - } - } - delete_tree(instance, p1); - cons = cdr(p1 = cons); - delete_object(instance, p1); - } while (cons != &alsa_lisp_nil); - return 0; -} - -static struct alisp_object * FA_hctl_find_elem(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_hctl_t *handle; - snd_ctl_elem_id_t id = {0}; - struct alisp_object *p1; - - handle = (snd_hctl_t *)get_ptr(instance, car(args), item->prefix); - if (handle == NULL) { - delete_tree(instance, cdr(args)); - delete_object(instance, args); - return &alsa_lisp_nil; - } - p1 = car(cdr(args)); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - if (parse_ctl_elem_id(instance, eval(instance, p1), &id) < 0) - return &alsa_lisp_nil; - return new_cons_pointer(instance, "hctl_elem", snd_hctl_find_elem(handle, &id)); -} - -static struct alisp_object * FA_hctl_elem_info(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_hctl_elem_t *handle; - struct alisp_object * lexpr, * p1, * p2; - snd_ctl_elem_info_t info = {0}; - snd_ctl_elem_id_t id = {0}; - snd_ctl_elem_type_t type; - int err; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (snd_hctl_elem_t *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - err = snd_hctl_elem_info(handle, &info); - lexpr = new_lexpr(instance, err); - if (err < 0) - return lexpr; - type = snd_ctl_elem_info_get_type(&info); - p1 = add_cons(instance, lexpr->value.c.cdr, 0, "id", p2 = new_object(instance, ALISP_OBJ_CONS)); - snd_ctl_elem_info_get_id(&info, &id); - if (create_ctl_elem_id(instance, &id, p2) == NULL) { - delete_tree(instance, lexpr); - return NULL; - } - p1 = add_cons(instance, p1, 1, "type", new_string(instance, snd_ctl_elem_type_name(type))); - p1 = add_cons(instance, p1, 1, "readable", new_integer(instance, snd_ctl_elem_info_is_readable(&info))); - p1 = add_cons(instance, p1, 1, "writable", new_integer(instance, snd_ctl_elem_info_is_writable(&info))); - p1 = add_cons(instance, p1, 1, "volatile", new_integer(instance, snd_ctl_elem_info_is_volatile(&info))); - p1 = add_cons(instance, p1, 1, "inactive", new_integer(instance, snd_ctl_elem_info_is_inactive(&info))); - p1 = add_cons(instance, p1, 1, "locked", new_integer(instance, snd_ctl_elem_info_is_locked(&info))); - p1 = add_cons(instance, p1, 1, "isowner", new_integer(instance, snd_ctl_elem_info_is_owner(&info))); - p1 = add_cons(instance, p1, 1, "owner", new_integer(instance, snd_ctl_elem_info_get_owner(&info))); - p1 = add_cons(instance, p1, 1, "count", new_integer(instance, snd_ctl_elem_info_get_count(&info))); - err = INTERNAL(snd_ctl_elem_info_get_dimensions)(&info); - if (err > 0) { - int idx; - p1 = add_cons(instance, p1, 1, "dimensions", p2 = new_object(instance, ALISP_OBJ_CONS)); - for (idx = 0; idx < err; idx++) - p2 = add_cons2(instance, p2, idx > 0, new_integer(instance, INTERNAL(snd_ctl_elem_info_get_dimension)(&info, idx))); - } - switch (type) { - case SND_CTL_ELEM_TYPE_ENUMERATED: { - unsigned int items, item; - items = snd_ctl_elem_info_get_items(&info); - p1 = add_cons(instance, p1, 1, "items", p2 = new_object(instance, ALISP_OBJ_CONS)); - for (item = 0; item < items; item++) { - snd_ctl_elem_info_set_item(&info, item); - err = snd_hctl_elem_info(handle, &info); - if (err < 0) { - p2 = add_cons2(instance, p2, item, &alsa_lisp_nil); - } else { - p2 = add_cons2(instance, p2, item, new_string(instance, snd_ctl_elem_info_get_item_name(&info))); - } - } - break; - } - case SND_CTL_ELEM_TYPE_INTEGER: - p1 = add_cons(instance, p1, 1, "min", new_integer(instance, snd_ctl_elem_info_get_min(&info))); - p1 = add_cons(instance, p1, 1, "max", new_integer(instance, snd_ctl_elem_info_get_max(&info))); - p1 = add_cons(instance, p1, 1, "step", new_integer(instance, snd_ctl_elem_info_get_step(&info))); - break; - case SND_CTL_ELEM_TYPE_INTEGER64: - p1 = add_cons(instance, p1, 1, "min64", new_float(instance, snd_ctl_elem_info_get_min64(&info))); - p1 = add_cons(instance, p1, 1, "max64", new_float(instance, snd_ctl_elem_info_get_max64(&info))); - p1 = add_cons(instance, p1, 1, "step64", new_float(instance, snd_ctl_elem_info_get_step64(&info))); - break; - default: - break; - } - if (p1 == NULL) { - delete_tree(instance, lexpr); - return NULL; - } - return lexpr; -} - -static struct alisp_object * FA_hctl_elem_read(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_hctl_elem_t *handle; - struct alisp_object * lexpr, * p1 = NULL, * obj; - snd_ctl_elem_info_t info = {0}; - snd_ctl_elem_value_t value = {0}; - snd_ctl_elem_type_t type; - unsigned int idx, count; - int err; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (snd_hctl_elem_t *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - err = snd_hctl_elem_info(handle, &info); - if (err >= 0) - err = snd_hctl_elem_read(handle, &value); - lexpr = new_lexpr(instance, err); - if (err < 0) - return lexpr; - type = snd_ctl_elem_info_get_type(&info); - count = snd_ctl_elem_info_get_count(&info); - if (type == SND_CTL_ELEM_TYPE_IEC958) { - count = sizeof(snd_aes_iec958_t); - type = SND_CTL_ELEM_TYPE_BYTES; - } - for (idx = 0; idx < count; idx++) { - switch (type) { - case SND_CTL_ELEM_TYPE_BOOLEAN: - obj = new_integer(instance, snd_ctl_elem_value_get_boolean(&value, idx)); - break; - case SND_CTL_ELEM_TYPE_INTEGER: - obj = new_integer(instance, snd_ctl_elem_value_get_integer(&value, idx)); - break; - case SND_CTL_ELEM_TYPE_INTEGER64: - obj = new_integer(instance, snd_ctl_elem_value_get_integer64(&value, idx)); - break; - case SND_CTL_ELEM_TYPE_ENUMERATED: - obj = new_integer(instance, snd_ctl_elem_value_get_enumerated(&value, idx)); - break; - case SND_CTL_ELEM_TYPE_BYTES: - obj = new_integer(instance, snd_ctl_elem_value_get_byte(&value, idx)); - break; - default: - obj = NULL; - break; - } - if (idx == 0) { - p1 = add_cons2(instance, lexpr->value.c.cdr, 0, obj); - } else { - p1 = add_cons2(instance, p1, 1, obj); - } - } - if (p1 == NULL) { - delete_tree(instance, lexpr); - return &alsa_lisp_nil; - } - return lexpr; -} - -static struct alisp_object * FA_hctl_elem_write(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_hctl_elem_t *handle; - struct alisp_object * p1 = NULL, * obj; - snd_ctl_elem_info_t info = {0}; - snd_ctl_elem_value_t value = {0}; - snd_ctl_elem_type_t type; - unsigned int idx, count; - int err; - - p1 = car(cdr(args)); - obj = eval(instance, car(args)); - delete_tree(instance, cdr(cdr(args))); - delete_object(instance, cdr(args)); - delete_object(instance, args); - handle = (snd_hctl_elem_t *)get_ptr(instance, obj, item->prefix); - if (handle == NULL) { - delete_tree(instance, p1); - return &alsa_lisp_nil; - } - err = snd_hctl_elem_info(handle, &info); - if (err < 0) { - delete_tree(instance, p1); - return new_integer(instance, err); - } - type = snd_ctl_elem_info_get_type(&info); - count = snd_ctl_elem_info_get_count(&info); - if (type == SND_CTL_ELEM_TYPE_IEC958) { - count = sizeof(snd_aes_iec958_t); - type = SND_CTL_ELEM_TYPE_BYTES; - } - idx = -1; - do { - if (++idx >= count) { - delete_tree(instance, p1); - break; - } - obj = car(p1); - switch (type) { - case SND_CTL_ELEM_TYPE_BOOLEAN: - snd_ctl_elem_value_set_boolean(&value, idx, get_integer(obj)); - break; - case SND_CTL_ELEM_TYPE_INTEGER: - snd_ctl_elem_value_set_integer(&value, idx, get_integer(obj)); - break; - case SND_CTL_ELEM_TYPE_INTEGER64: - snd_ctl_elem_value_set_integer64(&value, idx, get_integer(obj)); - break; - case SND_CTL_ELEM_TYPE_ENUMERATED: - snd_ctl_elem_value_set_enumerated(&value, idx, get_integer(obj)); - break; - case SND_CTL_ELEM_TYPE_BYTES: - snd_ctl_elem_value_set_byte(&value, idx, get_integer(obj)); - break; - default: - break; - } - delete_tree(instance, obj); - p1 = cdr(obj = p1); - delete_object(instance, obj); - } while (p1 != &alsa_lisp_nil); - err = snd_hctl_elem_write(handle, &value); - return new_integer(instance, err); -} - -static struct alisp_object * FA_pcm_info(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args) -{ - snd_pcm_t *handle; - struct alisp_object * lexpr, * p1; - snd_pcm_info_t info = {0}; - int err; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - handle = (snd_pcm_t *)get_ptr(instance, p1, item->prefix); - if (handle == NULL) - return &alsa_lisp_nil; - err = snd_pcm_info(handle, &info); - lexpr = new_lexpr(instance, err); - if (err < 0) - return lexpr; - p1 = add_cons(instance, lexpr->value.c.cdr, 0, "card", new_integer(instance, snd_pcm_info_get_card(&info))); - p1 = add_cons(instance, p1, 1, "device", new_integer(instance, snd_pcm_info_get_device(&info))); - p1 = add_cons(instance, p1, 1, "subdevice", new_integer(instance, snd_pcm_info_get_subdevice(&info))); - p1 = add_cons(instance, p1, 1, "id", new_string(instance, snd_pcm_info_get_id(&info))); - p1 = add_cons(instance, p1, 1, "name", new_string(instance, snd_pcm_info_get_name(&info))); - p1 = add_cons(instance, p1, 1, "subdevice_name", new_string(instance, snd_pcm_info_get_subdevice_name(&info))); - p1 = add_cons(instance, p1, 1, "class", new_integer(instance, snd_pcm_info_get_class(&info))); - p1 = add_cons(instance, p1, 1, "subclass", new_integer(instance, snd_pcm_info_get_subclass(&info))); - p1 = add_cons(instance, p1, 1, "subdevices_count", new_integer(instance, snd_pcm_info_get_subdevices_count(&info))); - p1 = add_cons(instance, p1, 1, "subdevices_avail", new_integer(instance, snd_pcm_info_get_subdevices_avail(&info))); - //p1 = add_cons(instance, p1, 1, "sync", new_string(instance, snd_pcm_info_get_sync(&info))); - return lexpr; -} - -/* - * main code - */ - -static const struct acall_table acall_table[] = { - { "card_get_index", &FA_int_str, (void *)snd_card_get_index, NULL }, - { "card_get_longname", &FA_int_int_strp, (void *)snd_card_get_longname, NULL }, - { "card_get_name", &FA_int_int_strp, (void *)snd_card_get_name, NULL }, - { "card_next", &FA_int_intp, (void *)&snd_card_next, NULL }, - { "ctl_card_info", &FA_card_info, NULL, "ctl" }, - { "ctl_close", &FA_int_p, (void *)&snd_ctl_close, "ctl" }, - { "ctl_open", &FA_int_pp_strp_int, (void *)&snd_ctl_open, "ctl" }, - { "hctl_close", &FA_int_p, (void *)&snd_hctl_close, "hctl" }, - { "hctl_ctl", &FA_p_p, (void *)&snd_hctl_ctl, "hctl" }, - { "hctl_elem_info", &FA_hctl_elem_info, (void *)&snd_hctl_elem_info, "hctl_elem" }, - { "hctl_elem_next", &FA_p_p, (void *)&snd_hctl_elem_next, "hctl_elem" }, - { "hctl_elem_prev", &FA_p_p, (void *)&snd_hctl_elem_prev, "hctl_elem" }, - { "hctl_elem_read", &FA_hctl_elem_read, (void *)&snd_hctl_elem_read, "hctl_elem" }, - { "hctl_elem_write", &FA_hctl_elem_write, (void *)&snd_hctl_elem_write, "hctl_elem" }, - { "hctl_find_elem", &FA_hctl_find_elem, (void *)&snd_hctl_find_elem, "hctl" }, - { "hctl_first_elem", &FA_p_p, (void *)&snd_hctl_first_elem, "hctl" }, - { "hctl_free", &FA_int_p, (void *)&snd_hctl_free, "hctl" }, - { "hctl_last_elem", &FA_p_p, (void *)&snd_hctl_last_elem, "hctl" }, - { "hctl_load", &FA_int_p, (void *)&snd_hctl_load, "hctl" }, - { "hctl_open", &FA_int_pp_strp_int, (void *)&snd_hctl_open, "hctl" }, - { "hctl_open_ctl", &FA_int_pp_p, (void *)&snd_hctl_open_ctl, "hctl" }, - { "pcm_info", &FA_pcm_info, NULL, "pcm" }, - { "pcm_name", &FA_str_p, (void *)&snd_pcm_name, "pcm" }, -}; - -static int acall_compar(const void *p1, const void *p2) -{ - return strcmp(((struct acall_table *)p1)->name, - ((struct acall_table *)p2)->name); -} - -static struct alisp_object * F_acall(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p1, *p2; - struct acall_table key, *item; - - p1 = eval(instance, car(args)); - p2 = cdr(args); - delete_object(instance, args); - if (!alisp_compare_type(p1, ALISP_OBJ_IDENTIFIER) && - !alisp_compare_type(p1, ALISP_OBJ_STRING)) { - delete_tree(instance, p2); - return &alsa_lisp_nil; - } - key.name = p1->value.s; - if ((item = bsearch(&key, acall_table, - sizeof acall_table / sizeof acall_table[0], - sizeof acall_table[0], acall_compar)) != NULL) { - delete_tree(instance, p1); - return item->func(instance, item, p2); - } - delete_tree(instance, p1); - delete_tree(instance, p2); - lisp_warn(instance, "acall function %s' is undefined", p1->value.s); - return &alsa_lisp_nil; -} - -static struct alisp_object * F_ahandle(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object *p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - args = car(cdr(p1)); - delete_tree(instance, cdr(cdr(p1))); - delete_object(instance, cdr(p1)); - delete_tree(instance, car(p1)); - delete_object(instance, p1); - return args; -} - -static struct alisp_object * F_aerror(struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object *p1; - - p1 = eval(instance, car(args)); - delete_tree(instance, cdr(args)); - delete_object(instance, args); - args = car(p1); - if (args == &alsa_lisp_nil) { - delete_tree(instance, p1); - return new_integer(instance, SND_ERROR_ALISP_NIL); - } else { - delete_tree(instance, cdr(p1)); - delete_object(instance, p1); - } - return args; -} - -static int common_error(snd_output_t **rout, struct alisp_instance *instance, struct alisp_object * args) -{ - struct alisp_object * p = args, * p1; - snd_output_t *out; - int err; - - err = snd_output_buffer_open(&out); - if (err < 0) { - delete_tree(instance, args); - return err; - } - - do { - p1 = eval(instance, car(p)); - if (alisp_compare_type(p1, ALISP_OBJ_STRING)) - snd_output_printf(out, "%s", p1->value.s); - else - princ_object(out, p1); - delete_tree(instance, p1); - p = cdr(p1 = p); - delete_object(instance, p1); - } while (p != &alsa_lisp_nil); - - *rout = out; - return 0; -} - -static struct alisp_object * F_snderr(struct alisp_instance *instance, struct alisp_object * args) -{ - snd_output_t *out; - char *str; - - if (common_error(&out, instance, args) < 0) - return &alsa_lisp_nil; - snd_output_buffer_string(out, &str); - SNDERR(str); - snd_output_close(out); - return &alsa_lisp_t; -} - -static struct alisp_object * F_syserr(struct alisp_instance *instance, struct alisp_object * args) -{ - snd_output_t *out; - char *str; - - if (common_error(&out, instance, args) < 0) - return &alsa_lisp_nil; - snd_output_buffer_string(out, &str); - SYSERR(str); - snd_output_close(out); - return &alsa_lisp_t; -} - -static const struct intrinsic snd_intrinsics[] = { - { "Acall", F_acall }, - { "Aerror", F_aerror }, - { "Ahandle", F_ahandle }, - { "Aresult", F_ahandle }, - { "Asnderr", F_snderr }, - { "Asyserr", F_syserr } -}; diff --git a/src/async.c b/src/async.c index 76c6fb9e..4117354a 100644 --- a/src/async.c +++ b/src/async.c @@ -149,12 +149,44 @@ int snd_async_add_handler(snd_async_handler_t **handler, int fd, */ int snd_async_del_handler(snd_async_handler_t *handler) { - int err = 0; - int was_empty = list_empty(&snd_async_handlers); + int err = 0, err2 = 0; + int was_empty; assert(handler); + if (handler->type != SND_ASYNC_HANDLER_GENERIC) { + 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(alist)) + goto _glist; + switch (handler->type) { +#ifdef BUILD_PCM + case SND_ASYNC_HANDLER_PCM: + err2 = snd_pcm_async(handler->u.pcm, -1, 1); + break; +#endif + case SND_ASYNC_HANDLER_CTL: + err2 = snd_ctl_async(handler->u.ctl, -1, 1); + break; + default: + assert(0); + } + } + _glist: + was_empty = list_empty(&snd_async_handlers); list_del(&handler->glist); - if (!was_empty - && list_empty(&snd_async_handlers)) { + if (!was_empty && list_empty(&snd_async_handlers)) { err = sigaction(snd_async_signo, &previous_action, NULL); if (err < 0) { SYSERR("sigaction"); @@ -162,27 +194,8 @@ int snd_async_del_handler(snd_async_handler_t *handler) } memset(&previous_action, 0, sizeof(previous_action)); } - if (handler->type == SND_ASYNC_HANDLER_GENERIC) - goto _end; - if (!list_empty(&handler->hlist)) - list_del(&handler->hlist); - if (!list_empty(&handler->hlist)) - goto _end; - switch (handler->type) { -#ifdef BUILD_PCM - case SND_ASYNC_HANDLER_PCM: - err = snd_pcm_async(handler->u.pcm, -1, 1); - break; -#endif - case SND_ASYNC_HANDLER_CTL: - err = snd_ctl_async(handler->u.ctl, -1, 1); - break; - default: - assert(0); - } - _end: free(handler); - return err; + return err ? err : err2; } /** diff --git a/src/conf.c b/src/conf.c index eca44c03..a48e0db0 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1268,13 +1268,13 @@ static int parse_array_def(snd_config_t *parent, input_t *input, int *idx, int s snd_config_t *n = NULL; if (!skip) { - snd_config_t *g; char static_id[12]; while (1) { snprintf(static_id, sizeof(static_id), "%i", *idx); - if (_snd_config_search(parent, static_id, -1, &g) == 0) { + if (_snd_config_search(parent, static_id, -1, &n) == 0) { if (override) { snd_config_delete(n); + n = NULL; } else { /* merge */ (*idx)++; @@ -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 diff --git a/src/conf/Makefile.am b/src/conf/Makefile.am index e7bcbbe0..dc59d7d1 100644 --- a/src/conf/Makefile.am +++ b/src/conf/Makefile.am @@ -1,9 +1,6 @@ SUBDIRS=cards ctl pcm cfg_files = alsa.conf -if BUILD_ALISP -cfg_files += sndo-mixer.alisp -endif if BUILD_MODULES if BUILD_MIXER_MODULES cfg_files += smixer.conf diff --git a/src/conf/cards/HDA-Intel.conf b/src/conf/cards/HDA-Intel.conf index d5a65906..5451606f 100644 --- a/src/conf/cards/HDA-Intel.conf +++ b/src/conf/cards/HDA-Intel.conf @@ -163,8 +163,6 @@ HDA-Intel.pcm.iec958.0 { hint.device 1 } - - HDA-Intel.pcm.hdmi.common { @args [ CARD DEVICE CTLINDEX AES0 AES1 AES2 AES3 ] @args.CARD { diff --git a/src/conf/cards/HdmiLpeAudio.conf b/src/conf/cards/HdmiLpeAudio.conf index a1e493da..a9104f43 100644 --- a/src/conf/cards/HdmiLpeAudio.conf +++ b/src/conf/cards/HdmiLpeAudio.conf @@ -2,8 +2,6 @@ # Configuration for the Intel HDMI/DP LPE audio # - - HdmiLpeAudio.pcm.hdmi.0 { @args [ CARD AES0 AES1 AES2 AES3 ] @args.CARD { diff --git a/src/conf/cards/Makefile.am b/src/conf/cards/Makefile.am index 00999f01..f387cf41 100644 --- a/src/conf/cards/Makefile.am +++ b/src/conf/cards/Makefile.am @@ -60,22 +60,6 @@ cfg_files = aliases.conf \ VXPocket.conf \ VXPocket440.conf -if BUILD_ALISP -cfg_files += aliases.alisp -endif - alsa_DATA = $(cfg_files) -if BUILD_ALISP -SI7018dir = $(alsaconfigdir)/cards/SI7018 -SI7018_files = \ - SI7018/sndoc-mixer.alisp \ - SI7018/sndop-mixer.alisp -SI7018_DATA = $(SI7018_files) -else -SI7018_files= -endif - -EXTRA_DIST = \ - $(cfg_files) \ - $(SI7018_files) +EXTRA_DIST = $(cfg_files) diff --git a/src/conf/cards/SI7018/sndoc-mixer.alisp b/src/conf/cards/SI7018/sndoc-mixer.alisp deleted file mode 100644 index ade1ea3f..00000000 --- a/src/conf/cards/SI7018/sndoc-mixer.alisp +++ /dev/null @@ -1,11 +0,0 @@ -; -; SiS SI7018 mixer abstract layer -; -; Copyright (c) 2003 Jaroslav Kysela -; License: GPL v2 (http://www.gnu.org/licenses/gpl.html) -; - -(defun sndoc_mixer_open (hctl pcm) - (princ "sndoc_mixer_open: hctl=" hctl " pcm=" pcm "\n") - 0 -) diff --git a/src/conf/cards/SI7018/sndop-mixer.alisp b/src/conf/cards/SI7018/sndop-mixer.alisp deleted file mode 100644 index 285e2898..00000000 --- a/src/conf/cards/SI7018/sndop-mixer.alisp +++ /dev/null @@ -1,11 +0,0 @@ -; -; SiS SI7018 mixer abstract layer -; -; Copyright (c) 2003 Jaroslav Kysela -; License: GPL v2 (http://www.gnu.org/licenses/gpl.html) -; - -(defun sndop_mixer_open (hctl pcm) - (princ "sndop_mixer_open: hctl=" hctl " pcm=" pcm "\n") - 0 -) diff --git a/src/conf/cards/USB-Audio.conf b/src/conf/cards/USB-Audio.conf index 80631b2e..2f6d2ee0 100644 --- a/src/conf/cards/USB-Audio.conf +++ b/src/conf/cards/USB-Audio.conf @@ -53,6 +53,7 @@ USB-Audio.pcm.iec958_device { "Blue Snowball" 999 "C-Media USB Headphone Set" 999 "Cmedia Audio" 999 + "Corsair HS60 PRO Surround USB S" 999 "DELL PROFESSIONAL SOUND BAR AE5" 999 "HP Digital Stereo Headset" 999 "GN 9330" 999 @@ -66,8 +67,11 @@ USB-Audio.pcm.iec958_device { "Plantronics USB Headset" 999 "Plantronics Wireless Audio" 999 "SB WoW Headset" 999 + "Scarlett 2i2 4th Gen" 999 "Scarlett 2i2 USB" 999 "Scarlett 2i4 USB" 999 + "Scarlett Solo 4th Gen" 999 + "Scarlett Solo USB" 999 "Sennheiser USB headset" 999 "SWTOR Gaming Headset by Razer" 999 "ThinkStation P620 Main" 999 diff --git a/src/conf/cards/aliases.alisp b/src/conf/cards/aliases.alisp deleted file mode 100644 index 1661caa3..00000000 --- a/src/conf/cards/aliases.alisp +++ /dev/null @@ -1,29 +0,0 @@ -(setq snd_card_aliases_array - ( - ("YMF724" . "YMF744") - ("YMF724F" . "YMF744") - ("YMF740" . "YMF744") - ("YMF740C" . "YMF744") - ("YMF754" . "YMF744") - ("CMIPCI" . "CMI8338") - ("CMI8738" . "CMI8338") - ("CMI8738-MC4" . "CMI8738-MC6") - ("E-mu APS" . "EMU10K1") - ("GUS Max" . "GUS") - ("GUS ACE" . "GUS") - ("GUS Extreme" . "GUS") - ("AMD InterWave" . "GUS") - ("Dynasonic 3-D" . "GUS") - ("InterWave STB" . "GUS") - ) -) - -(defun snd_card_alias (cardname) - (setq r (assq cardname snd_card_aliases_array)) - (setq r (if (null r) cardname r)) - (unsetq r) -) - -(defun snd_card_alias_unset () - (unsetq snd_card_aliases_array snd_card_alias) -) diff --git a/src/conf/cards/aliases.conf b/src/conf/cards/aliases.conf index 7e352f76..e2d59aa7 100644 --- a/src/conf/cards/aliases.conf +++ b/src/conf/cards/aliases.conf @@ -57,8 +57,10 @@ CMI8786 cards.CMI8788 CMI8787 cards.CMI8788 pistachio cards.pistachio-card VC4-HDMI cards.vc4-hdmi +hda-acpi cards.HDA-Intel + diff --git a/src/conf/cards/vc4-hdmi.conf b/src/conf/cards/vc4-hdmi.conf index af87b3a8..0f313d82 100644 --- a/src/conf/cards/vc4-hdmi.conf +++ b/src/conf/cards/vc4-hdmi.conf @@ -3,8 +3,6 @@ # subframe conversion # - - vc4-hdmi.pcm.hdmi.0 { @args [ CARD AES0 AES1 AES2 AES3 ] @args.CARD { diff --git a/src/conf/sndo-mixer.alisp b/src/conf/sndo-mixer.alisp deleted file mode 100644 index c8b03f06..00000000 --- a/src/conf/sndo-mixer.alisp +++ /dev/null @@ -1,115 +0,0 @@ -; -; Toplevel configuration for the ALSA Ordinary Mixer Interface -; -; Copyright (c) 2003 Jaroslav Kysela -; License: GPL v2 (http://www.gnu.org/licenses/gpl.html) -; - -(defun sndo_include (hctl stream) - (setq info (Acall "ctl_card_info" (Acall "hctl_ctl" hctl))) - (if (= (Aerror info) 0) - (progn - (setq info (Aresult info)) - (setq driver (cdr (assq "driver" (unsetq info)))) - (setq file (concat (path "data") "/alsa/cards/" (snd_card_alias driver) "/sndo" stream "-mixer.alisp")) - (setq r (include file)) - (when (= r -2) (Asyserr "unable to find file " file)) - ) - (setq r (Aerror info)) - ) - (unsetq info driver file r) -) - -(defun sndo_mixer_open_fcn (hctl stream pcm) - (setq fcn (concat "sndo" stream "_mixer_open")) - (setq r (if (exfun fcn) (funcall fcn hctl pcm) 0)) - (when (= r 0) - (setq hctls (if hctls (cons hctls (cons hctl)) hctl)) - ) - (unsetq fcn r) -) - -(defun sndo_mixer_open_hctl (name stream pcm) - (setq hctl (Acall "hctl_open" name nil)) - (setq r (Aerror hctl)) - (when (= r 0) - (setq hctl (Aresult hctl)) - (setq r (sndo_include hctl stream)) - (if (= r 0) - (setq r (sndo_mixer_open_fcn hctl stream pcm)) - (Acall "hctl_close" hctl) - ) - ) - (unsetq hctl r) -) - -(defun sndo_mixer_open_virtual (name stream pcm) - (setq file (concat (path "data") "/alsa/virtual/" name "/sndo" stream "-mixer.alisp")) - (setq r (include file)) - (when (= r -2) (Asyserr "unable to find file " file)) - (when (= r 0) (setq r (sndo_mixer_open_fcn nil stream pcm))) - (unsetq file r) -) - -(defun sndo_mixer_open1 (name stream) - (if (compare-strings name 0 2 "hw:" 0 2) - (sndo_mixer_open_hctl name stream nil) - (sndo_mixer_open_virtual name stream nil) - ) -) - -(defun sndo_mixer_open (pname cname) - (setq r (sndo_mixer_open1 pname "p")) - (when (= r 0) (setq r (sndo_mixer_open1 cname "c"))) - (when (!= r 0) (sndo_mixer_close)) - (unsetq sndo_mixer_open - sndo_mixer_open_pcm sndo_mixer_open_pcm1 - sndo_mixer_open_virtual sndo_mixer_open_fcn - sndo_include r) -) - -(defun sndo_mixer_open_pcm1 (pcm stream) - (setq info (Acall "pcm_info" pcm)) - (setq r (Aerror info)) - (when (= r 0) - (setq info (Aresult info)) - (setq card (cdr (assq "card" info))) - (setq r - (if (< card 0) - (sndo_mixer_open_virtual (Acall "pcm_name" pcm) stream pcm) - (sndo_mixer_open_hctl (format "hw:%i" card) stream pcm) - ) - ) - ) - (unsetq info card r) -) - -(defun sndo_mixer_open_pcm (ppcm cpcm) - (setq r (sndo_mixer_open_pcm1 ppcm "p")) - (when (= r 0) (setq r (sndo_mixer_open_pcm1 cpcm "c"))) - (when (!= r 0) (sndo_mixer_close)) - (unsetq sndo_mixer_open - sndo_mixer_open_pcm sndo_mixer_open_pcm1 - sndo_mixer_open_virtual sndo_mixer_open_fcn - sndo_include r) -) - -(defun sndo_mixer_close1 (hctl stream) - (when hctl - (progn - (setq fcn (concat "sndo" stream "_mixer_close")) - (when (exfun fcn) (funcall fcn hctl)) - (unsetq fcn) - (Acall "hctl_close" hctl) - ) - ) -) - -(defun sndo_mixer_close nil - (sndo_mixer_close1 (nth 1 hctls) "c") - (sndo_mixer_close1 (nth 0 hctls) "p") - (snd_card_alias_unset) - (unsetq hctls) -) - -(include (concat (path "data") "/alsa/cards/aliases.alisp")) diff --git a/src/control/control.c b/src/control/control.c index d77ab24c..e443d543 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -1293,7 +1293,10 @@ int snd_ctl_ump_next_device(snd_ctl_t *ctl, int *device) int snd_ctl_ump_endpoint_info(snd_ctl_t *ctl, snd_ump_endpoint_info_t *info) { assert(ctl && info); - return ctl->ops->ump_endpoint_info(ctl, info); + fprintf(stderr, "%s:%d\n", __func__, __LINE__); + if (ctl->ops->ump_endpoint_info) + return ctl->ops->ump_endpoint_info(ctl, info); + return -ENXIO; } /** @@ -1305,7 +1308,9 @@ int snd_ctl_ump_endpoint_info(snd_ctl_t *ctl, snd_ump_endpoint_info_t *info) int snd_ctl_ump_block_info(snd_ctl_t *ctl, snd_ump_block_info_t *info) { assert(ctl && info); - return ctl->ops->ump_block_info(ctl, info); + if (ctl->ops->ump_block_info) + return ctl->ops->ump_block_info(ctl, info); + return -ENXIO; } /** diff --git a/src/control/control_remap.c b/src/control/control_remap.c index 81cf38e3..226b3c0b 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -2,11 +2,11 @@ * \file control/control_remap.c * \brief CTL Remap Plugin Interface * \author Jaroslav Kysela - * \date 2021 + * \date 2021-2025 */ /* * Control - Remap Controls - * Copyright (c) 2021 by Jaroslav Kysela + * Copyright (c) 2021-2025 by Jaroslav Kysela * * * This library is free software; you can redistribute it and/or modify @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -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; diff --git a/src/control/tlv.c b/src/control/tlv.c index 3a2b731d..fed46acd 100644 --- a/src/control/tlv.c +++ b/src/control/tlv.c @@ -40,6 +40,8 @@ #define int_index(size) (((size) + sizeof(int) - 1) / sizeof(int)) /* max size of a TLV entry for dB information (including compound one) */ #define MAX_TLV_RANGE_SIZE 256 +/* min length of a TLV stream to contain type and size */ +#define MIN_TLV_STREAM_LEN ((SNDRV_CTL_TLVO_LEN + 1) * sizeof(int)) #endif /** @@ -47,12 +49,12 @@ * \param tlv the TLV source * \param tlv_size the byte size of TLV source * \param db_tlvp the pointer stored the dB TLV information - * \return the byte size of dB TLV information if found in the given - * TLV source, or a negative error code. + * \return The byte size of dB TLV information if found in the given TLV + * source, -ENOENT if not found, or a negative error code in case of an error. * * This function parses the given TLV source and stores the TLV start * point if the TLV information regarding dB conversion is found. - * The stored TLV pointer can be passed to the convesion functions + * The stored TLV pointer can be passed to the conversion functions * #snd_tlv_convert_to_dB(), #snd_tlv_convert_from_dB() and * #snd_tlv_get_dB_range(). */ @@ -64,6 +66,13 @@ int snd_tlv_parse_dB_info(unsigned int *tlv, unsigned int size; int err; + /* Validate that it is possible to read the type and size + * without reading past the end of the buffer. */ + if (tlv_size < MIN_TLV_STREAM_LEN) { + SNDERR("TLV stream too short"); + return -EINVAL; + } + *db_tlvp = NULL; type = tlv[SNDRV_CTL_TLVO_TYPE]; size = tlv[SNDRV_CTL_TLVO_LEN]; @@ -79,7 +88,7 @@ int snd_tlv_parse_dB_info(unsigned int *tlv, while (size > 0) { unsigned int len; err = snd_tlv_parse_dB_info(tlv, size, db_tlvp); - if (err < 0) + if (err < 0 && err != -ENOENT) return err; /* error */ if (err > 0) return err; /* found */ @@ -114,7 +123,7 @@ int snd_tlv_parse_dB_info(unsigned int *tlv, default: break; } - return -EINVAL; /* not found */ + return -ENOENT; } /** diff --git a/src/error.c b/src/error.c index c06af7c7..8db7556b 100644 --- a/src/error.c +++ b/src/error.c @@ -191,11 +191,38 @@ snd_lib_error_handler_t snd_err_msg = snd_err_msg_default; */ size_t snd_strlcpy(char *dst, const char *src, size_t size) { - size_t ret = strlen(src); - if (size) { - size_t len = ret >= size ? size - 1 : ret; - memcpy(dst, src, len); - dst[len] = '\0'; - } - return ret; + size_t ret = strlen(src); + if (size) { + size_t len = ret >= size ? size - 1 : ret; + memcpy(dst, src, len); + dst[len] = '\0'; + } + return ret; +} + +/** + * \brief Append a C-string into a sized buffer + * \param dst Where to append the string to + * \param src Where to copy the string from + * \param size Size of destination buffer + * \retval The total string length (no trimming) + * + * The result is always a valid NUL-terminated string that fits + * in the buffer (unless, of course, the buffer size is zero). + * It does not pad out the result. + */ +size_t snd_strlcat(char *dst, const char *src, size_t size) +{ + size_t dst_len = strlen(dst); + size_t len = strlen(src); + size_t ret = dst_len + len; + if (dst_len < size) { + dst += dst_len; + size -= dst_len; + if (len >= size) + len = size - 1; + memcpy(dst, src, len); + dst[len] = '\0'; + } + return ret; } diff --git a/src/mixer/bag.c b/src/mixer/bag.c index 18a8fbe8..8d06073d 100644 --- a/src/mixer/bag.c +++ b/src/mixer/bag.c @@ -68,6 +68,11 @@ int bag_del(bag_t *bag, void *ptr) void bag_del_all(bag_t *bag) { - while (!list_empty(bag)) - list_del(bag->next); + struct list_head *pos, *npos; + + list_for_each_safe(pos, npos, bag) { + bag1_t *b = list_entry(pos, bag1_t, list); + list_del(&b->list); + free(b); + } } diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index ab3bbda7..c59ea3b6 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -215,7 +215,8 @@ range, thus you may get the significant bits for linear samples via #snd_pcm_hw_params_get_sbits() function. The example: ICE1712 chips support 32-bit sample processing, but low byte is ignored (playback) or zero (capture). The function snd_pcm_hw_params_get_sbits() -returns 24 in this case. +returns 24 in this case. The significant bits are related to the usable +sample bits (width) not the physical sample space. \section alsa_transfers ALSA transfers @@ -493,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(). @@ -776,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); @@ -784,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 @@ -3097,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. */ @@ -3106,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)) { @@ -3117,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); @@ -3903,7 +3914,16 @@ int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params, /** * \brief Get sample resolution info from a configuration space * \param params Configuration space - * \return signification bits in sample otherwise a negative error code if the info is not available + * \return sample resolution (in bits) otherwise a negative error code if the info is not available + * + * For linear formats, this function returns sample resolution - + * used bits starting from the first usable significant bit defined by + * the format (e.g. bit 31 for S32_LE format or bit 23 for S24_LE format - + * starting from bit zero). Application may use full sample bit range defined + * by the format, but additional bits (outside this sample resolution) are + * stripped (not processed). + * + * For non-linear formats, this value may have a special meaning which may be defined in future. * * This function should only be called when the configuration space * contains a single configuration. Call #snd_pcm_hw_params to choose @@ -3938,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 @@ -7322,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 */ @@ -7330,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) @@ -8721,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) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index e53e5923..5b8ec08f 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -1018,6 +1018,7 @@ int snd_pcm_direct_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) } dshare->timer_ticks = hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE)->max / dshare->slave_period_size; params->info = dshare->shmptr->s.info; + params->info &= ~(SND_PCM_INFO_RESUME | 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 diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index bd3ecfc9..833cad02 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -379,12 +379,28 @@ static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) return 0; } -static inline int hw_params_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params) +#define hw_param_mask(params,var) \ + &((params)->masks[(var) - SND_PCM_HW_PARAM_FIRST_MASK]) + +static int hw_params_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params) { + int err; + /* check for new hw_params structure; it's available from 2.0.2 version of PCM API */ if (SNDRV_PROTOCOL_VERSION(2, 0, 2) <= pcm_hw->version) - return ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params); - return use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_PARAMS_OLD, params); + err = ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params); + else + err = use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_PARAMS_OLD, params); + if (err >= 0 && pcm_hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 17) && params->msbits > 0) { + snd_mask_t *m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + if (snd_mask_single(m)) { + snd_pcm_format_t format = snd_mask_min(m); + int width = snd_pcm_format_width(format); + if (width > 0 && params->msbits > (unsigned int)width) + params->msbits = width; + } + } + return err; } static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) @@ -1653,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, ×pec) == 0) { - int on = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; - if (ioctl(fd, SNDRV_PCM_IOCTL_TTSTAMP, &on) < 0) { - ret = -errno; - SNDMSG("TTSTAMP failed"); - return ret; + 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; diff --git a/src/pcm/pcm_ladspa.c b/src/pcm/pcm_ladspa.c index 56ee138f..25eac76f 100644 --- a/src/pcm/pcm_ladspa.c +++ b/src/pcm/pcm_ladspa.c @@ -1210,7 +1210,7 @@ static int snd_pcm_ladspa_look_for_plugin(snd_pcm_ladspa_plugin_t * const plugin return err; err = snd_pcm_ladspa_check_dir(plugin, fullpath, label, ladspa_id); free(fullpath); - if (err < 0) + if (err < 0 && err != -ENOENT) return err; if (err > 0) return 0; diff --git a/src/pcm/pcm_misc.c b/src/pcm/pcm_misc.c index 04b13125..3cff4326 100644 --- a/src/pcm/pcm_misc.c +++ b/src/pcm/pcm_misc.c @@ -203,9 +203,9 @@ int snd_pcm_format_cpu_endian(snd_pcm_format_t format) } /** - * \brief Return nominal bits per a PCM sample + * \brief Return the bit-width of the format * \param format Sample format - * \return bits per sample, a negative error code if not applicable + * \return the bit-width of the format, or a negative error code if not applicable */ int snd_pcm_format_width(snd_pcm_format_t format) { @@ -270,9 +270,9 @@ int snd_pcm_format_width(snd_pcm_format_t format) } /** - * \brief Return bits needed to store a PCM sample + * \brief Return the physical bit-width of the format (bits needed to store a PCM sample) * \param format Sample format - * \return bits per sample, a negative error code if not applicable + * \return the physical bit-width of the format, or a negative error code if not applicable */ int snd_pcm_format_physical_width(snd_pcm_format_t format) { diff --git a/src/pcm/pcm_params.c b/src/pcm/pcm_params.c index c2f78634..05bfe3b2 100644 --- a/src/pcm/pcm_params.c +++ b/src/pcm/pcm_params.c @@ -2075,6 +2075,7 @@ int snd_pcm_hw_refine_soft(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t { unsigned int k; snd_interval_t *i; + snd_mask_t *m; unsigned int rstamps[RULES]; unsigned int vstamps[SND_PCM_HW_PARAM_LAST_INTERVAL + 1]; unsigned int stamp = 2; @@ -2159,6 +2160,11 @@ int snd_pcm_hw_refine_soft(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t i = hw_param_interval(params, SND_PCM_HW_PARAM_SAMPLE_BITS); if (snd_interval_single(i)) params->msbits = snd_interval_value(i); + m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); + if (snd_mask_single(m)) { + snd_pcm_format_t format = snd_mask_min(m); + params->msbits = snd_pcm_format_width(format); + } } if (!params->rate_den) { diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index e5a3a189..bd681a9f 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -186,7 +186,8 @@ static const snd_pcm_format_t linear_preferred_formats[] = { #if defined(BUILD_PCM_PLUGIN_MULAW) || \ defined(BUILD_PCM_PLUGIN_ALAW) || \ - defined(BUILD_PCM_PLUGIN_ADPCM) + defined(BUILD_PCM_PLUGIN_ADPCM) || \ + defined(BUILD_PCM_PLUGIN_IEC958) #define BUILD_PCM_NONLINEAR #endif @@ -201,6 +202,10 @@ static const snd_pcm_format_t nonlinear_preferred_formats[] = { #ifdef BUILD_PCM_PLUGIN_ADPCM SND_PCM_FORMAT_IMA_ADPCM, #endif +#ifdef BUILD_PCM_PLUGIN_IEC958 + SND_PCM_FORMAT_IEC958_SUBFRAME_LE, + SND_PCM_FORMAT_IEC958_SUBFRAME_BE, +#endif }; #endif @@ -490,6 +495,18 @@ static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm } #endif +#ifdef BUILD_PCM_PLUGIN_IEC958 +static int iec958_open(snd_pcm_t **pcmp, const char *name, + snd_pcm_format_t sformat, snd_pcm_t *slave, + int close_slave) +{ + unsigned char preamble_vals[3] = { + 0x08, 0x02, 0x04 /* Z, X, Y */ + }; + return snd_pcm_iec958_open(pcmp, name, sformat, slave, close_slave, NULL, preamble_vals, 0); +} +#endif + static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv) { snd_pcm_plug_t *plug = pcm->private_data; @@ -526,6 +543,12 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_p case SND_PCM_FORMAT_IMA_ADPCM: f = snd_pcm_adpcm_open; break; +#endif +#ifdef BUILD_PCM_PLUGIN_IEC958 + case SND_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_BE: + f = iec958_open; + break; #endif default: #ifdef BUILD_PCM_PLUGIN_LFLOAT @@ -566,6 +589,12 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_p case SND_PCM_FORMAT_IMA_ADPCM: f = snd_pcm_adpcm_open; break; +#endif +#ifdef BUILD_PCM_PLUGIN_IEC958 + case SND_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_BE: + f = iec958_open; + break; #endif default: return -EINVAL; diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index c4b45fa2..9bb6d744 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -639,6 +639,22 @@ unsigned int snd_rawmidi_info_get_subdevices_avail(const snd_rawmidi_info_t *inf return info->subdevices_avail; } +/** + * \brief get the tied device number for the given rawmidi device + * \param info pointer to a snd_rawmidi_info_t structure + * \return the device number for the tied device, or -1 if untied / unknown. + * + * This function is useful for UMP rawmidi devices where each of them may + * have the mirroring legacy rawmidi device. Those are shown as "tied". + */ +int snd_rawmidi_info_get_tied_device(const snd_rawmidi_info_t *info) +{ + assert(info); + if (info->tied_device > 0) + return info->tied_device - 1; + return -1; +} + /** * \brief set rawmidi device number * \param info pointer to a snd_rawmidi_info_t structure diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c index 39c1c4a9..3ff5fc59 100644 --- a/src/rawmidi/ump.c +++ b/src/rawmidi/ump.c @@ -7,6 +7,29 @@ #include "rawmidi_local.h" #include "ump_local.h" +/*! \page rawmidi RawMidi interface + +\section rawmidi_ump UMP RawMidi Interface + +MIDI 2.0 devices have a different type of interface, communicating with +UMP (Universal MIDI Packet). For those devices, ALSA-library provides +API functions for accessing the raw UMP packet directly via the existing +RawMidi interface. + +#snd_ump_open() is the API function for opening a UMP RawMidi interface. +It works just like #snd_rawmidi_open() but for UMP devices. Similarly, +#snd_ump_close() is for closing, and there are other equivalent API functions +corresponding to the RawMidi ones. + +The new stuff for UMP is UMP Endpoint and UMP Function Blocks. The information +from Endpoint and Function Blocks can be obtained via #snd_ump_endpoint_info() +and #snd_ump_block_info() API functions. + +The objects #snd_ump_endpoint_info_t and #snd_ump_block_info_t are used for +creating a virtual UMP Endpoint and Function Blocks via ALSA sequencer, too. + +*/ + static int get_rawmidi_flags(snd_ump_t *ump) { snd_rawmidi_info_t info; @@ -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]; +} diff --git a/src/seq/seq.c b/src/seq/seq.c index 5eac4848..38cb1afd 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -490,6 +490,21 @@ the buffer is flushed by #snd_seq_drain_output() call. You can schedule the event in a certain queue so that the tempo change happens at the scheduled time, too. +The tempo is set as default in microsecond unit as defined for +Standard MIDI Format 1. But since the value in MIDI2 Set Tempo message +is based on 10-nanosecand unit, sequencer queue also allows to set up +in 10-nanosecond unit. For that, change the tempo-base value in +#snd_seq_queue_tempo_t to 10 via #snd_seq_queue_tempo_set_tempo_base() +along with the 10-nanobased tempo value. The default tempo base is 1000, +i.e. 1 microsecond. +Currently the API supports only either 0, 10 or 1000 as the tempo-base +(where 0 is treated as 1000). + +The older kernel might not support the different tempo-base, and setting a +different value from 1000 would fail. The application may heck the +availability of tempo-base change via #snd_seq_has_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; +} /*----------------------------------------------------------------*/ diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c index eeaf26e1..fe9d9bc7 100644 --- a/src/seq/seq_hw.c +++ b/src/seq/seq_hw.c @@ -125,6 +125,15 @@ static int snd_seq_hw_set_client_info(snd_seq_t *seq, snd_seq_client_info_t * in { snd_seq_hw_t *hw = seq->private_data; + /* added fields are not checked on older kernels */ + if (SNDRV_PROTOCOL_VERSION(1, 0, 3) > hw->version) { + if (info->midi_version > 0) + return -EINVAL; + if (info->filter & SNDRV_SEQ_FILTER_NO_CONVERT) + return -EINVAL; + if (info->group_filter != 0) + return -EINVAL; + } if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_CLIENT_INFO failed");*/ return -errno; @@ -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); diff --git a/src/seq/seq_local.h b/src/seq/seq_local.h index 46824806..00a7615a 100644 --- a/src/seq/seq_local.h +++ b/src/seq/seq_local.h @@ -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); diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 9ec93ee8..edffa675 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -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; +} diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index 04299588..e0b78373 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -2,7 +2,7 @@ EXTRA_DIST = Versions COMPATNUM=@LIBTOOL_VERSION_INFO@ if VERSIONED_SYMBOLS -VSYMS = -Wl,--version-script=Versions +VSYMS = -Wl,--version-script=$(srcdir)/Versions else VSYMS = endif diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 98acd97e..ac4a5fbc 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -415,7 +415,7 @@ static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr, snd_config_iterator_t i, next; snd_config_t *m, *mc, *a, *n; const char *mid, *id; - char name[128], *var; + char name[128], *var, *var2; const char *s; int err; @@ -446,12 +446,25 @@ static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr, err = snd_config_get_id(n, &id); if (err < 0) goto __err_path; + snprintf(name, sizeof(name), "__%s", id); + if (uc_mgr_get_variable(uc_mgr, name)) { + uc_error("Macro argument '%s' is already defined", name); + goto __err_path; + } err = snd_config_get_ascii(n, &var); if (err < 0) goto __err_path; - snprintf(name, sizeof(name), "__%s", id); - err = uc_mgr_set_variable(uc_mgr, name, var); - free(var); + if (uc_mgr->conf_format < 7) { + err = uc_mgr_set_variable(uc_mgr, name, var); + free(var); + } else { + err = uc_mgr_get_substituted_value(uc_mgr, &var2, var); + free(var); + if (err >= 0) { + err = uc_mgr_set_variable(uc_mgr, name, var2); + free(var2); + } + } if (err < 0) goto __err_path; } @@ -2915,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 diff --git a/src/ucm/ucm_cond.c b/src/ucm/ucm_cond.c index 985a366b..87bc9762 100644 --- a/src/ucm/ucm_cond.c +++ b/src/ucm/ucm_cond.c @@ -274,6 +274,7 @@ static int if_eval_path(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) { const char *path, *mode = ""; int err, amode = F_OK; + char *s; if (uc_mgr->conf_format < 4) { uc_error("Path condition is supported in v4+ syntax"); @@ -292,27 +293,44 @@ static int if_eval_path(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) return -EINVAL; } - if (strncasecmp(mode, "exist", 5) == 0) { + if (uc_mgr->conf_format < 7) { + s = (char *)mode; + } else { + err = uc_mgr_get_substituted_value(uc_mgr, &s, mode); + if (err < 0) + return err; + } + if (strncasecmp(s, "exist", 5) == 0) { amode = F_OK; - } else if (strcasecmp(mode, "read") == 0) { + } else if (strcasecmp(s, "read") == 0) { amode = R_OK; - } else if (strcasecmp(mode, "write") == 0) { + } else if (strcasecmp(s, "write") == 0) { amode = W_OK; - } else if (strcasecmp(mode, "exec") == 0) { + } else if (strcasecmp(s, "exec") == 0) { amode = X_OK; } else { - uc_error("Path unknown mode (If.Condition.Mode)"); + uc_error("Path unknown mode '%s' (If.Condition.Mode)", s); + free(s); return -EINVAL; } + if (s != mode) + free(s); + if (uc_mgr->conf_format < 7) { + s = (char *)path; + } else { + err = uc_mgr_get_substituted_value(uc_mgr, &s, path); + if (err < 0) + return err; + } #ifdef HAVE_EACCESS - if (eaccess(path, amode)) + err = eaccess(s, amode); #else - if (access(path, amode)) + err = access(s, amode); #endif - return 0; - - return 1; + if (s != path) + free(s); + return err ? 0 : 1; } static int if_eval(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index 7a907934..e6c0a091 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -63,7 +63,7 @@ use case verbs for that sound card. i.e.: # Example master file for blah sound card # By Joe Blogs -Syntax 6 +Syntax 7 # Use Case name for user interface Comment "Nice Abstracted Soundcard" @@ -376,7 +376,9 @@ Evaluation order | Configuration block | Evaluation restart ------------------:|---------------------|-------------------- 1 | Define | No 2 | Include | Yes -3 | If | Yes +3 | Variant | Yes +4 | Macro | Yes +5 | If | Yes ### Substitutions @@ -384,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:\} | Environment variable \ ${sys:\} | Contents of sysfs file \ +${sys-card:\} | Contents of sysfs file in /sys/class/sound/card? tree [**Syntax 8**] ${var:\} | UCM parser variable (set using a _Define_ block) ${eval:\} | Evaluate expression like *($var+2)/3* [**Syntax 5**] ${find-card:\} | Find a card - see _Find card substitution_ section ${find-device:\} | 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 @@ -489,7 +523,8 @@ DefineMacro.macro1 { The arguments in the macro are refered as the variables with the double underscore name prefix (like *__variable*). The configuration block in the DefineMacro subtree is always evaluated (including arguments and variables) -at the time of the instantiation. +at the time of the instantiation. Argument string substitutions +(for multiple macro call levels) were added in *Syntax* version *7*. The macros can be instantiated (expanded) using: @@ -563,6 +598,15 @@ Field | Description String | string Regex | regex expression (extended posix, ignore case) +#### Path is present (Type Path) + +Field | Description +---------------------|----------------------- +Path | path (filename) +Mode | exist,read,write,exec + +Note: Substitution for Path and Mode fields were added in *Syntax* version *7*. + #### ALSA control element exists (Type ControlExists) Field | Description diff --git a/src/ucm/ucm_exec.c b/src/ucm/ucm_exec.c index 276cf592..b79a84ae 100644 --- a/src/ucm/ucm_exec.c +++ b/src/ucm/ucm_exec.c @@ -254,8 +254,12 @@ int uc_mgr_exec(const char *prog) close(f); +#if defined(_GNU_SOURCE) + close_range(3, maxfd, 0); +#else for (f = 3; f < maxfd; f++) close(f); +#endif /* install default handlers for the forked process */ signal(SIGINT, SIG_DFL); diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index fc249058..b274277d 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -40,7 +40,7 @@ #include #include "use-case.h" -#define SYNTAX_VERSION_MAX 6 +#define SYNTAX_VERSION_MAX 8 #define MAX_CARD_SHORT_NAME 32 #define MAX_CARD_LONG_NAME 80 diff --git a/src/ucm/ucm_regex.c b/src/ucm/ucm_regex.c index 1471f076..dfcab042 100644 --- a/src/ucm/ucm_regex.c +++ b/src/ucm/ucm_regex.c @@ -154,7 +154,7 @@ int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name, err = regcomp(&re, s, options); free(s); if (err) { - uc_error("Regex '%s' compilation failed (code %d)", err); + uc_error("Regex '%s' compilation failed (code %d)", s, err); return -EINVAL; } diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c index 2b010338..a3d9c392 100644 --- a/src/ucm/ucm_subs.c +++ b/src/ucm/ucm_subs.c @@ -20,7 +20,7 @@ * transition sequences, multiple client access and user defined use * cases was kindly sponsored by Wolfson Microelectronics PLC. * - * Copyright (C) 2019 Red Hat Inc. + * Copyright (C) 2019-2025 Red Hat Inc. * Authors: Jaroslav Kysela */ @@ -30,6 +30,19 @@ #include #include +/* capabilities string like "*a*b*c" will be used to check library extensions */ +/* use Needle like "*a*" or regex expression for a match */ +#define LIB_CAPS_STRING "*" "*" + +static unsigned char _hex_table[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + +static char *rval_lib_caps(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED) +{ + if (uc_mgr->conf_format < 8) + return NULL; + return strdup(LIB_CAPS_STRING); +} + static char *rval_open_name(snd_use_case_mgr_t *uc_mgr) { const char *name; @@ -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); diff --git a/test/Makefile.am b/test/Makefile.am index 99c2c4ff..707063cc 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -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 diff --git a/test/latency.c b/test/latency.c index ae37c11a..c40b2ae7 100644 --- a/test/latency.c +++ b/test/latency.c @@ -260,11 +260,11 @@ int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int *bufsize) return -1; __set_it: if ((err = setparams_bufsize(phandle, p_params, pt_params, *bufsize, "playback")) < 0) { - printf("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err)); + printf("Unable to set hw parameters for playback stream: %s\n", snd_strerror(err)); exit(0); } if ((err = setparams_bufsize(chandle, c_params, ct_params, *bufsize, "capture")) < 0) { - printf("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err)); + printf("Unable to set hw parameters for capture stream: %s\n", snd_strerror(err)); exit(0); } diff --git a/test/midifile.c b/test/midifile.c index 4862b199..8580108e 100644 --- a/test/midifile.c +++ b/test/midifile.c @@ -79,34 +79,34 @@ /* public stuff */ /* Functions to be called while processing the MIDI file. */ -int (*Mf_getc) () = NULLFUNC; -void (*Mf_error) () = NULLFUNC; -void (*Mf_header) () = NULLFUNC; -void (*Mf_trackstart) () = NULLFUNC; -void (*Mf_trackend) () = NULLFUNC; -void (*Mf_noteon) () = NULLFUNC; -void (*Mf_noteoff) () = NULLFUNC; -void (*Mf_pressure) () = NULLFUNC; -void (*Mf_parameter) () = NULLFUNC; -void (*Mf_pitchbend) () = NULLFUNC; -void (*Mf_program) () = NULLFUNC; -void (*Mf_chanpressure) () = NULLFUNC; -void (*Mf_sysex) () = NULLFUNC; -void (*Mf_arbitrary) () = NULLFUNC; -void (*Mf_metamisc) () = NULLFUNC; -void (*Mf_seqnum) () = NULLFUNC; -void (*Mf_eot) () = NULLFUNC; -void (*Mf_smpte) () = NULLFUNC; -void (*Mf_tempo) () = NULLFUNC; -void (*Mf_timesig) () = NULLFUNC; -void (*Mf_keysig) () = NULLFUNC; -void (*Mf_seqspecific) () = NULLFUNC; -void (*Mf_text) () = NULLFUNC; +int (*Mf_getc) (void) = NULLFUNC; +void (*Mf_error) (char *s) = NULLFUNC; +void (*Mf_header) (int format, int ntrks, int division) = NULLFUNC; +void (*Mf_trackstart) (void) = NULLFUNC; +void (*Mf_trackend) (void) = NULLFUNC; +void (*Mf_noteon) (int chan, int c1, int c2) = NULLFUNC; +void (*Mf_noteoff) (int chan, int c1, int c2) = NULLFUNC; +void (*Mf_pressure) (int chan, int c1, int c2) = NULLFUNC; +void (*Mf_parameter) (int chan, int c1, int c2) = NULLFUNC; +void (*Mf_pitchbend) (int chan, int c1, int c2) = NULLFUNC; +void (*Mf_program) (int chan, int c1) = NULLFUNC; +void (*Mf_chanpressure) (int chan, int c1) = NULLFUNC; +void (*Mf_sysex) (int len, char *msg) = NULLFUNC; +void (*Mf_arbitrary) (int len, char *msg) = NULLFUNC; +void (*Mf_metamisc) (int type, int len, char *msg) = NULLFUNC; +void (*Mf_seqnum) (int num) = NULLFUNC; +void (*Mf_eot) (void) = NULLFUNC; +void (*Mf_smpte) (char m0, char m1, char m2, char m3, char m4) = NULLFUNC; +void (*Mf_tempo) (long tempo) = NULLFUNC; +void (*Mf_timesig) (char m0, char m1, char m2, char m3) = NULLFUNC; +void (*Mf_keysig) (char m0, char m1) = NULLFUNC; +void (*Mf_seqspecific) (int len, char *msg) = NULLFUNC; +void (*Mf_text) (int type, int len, char *msg) = NULLFUNC; /* Functions to implement in order to write a MIDI file */ -int (*Mf_putc) () = NULLFUNC; -int (*Mf_writetrack) () = NULLFUNC; -int (*Mf_writetempotrack) () = NULLFUNC; +int (*Mf_putc) (unsigned char c) = NULLFUNC; +int (*Mf_writetrack) (int track) = NULLFUNC; +int (*Mf_writetempotrack) (void) = NULLFUNC; int Mf_nomerge = 0; /* 1 => continue'ed system exclusives are */ /* not collapsed. */ @@ -132,29 +132,34 @@ static int tempo_history_count = 0; static long Mf_toberead = 0L; static long Mf_numbyteswritten = 0L; -static long readvarinum (); -static long read32bit (); -static long to32bit (); -static int read16bit (); -static int to16bit (); -static char *msg (); -static void readheader (); -static int readtrack (); -static void badbyte (); -static void metaevent (); -static void sysex (); -static void chanmessage (); -static void msginit (); -static int msgleng (); -static void msgadd (); -static void biggermsg (); -static int eputc (); +static long readvarinum (void); +static long read32bit (void); +static long to32bit (int, int, int, int); +static int read16bit (void); +static int to16bit (int, int); +static char *msg (void); +static void readheader (void); +static int readtrack (void); +static void badbyte (int); +static void metaevent (int); +static void sysex (void); +static void chanmessage (int, int, int); +static void msginit (void); +static int msgleng (void); +static void msgadd (int); +static void biggermsg (void); +static int eputc (unsigned char); double mf_ticks2sec (unsigned long ticks, int division, unsigned long tempo); -int mf_write_meta_event (); -void mf_write_tempo (); -void mf_write_seqnum (); -void WriteVarLen (); +void write32bit (unsigned long data); +void write16bit (int data); +void mf_write_track_chunk (int which_track, FILE *fp); +void mf_write_header_chunk (int format, int ntracks, int division); +int mf_write_meta_event (unsigned long delta_time, unsigned char type, + unsigned char *data, unsigned long size); +void mf_write_tempo (unsigned long delta_time, unsigned long tempo); +void mf_write_seqnum (unsigned long delta_time, unsigned int seqnum); +void WriteVarLen (unsigned long value); #ifdef READ_MODS #include "mp_mod.c" @@ -163,7 +168,7 @@ static int mod_file_flag = 0; static int force_exit; void -mfread () +mfread (void) { force_exit = 0; if (Mf_getc == NULLFUNC) @@ -181,15 +186,13 @@ mfread () /* for backward compatibility with the original lib */ void -midifile () +midifile (void) { mfread (); } -static -int -readmt (s) /* read through the "MThd" or "MTrk" header string */ - char *s; +static int +readmt (char *s) /* read through the "MThd" or "MTrk" header string */ { int n = 0; char *p = s; @@ -211,9 +214,8 @@ readmt (s) /* read through the "MThd" or "MTrk" header string */ return (c); } -static -int -egetc () /* read a single character and abort on EOF */ +static int +egetc (void) /* read a single character and abort on EOF */ { int c = (*Mf_getc) (); @@ -225,9 +227,8 @@ egetc () /* read a single character and abort on EOF */ return (c); } -static -void -readheader () /* read a header chunk */ +static void +readheader (void) /* read a header chunk */ { int format, ntrks, division; @@ -280,9 +281,8 @@ readheader () /* read a header chunk */ /*#define DEBUG_TIMES*/ -static -unsigned long -find_tempo() +static unsigned long +find_tempo(void) { int i; unsigned long old_tempo = Mf_currtempo; @@ -307,9 +307,8 @@ printf("[revised_time %lu, new_tempo %lu]\n", revised_time, new_tempo); return(new_tempo); } -static -int -readtrack () /* read a track chunk */ +static int +readtrack (void) /* read a track chunk */ { /* This array is indexed by the high half of a status byte. It's */ /* value is either the number of bytes needed (1 or 2) for a channel */ @@ -499,10 +498,8 @@ old_f_realtime, delta_secs * 1600.0); return (1); } -static -void -badbyte (c) - int c; +static void +badbyte (int c) { char buff[32]; @@ -510,8 +507,7 @@ badbyte (c) mferror (buff); } -static -void +static void metaevent (int type) { int leng = msgleng (); @@ -577,19 +573,15 @@ metaevent (int type) } } -static -void -sysex () +static void +sysex (void) { if (Mf_sysex) (*Mf_sysex) (msgleng (), msg ()); } -static -void -chanmessage (status, c1, c2) - int status; - int c1, c2; +static void +chanmessage (int status, int c1, int c2) { int chan = status & 0xf; @@ -635,7 +627,7 @@ chanmessage (status, c1, c2) /* number of characters it took. */ static long -readvarinum () +readvarinum (void) { long value; int c; @@ -668,14 +660,13 @@ to32bit (int c1, int c2, int c3, int c4) } static int -to16bit (c1, c2) - int c1, c2; +to16bit (int c1, int c2) { return ((c1 & 0xff) << 8) + (c2 & 0xff); } static long -read32bit () +read32bit (void) { int c1, c2, c3, c4; @@ -687,7 +678,7 @@ read32bit () } static int -read16bit () +read16bit (void) { int c1, c2; c1 = egetc (); @@ -697,8 +688,7 @@ read16bit () /* static */ void -mferror (s) - char *s; +mferror (char *s) { if (Mf_error) (*Mf_error) (s); @@ -714,30 +704,26 @@ static char *Msgbuff = NULL; /* message buffer */ static int Msgsize = 0; /* Size of currently allocated Msg */ static int Msgindex = 0; /* index of next available location in Msg */ -static -void -msginit () +static void +msginit (void) { Msgindex = 0; } static char * -msg () +msg (void) { return (Msgbuff); } -static -int -msgleng () +static int +msgleng (void) { return (Msgindex); } -static -void -msgadd (c) - int c; +static void +msgadd (int c) { /* If necessary, allocate larger message buffer. */ if (Msgindex >= Msgsize) @@ -745,11 +731,9 @@ msgadd (c) Msgbuff[Msgindex++] = c; } -static -void -biggermsg () +static void +biggermsg (void) { -/* char *malloc(); */ char *newmess; char *oldmess = Msgbuff; int oldleng = Msgsize; @@ -805,12 +789,9 @@ static int laststatus = 0; * to work with Mf_putc. */ void -mfwrite (format, ntracks, division, fp) - int format, ntracks, division; - FILE *fp; +mfwrite (int format, int ntracks, int division, FILE *fp) { int i; - void mf_write_track_chunk (), mf_write_header_chunk (); if (Mf_putc == NULLFUNC) mferror ("mfmf_write() called without setting Mf_putc"); @@ -837,14 +818,10 @@ mfwrite (format, ntracks, division, fp) } void -mf_write_track_chunk (which_track, fp) - int which_track; - FILE *fp; +mf_write_track_chunk (int which_track, FILE *fp) { unsigned long trkhdr, trklength; long offset, place_marker; - void write16bit (), write32bit (); - laststatus = 0; @@ -910,11 +887,9 @@ mf_write_track_chunk (which_track, fp) void -mf_write_header_chunk (format, ntracks, division) - int format, ntracks, division; +mf_write_header_chunk (int format, int ntracks, int division) { unsigned long ident, length; - void write16bit (), write32bit (); ident = MThd; /* Head chunk identifier */ length = 6; /* Chunk length */ @@ -948,11 +923,8 @@ mf_write_header_chunk (format, ntracks, division) * size - The length of the meta-event data. */ int -mf_write_midi_event (delta_time, type, chan, data, size) - unsigned long delta_time; - int chan, type; - unsigned long size; - char *data; +mf_write_midi_event (unsigned long delta_time, int type, int chan, + char *data, unsigned long size) { int i; unsigned char c; @@ -999,11 +971,9 @@ mf_write_midi_event (delta_time, type, chan, data, size) * data. * size - The length of the meta-event data. */ -int -mf_write_meta_event (delta_time, type, data, size) - unsigned long delta_time; - unsigned char *data, type; - unsigned long size; +int +mf_write_meta_event (unsigned long delta_time, unsigned char type, + unsigned char *data, unsigned long size) { int i; @@ -1027,9 +997,7 @@ mf_write_meta_event (delta_time, type, data, size) } /* end mf_write_meta_event */ void -mf_write_tempo (delta_time, tempo) - unsigned long delta_time; - unsigned long tempo; +mf_write_tempo (unsigned long delta_time, unsigned long tempo) { /* Write tempo */ /* all tempos are written as 120 beats/minute, */ @@ -1046,9 +1014,7 @@ mf_write_tempo (delta_time, tempo) } void -mf_write_seqnum (delta_time, seqnum) - unsigned long delta_time; - unsigned seqnum; +mf_write_seqnum (unsigned long delta_time, unsigned int seqnum) { WriteVarLen (delta_time); @@ -1060,10 +1026,7 @@ mf_write_seqnum (delta_time, seqnum) } unsigned long -mf_sec2ticks (secs, division, tempo) - int division; - unsigned long tempo; - double secs; +mf_sec2ticks (double secs, int division, unsigned long tempo) { return (unsigned long) (((secs * 1000.0) / 4.0 * division) / tempo); } @@ -1072,8 +1035,7 @@ mf_sec2ticks (secs, division, tempo) * Write multi-length bytes to MIDI format files */ void -WriteVarLen (value) - unsigned long value; +WriteVarLen (unsigned long value) { unsigned long buffer; @@ -1102,10 +1064,7 @@ WriteVarLen (value) * */ double -mf_ticks2sec (ticks, division, tempo) - int division; - unsigned long tempo; - unsigned long ticks; +mf_ticks2sec (unsigned long ticks, int division, unsigned long tempo) { double smpte_format, smpte_resolution; @@ -1133,8 +1092,7 @@ mf_ticks2sec (ticks, division, tempo) * */ void -write32bit (data) - unsigned long data; +write32bit (unsigned long data) { eputc ((unsigned) ((data >> 24) & 0xff)); eputc ((unsigned) ((data >> 16) & 0xff)); @@ -1143,8 +1101,7 @@ write32bit (data) } void -write16bit (data) - int data; +write16bit (int data) { eputc ((unsigned) ((data & 0xff00) >> 8)); eputc ((unsigned) (data & 0xff)); @@ -1152,8 +1109,7 @@ write16bit (data) /* write a single character and abort on error */ static int -eputc (c) - unsigned char c; +eputc (unsigned char c) { int return_val; diff --git a/test/midifile.h b/test/midifile.h index 7dd4626e..5d408421 100644 --- a/test/midifile.h +++ b/test/midifile.h @@ -1,27 +1,27 @@ /* definitions for MIDI file parsing code */ -extern int (*Mf_getc)(); -extern void (*Mf_header)(); -extern void (*Mf_trackstart)(); -extern void (*Mf_trackend)(); -extern void (*Mf_noteon)(); -extern void (*Mf_noteoff)(); -extern void (*Mf_pressure)(); -extern void (*Mf_parameter)(); -extern void (*Mf_pitchbend)(); -extern void (*Mf_program)(); -extern void (*Mf_chanpressure)(); -extern void (*Mf_sysex)(); -extern void (*Mf_metamisc)(); -extern void (*Mf_seqspecific)(); -extern void (*Mf_seqnum)(); -extern void (*Mf_text)(); -extern void (*Mf_eot)(); -extern void (*Mf_timesig)(); -extern void (*Mf_smpte)(); -extern void (*Mf_tempo)(); -extern void (*Mf_keysig)(); -extern void (*Mf_arbitrary)(); -extern void (*Mf_error)(); +extern int (*Mf_getc)(void); +extern void (*Mf_error)(char *s); +extern void (*Mf_header)(int format, int ntrks, int division); +extern void (*Mf_trackstart)(void); +extern void (*Mf_trackend)(void); +extern void (*Mf_noteon)(int chan, int c1, int c2); +extern void (*Mf_noteoff)(int chan, int c1, int c2); +extern void (*Mf_pressure)(int chan, int c1, int c2); +extern void (*Mf_parameter)(int chan, int c1, int c2); +extern void (*Mf_pitchbend)(int chan, int c1, int c2); +extern void (*Mf_program)(int chan, int c1); +extern void (*Mf_chanpressure)(int chan, int c1); +extern void (*Mf_sysex)(int len, char *msg); +extern void (*Mf_arbitrary)(int len, char *msg); +extern void (*Mf_metamisc)(int type, int len, char *msg); +extern void (*Mf_seqnum)(int num); +extern void (*Mf_eot)(void); +extern void (*Mf_smpte)(char m0, char m1, char m2, char m3, char m4); +extern void (*Mf_tempo)(long tempo); +extern void (*Mf_timesig)(char m0, char m1, char m2, char m3); +extern void (*Mf_keysig)(char m0, char m1); +extern void (*Mf_seqspecific)(int len, char *msg); +extern void (*Mf_text)(int type, int len, char *msg); extern unsigned long Mf_currtime; extern unsigned long Mf_realtime; extern unsigned long Mf_currtempo; @@ -33,20 +33,23 @@ extern int Mf_file_size; #endif /* definitions for MIDI file writing code */ -extern int (*Mf_putc)(); -extern int (*Mf_writetrack)(); -extern int (*Mf_writetempotrack)(); +extern int (*Mf_putc)(unsigned char c); +extern int (*Mf_writetrack)(int track); +extern int (*Mf_writetempotrack)(void); -extern void midifile(); -extern unsigned long mf_sec2ticks(); -extern void mfwrite(); -extern int mf_write_meta_event(); +extern void midifile(void); +extern unsigned long mf_sec2ticks(double secs, int division, + unsigned long tempo); +extern void mfwrite(int format, int ntracks, int division, FILE *fp); +extern int mf_write_meta_event(unsigned long delta_time, unsigned char type, + unsigned char *data, unsigned long size); extern int mf_write_midi_event(unsigned long delta_time, int type, int chan, char *data, unsigned long size); -extern double mf_ticks2sec(unsigned long ticks,int division,unsigned long tempo); -extern void mf_write_tempo(); -extern void mf_write_seqnum(); -extern void mfread(); +extern double mf_ticks2sec(unsigned long ticks, int division, + unsigned long tempo); +extern void mf_write_tempo(unsigned long delta_time, unsigned long tempo); +extern void mf_write_seqnum(unsigned long delta_time, unsigned int seqnum); +extern void mfread(void); extern void mferror(char *s); #ifndef NO_LC_DEFINES diff --git a/test/playmidi1.c b/test/playmidi1.c index 831e9578..6ca0e397 100644 --- a/test/playmidi1.c +++ b/test/playmidi1.c @@ -45,10 +45,11 @@ #include #include +#include "../include/asoundlib.h" + #include "midifile.h" /* SMF library header */ #include "midifile.c" /* SMF library code */ -#include "../include/asoundlib.h" /* send the real-time time stamps (instead of midi ticks) to the ALSA sequencer */ static int use_realtime = 0; @@ -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 */ diff --git a/test/seq-ump-example.c b/test/seq-ump-example.c new file mode 100644 index 00000000..7f628682 --- /dev/null +++ b/test/seq-ump-example.c @@ -0,0 +1,187 @@ +// An example program to create a virtual UMP Endpoint +// +// A client simply reads each UMP packet and sends to subscribers +// while the note on/off velocity is halved + +#include +#include +#include +#include + +/* make the note on/off velocity half for MIDI1 CVM */ +static void midi1_half_note_velocity(snd_seq_ump_event_t *ev) +{ + snd_ump_msg_midi1_t *midi1 = (snd_ump_msg_midi1_t *)ev->ump; + + switch (snd_ump_msg_status(ev->ump)) { + case SND_UMP_MSG_NOTE_OFF: + case SND_UMP_MSG_NOTE_ON: + midi1->note_on.velocity >>= 1; + break; + } +} + +/* make the note on/off velocity half for MIDI2 CVM */ +static void midi2_half_note_velocity(snd_seq_ump_event_t *ev) +{ + snd_ump_msg_midi2_t *midi2 = (snd_ump_msg_midi2_t *)ev->ump; + + switch (snd_ump_msg_status(ev->ump)) { + case SND_UMP_MSG_NOTE_OFF: + case SND_UMP_MSG_NOTE_ON: + midi2->note_on.velocity >>= 1; + break; + } +} + +static void help(void) +{ + printf("seq-ump-example: Create a virtual UMP Endpoint and Blocks\n" + "\n" + "Usage: seq-ump-example [OPTIONS]\n" + "\n" + "-n,--num-blocks blocks Number of blocks (groups) to create\n" + "-m,--midi-version version MIDI protocol version (1 or 2)\n" + "-N--name UMP Endpoint name string\n" + "-P,--product name UMP Product ID string\n" + "-M,--manufacturer id UMP Manufacturer ID value (24bit)\n" + "-F,--family id UMP Family ID value (16bit)\n" + "-O,--model id UMP Model ID value (16bit)\n" + "-R,--sw-revision id UMP Software Revision ID (32bit)\n"); +} + +int main(int argc, char **argv) +{ + int midi_version = 2; + int num_blocks = 1; + const char *name = "ACMESynth"; + const char *product = "Halfmoon"; + unsigned int manufacturer = 0x123456; + unsigned int family = 0x1234; + unsigned int model = 0xabcd; + unsigned int sw_revision = 0x12345678; + snd_seq_t *seq; + snd_ump_endpoint_info_t *ep; + snd_ump_block_info_t *blk; + snd_seq_ump_event_t *ev; + int i, c, err; + unsigned char tmp[4]; + + static const struct option long_option[] = { + {"num-blocks", required_argument, 0, 'n'}, + {"midi-version", required_argument, 0, 'm'}, + {"name", required_argument, 0, 'N'}, + {"product", required_argument, 0, 'P'}, + {"manufacturer", required_argument, 0, 'M'}, + {"family", required_argument, 0, 'F'}, + {"model", required_argument, 0, 'O'}, + {"sw-revision", required_argument, 0, 'R'}, + {0, 0, 0, 0} + }; + + while ((c = getopt_long(argc, argv, "n:m:N:P:M:F:O:R:", + long_option, NULL)) >= 0) { + switch (c) { + case 'n': + num_blocks = atoi(optarg); + break; + case 'm': + midi_version = atoi(optarg); + break; + case 'N': + name = optarg; + break; + case 'P': + product = optarg; + break; + case 'M': + manufacturer = strtol(optarg, NULL, 0); + break; + case 'F': + family = strtol(optarg, NULL, 0); + break; + case 'O': + model = strtol(optarg, NULL, 0); + break; + case 'R': + sw_revision = strtol(optarg, NULL, 0); + break; + default: + help(); + return 1; + } + } + + err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0); + if (err < 0) { + fprintf(stderr, "failed to open sequencer: %d\n", err); + return 1; + } + + snd_ump_endpoint_info_alloca(&ep); + snd_ump_endpoint_info_set_name(ep, name); + snd_ump_endpoint_info_set_product_id(ep, product); + if (midi_version == 1) { + snd_ump_endpoint_info_set_protocol_caps(ep, SND_UMP_EP_INFO_PROTO_MIDI1); + snd_ump_endpoint_info_set_protocol(ep, SND_UMP_EP_INFO_PROTO_MIDI1); + } else { + snd_ump_endpoint_info_set_protocol_caps(ep, SND_UMP_EP_INFO_PROTO_MIDI2); + snd_ump_endpoint_info_set_protocol(ep, SND_UMP_EP_INFO_PROTO_MIDI2); + } + snd_ump_endpoint_info_set_num_blocks(ep, num_blocks); + snd_ump_endpoint_info_set_manufacturer_id(ep, manufacturer); + snd_ump_endpoint_info_set_family_id(ep, family); + snd_ump_endpoint_info_set_model_id(ep, model); + for (i = 0; i < 4; i++) + tmp[i] = (sw_revision >> ((3 - i) * 8)) & 0xff; + snd_ump_endpoint_info_set_sw_revision(ep, tmp); + + err = snd_seq_create_ump_endpoint(seq, ep, num_blocks); + if (err < 0) { + fprintf(stderr, "failed to set UMP EP info: %d\n", err); + return 1; + } + + snd_ump_block_info_alloca(&blk); + + for (i = 0; i < num_blocks; i++) { + char blkname[32]; + + sprintf(blkname, "Filter %d", i + 1); + snd_ump_block_info_set_name(blk, blkname); + snd_ump_block_info_set_direction(blk, SND_UMP_DIR_BIDIRECTION); + snd_ump_block_info_set_first_group(blk, i); + snd_ump_block_info_set_num_groups(blk, 1); + snd_ump_block_info_set_ui_hint(blk, SND_UMP_BLOCK_UI_HINT_BOTH); + + err = snd_seq_create_ump_block(seq, i, blk); + if (err < 0) { + fprintf(stderr, "failed to set UMP block info %d: %d\n", + i, err); + return 1; + } + } + + /* halve the incoming note-on / off velocity and pass through + * to subscribers + */ + while (snd_seq_ump_event_input(seq, &ev) >= 0) { + if (!snd_seq_ev_is_ump(ev)) + continue; + switch (snd_ump_msg_type(ev->ump)) { + case SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE: + midi1_half_note_velocity(ev); + break; + case SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE: + midi2_half_note_velocity(ev); + break; + } + + snd_seq_ev_set_subs(ev); + snd_seq_ev_set_direct(ev); + snd_seq_ump_event_output(seq, ev); + snd_seq_drain_output(seq); + } + + return 0; +} diff --git a/test/umpinfo.c b/test/umpinfo.c new file mode 100644 index 00000000..92a20936 --- /dev/null +++ b/test/umpinfo.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Inquire UMP Endpoint and Block info in various APIs + +#include +#include + +enum { UNKNOWN, SEQ, RAWMIDI, CTL }; +static int mode = UNKNOWN; + +static snd_seq_t *seq; +static int client; +static snd_ump_t *ump; +static snd_ctl_t *ctl; + +static int get_ump_endpoint_info(snd_ump_endpoint_info_t *ep) +{ + switch (mode) { + case SEQ: + return snd_seq_get_ump_endpoint_info(seq, client, ep); + case RAWMIDI: + return snd_ump_endpoint_info(ump, ep); + case CTL: + return snd_ctl_ump_endpoint_info(ctl, ep); + default: + return -EINVAL; + } +} + +static int get_ump_block_info(int blk, snd_ump_block_info_t *bp) +{ + switch (mode) { + case SEQ: + return snd_seq_get_ump_block_info(seq, client, blk, bp); + case RAWMIDI: + snd_ump_block_info_set_block_id(bp, blk); + return snd_ump_block_info(ump, bp); + case CTL: + snd_ump_block_info_set_block_id(bp, blk); + return snd_ctl_ump_block_info(ctl, bp); + default: + return -EINVAL; + } +} + +static void help(void) +{ + printf("umpinfo: inquire UMP Endpoint and Block info\n" + " umpinfo -c target inquiry via control API\n" + " umpinfo -m target inquiry via UMP rawmidi API\n" + " umpinfo -s client inquiry via sequencer API\n" + " target = hw:0, etc\n" + " client = sequencer client number\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + const char *target = "hw:0"; + snd_ump_endpoint_info_t *ep; + snd_ump_block_info_t *blk; + const unsigned char *s; + int c, i, num_blks; + + while ((c = getopt(argc, argv, "s:m:c:h")) >= 0) { + switch (c) { + case 's': + mode = SEQ; + client = atoi(target); + break; + case 'm': + mode = RAWMIDI; + target = optarg; + break; + case 'c': + mode = CTL; + target = optarg; + break; + default: + help(); + break; + } + } + + switch (mode) { + case SEQ: + if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0)) { + fprintf(stderr, "failed to open sequencer\n"); + return 1; + } + break; + case RAWMIDI: + if (snd_ump_open(&ump, NULL, target, 0)) { + fprintf(stderr, "failed to open UMP rawmidi\n"); + return 1; + } + break; + case CTL: + if (snd_ctl_open(&ctl, target, 0)) { + fprintf(stderr, "failed to open control\n"); + return 1; + } + break; + default: + help(); + break; + } + + snd_ump_endpoint_info_alloca(&ep); + snd_ump_block_info_alloca(&blk); + + if (get_ump_endpoint_info(ep)) { + fprintf(stderr, "failed to get UMP EP info\n"); + return 1; + } + + printf("Name: %s\n", snd_ump_endpoint_info_get_name(ep)); + printf("Product ID: %s\n", snd_ump_endpoint_info_get_product_id(ep)); + printf("Flags: 0x%x\n", snd_ump_endpoint_info_get_flags(ep)); + printf("Protocol Caps: 0x%x\n", snd_ump_endpoint_info_get_protocol_caps(ep)); + printf("Protocol: 0x%x\n", snd_ump_endpoint_info_get_protocol(ep)); + printf("Num Blocks: %d\n", snd_ump_endpoint_info_get_num_blocks(ep)); + printf("Version: 0x%x\n", snd_ump_endpoint_info_get_version(ep)); + printf("Manufacturer ID: 0x%x\n", snd_ump_endpoint_info_get_manufacturer_id(ep)); + printf("Family ID: 0x%x\n", snd_ump_endpoint_info_get_family_id(ep)); + printf("Model ID: 0x%x\n", snd_ump_endpoint_info_get_model_id(ep)); + s = snd_ump_endpoint_info_get_sw_revision(ep); + printf("SW Revision: %02x:%02x:%02x:%02x\n", s[0], s[1], s[2], s[3]); + num_blks = snd_ump_endpoint_info_get_num_blocks(ep); + for (i = 0; i < num_blks; i++) { + if (get_ump_block_info(i, blk)) { + fprintf(stderr, "failed to get UMP Block info for %d\n", i); + continue; + } + printf("\n"); + printf("Block ID: %d\n", snd_ump_block_info_get_block_id(blk)); + printf("Name: %s\n", snd_ump_block_info_get_name(blk)); + printf("Active: %d\n", snd_ump_block_info_get_active(blk)); + printf("Flags: 0x%x\n", snd_ump_block_info_get_flags(blk)); + printf("Direction: %d\n", snd_ump_block_info_get_direction(blk)); + printf("First Group: %d\n", snd_ump_block_info_get_first_group(blk)); + printf("Num Groups: %d\n", snd_ump_block_info_get_num_groups(blk)); + printf("MIDI-CI Version: %d\n", snd_ump_block_info_get_midi_ci_version(blk)); + printf("Sysex8 Streams: %d\n", snd_ump_block_info_get_sysex8_streams(blk)); + } + + return 0; +} diff --git a/utils/Makefile.am b/utils/Makefile.am index 94a71053..792ff1cc 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -2,7 +2,7 @@ if INSTALL_M4 aclocaldir=$(datadir)/aclocal aclocal_DATA=alsa.m4 endif -EXTRA_DIST=alsa.m4 buildrpm alsa.pc.in +EXTRA_DIST=alsa.m4 buildrpm alsa.pc.in alsa-topology.pc.in alsapkgconfdir = @ALSA_PKGCONF_DIR@ pkgconfigdir = $(alsapkgconfdir)