!29 merge master into master

【third_party】三方库升级 libdrm 2.4.120 -> 2.4.130;alsa-lib 1.2.11 -> 1.2.15.3;alsa-utils 1.2.11 -> 1.2.15.2;

Created-by: ykkwang
Commit-by: w30051647
Merged-by: openharmony_ci
Description: ### 一、内容说明(相关的Issue)
https://gitcode.com/openharmony/third_party_libdrm/issues/63?ref=&did=3841281#tid-3841281


### 二、建议测试周期和提测地址  
  建议测试完成时间:xxxx.xx.xx  
  投产上线时间:xxxx.xx.xx  
  提测地址:CI环境/压测环境  
  测试账号:  

### 三、变更内容
  * 3.1 关联PR列表
【third_party】三方库升级
libdrm 2.4.120 -> 2.4.130;
alsa-lib 1.2.11 -> 1.2.15.3;
alsa-utils 1.2.11 -> 1.2.15.2;

  * 3.2 数据库和部署说明  
    1. 常规更新 
    2. 重启unicorn
    3. 重启sidekiq
    4. 迁移任务:是否有迁移任务,没有写 "无"
    5. rake脚本:`bundle exec xxx RAILS_ENV = production`;没有写 "无"

  * 3.4 其他技术优化内容(做了什么,变更了什么)
    - 重构了 xxxx 代码
    - xxxx 算法优化


  * 3.5 废弃通知(什么字段、方法弃用?)



  * 3.6  后向不兼容变更(是否有无法向后兼容的变更?)


  
### 四、研发自测点(自测哪些?冒烟用例全部自测?)
  自测测试结论:


### 五、测试关注点(需要提醒QA重点关注的、可能会忽略的地方)
  检查点:

| 需求名称 | 是否影响xx公共模块 | 是否需要xx功能 | 需求升级是否依赖其他子产品 |
|------|------------|----------|---------------|
| xxx  | 否          | 需要       | 不需要           |
|      |            |          |               |

  接口测试:

  性能测试:

  并发测试:

  其他:



See merge request: openharmony/third_party_alsa-utils!29
This commit is contained in:
openharmony_ci
2026-04-11 11:55:32 +08:00
73 changed files with 8671 additions and 2658 deletions
+4 -1
View File
@@ -148,6 +148,9 @@ ohos_executable("alsactl") {
"alsactl/monitor.c",
"alsactl/state.c",
"alsactl/utils.c",
"alsactl/export.c",
"alsactl/wait.c",
"alsactl/boot_params.c",
]
include_dirs = [
@@ -166,7 +169,7 @@ ohos_executable("alsactl") {
install_images = [ "system" ]
}
group("alsa-utils") {
group("entry") {
deps = [
":aconnect",
":alsactl",
+1 -1
View File
@@ -3,7 +3,7 @@
"Name": "alsa-utils",
"License": "GPL V2",
"License File": "COPYING",
"Version Number": "v1.2.11",
"Version Number": "v1.2.15.2",
"Owner": "zhangyunhu@huawei.com",
"Upstream URL": "https://github.com/alsa-project/alsa-utils",
"Description": "This package contains the command line utilities for the ALSA project.",
+2 -2
View File
@@ -1,3 +1,3 @@
EXTRA_DIST = alsa-info.sh alsa-info.sh.1
EXTRA_DIST = alsa-info.sh alsa-info.sh.8
sbin_SCRIPTS = alsa-info.sh
man_MANS = alsa-info.sh.1
man_MANS = alsa-info.sh.8
+21 -1
View File
@@ -244,7 +244,7 @@ withpackages() {
RPM="$(command -v rpmquery)"
DPKG="$(command -v dpkg)"
[ -n "$RPM$DPKG" ] || return
local PATTERN='(alsa-(lib|oss|plugins|tools|(topology|ucm)-conf|utils|sof-firmware)|libalsa|tinycompress|sof-firmware)'
local PATTERN='(alsa-(lib|oss|plugins|tools|ucm|(topology|ucm)-conf|utils|sof-firmware)|libalsa|tinycompress|sof-firmware)'
{
echo "!!Packages installed"
echo "!!--------------------"
@@ -461,6 +461,20 @@ if [ -d /sys/bus/acpi/devices ]; then
done
fi
# Check for SoundWire ACPI _adr device status
if [ -d /sys/bus/acpi/devices ]; then
for f in /sys/bus/acpi/devices/*/adr; do
ACPI_ADR=$(cat $f 2>/dev/null);
if [[ "$ACPI_ADR" -ne 0 ]]; then
case $ACPI_ADR in
0x??????025d*) echo "Realtek $ACPI_ADR" >>$TEMPDIR/sdwstatus.tmp;;
0x??????01fa*) echo "Cirrus Logic $ACPI_ADR" >>$TEMPDIR/sdwstatus.tmp;;
0x??????0102*) echo "TI $ACPI_ADR" >>$TEMPDIR/sdwstatus.tmp;;
esac
fi
done
fi
awk '{ print $2 " (card " $1 ")" }' < /proc/asound/modules > $TEMPDIR/alsamodules.tmp 2> /dev/null
cat /proc/asound/cards > $TEMPDIR/alsacards.tmp
if [[ ! -z "$LSPCI" ]]; then
@@ -528,6 +542,12 @@ echo "" >> $FILE
cat $TEMPDIR/acpidevicestatus.tmp >> $FILE
echo "" >> $FILE
echo "" >> $FILE
echo "!!ACPI SoundWire Device Status Information" >> $FILE
echo "!!---------------" >> $FILE
echo "" >> $FILE
cat $TEMPDIR/sdwstatus.tmp >> $FILE
echo "" >> $FILE
echo "" >> $FILE
echo "!!Kernel Information" >> $FILE
echo "!!------------------" >> $FILE
echo "" >> $FILE
+62
View File
@@ -0,0 +1,62 @@
.TH ALSA-INFO.SH 1 "13 January 2016"
.SH NAME
alsa-info.sh \- command\-line utility to gather information about
the ALSA subsystem
.SH SYNOPSIS
\fBalsa-info.sh\fP [\fIoptions\fP]
.SH DESCRIPTION
\fBalsa-info.sh\fP is a command\-line utility gathering information
about the ALSA subsystem. It is used mostly for debugging purposes.
.SH OPTIONS
.TP
\fI\-\-upload\fP
Upload contents to the server (www.alsa-project.org or pastebin.ca).
.TP
\fI\-\-no-upload\fP
Do not upload contents to the remote server.
.TP
\fI\-\-stdout\fP
Print information to standard output.
.TP
\fI\-\-output FILE\fP
Specify file for output in no-upload mode.
.TP
\fI\-\-debug\fP
Run utility as normal, but will not delete file (usually
/tmp/alsa-info.txt).
.TP
\fI\-\-with-aplay\fP
Includes output from \fIaplay -l\fP.
.TP
\fI\-\-with-amixer\fP
Includes output from \fIamixer\fP.
.TP
\fI\-\-with-alsactl\fP
Includes output from \fIalsactl\fP.
.TP
\fI\-\-with-configs\fP
Includes output from ~/.asoundrc and /etc/asound.conf if they exist.
.TP
\fI\-\-update\fP
Check server for updates.
.TP
\fI\-\-about\fP
Print information about authors.
.SH EXAMPLES
.TP
\fBalsa-info.sh \-\-no-upload\fR
Will gather all information and show the output file.
.SH SEE ALSO
\fB
aplay(1)
amixer(1)
alsactl(1)
\fP
.SH AUTHOR
\fBalsa-info.sh\fP was created by the ALSA team, see \fI\-\-about\fP .
+5 -3
View File
@@ -1,3 +1,5 @@
alsa-store.service
alsa-restore.service
90-alsa-restore.rules
conf/90-alsa-restore.rules
conf/alsa-state.service
conf/alsa-store.service
conf/alsa-restore.service
conf/alsa-card-wait@.service
+26 -14
View File
@@ -11,8 +11,9 @@ AM_CFLAGS = -D_GNU_SOURCE
AM_CPPFLAGS = -I$(top_srcdir)/include
alsactl_SOURCES=alsactl.c state.c lock.c utils.c init_parse.c init_ucm.c \
daemon.c monitor.c clean.c info.c
alsactl_SOURCES=alsactl.c state.c lock.c utils.c wait.c \
init_parse.c init_ucm.c boot_params.c \
daemon.c monitor.c clean.c info.c export.c
alsactl_CFLAGS=$(AM_CFLAGS) -D__USE_GNU \
-DSYS_ASOUNDRC=\"$(ASOUND_STATE_DIR)/asound.state\" \
@@ -24,13 +25,14 @@ noinst_HEADERS=alsactl.h list.h init_sysdeps.c init_utils_string.c \
init_utils_run.c init_sysfs.c
udevrules_DATA = \
90-alsa-restore.rules
conf/90-alsa-restore.rules
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
alsa-state.service \
alsa-restore.service
conf/alsa-state.service \
conf/alsa-restore.service \
conf/alsa-card-wait@.service
install-data-hook:
$(MKDIR_P) -m 0755 \
@@ -43,30 +45,40 @@ install-data-hook:
endif
edit = \
extratest=$$(echo ' $(ALSACTL_UDEV_EXTRATEST)' | sed -e 's/__/ /g' -e 's/^ $$//'); \
args=$$(echo ' $(ALSACTL_UDEV_ARGS)' | sed -e 's/__/ /g' -e 's/^ $$//'); \
mkdir -p conf; \
$(SED) -r -e 's,@sbindir\@,$(sbindir),g' \
-e 's,@mydatadir\@,$(mydatadir),g' \
-e 's,@daemonswitch\@,$(ALSACTL_DAEMONSWITCH),g' \
-e 's,@asoundrcfile\@,$(ASOUND_STATE_DIR)/asound.state,g' \
-e "s;@extratest\@;$${extratest};g" \
-e "s;@args\@;$${args};g" \
< $< > $@ || rm $@
alsa-state.service: alsa-state.service.in
conf/alsa-state.service: conf/alsa-state.service.in
$(edit)
alsa-restore.service: alsa-restore.service.in
conf/alsa-restore.service: conf/alsa-restore.service.in
$(edit)
90-alsa-restore.rules: 90-alsa-restore.rules.in
conf/alsa-card-wait@.service: conf/alsa-card-wait@.service.in
$(edit)
conf/90-alsa-restore.rules: conf/90-alsa-restore.rules.in
$(edit)
EXTRA_DIST += \
alsa-state.service.in \
alsa-restore.service.in \
90-alsa-restore.rules.in
conf/alsa-state.service.in \
conf/alsa-restore.service.in \
conf/alsa-card-wait@.service.in \
conf/90-alsa-restore.rules.in
CLEANFILES = \
alsa-state.service \
alsa-restore.service \
90-alsa-restore.rules
conf/alsa-state.service \
conf/alsa-restore.service \
conf/alsa-card-wait@.service \
conf/90-alsa-restore.rules
%.7: %.xml
xmlto man $?
+2 -2
View File
@@ -90,7 +90,7 @@ Note that the configuration hooks are evaluated.
.SH OPTIONS
.TP
\fI\-h, \-\-help\fP
\fI\-h, \-\-help\fP
Help: show available flags and commands.
.TP
@@ -223,7 +223,7 @@ aplay(1),
alsactl_init(7)
\fP
.SH BUGS
.SH BUGS
None known.
.SH AUTHOR
+28 -1
View File
@@ -44,14 +44,19 @@
#ifndef SYS_LOCKPATH
#define SYS_LOCKPATH "/var/lock"
#endif
#ifndef SYS_CARD_GROUP
#define SYS_CARD_GROUP SYS_ASOUND_DIR "/card-group.state"
#endif
int debugflag = 0;
int force_restore = 1;
int ignore_nocards = 0;
int do_lock = 0;
int use_syslog = 0;
int do_export = 0;
char *command;
char *statefile = NULL;
char *groupfile = SYS_CARD_GROUP;
char *lockpath = SYS_LOCKPATH;
char *lockfile = SYS_LOCKFILE;
@@ -78,6 +83,7 @@ static struct arg args[] = {
{ 'v', "version", "print version of this program" },
{ HEADER, NULL, "Available state options:" },
{ FILEARG | 'f', "file", "configuration file (default " SYS_ASOUNDRC ")" },
{ FILEARG | 'G', "group-file", "card group configuration file (default " SYS_CARD_GROUP ")" },
{ FILEARG | 'a', "config-dir", "boot / hotplug configuration directory (default " SYS_ASOUND_DIR ")" },
{ 'l', "lock", "use file locking to serialize concurrent access" },
{ 'L', "no-lock", "do not use file locking to serialize concurrent access" },
@@ -92,6 +98,7 @@ static struct arg args[] = {
{ FILEARG | 'r', "runstate", "save restore and init state to this file (only errors)" },
{ 0, NULL, " default settings is 'no file set'" },
{ 'R', "remove", "remove runstate file at first, otherwise append errors" },
{ 'Y', "export", "export card state as key=value pairs (restore command only)" },
{ INTARG | 'p', "period", "store period in seconds for the daemon command" },
{ FILEARG | 'e', "pid-file", "pathname for the process id (daemon mode)" },
{ HEADER, NULL, "Available init options:" },
@@ -105,6 +112,7 @@ static struct arg args[] = {
#ifdef HAVE_ALSA_USE_CASE_H
{ 'D', "ucm-defaults", "execute also the UCM 'defaults' section" },
{ 'U', "no-ucm", "don't init with UCM" },
{ 'm', "force-ucm-restore", "force UCM restore for boot card groups" },
#if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION
{ 'X', "ucm-nodev", "show UCM no device errors" },
#endif
@@ -115,6 +123,7 @@ static struct arg args[] = {
{ CARDCMD, "restore", "load current driver setup for one or each soundcards" },
{ EMPCMD, NULL, " from configuration file" },
{ CARDCMD, "nrestore", "like restore, but notify the daemon to rescan soundcards" },
{ CARDCMD, "wrestore", "wait for card ready, then restore" },
{ CARDCMD, "init", "initialize driver to a default state" },
{ CARDCMD, "daemon", "store state periodically for one or each soundcards" },
{ CARDCMD, "rdaemon", "like daemon but do the state restore at first" },
@@ -301,6 +310,9 @@ int main(int argc, char *argv[])
case 'f':
cfgfile = optarg;
break;
case 'G':
groupfile = optarg;
break;
case 'a':
cfgdir = optarg;
break;
@@ -341,6 +353,9 @@ int main(int argc, char *argv[])
case 'U':
initflags |= FLAG_UCM_DISABLED;
break;
case 'm':
initflags |= FLAG_UCM_RESTORE;
break;
case 'X':
initflags |= FLAG_UCM_NODEV;
break;
@@ -350,6 +365,9 @@ int main(int argc, char *argv[])
case 'R':
removestate = 1;
break;
case 'Y':
do_export = 1;
break;
case 'P':
force_restore = 0;
break;
@@ -441,7 +459,11 @@ int main(int argc, char *argv[])
syslog(LOG_INFO, "alsactl " SND_UTIL_VERSION_STR " daemon started");
}
#if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
snd_lib_error_set_handler(error_handler);
#else
snd_lib_log_set_handler(log_handler);
#endif
if (!strcmp(cmd, "init")) {
res = init(cfgdir, initfile, initflags | FLAG_UCM_FBOOT | FLAG_UCM_BOOT, cardname);
@@ -450,10 +472,15 @@ int main(int argc, char *argv[])
res = save_state(cfgfile, cardname);
} else if (!strcmp(cmd, "restore") ||
!strcmp(cmd, "rdaemon") ||
!strcmp(cmd, "nrestore")) {
!strcmp(cmd, "nrestore") ||
!strcmp(cmd, "wrestore")) {
if (removestate)
remove(statefile);
if (!strcmp(cmd, "wrestore"))
initflags |= FLAG_UCM_WAIT;
res = load_state(cfgdir, cfgfile, initfile, initflags, cardname, init_fallback);
if (do_export && res >= 0)
res = export_cards(cardname);
if (!strcmp(cmd, "rdaemon")) {
do_nice(use_nice, sched_idle);
res = state_daemon(cfgfile, cardname, period, pidfile);
+38 -2
View File
@@ -2,14 +2,17 @@
#include <alsa/asoundlib.h>
#define LOCK_TIMEOUT 10
#define DEFAULT_SYNC_TIME 20
extern int debugflag;
extern int force_restore;
extern int ignore_nocards;
extern int do_lock;
extern int use_syslog;
extern int do_export;
extern char *command;
extern char *statefile;
extern char *groupfile;
extern char *lockpath;
extern char *lockfile;
@@ -24,7 +27,8 @@ void info_(const char *fcn, long line, const char *fmt, ...);
void error_(const char *fcn, long line, const char *fmt, ...);
void cerror_(const char *fcn, long line, int cond, const char *fmt, ...);
void dbg_(const char *fcn, long line, const char *fmt, ...);
void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...);
void error_handler(const char *file, int line, const char *function, int errcode, const char *fmt, ...);
void log_handler(int prio, int interface, const char *file, int line, const char *function, int errcode, const char *fmt, va_list arg);
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define info(...) do { info_(__func__, __LINE__, __VA_ARGS__); } while (0)
@@ -43,6 +47,19 @@ void error_handler(const char *file, int line, const char *function, int err, co
#define FLAG_UCM_BOOT (1<<2)
#define FLAG_UCM_DEFAULTS (1<<3)
#define FLAG_UCM_NODEV (1<<4)
#define FLAG_UCM_RESTORE (1<<5)
#define FLAG_UCM_WAIT (1<<6)
enum {
CARD_STATE_WAIT = 1, /* skip configuration (wait for sync) */
CARD_STATE_SKIP = 2, /* skip card */
CARD_STATE_RESTORED = 3, /* card was restored */
};
static inline bool card_state_is_okay(int state)
{
return state >= CARD_STATE_WAIT && state <= CARD_STATE_RESTORED;
}
void snd_card_iterator_init(struct snd_card_iterator *iter, int cardno);
int snd_card_iterator_sinit(struct snd_card_iterator *iter, const char *cardname);
@@ -51,15 +68,28 @@ int snd_card_iterator_error(struct snd_card_iterator *iter);
int load_configuration(const char *file, snd_config_t **top, int *open_failed);
int init(const char *cfgdir, const char *file, int flags, const char *cardname);
int init_ucm(int flags, int cardno);
int init_ucm(const char *cfgdir, int flags, int cardno);
bool validate_boot_time(long long boot_time, long long current_time, long long synctime);
int read_boot_params(snd_ctl_t *handle, long long *boot_time, long long *sync_time, long long *restore_time, long long *primary_card);
int write_boot_params(snd_ctl_t *handle, long long boot_time, long long sync_time, long long restore_time, long long primary_card);
int card_group_load(snd_config_t **config);
int card_group_save(snd_config_t *config);
int card_group_get_int64(snd_config_t *config_group, const char *id, long long *val);
int card_group_set_int64(snd_config_t *config_group, const char *id, long long val);
int check_boot_params_validity(snd_ctl_t *handle, int cardno, char **boot_card_group, bool *valid, bool *in_sync, bool *restored, int *primary_card, long long *synctime);
int update_boot_params(snd_ctl_t *handle, int cardno, const char *boot_card_group, bool valid, bool restored, long long synctime);
int boot_params_remove_card(int cardno);
int state_lock(const char *file, int timeout);
int state_unlock(int lock_fd, const char *file);
int card_lock(int card_number, int timeout);
int card_unlock(int lock_fd, int card_number);
int group_state_lock(const char *file, int timeout);
int group_state_unlock(int lock_fd, const char *file);
int save_state(const char *file, const char *cardname);
int load_state(const char *cfgdir, const char *file,
const char *initfile, int initflags,
const char *cardname, int do_init);
int wait_for_card(long long timeout, int cardno);
int power(const char *argv[], int argc);
int monitor(const char *name);
int general_info(const char *name);
@@ -68,6 +98,12 @@ int state_daemon(const char *file, const char *cardname, int period,
int state_daemon_kill(const char *pidfile, const char *cmd);
int clean(const char *cardname, char *const *extra_args);
int snd_card_clean_cfgdir(const char *cfgdir, int cardno);
void add_linked_card(int cardno);
/* export */
int export_card_state_set(int card, int state);
int export_cards(const char *cardname);
/* utils */
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
# do not edit this file, it will be overwritten on update
ACTION=="add", SUBSYSTEM=="sound", KERNEL=="controlC*", KERNELS!="card*",@extratest@ GOTO="alsa_restore_go"
GOTO="alsa_restore_end"
LABEL="alsa_restore_go"
ENV{ALSA_CARD_NUMBER}="$attr{device/number}"
# mark HDA analog card; HDMI/DP card does not have capture devices
DRIVERS=="snd_hda_intel", TEST=="device/pcmC$env{ALSA_CARD_NUMBER}D0c", RUN+="/bin/sh -c 'echo ALSA_CARD_HDA_ANALOG=$env{ALSA_CARD_NUMBER} >> /run/udev/alsa-hda-analog-card'"
# check for ACP hardware
TEST=="device/device/acp3x-dmic-capture", GOTO="alsa_hda_analog"
TEST=="device/device/acp6x-dmic-capture", GOTO="alsa_hda_analog"
TEST=="device/device/acp63-dmic-capture", GOTO="alsa_hda_analog"
TEST=="device/device/acp-dmic-codec", GOTO="alsa_hda_analog"
GOTO="alsa_restore_std"
LABEL="alsa_hda_analog"
# restore configuration for profile with combined cards (HDA + digital mic)
TEST!="/run/udev/alsa-hda-analog-card", GOTO="alsa_restore_std"
IMPORT{program}="/usr/bin/cat /run/udev/alsa-hda-analog-card"
ENV{ALSA_CARD_HDA_ANALOG}!="", ENV{ALSA_CARD_NUMBER}="$env{ALSA_CARD_HDA_ANALOG}"
LABEL="alsa_restore_std"
TEST!="@daemonswitch@", IMPORT{program}="@sbindir@/alsactl@args@ --export restore $env{ALSA_CARD_NUMBER}"
TEST=="@daemonswitch@", IMPORT{program}="@sbindir@/alsactl@args@ --export nrestore $env{ALSA_CARD_NUMBER}"
ENV{ALSA_CARD_STATE}=="waiting", ENV{SYSTEMD_WANTS}="alsa-card-wait@$env{ALSA_CARD_NUMBER}.service"
LABEL="alsa_restore_end"
+12
View File
@@ -0,0 +1,12 @@
#
# ALSA card initialization handler for cards in waiting state
# This service is triggered by udev when ALSA_CARD_STATE=waiting
#
[Unit]
Description=ALSA Card Initialization for card %I
[Service]
Type=oneshot
RemainAfterExit=no
ExecStart=@sbindir@/alsactl@args@ wrestore %i
+15
View File
@@ -0,0 +1,15 @@
#
# Note that two different ALSA card state management schemes exist and they
# can be switched using a file exist check - /etc/alsa/state-daemon.conf .
#
[Unit]
Description=Save/Restore Sound Card State
ConditionPathExists=!@daemonswitch@
ConditionPathExistsGlob=/dev/snd/control*
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=-@sbindir@/alsactl restore
ExecStop=-@sbindir@/alsactl store
+13
View File
@@ -0,0 +1,13 @@
#
# Note that two different ALSA card state management schemes exist and they
# can be switched using a file exist check - /etc/alsa/state-daemon.conf .
#
[Unit]
Description=Manage Sound Card State (restore and store)
ConditionPathExists=@daemonswitch@
[Service]
Type=simple
ExecStart=-@sbindir@/alsactl -s -n 19 -c rdaemon
ExecStop=-@sbindir@/alsactl -s kill save_and_quit
+2
View File
@@ -79,6 +79,8 @@ static void card_free(struct card **card)
{
struct card *c = *card;
if (c == NULL)
return;
free_list(&c->blacklist);
free_list(&c->whitelist);
if (c->handle)
+133
View File
@@ -0,0 +1,133 @@
/*
* Advanced Linux Sound Architecture Control Program - Export
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/**
* Export variable syntax:
*
* For single card:
*
* ALSA_CARD_NUMBER=<number>
* ALSA_CARD_STATE=<state>
*
* For multiple cards:
*
* ALSA_CARD#_STATE=<state> # where # is replaced with the card number
*
* State list:
*
* active # card was initialized and active
* skip # card was skipped (other card in group)
* waiting # card is waiting for the initial configuration
*/
#include "aconfig.h"
#include "version.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alsactl.h"
/* Global array to store card states (0 = active, 1 = waiting) */
static int export_card_state[32];
/**
* export_card_state_set - Set card state
* @card: Card number
* @state: Card state (0 = active, 1 = waiting)
*
* Returns 0 on success, negative error code on failure
*/
int export_card_state_set(int card, int state)
{
/* Check bounds */
if (card < 0 || (unsigned long)card >= ARRAY_SIZE(export_card_state))
return -EINVAL;
export_card_state[card] = state;
return 0;
}
/**
* export_card_state_print - Export and print card state information
* @iter: Card iterator containing current card information
*
* Prints key=value pairs based on iterator's single flag and export_card_state array.
* Returns 0 on success, negative error code on failure
*/
static int export_card_state_print(struct snd_card_iterator *iter)
{
const char *state;
int istate;
if (!iter)
return -EINVAL;
/* Check bounds */
if (iter->card < 0 || (unsigned long)iter->card >= ARRAY_SIZE(export_card_state))
return -EINVAL;
/* Determine state from export_card_state array */
istate = export_card_state[iter->card];
switch (istate) {
case CARD_STATE_WAIT: state = "waiting"; break;
case CARD_STATE_SKIP: state = "skip"; break;
default: state = "active"; break;
}
if (iter->single) {
/* Single card export format */
printf("ALSA_CARD_NUMBER=%d\n", iter->card);
printf("ALSA_CARD_STATE=%s\n", state);
} else {
/* Multiple cards export format */
printf("ALSA_CARD%d_STATE=%s\n", iter->card, state);
}
return 0;
}
/**
* export_cards - Export state for all cards
* @cardname: Card name or NULL for all cards
*
* Returns 0 on success, negative error code on failure
*/
int export_cards(const char *cardname)
{
struct snd_card_iterator iter;
const char *name;
int ret;
ret = snd_card_iterator_sinit(&iter, cardname);
if (ret < 0)
return ret;
while ((name = snd_card_iterator_next(&iter)) != NULL) {
ret = export_card_state_print(&iter);
if (ret < 0)
break;
}
if (ret == 0)
ret = snd_card_iterator_error(&iter);
return ret;
}
+9 -3
View File
@@ -35,7 +35,9 @@ static int pcm_device_list(snd_ctl_t *ctl, snd_pcm_stream_t stream, bool *first)
streamfirst = true;
while (1) {
if ((err = snd_ctl_pcm_next_device(ctl, &dev)) < 0) {
error("snd_ctl_pcm_next_device");
if (err == ENOTTY) /* no kernel support */
return 0;
error("snd_ctl_pcm_next_device: %s", snd_strerror(err));
return err;
}
if (dev < 0)
@@ -102,7 +104,9 @@ static int rawmidi_device_list(snd_ctl_t *ctl, snd_rawmidi_stream_t stream, bool
streamfirst = true;
while (1) {
if ((err = snd_ctl_rawmidi_next_device(ctl, &dev)) < 0) {
error("snd_ctl_rawmidi_next_device");
if (err == ENOTTY) /* no kernel support */
return 0;
error("snd_ctl_rawmidi_next_device: %s", snd_strerror(err));
return err;
}
if (dev < 0)
@@ -159,7 +163,9 @@ static int hwdep_device_list(snd_ctl_t *ctl)
first = true;
while (1) {
if ((err = snd_ctl_hwdep_next_device(ctl, &dev)) < 0) {
error("snd_ctl_pcm_next_device");
if (err == ENOTTY) /* no kernel support */
return 0;
error("snd_ctl_pcm_next_device: %s", snd_strerror(err));
return err;
}
if (dev < 0)
+2 -2
View File
@@ -1761,8 +1761,8 @@ int init(const char *cfgdir, const char *filename, int flags, const char *cardna
lasterr = err;
continue;
}
err = init_ucm(flags, iter.card);
if (err == 0)
err = init_ucm(cfgdir, flags, iter.card);
if (err == 0 || card_state_is_okay(err))
continue;
err = init_space(&space, iter.card);
if (err != 0)
+262 -21
View File
@@ -21,6 +21,8 @@
#include "aconfig.h"
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include "alsactl.h"
#ifdef HAVE_ALSA_USE_CASE_H
@@ -28,51 +30,290 @@
#include <alsa/use-case.h>
/*
* Keep it as simple as possible. Execute commands from the
* FixedBootSequence and BootSequence only.
* Helper: Check if card should skip initialization based on boot parameters
* Returns: 1 if should skip, 2 if should skip other card, 0 if should continue, negative on error
* If check_restored is true, also checks if card state is already restored
*/
int init_ucm(int flags, int cardno)
static int should_skip_initialization(snd_ctl_t *ctl, int cardno, int flags,
char **boot_card_group, bool *valid,
bool *in_sync, bool *restored, int *primary_card,
long long *synctime)
{
int err;
err = check_boot_params_validity(ctl, cardno, boot_card_group, valid, in_sync, restored, primary_card, synctime);
if (err < 0) {
dbg("boot parameters validity failed: %s", snd_strerror(err));
return err;
}
/* do nothing for other cards in group */
if (*valid && *primary_card != cardno) {
dbg("Skipping card %d - not primary (primary is %d)", cardno, *primary_card);
return CARD_STATE_SKIP;
}
/* for immediate initialization, caller must set UCM force-restore flag */
if (*valid && *in_sync && (flags & FLAG_UCM_RESTORE) == 0) {
dbg("Skipping card %d - in sync and no force-restore flag", cardno);
return CARD_STATE_WAIT;
}
return 0;
}
/*
* Helper: Get boot card group configuration from UCM
* Returns: 0 on success, negative on error
*/
static int get_boot_card_group_config(snd_use_case_mgr_t *uc_mgr, char **boot_card_group, long long *synctime)
{
char *sync_time = NULL;
int err;
err = snd_use_case_get(uc_mgr, "=BootCardGroup", (const char **)boot_card_group);
if (err != 0 || *boot_card_group == NULL) {
return -ENOENT;
}
dbg("BootCardGroup found: %s", *boot_card_group);
/* Get optional sync time */
err = snd_use_case_get(uc_mgr, "=BootCardSyncTime", (const char **)&sync_time);
if (err == 0 && sync_time != NULL) {
char *endptr;
errno = 0;
*synctime = strtoll(sync_time, &endptr, 10);
if (errno != 0 || *endptr != '\0' || endptr == sync_time) {
error("Invalid BootCardSyncTime value '%s'", sync_time);
*synctime = DEFAULT_SYNC_TIME;
}
free(sync_time);
}
return 0;
}
/*
* Helper: Open UCM manager with appropriate flags
* Returns: 0 on success, negative on error
*/
static int open_ucm_manager(snd_use_case_mgr_t **uc_mgr, int cardno, int flags,
bool valid, bool fixed_boot)
{
char id[64], *nodev, *in_boot;
int err;
nodev = (flags & FLAG_UCM_NODEV) ? "" : "-";
in_boot = (valid || !fixed_boot) ? "" : "<<<InBoot=1>>>";
snprintf(id, sizeof(id), "%s%shw:%d", nodev, in_boot, cardno);
err = snd_use_case_mgr_open(uc_mgr, id);
dbg("ucm open '%s': %d", id, err);
return err;
}
/*
* Helper: Reopen UCM manager without InBoot flag
* Returns: 0 on success, negative on error
*/
static int reopen_ucm_manager(snd_use_case_mgr_t **uc_mgr, int cardno, int flags)
{
char id[64], *nodev;
int err;
snd_use_case_mgr_close(*uc_mgr);
nodev = (flags & FLAG_UCM_NODEV) ? "" : "-";
snprintf(id, sizeof(id), "%shw:%d", nodev, cardno);
err = snd_use_case_mgr_open(uc_mgr, id);
dbg("ucm reopen '%s': %d", id, err);
return err;
}
/*
* Helper: Execute boot sequences
* Returns: 0 on success, negative on error
*/
static int execute_boot_sequences(const char *cfgdir, snd_use_case_mgr_t *uc_mgr,
int cardno, int flags, bool fixed_boot)
{
int err = 0;
if (fixed_boot) {
err = snd_card_clean_cfgdir(cfgdir, cardno);
if (err < 0) {
dbg("ucm clean cfgdir: %d", err);
return err;
}
err = snd_use_case_set(uc_mgr, "_fboot", NULL);
dbg("ucm _fboot: %d", err);
if (err == -ENOENT && (flags & FLAG_UCM_BOOT) != 0) {
/* _fboot not found but _boot requested - continue */
err = 0;
} else if (err < 0) {
return err;
}
}
if (flags & FLAG_UCM_BOOT) {
err = snd_use_case_set(uc_mgr, "_boot", NULL);
dbg("ucm _boot: %d", err);
if (err < 0)
return err;
if ((flags & FLAG_UCM_DEFAULTS) != 0)
err = snd_use_case_set(uc_mgr, "_defaults", NULL);
}
return err;
}
/*
* Execute commands from the FixedBootSequence and BootSequence.
* Handle also card groups.
* Returns: 0 = success, 1 = skip this card (e.g. linked or in-sync), negative on error
*/
int init_ucm(const char *cfgdir, int flags, int cardno)
{
snd_use_case_mgr_t *uc_mgr;
char id[32], *nodev;
int err;
char id[64];
char *boot_card_group = NULL, *boot_card_group_verify = NULL;
bool fixed_boot, valid = false, in_sync = false, restored = false;
snd_ctl_t *ctl = NULL;
int err, primary_card = -1, lock_fd = -1;
long long synctime = -1;
if (flags & FLAG_UCM_DISABLED) {
dbg("ucm disabled");
return -ENXIO;
}
nodev = (flags & FLAG_UCM_NODEV) ? "" : "-";
snprintf(id, sizeof(id), "%shw:%d", nodev, cardno);
err = snd_use_case_mgr_open(&uc_mgr, id);
dbg("ucm open '%s': %d", id, err);
if (err < 0)
fixed_boot = (flags & FLAG_UCM_FBOOT) != 0;
snprintf(id, sizeof(id), "hw:%d", cardno);
err = snd_ctl_open(&ctl, id, 0);
if (err < 0) {
dbg("UCM: unable to open control device '%s': %s", id, snd_strerror(err));
return err;
if (flags & FLAG_UCM_FBOOT) {
err = snd_use_case_set(uc_mgr, "_fboot", NULL);
dbg("ucm _fboot: %d", err);
if (err == -ENOENT && (flags & FLAG_UCM_BOOT) != 0) {
/* nothing */
}
err = should_skip_initialization(ctl, cardno, flags, &boot_card_group, &valid,
&in_sync, &restored, &primary_card, &synctime);
if (err != 0)
goto _fin;
if (valid) {
if (restored) {
err = CARD_STATE_RESTORED;
goto _fin;
}
lock_fd = group_state_lock(groupfile, LOCK_TIMEOUT);
if (lock_fd < 0) {
err = lock_fd;
goto _fin;
}
}
err = open_ucm_manager(&uc_mgr, cardno, flags, valid, fixed_boot);
if (err < 0)
goto _fin;
if (!fixed_boot)
goto _execute_boot;
if (!valid) {
err = get_boot_card_group_config(uc_mgr, &boot_card_group, &synctime);
if (err == -ENOENT) {
/* No BootCardGroup - remove any existing boot params */
err = boot_params_remove_card(cardno);
if (err < 0)
goto _error;
goto _execute_boot;
} else if (err < 0) {
goto _error;
}
if (lock_fd < 0) {
lock_fd = group_state_lock(groupfile, LOCK_TIMEOUT);
if (lock_fd < 0) {
err = lock_fd;
goto _error;
}
}
err = should_skip_initialization(ctl, cardno, flags, &boot_card_group_verify,
&valid, &in_sync, &restored, &primary_card, &synctime);
if (err != 0)
goto _error;
if (valid && (boot_card_group_verify == NULL || strcmp(boot_card_group_verify, boot_card_group) != 0)) {
dbg("expected different boot card group (got '%s', expected '%s')", boot_card_group_verify, boot_card_group);
err = -EINVAL;
goto _error;
}
if ((flags & FLAG_UCM_RESTORE) == 0 && (!valid || restored)) {
dbg("Skipping card %d (group '%s') - %s and no force-restore flag", cardno, boot_card_group,
!valid ? "validity not passed" : "already restored");
if (!valid) {
/* create initial 'Boot' element */
err = update_boot_params(ctl, cardno, boot_card_group, 0, restored, synctime);
if (err < 0)
goto _error;
}
err = restored ? CARD_STATE_RESTORED : CARD_STATE_WAIT;
goto _error;
}
err = reopen_ucm_manager(&uc_mgr, cardno, flags);
if (err < 0)
goto _fin;
}
if (flags & FLAG_UCM_BOOT) {
err = snd_use_case_set(uc_mgr, "_boot", NULL);
dbg("ucm _boot: %d", err);
_execute_boot:
if (fixed_boot)
restored = true;
if (boot_card_group) {
err = update_boot_params(ctl, cardno, boot_card_group, valid, restored, synctime);
if (err < 0)
goto _error;
if ((flags & FLAG_UCM_DEFAULTS) != 0)
err = snd_use_case_set(uc_mgr, "_defaults", NULL);
}
err = execute_boot_sequences(cfgdir, uc_mgr, cardno, flags, fixed_boot);
if (err < 0)
goto _error;
err = 0;
_error:
snd_use_case_mgr_close(uc_mgr);
_fin:
if (fixed_boot && primary_card >= 0 && primary_card != cardno) {
/* remove card specific configuration files for other cards in group */
int clean_err = snd_card_clean_cfgdir(cfgdir, cardno);
if (clean_err < 0) {
dbg("ucm clean cfgdir: %d", clean_err);
if (err >= 0)
err = clean_err;
}
}
if (lock_fd >= 0)
group_state_unlock(lock_fd, groupfile);
if (ctl)
snd_ctl_close(ctl);
free(boot_card_group);
free(boot_card_group_verify);
dbg("ucm init complete %d", err);
return err;
}
#else
int init_ucm(int flags, int cardno)
int init_ucm(const char *cfgdir, int flags, int cardno)
{
return -ENXIO;
}
+34
View File
@@ -181,6 +181,40 @@ int state_unlock(int _fd, const char *file)
return err;
}
static void group_state_lock_file(char *buf, size_t buflen)
{
const char *name = strrchr(groupfile, '/');
if (name && name[0])
name++;
else
name = "card-group.state";
snprintf(buf, buflen, "%s/%s.lock", lockpath, name);
}
int group_state_lock(const char *file, int timeout)
{
char fn[PATH_SIZE];
int err;
group_state_lock_file(fn, sizeof(fn));
err = state_lock_(fn, 1, timeout, -1);
if (err < 0)
error("file %s lock error: %s", file, strerror(-err));
return err;
}
int group_state_unlock(int _fd, const char *file)
{
char fn[PATH_SIZE];
int err;
group_state_lock_file(fn, sizeof(fn));
err = state_lock_(fn, 0, 10, _fd);
if (err < 0)
error("file %s unlock error: %s", file, strerror(-err));
return err;
}
static void card_lock_file(char *buf, size_t buflen, int card_number)
{
snprintf(buf, buflen, "%s/card%i.lock", lockpath, card_number);
+1 -1
View File
@@ -176,7 +176,7 @@ static int check_control_cdev(int infd, bool *retry)
ssize_t len = read(infd, buf, sizeof(*ev) + NAME_MAX);
if (len < 0) {
if (errno != EAGAIN)
err = errno;
err = -errno;
break;
} else if (len == 0) {
break;
+218 -128
View File
@@ -29,6 +29,33 @@
#include <errno.h>
#include "alsactl.h"
static int linked_cards[16];
static void init_linked_cards(void)
{
size_t index;
for (index = 0; index < ARRAY_SIZE(linked_cards); index++)
linked_cards[index] = -1;
}
void add_linked_card(int cardno)
{
size_t index;
for (index = 0; index < ARRAY_SIZE(linked_cards); index++) {
if (linked_cards[index] == cardno)
return;
}
for (index = 0; index < ARRAY_SIZE(linked_cards); index++) {
if (linked_cards[index] < 0) {
linked_cards[index] = cardno;
return;
}
}
error("Too many linked cards!");
}
static char *id_str(snd_ctl_elem_id_t *id)
{
@@ -1201,32 +1228,114 @@ static int restore_config_value2(snd_ctl_t *handle, snd_ctl_elem_info_t *info,
return 0;
}
static int set_control(snd_ctl_t *handle, snd_config_t *control,
int *maxnumid, int doit)
static int parse_config_id(snd_config_t *control, snd_ctl_elem_id_t *id,
snd_config_t **comment, snd_config_t **value, int doit)
{
snd_config_iterator_t i, next;
const char *numid;
snd_ctl_elem_iface_t iface = -1;
long device = -1;
long subdevice = -1;
const char *name = NULL;
long index = -1;
snd_ctl_elem_id_clear(id);
if (snd_config_get_id(control, &numid) < 0)
return -EINVAL;
snd_config_for_each(i, next, control) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *fld;
if (snd_config_get_id(n, &fld) < 0)
continue;
if (strcmp(fld, "comment") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
cerror(doit, "control.%s.%s is invalid", numid, fld);
return -EINVAL;
}
if (comment)
*comment = n;
continue;
}
if (strcmp(fld, "iface") == 0) {
iface = (snd_ctl_elem_iface_t)config_iface(n);
if (iface < 0)
return -EINVAL;
continue;
}
if (strcmp(fld, "device") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
cerror(doit, "control.%s.%s is invalid", numid, fld);
return -EINVAL;
}
if (snd_config_get_integer(n, &device) < 0)
return -EINVAL;
continue;
}
if (strcmp(fld, "subdevice") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
cerror(doit, "control.%s.%s is invalid", numid, fld);
return -EINVAL;
}
if (snd_config_get_integer(n, &subdevice) < 0)
return -EINVAL;
continue;
}
if (strcmp(fld, "name") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
cerror(doit, "control.%s.%s is invalid", numid, fld);
return -EINVAL;
}
if (snd_config_get_string(n, &name) < 0)
return -EINVAL;
continue;
}
if (strcmp(fld, "index") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
cerror(doit, "control.%s.%s is invalid", numid, fld);
return -EINVAL;
}
if (snd_config_get_integer(n, &index) < 0)
return -EINVAL;
continue;
}
if (strcmp(fld, "value") == 0) {
if (value)
*value = n;
continue;
}
cerror(doit, "unknown control.%s.%s field", numid, fld);
}
if (device < 0)
device = 0;
if (subdevice < 0)
subdevice = 0;
if (index < 0)
index = 0;
snd_ctl_elem_id_set_interface(id, iface);
snd_ctl_elem_id_set_device(id, device);
snd_ctl_elem_id_set_subdevice(id, subdevice);
snd_ctl_elem_id_set_name(id, name);
snd_ctl_elem_id_set_index(id, index);
return 0;
}
static int set_control(snd_ctl_t *handle, snd_config_t *control, int doit)
{
snd_ctl_elem_value_t *ctl;
snd_ctl_elem_info_t *info;
snd_config_iterator_t i, next;
unsigned int numid1;
snd_ctl_elem_iface_t iface = -1;
int iface1;
const char *name1;
snd_ctl_elem_id_t *elem_id, *elem_id1;
unsigned int numid;
snd_ctl_elem_type_t type;
unsigned int count;
long device = -1;
long device1;
long subdevice = -1;
long subdevice1;
const char *name = NULL;
long index1;
long index = -1;
snd_config_t *value = NULL;
snd_config_t *comment = NULL;
unsigned int idx;
int err;
char *set;
const char *id;
snd_ctl_elem_id_alloca(&elem_id);
snd_ctl_elem_id_alloca(&elem_id1);
snd_ctl_elem_value_alloca(&ctl);
snd_ctl_elem_info_alloca(&info);
if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) {
@@ -1239,92 +1348,17 @@ static int set_control(snd_ctl_t *handle, snd_config_t *control,
return -EINVAL;
}
numid = atoi(id);
if ((int)numid > *maxnumid)
*maxnumid = numid;
snd_config_for_each(i, next, control) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *fld;
if (snd_config_get_id(n, &fld) < 0)
continue;
if (strcmp(fld, "comment") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
cerror(doit, "control.%d.%s is invalid", numid, fld);
return -EINVAL;
}
comment = n;
continue;
}
if (strcmp(fld, "iface") == 0) {
iface = (snd_ctl_elem_iface_t)config_iface(n);
if (iface < 0)
return -EINVAL;
continue;
}
if (strcmp(fld, "device") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
cerror(doit, "control.%d.%s is invalid", numid, fld);
return -EINVAL;
}
if (snd_config_get_integer(n, &device) < 0)
return -EINVAL;
continue;
}
if (strcmp(fld, "subdevice") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
cerror(doit, "control.%d.%s is invalid", numid, fld);
return -EINVAL;
}
if (snd_config_get_integer(n, &subdevice) < 0)
return -EINVAL;
continue;
}
if (strcmp(fld, "name") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
cerror(doit, "control.%d.%s is invalid", numid, fld);
return -EINVAL;
}
if (snd_config_get_string(n, &name) < 0)
return -EINVAL;
continue;
}
if (strcmp(fld, "index") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
cerror(doit, "control.%d.%s is invalid", numid, fld);
return -EINVAL;
}
if (snd_config_get_integer(n, &index) < 0)
return -EINVAL;
continue;
}
if (strcmp(fld, "value") == 0) {
value = n;
continue;
}
cerror(doit, "unknown control.%d.%s field", numid, fld);
}
if (!value) {
cerror(doit, "missing control.%d.value", numid);
return -EINVAL;
}
if (device < 0)
device = 0;
if (subdevice < 0)
subdevice = 0;
if (index < 0)
index = 0;
err = parse_config_id(control, elem_id, &comment, &value, doit);
if (err < 0)
return err;
err = -EINVAL;
if (!force_restore) {
snd_ctl_elem_info_set_numid(info, numid);
err = snd_ctl_elem_info(handle, info);
}
if (err < 0 && name) {
snd_ctl_elem_info_set_numid(info, 0);
snd_ctl_elem_info_set_interface(info, iface);
snd_ctl_elem_info_set_device(info, device);
snd_ctl_elem_info_set_subdevice(info, subdevice);
snd_ctl_elem_info_set_name(info, name);
snd_ctl_elem_info_set_index(info, index);
id = snd_ctl_elem_id_get_name(elem_id);
if (err < 0 && id && id[0]) {
snd_ctl_elem_info_set_id(info, elem_id);
err = snd_ctl_elem_info(handle, info);
if (err < 0 && comment && check_comment_access(comment, "user")) {
err = add_user_control(handle, info, comment);
@@ -1339,28 +1373,39 @@ static int set_control(snd_ctl_t *handle, snd_config_t *control,
cerror(doit, "failed to obtain info for control #%d (%s)", numid, snd_strerror(err));
return -ENOENT;
}
numid1 = snd_ctl_elem_info_get_numid(info);
iface1 = snd_ctl_elem_info_get_interface(info);
device1 = snd_ctl_elem_info_get_device(info);
subdevice1 = snd_ctl_elem_info_get_subdevice(info);
name1 = snd_ctl_elem_info_get_name(info);
index1 = snd_ctl_elem_info_get_index(info);
count = snd_ctl_elem_info_get_count(info);
snd_ctl_elem_info_get_id(info, elem_id1);
type = snd_ctl_elem_info_get_type(info);
if (err |= numid != numid1 && !force_restore)
cerror(doit, "warning: numid mismatch (%d/%d) for control #%d",
numid, numid1, numid);
if (err |= (int)iface != iface1)
cerror(doit, "warning: iface mismatch (%d/%d) for control #%d", iface, iface1, numid);
if (err |= device != device1)
cerror(doit, "warning: device mismatch (%ld/%ld) for control #%d", device, device1, numid);
if (err |= subdevice != subdevice1)
cerror(doit, "warning: subdevice mismatch (%ld/%ld) for control #%d", subdevice, subdevice1, numid);
if (err |= strcmp(name, name1))
cerror(doit, "warning: name mismatch (%s/%s) for control #%d", name, name1, numid);
if (err |= index != index1)
cerror(doit, "warning: index mismatch (%ld/%ld) for control #%d", index, index1, numid);
if (err < 0) {
err = 0;
if (err |= !force_restore && numid != snd_ctl_elem_id_get_numid(elem_id1))
cerror(doit, "warning: numid mismatch (%u/%u) for control #%d",
(unsigned int)numid, snd_ctl_elem_id_get_numid(elem_id1), numid);
if (err |= snd_ctl_elem_id_get_interface(elem_id) != snd_ctl_elem_id_get_interface(elem_id1))
cerror(doit, "warning: iface mismatch (%d/%d) for control #%d",
(int)snd_ctl_elem_id_get_interface(elem_id),
(int)snd_ctl_elem_id_get_interface(elem_id1),
numid);
if (err |= snd_ctl_elem_id_get_device(elem_id) != snd_ctl_elem_id_get_device(elem_id1))
cerror(doit, "warning: device mismatch (%u/%u) for control #%d",
snd_ctl_elem_id_get_device(elem_id),
snd_ctl_elem_id_get_device(elem_id1),
numid);
if (err |= snd_ctl_elem_id_get_subdevice(elem_id) != snd_ctl_elem_id_get_subdevice(elem_id1))
cerror(doit, "warning: subdevice mismatch (%u/%%u) for control #%d",
snd_ctl_elem_id_get_subdevice(elem_id),
snd_ctl_elem_id_get_subdevice(elem_id1),
numid);
if (err |= strcmp(snd_ctl_elem_id_get_name(elem_id), snd_ctl_elem_id_get_name(elem_id1)))
cerror(doit, "warning: name mismatch (%s/%s) for control #%d",
snd_ctl_elem_id_get_name(elem_id),
snd_ctl_elem_id_get_name(elem_id1),
numid);
if (err |= snd_ctl_elem_id_get_index(elem_id) != snd_ctl_elem_id_get_index(elem_id1))
cerror(doit, "warning: index mismatch (%u/%u) for control #%d",
snd_ctl_elem_id_get_index(elem_id),
snd_ctl_elem_id_get_index(elem_id1),
numid);
if (err) {
cerror(doit, "failed to obtain info for control #%d (%s)", numid, snd_strerror(err));
return -ENOENT;
}
@@ -1383,7 +1428,7 @@ static int set_control(snd_ctl_t *handle, snd_config_t *control,
if (snd_ctl_elem_info_is_inactive(info) ||
!snd_ctl_elem_info_is_writable(info))
return 0;
snd_ctl_elem_value_set_numid(ctl, numid1);
snd_ctl_elem_value_set_numid(ctl, snd_ctl_elem_id_get_numid(elem_id1));
if (count == 1) {
err = restore_config_value(handle, info, type, value, ctl, 0, doit);
@@ -1472,8 +1517,14 @@ static int set_control(snd_ctl_t *handle, snd_config_t *control,
_ok:
err = doit ? snd_ctl_elem_write(handle, ctl) : 0;
if (err < 0) {
error("Cannot write control '%d:%ld:%ld:%s:%ld' : %s", (int)iface, device, subdevice, name, index, snd_strerror(err));
char *s = snd_ctl_ascii_elem_id_get(elem_id1);
error("Cannot write control '%s' : %s", s, snd_strerror(err));
free(s);
return err;
} else if (debugflag) {
char *s = snd_ctl_ascii_elem_id_get(elem_id1);
dbg("control '%s' restored", s);
free(s);
}
return 0;
}
@@ -1487,7 +1538,7 @@ static int set_controls(int card, snd_config_t *top, int doit)
snd_ctl_elem_id_t *elem_id;
snd_config_t *control;
snd_config_iterator_t i, next;
int err, maxnumid = -1, maxnumid2 = -1;
int err, controls1 = -1, controls2 = -1, ucontrols = -1, diff;
unsigned int idx, count = 0;
char name[32], tmpid[16];
const char *id;
@@ -1527,11 +1578,13 @@ static int set_controls(int card, snd_config_t *top, int doit)
cerror(doit, "state.%s.control is not a compound\n", id);
return -EINVAL;
}
controls1 = 0;
snd_config_for_each(i, next, control) {
snd_config_t *n = snd_config_iterator_entry(i);
err = set_control(handle, n, &maxnumid, doit);
err = set_control(handle, n, doit);
if (err < 0 && (!force_restore || !doit))
goto _close;
controls1++;
}
if (doit)
@@ -1545,7 +1598,7 @@ static int set_controls(int card, snd_config_t *top, int doit)
count = snd_ctl_elem_list_get_count(list);
dbg("list count: %u", count);
if (count == 0)
goto _check;
goto _free;
snd_ctl_elem_list_set_offset(list, 0);
if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
error("No enough memory...");
@@ -1555,28 +1608,30 @@ static int set_controls(int card, snd_config_t *top, int doit)
error("Cannot determine controls (2): %s", snd_strerror(err));
goto _free;
}
maxnumid2 = 0;
/* skip non-readable elements */
controls2 = ucontrols = 0;
/* skip non-readable and count user elements */
for (idx = 0; idx < count; ++idx) {
snd_ctl_elem_info_clear(elem_info);
snd_ctl_elem_list_get_id(list, idx, elem_id);
snd_ctl_elem_info_set_id(elem_info, elem_id);
if (snd_ctl_elem_info(handle, elem_info) == 0) {
if (snd_ctl_elem_info_is_user(elem_info))
ucontrols++;
if (!snd_ctl_elem_info_is_readable(elem_info))
continue;
maxnumid2++;
controls2++;
}
}
/* check if we have additional controls in driver */
/* in this case we should go through init procedure */
_check:
dbg("maxnumid=%i maxnumid2=%i", maxnumid, maxnumid2);
if (maxnumid >= 0 && maxnumid != maxnumid2) {
diff = controls2 - controls1;
dbg("controls1=%i controls2=%i ucontrols=%i diff=%i", controls1, controls2, ucontrols, diff);
if (controls1 >= 0 && (-diff > ucontrols || diff > ucontrols)) {
/* not very informative */
/* but value is used for check only */
err = -EAGAIN;
dbg("more controls than maxnumid?");
dbg("config controls mismatch with hardware?");
}
_free:
@@ -1675,10 +1730,11 @@ int load_state(const char *cfgdir, const char *file,
const char *initfile, int initflags,
const char *cardname, int do_init)
{
int err, finalerr = 0, open_failed, lock_fd;
int err, finalerr = 0, open_failed, lock_fd, cardno;
struct snd_card_iterator iter;
snd_config_t *config;
const char *cardname1;
size_t index;
config = NULL;
err = load_configuration(file, &config, &open_failed);
@@ -1695,6 +1751,9 @@ int load_state(const char *cfgdir, const char *file,
while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) {
if (!do_init)
break;
init_linked_cards();
if (initflags & FLAG_UCM_WAIT)
wait_for_card(-1, iter.card);
lock_fd = card_lock(iter.card, LOCK_TIMEOUT);
if (lock_fd < 0) {
finalerr = lock_fd;
@@ -1703,9 +1762,12 @@ int load_state(const char *cfgdir, const char *file,
}
err = init(cfgdir, initfile, initflags | FLAG_UCM_FBOOT | FLAG_UCM_BOOT, cardname1);
card_unlock(lock_fd, iter.card);
if (card_state_is_okay(err))
export_card_state_set(iter.card, err);
if (err < 0) {
finalerr = err;
initfailed(iter.card, "init", err);
continue;
}
initfailed(iter.card, "restore", -ENOENT);
}
@@ -1719,6 +1781,9 @@ int load_state(const char *cfgdir, const char *file,
if (err < 0)
goto out;
while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) {
init_linked_cards();
if (initflags & FLAG_UCM_WAIT)
wait_for_card(-1, iter.card);
lock_fd = card_lock(iter.card, LOCK_TIMEOUT);
if (lock_fd < 0) {
initfailed(iter.card, "lock", lock_fd);
@@ -1726,7 +1791,20 @@ int load_state(const char *cfgdir, const char *file,
continue;
}
/* error is ignored */
init_ucm(initflags | FLAG_UCM_FBOOT, iter.card);
err = init_ucm(cfgdir, initflags | FLAG_UCM_FBOOT, iter.card);
/* return code 1 and 2 -> postpone initialization */
if (card_state_is_okay(err)) {
export_card_state_set(iter.card, err);
goto unlock_card;
} else if (err < 0) {
/* no UCM - remove card specific configuration */
err = snd_card_clean_cfgdir(cfgdir, iter.card);
if (err < 0) {
initfailed(iter.card, "cfgdir", err);
finalerr = err;
continue;
}
}
/* do a check if controls matches state file */
if (do_init && set_controls(iter.card, config, 0)) {
err = init(cfgdir, initfile, initflags | FLAG_UCM_BOOT, cardname1);
@@ -1740,6 +1818,18 @@ int load_state(const char *cfgdir, const char *file,
finalerr = err;
initfailed(iter.card, "restore", err);
}
/* for linked cards, restore controls, too */
for (index = 0; index < ARRAY_SIZE(linked_cards); index++) {
if ((cardno = linked_cards[index]) < 0)
break;
dbg("Restore for linked card %d", cardno);
if ((err = set_controls(cardno, config, 1))) {
if (!force_restore)
finalerr = err;
initfailed(cardno, "restore", err);
}
}
unlock_card:
card_unlock(lock_fd, iter.card);
}
err = finalerr ? finalerr : snd_card_iterator_error(&iter);
+28 -3
View File
@@ -177,7 +177,7 @@ void dbg_(const char *fcn, long line, const char *fmt, ...)
va_end(ap);
}
void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
void error_handler(const char *file, int line, const char *function, int errcode, const char *fmt, ...)
{
char buf[2048];
va_list arg;
@@ -187,12 +187,36 @@ void error_handler(const char *file, int line, const char *function, int err, co
va_end(arg);
if (use_syslog)
syslog(LOG_ERR, "alsa-lib %s:%i:(%s) %s%s%s\n", file, line, function,
buf, err ? ": " : "", err ? snd_strerror(err) : "");
buf, errcode ? ": " : "", errcode ? snd_strerror(errcode) : "");
else
fprintf(stderr, "alsa-lib %s:%i:(%s) %s%s%s\n", file, line, function,
buf, err ? ": " : "", err ? snd_strerror(err) : "");
buf, errcode ? ": " : "", errcode ? snd_strerror(errcode) : "");
}
#if SND_LIB_VER(1, 2, 15) >= SND_LIB_VERSION
void log_handler(int prio, int interface, const char *file, int line, const char *function, int errcode, const char *fmt, va_list arg)
{
char buf[2048], level[50] = "";
const char *text1, *text2;
if (!snd_lib_log_filter(prio, interface, NULL))
return;
text1 = snd_lib_log_priority(prio);
text2 = snd_lib_log_interface(interface);
if (text1 || text2)
snprintf(level, sizeof(level), "[%s.%s] ", text1 ? text1 : "", text2 ? text2 : "");
vsnprintf(buf, sizeof(buf), fmt, arg);
if (use_syslog)
syslog(LOG_ERR, "alsa-lib %s:%i:(%s) %s%s%s%s\n", file, line, function, level,
buf, errcode ? ": " : "", errcode ? snd_strerror(errcode) : "");
else
fprintf(stderr, "alsa-lib %s:%i:(%s) %s%s%s%s\n", file, line, function, level,
buf, errcode ? ": " : "", errcode ? snd_strerror(errcode) : "");
}
#endif
int load_configuration(const char *file, snd_config_t **top, int *open_failed)
{
snd_config_t *config;
@@ -329,6 +353,7 @@ int snd_card_clean_cfgdir(const char *cfgdir, int cardno)
lasterr = -errno;
}
}
free(list);
return lasterr;
}
+200
View File
@@ -0,0 +1,200 @@
/*
* Advanced Linux Sound Architecture Control Program - Wait for Boot
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "aconfig.h"
#include "version.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <alsa/asoundlib.h>
#include "alsactl.h"
/**
* \brief Wait for card boot synchronization using Boot control element
* \param timeout Maximum wait time in seconds
* \param cardno Card number
* \return 0 on success, negative error code on failure
*
* This function waits until the card releases the 'waiting' state (UCM).
* It monitors the '.Boot' control element and uses snd_ctl_wait() and
* snd_ctl_read() for event-based waiting, similar to boot_wait() in
* ../alsa-lib/alsa-lib/src/ucm/main.c
*/
int wait_for_card(long long timeout, int cardno)
{
snd_ctl_t *handle;
snd_ctl_event_t *event;
snd_ctl_elem_id_t *id;
snd_ctl_elem_info_t *info;
snd_ctl_elem_value_t *value;
long long boot_time_val, restore_time_val;
long long synctime = -1;
struct timespec start_time, now;
char name[32];
int err;
sprintf(name, "hw:%d", cardno);
err = snd_ctl_open(&handle, name, SND_CTL_READONLY);
if (err < 0) {
error("snd_ctl_open error for %s: %s", name, snd_strerror(err));
return err;
}
/* Try to get synctime from boot_synctime in group configuration */
if (timeout <= 0) {
bool valid = false, restored = false;
err = check_boot_params_validity(handle, cardno, NULL, &valid, NULL, &restored, NULL, &synctime);
if (err == 0 && synctime > 0) {
timeout = synctime;
dbg("Using boot_synctime value: %lld seconds", timeout);
} else {
timeout = DEFAULT_SYNC_TIME;
}
/* Break early if boot params are invalid or already restored */
if (!valid || restored) {
dbg("Boot params check: valid=%d, restored=%d - skipping wait", valid, restored);
snd_ctl_close(handle);
return 0;
}
}
snd_ctl_event_alloca(&event);
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_info_alloca(&info);
snd_ctl_elem_value_alloca(&value);
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD);
snd_ctl_elem_id_set_name(id, ".Boot");
snd_ctl_elem_info_set_id(info, id);
err = snd_ctl_elem_info(handle, info);
if (err < 0) {
dbg("Boot control element not present on card %d, skipping wait", cardno);
snd_ctl_close(handle);
return 0; /* No Boot element, no wait needed */
}
if (snd_ctl_elem_info_get_type(info) != SND_CTL_ELEM_TYPE_INTEGER64) {
error("Boot control element is not INTEGER64 type on card %d", cardno);
snd_ctl_close(handle);
return -EINVAL;
}
if (snd_ctl_elem_info_get_count(info) < 3) {
error("Boot control element does not have count >= 3 on card %d", cardno);
snd_ctl_close(handle);
return -EINVAL;
}
err = snd_ctl_subscribe_events(handle, 1);
if (err < 0) {
error("Cannot subscribe to control events: %s", snd_strerror(err));
snd_ctl_close(handle);
return err;
}
clock_gettime(CLOCK_MONOTONIC_RAW, &start_time);
dbg("Waiting for card %d to become ready (timeout=%lld seconds)", cardno, timeout);
while (1) {
long long diff, remaining = 0;
long long sync_time_val = -1;
clock_gettime(CLOCK_MONOTONIC_RAW, &now);
/* Read current Boot control values */
err = read_boot_params(handle, &boot_time_val, &sync_time_val, &restore_time_val, NULL);
if (err < 0) {
error("Failed to read Boot control element: %s", snd_strerror(err));
goto _fin;
}
dbg("Boot info: boot_time=%lld, sync_time=%lld, restore_time=%lld", boot_time_val, sync_time_val, restore_time_val);
if (restore_time_val > 0) {
diff = now.tv_sec - restore_time_val;
dbg("Controls already restored (diff=%lld seconds), card is ready", diff);
err = 0;
goto _fin;
}
/* note that realtime may differ from monotonic time, add one second for safety */
diff = now.tv_sec - start_time.tv_sec;
if (diff > timeout + 1) {
dbg("Maximum wait time exceeded (%lld >= %lld seconds), proceeding", diff, timeout);
break;
}
remaining = timeout - diff;
/* Use synctime from element to limit timeout if available */
if (sync_time_val > 0 && sync_time_val < timeout) {
dbg("Limiting timeout from %lld to sync_time %lld seconds", timeout, sync_time_val);
timeout = sync_time_val;
}
if (!validate_boot_time(boot_time_val, now.tv_sec, timeout)) {
if (boot_time_val > 0) {
diff = now.tv_sec - boot_time_val;
dbg("Boot timeout reached (%lld >= %lld seconds), proceeding", diff, timeout);
}
break;
}
diff = now.tv_sec - boot_time_val;
if (timeout - diff < remaining)
remaining = timeout - diff;
if (remaining <= 0)
remaining = 1;
dbg("Waiting %lld seconds", remaining);
err = snd_ctl_wait(handle, remaining * 1000);
if (err < 0) {
error("snd_ctl_wait failed: %s", snd_strerror(err));
goto _fin;
}
if (err == 0)
continue; /* Timeout, no events */
/* Read and check events */
while (snd_ctl_read(handle, event) > 0) {
if (!(snd_ctl_event_elem_get_mask(event) & SND_CTL_EVENT_MASK_VALUE))
continue; /* Not a value change event */
if (snd_ctl_event_elem_get_interface(event) != SND_CTL_ELEM_IFACE_CARD ||
snd_ctl_event_elem_get_index(event) != 0 ||
strcmp(snd_ctl_event_elem_get_name(event), ".Boot") != 0)
continue;
dbg("Boot control element value changed");
break;
}
}
err = 0;
_fin:
snd_ctl_subscribe_events(handle, 0);
snd_ctl_close(handle);
return err;
}
+4 -2
View File
@@ -647,13 +647,15 @@ static int xrun(struct loopback_handle *lhandle)
int err;
if (lhandle == lhandle->loopback->play) {
logit(LOG_DEBUG, "underrun for %s\n", lhandle->id);
if (verbose)
logit(LOG_DEBUG, "underrun for %s\n", lhandle->id);
xrun_stats(lhandle->loopback);
if ((err = snd_pcm_prepare(lhandle->handle)) < 0)
return err;
lhandle->xrun_pending = 1;
} else {
logit(LOG_DEBUG, "overrun for %s\n", lhandle->id);
if (verbose)
logit(LOG_DEBUG, "overrun for %s\n", lhandle->id);
xrun_stats(lhandle->loopback);
if ((err = snd_pcm_prepare(lhandle->handle)) < 0)
return err;
+17
View File
@@ -34,6 +34,7 @@
static WINDOW *curses_initialized;
#if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
static void black_hole_error_handler(const char *file ATTRIBUTE_UNUSED,
int line ATTRIBUTE_UNUSED,
const char *function ATTRIBUTE_UNUSED,
@@ -41,6 +42,18 @@ static void black_hole_error_handler(const char *file ATTRIBUTE_UNUSED,
const char *fmt ATTRIBUTE_UNUSED, ...)
{
}
#else
static void black_hole_log_handler(int prio ATTRIBUTE_UNUSED,
int interface ATTRIBUTE_UNUSED,
const char *file ATTRIBUTE_UNUSED,
int line ATTRIBUTE_UNUSED,
const char *function ATTRIBUTE_UNUSED,
int errcode ATTRIBUTE_UNUSED,
const char *fmt ATTRIBUTE_UNUSED,
va_list arg ATTRIBUTE_UNUSED)
{
}
#endif
void initialize_curses(bool use_color, bool use_mouse)
{
@@ -55,7 +68,11 @@ void initialize_curses(bool use_color, bool use_mouse)
if (use_mouse)
mousemask(ALL_MOUSE_EVENTS, NULL);
#if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
snd_lib_error_set_handler(black_hole_error_handler);
#else
snd_lib_log_set_handler(black_hole_log_handler);
#endif
}
void app_shutdown(void)
+2
View File
@@ -146,6 +146,8 @@ static int set_normalized_volume(snd_mixer_elem_t *elem,
min_norm = pow(10, (min - max) / 6000.0);
volume = volume * (1 - min_norm) + min_norm;
}
if (volume <= 0)
volume = 1e-36;
value = lrint_dir(6000.0 * log10(volume), dir) + max;
return set_dB[ctl_dir](elem, channel, value, dir);
}
+5
View File
@@ -52,6 +52,11 @@ Prints the current version.
.I \-l, \-\-list\-devices
Prints a list of all hardware MIDI ports.
.TP
.I \-x, \-\-list\-inactive
Use together with \fI\-l\fP option.
Print all MIDI ports including inactive ports.
.TP
.I \-L, \-\-list\-rawmidis
Prints all RawMIDI definitions.
+10 -1
View File
@@ -57,6 +57,7 @@ static int stop;
static int sysex_interval;
static snd_rawmidi_t *input, **inputp;
static snd_rawmidi_t *output, **outputp;
static int list_all;
static void error(const char *format, ...)
{
@@ -76,6 +77,7 @@ static void usage(void)
"-h, --help this help\n"
"-V, --version print current version\n"
"-l, --list-devices list all hardware ports\n"
"-x, --list-inactive list inactive ports, too\n"
"-L, --list-rawmidis list all RawMIDI definitions\n"
"-p, --port=name select port by name\n"
"-s, --send=file send the contents of a (.syx) file\n"
@@ -151,6 +153,9 @@ static void list_device(snd_ctl_t *ctl, int card, int device)
card, device, sub, snd_strerror(err));
return;
}
if (!list_all &&
(snd_rawmidi_info_get_flags(info) & SNDRV_RAWMIDI_INFO_STREAM_INACTIVE))
continue;
name = snd_rawmidi_info_get_name(info);
sub_name = snd_rawmidi_info_get_subdevice_name(info);
if (sub == 0 && sub_name[0] == '\0') {
@@ -471,11 +476,12 @@ static void add_send_hex_data(const char *str)
int main(int argc, char *argv[])
{
static const char short_options[] = "hVlLp:s:r:S::dt:aci:T:";
static const char short_options[] = "hVlxLp:s:r:S::dt:aci:T:";
static const struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'V'},
{"list-devices", 0, NULL, 'l'},
{"list-inactive", 0, NULL, 'x'},
{"list-rawmidis", 0, NULL, 'L'},
{"port", 1, NULL, 'p'},
{"send", 1, NULL, 's'},
@@ -508,6 +514,9 @@ int main(int argc, char *argv[])
case 'l':
do_device_list = 1;
break;
case 'x':
list_all = 1;
break;
case 'L':
do_rawmidi_list = 1;
break;
+8 -1
View File
@@ -587,10 +587,17 @@ static void decode_tlv(unsigned int spaces, unsigned int *tlv, unsigned int tlv_
#endif
default:
printf("unk-%u-", type);
while (size > 0) {
while (size > sizeof(unsigned int)) {
printf("0x%08x,", tlv[idx++]);
size -= sizeof(unsigned int);
}
if (size > 0) {
unsigned char *b = (void *)&tlv[idx];
while (size > 0) {
printf("E-0x%02x,", *b++);
size--;
}
}
break;
}
if (lf)
+16 -16
View File
@@ -1,6 +1,6 @@
.TH APLAY 1 "1 January 2010"
.SH NAME
arecord, aplay \- command\-line sound recorder and player for ALSA
arecord, aplay \- command\-line sound recorder and player for ALSA
soundcard driver
.SH SYNOPSIS
\fBarecord\fP [\fIflags\fP] [filename]
@@ -84,41 +84,41 @@ A value of zero means infinity.
The default is zero, so if this options is omitted then the record/playback process will run until it is killed.
Either '-d' or '-s' option is available exclusively.
.TP
\fI\-M, \-\-mmap\fP
\fI\-M, \-\-mmap\fP
Use memory\-mapped (mmap) I/O mode for the audio stream.
If this option is not set, the read/write I/O mode will be used.
.TP
\fI\-N, \-\-nonblock\fP
\fI\-N, \-\-nonblock\fP
Open the audio device in non\-blocking mode. If the device is busy the program will exit immediately.
If this option is not set the program will block until the audio device is available again.
.TP
\fI\-F, \-\-period\-time=#\fP
\fI\-F, \-\-period\-time=#\fP
Distance between interrupts is # microseconds.
If no period time and no period size is given then a quarter of the buffer time is set.
.TP
\fI\-B, \-\-buffer\-time=#\fP
\fI\-B, \-\-buffer\-time=#\fP
Buffer duration is # microseconds
If no buffer time and no buffer size is given then the maximal allowed buffer time but not more than 500ms is set.
.TP
\fI\-\-period\-size=#\fP
\fI\-\-period\-size=#\fP
Distance between interrupts is # frames
If no period size and no period time is given then a quarter of the buffer size is set.
.TP
\fI\-\-buffer\-size=#\fP
\fI\-\-buffer\-size=#\fP
Buffer duration is # frames
If no buffer time and no buffer size is given then the maximal allowed buffer time but not more than 500ms is set.
.TP
\fI\-A, \-\-avail\-min=#\fP
\fI\-A, \-\-avail\-min=#\fP
Min available space for wakeup is # microseconds
.TP
\fI\-R, \-\-start\-delay=#\fP
Delay for automatic PCM start is # microseconds
\fI\-R, \-\-start\-delay=#\fP
Delay for automatic PCM start is # microseconds
(relative to buffer size if <= 0)
.TP
\fI\-T, \-\-stop\-delay=#\fP
\fI\-T, \-\-stop\-delay=#\fP
Delay for automatic PCM stop is # microseconds from xrun
.TP
\fI\-v, \-\-verbose\fP
\fI\-v, \-\-verbose\fP
Show PCM structure and setup.
This option is accumulative. The VU meter is displayed when this
is given twice or three times.
@@ -128,7 +128,7 @@ Specifies the VU\-meter type, either \fIstereo\fP or \fImono\fP.
The stereo VU\-meter is available only for 2\-channel stereo samples
with interleaved format.
.TP
\fI\-I, \-\-separate\-channels\fP
\fI\-I, \-\-separate\-channels\fP
One file for each channel. This option disables max\-file\-time
and use\-strftime, and ignores SIGUSR1. The stereo VU meter is
not available with separate channels.
@@ -212,7 +212,7 @@ Disables recovery attempts when errors (e.g. xrun) are encountered; the
aplay process instead aborts immediately.
.SH SIGNALS
When recording, SIGINT, SIGTERM and SIGABRT will close the output
When recording, SIGINT, SIGTERM and SIGABRT will close the output
file and exit. SIGUSR1 will close the output file, open a new one,
and continue recording. However, SIGUSR1 does not work with
\-\-separate\-channels.
@@ -222,7 +222,7 @@ and continue recording. However, SIGUSR1 does not work with
.TP
\fBaplay \-c 1 \-t raw \-r 22050 \-f mu_law foobar\fR
will play the raw file "foobar" as a
22050\-Hz, mono, 8\-bit, Mu\-Law .au file.
22050\-Hz, mono, 8\-bit, Mu\-Law .au file.
.TP
\fBarecord \-d 10 \-f cd \-t wav \-D copy foobar.wav\fP
@@ -257,7 +257,7 @@ alsamixer(1),
amixer(1)
\fP
.SH BUGS
.SH BUGS
Note that .aiff files are not currently supported.
.SH AUTHOR
+30 -21
View File
@@ -1245,6 +1245,12 @@ static int test_au(int fd, void *buffer)
fprintf(stderr, _("Warning: format is changed to S16_BE\n"));
hwparams.format = SND_PCM_FORMAT_S16_BE;
break;
case AU_FMT_ALAW:
if (hwparams.format != DEFAULT_FORMAT &&
hwparams.format != SND_PCM_FORMAT_A_LAW)
fprintf(stderr, _("Warning: format is changed to A_LAW\n"));
hwparams.format = SND_PCM_FORMAT_A_LAW;
break;
default:
return -1;
}
@@ -1464,6 +1470,13 @@ static void set_params(void)
chunk_size, buffer_size);
prg_exit(EXIT_FAILURE);
}
if (dump_hw_params) {
fprintf(stderr, _("HW Params of device \"%s\":\n"),
snd_pcm_name(handle));
fprintf(stderr, "--------------------\n");
snd_pcm_hw_params_dump(params, log);
fprintf(stderr, "--------------------\n");
}
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0) {
error(_("Unable to get current sw params."));
@@ -1618,6 +1631,8 @@ static void do_pause(void)
error(_("pause push error: %s"), snd_strerror(err));
return;
}
fprintf(stderr, _("\r=== PAUSE === "));
fflush(stderr);
while (1) {
b = wait_for_input();
if (b == ' ' || b == '\r') {
@@ -1642,8 +1657,6 @@ static void check_stdin(void)
while (read(fileno(stdin), &b, 1) == 1) {
if (b == ' ' || b == '\r') {
while (read(fileno(stdin), &b, 1) == 1);
fprintf(stderr, _("\r=== PAUSE === "));
fflush(stderr);
do_pause();
fprintf(stderr, " \r");
fflush(stderr);
@@ -2703,7 +2716,7 @@ static void begin_wave(int fd, size_t cnt)
WaveHeader h;
WaveFmtBody f;
WaveChunkHeader cf, cd;
int bits;
int width, physical_width;
uint32_t tmp;
uint16_t tmp2;
@@ -2711,23 +2724,22 @@ static void begin_wave(int fd, size_t cnt)
if (cnt == (size_t)-2)
cnt = 0x7fffff00;
bits = 8;
width = snd_pcm_format_physical_width(hwparams.format);
physical_width = snd_pcm_format_width(hwparams.format);
if (width < 0 || physical_width < 0)
goto _format;
switch ((unsigned long) hwparams.format) {
case SND_PCM_FORMAT_U8:
bits = 8;
break;
case SND_PCM_FORMAT_S16_LE:
bits = 16;
break;
case SND_PCM_FORMAT_S24_LE: /* S24_LE is 24 bits stored in 32 bit width with 8 bit padding */
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_FLOAT_LE:
bits = 32;
break;
case SND_PCM_FORMAT_S24_LE:
case SND_PCM_FORMAT_FLOAT_LE:
case SND_PCM_FORMAT_S24_3LE:
bits = 24;
break;
default:
_format:
error(_("Wave doesn't support %s format..."), snd_pcm_format_name(hwparams.format));
prg_exit(EXIT_FAILURE);
}
@@ -2745,17 +2757,11 @@ static void begin_wave(int fd, size_t cnt)
f.format = LE_SHORT(WAV_FMT_PCM);
f.channels = LE_SHORT(hwparams.channels);
f.sample_fq = LE_INT(hwparams.rate);
#if 0
tmp2 = (samplesize == 8) ? 1 : 2;
f.byte_p_spl = LE_SHORT(tmp2);
tmp = dsp_speed * hwparams.channels * (uint32_t) tmp2;
#else
tmp2 = hwparams.channels * snd_pcm_format_physical_width(hwparams.format) / 8;
tmp2 = hwparams.channels * physical_width / 8;
f.byte_p_spl = LE_SHORT(tmp2);
tmp = (uint32_t) tmp2 * hwparams.rate;
#endif
f.byte_p_sec = LE_INT(tmp);
f.bit_p_spl = LE_SHORT(bits);
f.bit_p_spl = LE_SHORT(width);
cd.type = WAV_DATA;
cd.length = LE_INT(cnt);
@@ -2787,6 +2793,9 @@ static void begin_au(int fd, size_t cnt)
case SND_PCM_FORMAT_S16_BE:
ah.encoding = BE_INT(AU_FMT_LIN16);
break;
case SND_PCM_FORMAT_A_LAW:
ah.encoding = BE_INT(AU_FMT_ALAW);
break;
default:
error(_("Sparc Audio doesn't support %s format..."), snd_pcm_format_name(hwparams.format));
prg_exit(EXIT_FAILURE);
+1
View File
@@ -120,6 +120,7 @@ typedef struct {
#define AU_FMT_ULAW 1
#define AU_FMT_LIN8 2
#define AU_FMT_LIN16 3
#define AU_FMT_ALAW 27
typedef struct au_header {
uint32_t magic; /* '.snd' */
+1 -1
View File
@@ -9,7 +9,7 @@
#ifndef __ALSA_UTILS_AXFER_WAITER__H_
#define __ALSA_UTILS_AXFER_WAITER__H_
#include <alsa/global.h>
#include <alsa/asoundlib.h>
#include <poll.h>
enum waiter_type {
+11 -11
View File
@@ -221,7 +221,17 @@ static int set_snd_pcm_params(struct bat *bat, struct pcm_container *sndpcm)
period_time = buffer_time / DIV_BUFFERTIME;
/* Set buffer time and period time */
/* Set period time and buffer time */
err = snd_pcm_hw_params_set_period_time_near(sndpcm->handle,
params, &period_time, 0);
if (err < 0) {
fprintf(bat->err, _("Set parameter to device error: "));
fprintf(bat->err, _("period time: %d %s: %s(%d)\n"),
period_time,
device_name, snd_strerror(err), err);
return err;
}
err = snd_pcm_hw_params_set_buffer_time_near(sndpcm->handle,
params, &buffer_time, 0);
if (err < 0) {
@@ -231,16 +241,6 @@ static int set_snd_pcm_params(struct bat *bat, struct pcm_container *sndpcm)
device_name, snd_strerror(err), err);
return err;
}
err = snd_pcm_hw_params_set_period_time_near(sndpcm->handle,
params, &period_time, 0);
if (err < 0) {
fprintf(bat->err, _("Set parameter to device error: "));
fprintf(bat->err, _("period time: %d %s: %s(%d)\n"),
period_time,
device_name, snd_strerror(err), err);
return err;
}
}
/* Write the parameters to the driver */
+1 -1
View File
@@ -27,7 +27,7 @@
"third_party": [ "alsa-lib" ]
},
"build": {
"sub_component": [ "//third_party/alsa-utils:alsa-utils" ],
"sub_component": [ "//third_party/alsa-utils:entry" ],
"inner_kits": [],
"test": []
}
+22 -18
View File
@@ -1,6 +1,6 @@
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(alsa-utils, 1.2.11)
AC_INIT(alsa-utils, 1.2.15.2)
AC_CONFIG_SRCDIR([aplay/aplay.c])
AC_PREFIX_DEFAULT(/usr)
AM_INIT_AUTOMAKE([subdir-objects])
@@ -8,7 +8,7 @@ AM_INIT_AUTOMAKE([subdir-objects])
AM_MAINTAINER_MODE([enable])
AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION([0.19.8])
AM_GNU_GETTEXT_VERSION([0.22.5])
dnl Checks for programs.
@@ -21,7 +21,7 @@ AC_PROG_SED
AC_DISABLE_STATIC
AM_PROG_LIBTOOL
PKG_PROG_PKG_CONFIG
AM_PATH_ALSA(1.2.5)
AM_PATH_ALSA(1.2.13)
if test "x$enable_alsatest" = "xyes"; then
AC_CHECK_FUNC([snd_ctl_elem_add_enumerated],
, [AC_ERROR([No user enum control support in alsa-lib])])
@@ -47,17 +47,6 @@ AC_CHECK_HEADERS([samplerate.h], [have_samplerate="yes"], [have_samplerate="no"]
[#include <samplerate.h>])
AC_CHECK_LIB([asound], [snd_seq_client_info_get_card], [HAVE_SEQ_CLIENT_INFO_GET_CARD="yes"])
if test "$HAVE_SEQ_CLIENT_INFO_GET_CARD" = "yes" ; then
AC_DEFINE([HAVE_SEQ_CLIENT_INFO_GET_CARD], 1, [alsa-lib supports snd_seq_client_info_get_card])
fi
AC_CHECK_LIB([asound], [snd_seq_client_info_get_pid], [HAVE_SEQ_CLIENT_INFO_GET_PID="yes"])
if test "$HAVE_SEQ_CLIENT_INFO_GET_PID" = "yes" ; then
AC_DEFINE([HAVE_SEQ_CLIENT_INFO_GET_PID], 1, [alsa-lib supports snd_seq_client_info_get_pid])
fi
AC_CHECK_LIB([asound], [snd_seq_client_info_get_midi_version], [HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION="yes"])
if test "$HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION" = "yes" -a "$have_rawmidi" = "yes"; then
AC_DEFINE([HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION], 1, [alsa-lib supports snd_seq_client_info_get_midi_version])
fi
AC_CHECK_LIB([atopology], [snd_tplg_save], [have_topology="yes"], [have_topology="no"])
#
@@ -430,8 +419,8 @@ AC_ARG_WITH([systemdsystemunitdir],
if test "x$with_systemdsystemunitdir" != xno; then
AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test "$have_min_systemd" = "yes" \
-a -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ])
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" \
-a "x$with_systemdsystemunitdir" != xno ])
AC_ARG_WITH([asound-state-dir],
AS_HELP_STRING([--with-asound-state-dir=DIR], [Directory to place asound.state file in]),
@@ -457,6 +446,21 @@ AC_ARG_WITH([alsactl-daemonswitch],
[ALSACTL_DAEMONSWITCH="/etc/alsa/state-daemon.conf"])
AC_SUBST(ALSACTL_DAEMONSWITCH)
AC_ARG_WITH([alsactl-udev-extra-test],
AS_HELP_STRING([--with-alsactl-udev-extra-test=TEST], [Extra udev tests]),
[ALSACTL_UDEV_EXTRATEST="$withval"],
[ALSACTL_UDEV_EXTRATEST="default"])
if test "$ALSACTL_UDEV_EXTRATEST" = "default"; then
ALSACTL_UDEV_EXTRATEST="TEST==\"${sbindir}\", TEST==\"${mydatadir}\","
fi
AC_SUBST(ALSACTL_UDEV_EXTRATEST)
AC_ARG_WITH([alsactl-udev-args],
AS_HELP_STRING([--with-alsactl-udev-args=ARGS], [Extra alsactl arguments (udev rules)]),
[ALSACTL_UDEV_ARGS="$withval"],
[ALSACTL_UDEV_ARGS=""])
AC_SUBST(ALSACTL_UDEV_ARGS)
dnl pre-process plugin directory
AC_ARG_WITH(plugindir,
AS_HELP_STRING([--with-plugindir=dir],
@@ -486,8 +490,8 @@ AC_OUTPUT(Makefile alsactl/Makefile alsactl/init/Makefile \
bat/Makefile bat/tests/Makefile bat/tests/asound_state/Makefile \
aplay/Makefile include/Makefile iecset/Makefile utils/Makefile \
utils/alsa-utils.spec seq/Makefile seq/aconnect/Makefile \
seq/aplaymidi/Makefile seq/aseqdump/Makefile seq/aseqnet/Makefile \
speaker-test/Makefile speaker-test/samples/Makefile \
seq/aplaymidi/Makefile seq/aplaymidi2/Makefile seq/aseqdump/Makefile seq/aseqnet/Makefile \
seq/aseqsend/Makefile speaker-test/Makefile speaker-test/samples/Makefile \
alsaloop/Makefile alsa-info/Makefile \
axfer/Makefile axfer/test/Makefile \
nhlt/Makefile)
+2 -2
View File
@@ -1,9 +1,9 @@
#!/bin/bash
if test -d ../alsa-lib/utils && ! test -r `aclocal --print-ac-dir`/alsa.m4; then
alsa_m4_flags="-I ../alsa-lib/utils"
ACLOCAL_FLAGS="$ACLOCAL_FLAGS -I ../alsa-lib/utils"
fi
aclocal $alsa_m4_flags $ACLOCAL_FLAGS
aclocal $ACLOCAL_FLAGS
# save original files to avoid stupid modifications by gettextize
cp Makefile.am Makefile.am.ok
cp configure.ac configure.ac.ok
+20 -28
View File
@@ -2,10 +2,10 @@
/* include/aconfig.h.in. Generated from configure.ac by autoheader. */
/* directory containing ALSA topology pre-process plugins */
#define ALSA_TOPOLOGY_PLUGIN_DIR "/home/xuxuehai/code/alsa-utils-1.2.11/build/lib/alsa-topology"
#define ALSA_TOPOLOGY_PLUGIN_DIR "/usr/lib/alsa-topology"
/* directory containing alsa configuration */
#define DATADIR "/home/xuxuehai/code/alsa-utils-1.2.11/build/share/alsa"
#define DATADIR "/usr/share/alsa"
/* Define to 1 if translation of program messages to the user's native
language is requested. */
@@ -32,9 +32,9 @@
/* Define to 1 if you have the <alsa/use-case.h> header file. */
#define HAVE_ALSA_USE_CASE_H 1
/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the
CoreFoundation framework. */
/* #undef HAVE_CFLOCALECOPYCURRENT */
/* Define to 1 if you have the Mac OS X function
CFLocaleCopyPreferredLanguages in the CoreFoundation framework. */
/* #undef HAVE_CFLOCALECOPYPREFERREDLANGUAGES */
/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in
the CoreFoundation framework. */
@@ -92,9 +92,6 @@
/* Define if Linux kernel supports memfd_create system call */
#define HAVE_MEMFD_CREATE 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the <menu.h> header file. */
#define HAVE_MENU_H 1
@@ -104,18 +101,12 @@
/* Define to 1 if you have the <samplerate.h> header file. */
/* #undef HAVE_SAMPLERATE_H */
/* alsa-lib supports snd_seq_client_info_get_card */
#define HAVE_SEQ_CLIENT_INFO_GET_CARD 1
/* alsa-lib supports snd_seq_client_info_get_midi_version */
/* #undef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION */
/* alsa-lib supports snd_seq_client_info_get_pid */
#define HAVE_SEQ_CLIENT_INFO_GET_PID 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
@@ -128,6 +119,9 @@
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
@@ -147,7 +141,7 @@
#define PACKAGE_NAME "alsa-utils"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "alsa-utils 1.2.11"
#define PACKAGE_STRING "alsa-utils 1.2.15.2"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "alsa-utils"
@@ -156,28 +150,26 @@
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.2.11"
#define PACKAGE_VERSION "1.2.15.2"
/* directory containing sample data */
#define SOUNDSDIR "/home/xuxuehai/code/alsa-utils-1.2.11/build/share/sounds/alsa"
#define SOUNDSDIR "/usr/share/sounds/alsa"
/* Define to 1 if you have the ANSI C header files. */
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#define STDC_HEADERS 1
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. This
macro is obsolete. */
#define TIME_WITH_SYS_TIME 1
/* ALSA util version */
#define VERSION "1.2.11"
#define VERSION "1.2.15.2"
/* Define if FFADO library is available */
/* #undef WITH_FFADO */
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
+2 -2
View File
@@ -4,9 +4,9 @@
#define SND_UTIL_MAJOR 1
#define SND_UTIL_MINOR 2
#define SND_UTIL_SUBMINOR 11
#define SND_UTIL_SUBMINOR 15
#define SND_UTIL_VERSION ((SND_UTIL_MAJOR<<16)|\
(SND_UTIL_MINOR<<8)|\
SND_UTIL_SUBMINOR)
#define SND_UTIL_VERSION_STR "1.2.11"
#define SND_UTIL_VERSION_STR "1.2.15.2"
+440 -312
View File
File diff suppressed because it is too large Load Diff
+514 -314
View File
File diff suppressed because it is too large Load Diff
+541 -327
View File
File diff suppressed because it is too large Load Diff
+425 -312
View File
File diff suppressed because it is too large Load Diff
+386 -267
View File
File diff suppressed because it is too large Load Diff
+509 -318
View File
File diff suppressed because it is too large Load Diff
+475 -289
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
SUBDIRS=aconnect aplaymidi aseqdump aseqnet
SUBDIRS=aconnect aplaymidi aplaymidi2 aseqdump aseqnet aseqsend
+22 -36
View File
@@ -29,18 +29,15 @@
#include <alsa/asoundlib.h>
#include "gettext.h"
#ifdef SND_SEQ_PORT_CAP_INACTIVE
#define HANDLE_SHOW_ALL
static int show_all;
#else
#define show_all 0
#endif
static void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
#if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
static void error_handler(const char *file, int line, const char *function, int errcode, const char *fmt, ...)
{
va_list arg;
if (err == ENOENT) /* Ignore those misleading "warnings" */
if (errcode == ENOENT) /* Ignore those misleading "warnings" */
return;
va_start(arg, fmt);
fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
@@ -50,6 +47,16 @@ static void error_handler(const char *file, int line, const char *function, int
putc('\n', stderr);
va_end(arg);
}
#else
static snd_lib_log_handler_t original_log_handler;
static void log_handler(int prio, int interface, const char *file, int line, const char *function, int errcode, const char *fmt, va_list arg)
{
if (prio == SND_LOG_ERROR && errcode == ENOENT) /* Ignore those misleading "warnings" */
return;
if (original_log_handler)
original_log_handler(prio, interface, file, line, function, errcode, fmt, arg);
}
#endif
static void usage(void)
{
@@ -67,9 +74,7 @@ static void usage(void)
printf(_(" aconnect -i|-o [-options]\n"));
printf(_(" -i,--input list input (readable) ports\n"));
printf(_(" -o,--output list output (writable) ports\n"));
#ifdef HANDLE_SHOW_ALL
printf(_(" -a,--all show inactive ports, too\n"));
#endif
printf(_(" -l,--list list current connections of each port\n"));
printf(_(" * Remove all exported connections\n"));
printf(_(" -x, --removeall\n"));
@@ -84,15 +89,11 @@ static void usage(void)
#define perm_ok(cap,bits) (((cap) & (bits)) == (bits))
#ifdef SND_SEQ_PORT_DIR_INPUT
static int check_direction(snd_seq_port_info_t *pinfo, int bit)
{
int dir = snd_seq_port_info_get_direction(pinfo);
return !dir || (dir & bit);
}
#else
#define check_direction(x, y) 1
#endif
static int check_permission(snd_seq_port_info_t *pinfo, int perm)
{
@@ -174,20 +175,16 @@ static void do_search_port(snd_seq_t *seq, int perm, action_func_t do_action)
/* reset query info */
snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
snd_seq_port_info_set_port(pinfo, -1);
#ifdef HANDLE_SHOW_ALL
if (show_all)
snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE);
#endif
count = 0;
while (snd_seq_query_next_port(seq, pinfo) >= 0) {
if (check_permission(pinfo, perm)) {
do_action(seq, cinfo, pinfo, count);
count++;
}
#ifdef HANDLE_SHOW_ALL
if (show_all)
snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE);
#endif
}
}
}
@@ -205,7 +202,6 @@ static void print_port(snd_seq_t *seq ATTRIBUTE_UNUSED,
snd_seq_client_info_get_name(cinfo),
(snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ?
_("user") : _("kernel")));
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
switch (snd_seq_client_info_get_midi_version(cinfo)) {
case SND_SEQ_CLIENT_UMP_MIDI_1_0:
printf(",UMP-MIDI1");
@@ -214,27 +210,21 @@ static void print_port(snd_seq_t *seq ATTRIBUTE_UNUSED,
printf(",UMP-MIDI2");
break;
}
#endif
#ifdef HANDLE_SHOW_ALL
if (snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_INACTIVE)
printf(",INACTIVE");
#endif
#ifdef HAVE_SEQ_CLIENT_INFO_GET_CARD
card = snd_seq_client_info_get_card(cinfo);
#endif
if (card != -1)
printf(",card=%d", card);
#ifdef HAVE_SEQ_CLIENT_INFO_GET_PID
pid = snd_seq_client_info_get_pid(cinfo);
#endif
if (pid != -1)
printf(",pid=%d", pid);
printf("]\n");
}
printf(" %3d '%-16s'\n",
printf(" %3d '%-16s'",
snd_seq_port_info_get_port(pinfo),
snd_seq_port_info_get_name(pinfo));
if (snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_INACTIVE)
printf(" [INACTIVE]");
printf("\n");
}
static void print_port_and_subs(snd_seq_t *seq, snd_seq_client_info_t *cinfo,
@@ -298,11 +288,7 @@ enum {
SUBSCRIBE, UNSUBSCRIBE, LIST, REMOVE_ALL
};
#ifdef HANDLE_SHOW_ALL
#define ACONNECT_OPTS "dior:t:elxa"
#else
#define ACONNECT_OPTS "dior:t:elx"
#endif
static const struct option long_option[] = {
{"disconnect", 0, NULL, 'd'},
@@ -313,9 +299,7 @@ static const struct option long_option[] = {
{"exclusive", 0, NULL, 'e'},
{"list", 0, NULL, 'l'},
{"removeall", 0, NULL, 'x'},
#ifdef HANDLE_SHOW_ALL
{"all", 0, NULL, 'a'},
#endif
{NULL, 0, NULL, 0},
};
@@ -369,12 +353,10 @@ int main(int argc, char **argv)
case 'x':
command = REMOVE_ALL;
break;
#ifdef HANDLE_SHOW_ALL
case 'a':
command = LIST;
show_all = 1;
break;
#endif
default:
usage();
exit(1);
@@ -386,7 +368,11 @@ int main(int argc, char **argv)
return 1;
}
#if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
snd_lib_error_set_handler(error_handler);
#else
original_log_handler = snd_lib_log_set_handler(log_handler);
#endif
switch (command) {
case LIST:
+4 -28
View File
@@ -30,9 +30,7 @@
#include <unistd.h>
#include <alsa/asoundlib.h>
#include "version.h"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
#include <alsa/ump_msg.h>
#endif
/*
* 31.25 kbaud, one start bit, eight data bits, two stop bits.
@@ -78,9 +76,7 @@ static int file_offset; /* current offset in input file */
static int num_tracks;
static struct track *tracks;
static int smpte_timing;
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
static int ump_mode;
#endif
/* prints an error message to stderr */
static void errormsg(const char *msg, ...)
@@ -685,7 +681,6 @@ static int fill_legacy_event(struct event* event, snd_seq_event_t *ev)
return 0;
}
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
static unsigned char to_ump_status(unsigned char ev_type)
{
switch (ev_type) {
@@ -762,13 +757,10 @@ static int fill_ump_event(struct event* event, snd_seq_ump_event_t *ump_ev,
snd_seq_ev_set_ump_data(ump_ev, &ump, sizeof(ump));
return 0;
}
#endif /* HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION */
static void play_midi(void)
{
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
snd_seq_ump_event_t ump_ev;
#endif
snd_seq_event_t ev;
int i, max_tick, err;
@@ -830,7 +822,7 @@ static void play_midi(void)
if (err < 0)
continue;
}
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
if (ump_mode) {
err = fill_ump_event(event, &ump_ev, &ev);
if (err < 0)
@@ -839,7 +831,6 @@ static void play_midi(void)
check_snd("output event", err);
continue;
}
#endif
/* this blocks when the output pool has been filled */
err = snd_seq_event_output(seq, &ev);
@@ -957,9 +948,7 @@ static void usage(const char *argv0)
"-V, --version print current version\n"
"-l, --list list all possible output ports\n"
"-p, --port=client:port,... set port(s) to play to\n"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
"-u, --ump=version UMP output (only version=1 is supported)\n"
#endif
"-d, --delay=seconds delay after song ends\n",
argv0);
}
@@ -969,12 +958,7 @@ static void version(void)
puts("aplaymidi version " SND_UTIL_VERSION_STR);
}
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
#define OPTIONS "hVlp:d:u:"
#else
#define OPTIONS "hVlp:d:"
#endif
int main(int argc, char *argv[])
{
@@ -984,9 +968,7 @@ int main(int argc, char *argv[])
{"version", 0, NULL, 'V'},
{"list", 0, NULL, 'l'},
{"port", 1, NULL, 'p'},
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
{"ump", 1, NULL, 'u'},
#endif
{"delay", 1, NULL, 'd'},
{0}
};
@@ -1013,15 +995,11 @@ int main(int argc, char *argv[])
case 'd':
end_delay = atoi(optarg);
break;
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
case 'u':
if (strcmp(optarg, "1")) {
errormsg("Only MIDI 1.0 is supported");
return 1;
}
ump_mode = 1;
ump_mode = atoi(optarg);
if (ump_mode < 0 || ump_mode > 1)
fatal("Only MIDI 1.0 is supported");
break;
#endif
default:
usage(argv[0]);
return 1;
@@ -1029,13 +1007,11 @@ int main(int argc, char *argv[])
}
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
if (ump_mode) {
int err;
err = snd_seq_set_client_midi_version(seq, SND_SEQ_CLIENT_UMP_MIDI_1_0);
check_snd("set midi version", err);
}
#endif
if (do_list) {
list_ports();
+2 -2
View File
@@ -66,7 +66,7 @@ will generate a "format 0" file.
.I \-m,\-\-metronome=client:port
Plays a metronome signal on the specified sequencer port.
Metronome sounds are played on channel 10, MIDI notes 33 & 34 (GM2/GS/XG
Metronome sounds are played on channel 10, MIDI notes 33 & 34 (GM2/GS/XG
metronome standard notes), with velocity 100 and duration 1.
.TP
@@ -74,7 +74,7 @@ metronome standard notes), with velocity 100 and duration 1.
Sets the time signature for the MIDI file and metronome.
The time signature is specified as usual with two numbers, representing
the numerator and denominator of the time signature as it would be
the numerator and denominator of the time signature as it would be
notated. The denominator must be a power of two. Both numbers should be
separated by a colon. The time signature is 4:4 by default.
+5
View File
@@ -0,0 +1,5 @@
AM_CPPFLAGS = -I$(top_srcdir)/include
EXTRA_DIST = aplaymidi2.1 arecordmidi2.1
bin_PROGRAMS = aplaymidi2 arecordmidi2
man_MANS = aplaymidi2.1 arecordmidi2.1
+84
View File
@@ -0,0 +1,84 @@
.TH APLAYMIDI2 1 "4 July 2024"
.SH NAME
aplaymidi2 \- play MIDI Clip Files
.SH SYNOPSIS
.B aplaymidi2
\-p client:port[,...] midi2file ...
.SH DESCRIPTION
.B aplaymidi2
is a command-line utility that plays the specified MIDI Clip file(s) to one
or more ALSA sequencer ports.
.SH OPTIONS
.TP
.I \-h, \-\-help
Prints a list of options.
.TP
.I \-V, \-\-version
Prints the current version.
.TP
.I \-p, \-\-port=client:port,...
Sets the sequencer port(s) to which the events in the MIDI Clip file(s) are
sent.
A client can be specified by its number, its name, or a prefix of its
name. A port is specified by its number; for port 0 of a client, the
":0" part of the port specification can be omitted.
Multiple ports can be specified to allow playback of MIDI Clip file(s) that
contain events for multiple devices (ports) corresponding to the
multiple UMP Groups.
For compatibility with
.B pmidi(1),
the port specification is taken from the
.I ALSA_OUTPUT_PORTS
environment variable if none is given on the command line.
.B aplaymidi2
supports only basic UMP events: in addition to the standard MIDI1 and
MIDI2 CVMs and 7bit SysEx, only the following are supported:
DCTPQ, DC, Set Tempo, Start Clip, End Clip.
Lyrics and other meta data in Flex Data are printed, too, unless
\fI\-s\fP option is given.
The multiple output ports are useful when the given MIDI Clip file
contains the UMP packets for multiple Groups.
When the destination port is a UMP MIDI 2.0 port, the single
connection should suffice, though, since a MIDI 2.0 port can process
the inputs for multiple Groups. For other cases (e.g. connecting to a
legacy MIDI port), you would need to specify the destination port per
Group. If undefined, it's sent to the first destination port as
default.
.TP
.I \-d, \-\-delay=seconds
Specifies how long to wait after the end of each MIDI Clip file,
to allow the last notes to die away.
Default is 2 seconds.
.TP
.I \-s, \-\-silent
Don't show message texts.
.TP
.I \-a, \-\-passall
Pass all UMP packets as is.
As default, \fBaplaymidi2\fP passes only MIDI1 and MIDI2 channel voice
messages and process other UMP packets internally.
With this option, it passes all UMP packets to the target.
.SH SEE ALSO
pmidi(1)
.br
aplaymidi(1)
.SH AUTHOR
Takashi Iwai <tiwai@suse.de>
+581
View File
@@ -0,0 +1,581 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* aplaymidi2.c - simple player of a MIDI Clip File over ALSA sequencer
*/
#include "aconfig.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <alsa/asoundlib.h>
#include <alsa/ump_msg.h>
#include "version.h"
static snd_seq_t *seq;
static int client;
static int port_count;
static snd_seq_addr_t ports[16];
static int queue;
static int end_delay = 2;
static int silent;
static int passall;
static unsigned int _current_tempo = 50000000; /* default 120 bpm */
static unsigned int tempo_base = 10;
static unsigned int current_tick;
/* prints an error message to stderr */
static void errormsg(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
fputc('\n', stderr);
}
/* prints an error message to stderr, and dies */
static void fatal(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
/* memory allocation error handling */
static void check_mem(void *p)
{
if (!p)
fatal("Out of memory");
}
/* error handling for ALSA functions */
static void check_snd(const char *operation, int err)
{
if (err < 0)
fatal("Cannot %s - %s", operation, snd_strerror(err));
}
/* open and initialize the sequencer client */
static void init_seq(void)
{
int err;
err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0);
check_snd("open sequencer", err);
err = snd_seq_set_client_name(seq, "aplaymidi2");
check_snd("set client name", err);
client = snd_seq_client_id(seq);
check_snd("get client id", client);
err = snd_seq_set_client_midi_version(seq, SND_SEQ_CLIENT_UMP_MIDI_2_0);
check_snd("set midi version", err);
}
/* parses one or more port addresses from the string */
static void parse_ports(const char *arg)
{
char *buf, *s, *port_name;
int err;
/* make a copy of the string because we're going to modify it */
buf = strdup(arg);
check_mem(buf);
for (port_name = s = buf; s; port_name = s + 1) {
/* Assume that ports are separated by commas. We don't use
* spaces because those are valid in client names. */
s = strchr(port_name, ',');
if (s)
*s = '\0';
++port_count;
if (port_count > 16)
fatal("Too many ports specified");
err = snd_seq_parse_address(seq, &ports[port_count - 1], port_name);
if (err < 0)
fatal("Invalid port %s - %s", port_name, snd_strerror(err));
}
free(buf);
}
/* create a source port to send from */
static void create_source_port(void)
{
snd_seq_port_info_t *pinfo;
int err;
snd_seq_port_info_alloca(&pinfo);
/* the first created port is 0 anyway, but let's make sure ... */
snd_seq_port_info_set_port(pinfo, 0);
snd_seq_port_info_set_port_specified(pinfo, 1);
snd_seq_port_info_set_name(pinfo, "aplaymidi2");
snd_seq_port_info_set_capability(pinfo, 0); /* sic */
snd_seq_port_info_set_type(pinfo,
SND_SEQ_PORT_TYPE_MIDI_GENERIC |
SND_SEQ_PORT_TYPE_APPLICATION);
err = snd_seq_create_port(seq, pinfo);
check_snd("create port", err);
}
/* create a queue */
static void create_queue(void)
{
if (!snd_seq_has_queue_tempo_base(seq))
tempo_base = 1000;
queue = snd_seq_alloc_named_queue(seq, "aplaymidi2");
check_snd("create queue", queue);
}
/* connect to destination ports */
static void connect_ports(void)
{
int i, err;
for (i = 0; i < port_count; ++i) {
err = snd_seq_connect_to(seq, 0, ports[i].client, ports[i].port);
if (err < 0)
fatal("Cannot connect to port %d:%d - %s",
ports[i].client, ports[i].port, snd_strerror(err));
}
}
/* read 32bit word and convert to native endian:
* return 0 on success, -1 on error
*/
static int read_word(FILE *file, uint32_t *dest)
{
uint32_t v;
if (fread(&v, 4, 1, file) != 1)
return -1;
*dest = be32toh(v);
return 0;
}
/* read a UMP packet: return the number of packets, -1 on error */
static int read_ump_packet(FILE *file, uint32_t *buf)
{
snd_ump_msg_hdr_t *h = (snd_ump_msg_hdr_t *)buf;
int i, num;
if (read_word(file, buf) < 0)
return -1;
num = snd_ump_packet_length(h->type);
for (i = 1; i < num; i++) {
if (read_word(file, buf + i) < 0)
return -1;
}
return num;
}
/* read the file header and verify it's MIDI Clip File: return 0 on success */
static int verify_file_header(FILE *file)
{
unsigned char buf[8];
if (fread(buf, 1, 8, file) != 8)
return -1;
if (memcmp(buf, "SMF2CLIP", 8))
return -1;
return 0;
}
/* return the current tempo, corrected to be sent to host */
static int current_tempo(void)
{
if (tempo_base != 10)
return _current_tempo / 100; /* down to us */
return _current_tempo;
}
/* send a timer event */
static void send_timer_event(unsigned int type, unsigned int val)
{
snd_seq_ump_event_t ev = {
.type = type,
.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_EVENT_LENGTH_FIXED,
};
ev.queue = queue;
ev.source.port = 0;
ev.time.tick = current_tick;
ev.dest.client = SND_SEQ_CLIENT_SYSTEM;
ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER;
ev.data.queue.queue = queue;
ev.data.queue.param.value = val;
snd_seq_ump_event_output(seq, &ev);
}
/* set DCTPQ */
static void set_dctpq(unsigned int ppq)
{
snd_seq_queue_tempo_t *queue_tempo;
snd_seq_queue_tempo_alloca(&queue_tempo);
snd_seq_queue_tempo_set_tempo(queue_tempo, current_tempo());
snd_seq_queue_tempo_set_ppq(queue_tempo, ppq);
snd_seq_queue_tempo_set_tempo_base(queue_tempo, tempo_base);
if (snd_seq_set_queue_tempo(seq, queue, queue_tempo) < 0)
errormsg("Cannot set queue tempo (%d)", queue);
}
/* set DC */
static void set_dc(unsigned int ticks)
{
current_tick += ticks;
}
/* set tempo event */
static void set_tempo(unsigned int tempo)
{
_current_tempo = tempo;
send_timer_event(SND_SEQ_EVENT_TEMPO, current_tempo());
}
/* start clip */
static void start_clip(void)
{
if (snd_seq_start_queue(seq, queue, NULL) < 0)
errormsg("Cannot start queue (%d)", queue);
}
/* end clip */
static void end_clip(void)
{
send_timer_event(SND_SEQ_EVENT_STOP, 0);
}
/* send a UMP packet */
static void send_ump(const uint32_t *ump, int len)
{
snd_seq_ump_event_t ev = {
.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_EVENT_LENGTH_FIXED |
SND_SEQ_EVENT_UMP,
};
int group;
memcpy(ev.ump, ump, len * 4);
ev.queue = queue;
ev.source.port = 0;
ev.time.tick = current_tick;
group = snd_ump_msg_group(ump);
if (group >= port_count)
ev.dest = ports[0];
else
ev.dest = ports[group];
snd_seq_ump_event_output(seq, &ev);
}
struct flexdata_text_prefix {
unsigned char status_bank;
unsigned char status;
const char *prefix;
};
static struct flexdata_text_prefix text_prefix[] = {
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_PROJECT_NAME,
.prefix = "Project" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_SONG_NAME,
.prefix = "Song" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_MIDI_CLIP_NAME,
.prefix = "MIDI Clip" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_COPYRIGHT_NOTICE,
.prefix = "Copyright" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_COMPOSER_NAME,
.prefix = "Composer" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_LYRICIST_NAME,
.prefix = "Lyricist" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_ARRANGER_NAME,
.prefix = "Arranger" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_PUBLISHER_NAME,
.prefix = "Publisher" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_PRIMARY_PERFORMER,
.prefix = "Performer" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_ACCOMPANY_PERFORMAER,
.prefix = "Accompany Performer" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RECORDING_DATE,
.prefix = "Recording Date" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RECORDING_LOCATION,
.prefix = "Recording Location" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_LYRICS,
.prefix = "Lyrics" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_LYRICS_LANGUAGE,
.prefix = "Lyrics Language" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RUBY,
.prefix = "Ruby" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RUBY_LANGUAGE,
.prefix = "Ruby Language" },
{}
};
static void show_text(const uint32_t *ump)
{
static unsigned char textbuf[256];
static int len;
const snd_ump_msg_flex_data_t *fh =
(const snd_ump_msg_flex_data_t *)ump;
const char *prefix;
int i;
if (fh->meta.format == SND_UMP_FLEX_DATA_MSG_FORMAT_SINGLE ||
fh->meta.format == SND_UMP_FLEX_DATA_MSG_FORMAT_START)
len = 0;
for (i = 0; i < 12 && len < (int)sizeof(textbuf); i++) {
textbuf[len] = snd_ump_get_byte(ump, 4 + i);
if (!textbuf[len])
break;
switch (textbuf[len]) {
case 0x0a: /* end of paragraph */
case 0x0d: /* end of line */
textbuf[len] = '\n';
break;
}
len++;
}
if (fh->meta.format != SND_UMP_FLEX_DATA_MSG_FORMAT_SINGLE &&
fh->meta.format != SND_UMP_FLEX_DATA_MSG_FORMAT_END)
return;
if (len >= (int)sizeof(textbuf))
len = sizeof(textbuf) - 1;
textbuf[len] = 0;
prefix = NULL;
for (i = 0; text_prefix[i].status_bank; i++) {
if (text_prefix[i].status_bank == fh->meta.status_bank &&
text_prefix[i].status == fh->meta.status) {
prefix = text_prefix[i].prefix;
break;
}
}
if (prefix) {
printf("%s: %s\n", prefix, textbuf);
} else {
printf("(%d:%d): %s\n", fh->meta.status_bank, fh->meta.status,
textbuf);
}
len = 0;
}
/* play the given MIDI Clip File content */
static void play_midi(FILE *file)
{
uint32_t ump[4];
int len;
current_tick = 0;
while ((len = read_ump_packet(file, ump)) > 0) {
const snd_ump_msg_hdr_t *h = (snd_ump_msg_hdr_t *)ump;
if (passall)
send_ump(ump, len);
if (h->type == SND_UMP_MSG_TYPE_UTILITY) {
const snd_ump_msg_utility_t *uh =
(const snd_ump_msg_utility_t *)ump;
switch (h->status) {
case SND_UMP_UTILITY_MSG_STATUS_DCTPQ:
set_dctpq(uh->dctpq.ticks);
continue;
case SND_UMP_UTILITY_MSG_STATUS_DC:
set_dc(uh->dctpq.ticks);
continue;
}
} else if (h->type == SND_UMP_MSG_TYPE_FLEX_DATA) {
const snd_ump_msg_flex_data_t *fh =
(const snd_ump_msg_flex_data_t *)ump;
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_SETUP &&
fh->meta.status == SND_UMP_FLEX_DATA_MSG_STATUS_SET_TEMPO) {
set_tempo(fh->set_tempo.tempo);
continue;
}
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_METADATA ||
fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT) {
if (!silent)
show_text(ump);
continue;
}
} else if (h->type == SND_UMP_MSG_TYPE_STREAM) {
const snd_ump_msg_stream_t *sh =
(const snd_ump_msg_stream_t *)ump;
switch (sh->gen.status) {
case SND_UMP_STREAM_MSG_STATUS_START_CLIP:
start_clip();
continue;
case SND_UMP_STREAM_MSG_STATUS_END_CLIP:
end_clip();
continue;
}
} else if (!passall &&
(h->type == SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE ||
h->type == SND_UMP_MSG_TYPE_DATA ||
h->type == SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE)) {
send_ump(ump, len);
}
}
snd_seq_drain_output(seq);
snd_seq_sync_output_queue(seq);
/* give the last notes time to die away */
if (end_delay > 0)
sleep(end_delay);
}
static void play_file(const char *file_name)
{
FILE *file;
if (!strcmp(file_name, "-"))
file = stdin;
else
file = fopen(file_name, "rb");
if (!file) {
errormsg("Cannot open %s - %s", file_name, strerror(errno));
return;
}
if (verify_file_header(file) < 0) {
errormsg("%s is not a MIDI Clip File", file_name);
goto error;
}
play_midi(file);
error:
if (file != stdin)
fclose(file);
}
static void usage(const char *argv0)
{
printf(
"Usage: %s -p client:port[,...] [-d delay] midifile ...\n"
"-h, --help this help\n"
"-V, --version print current version\n"
"-p, --port=client:port,... set port(s) to play to\n"
"-d, --delay=seconds delay after song ends\n"
"-s, --silent don't show texts\n"
"-a, --passall pass all UMP packets as-is\n",
argv0);
}
static void version(void)
{
puts("aplaymidi2 version " SND_UTIL_VERSION_STR);
}
int main(int argc, char *argv[])
{
static const struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'V'},
{"port", 1, NULL, 'p'},
{"delay", 1, NULL, 'd'},
{"silent", 0, NULL, 's'},
{"passall", 0, NULL, 'a'},
{0}
};
int c;
init_seq();
while ((c = getopt_long(argc, argv, "hVp:d:sa",
long_options, NULL)) != -1) {
switch (c) {
case 'h':
usage(argv[0]);
return 0;
case 'V':
version();
return 0;
case 'p':
parse_ports(optarg);
break;
case 'd':
end_delay = atoi(optarg);
break;
case 's':
silent = 1;
break;
case 'a':
passall = 1;
break;
default:
usage(argv[0]);
return 1;
}
}
if (port_count < 1) {
/* use env var for compatibility with pmidi */
const char *ports_str = getenv("ALSA_OUTPUT_PORTS");
if (ports_str)
parse_ports(ports_str);
if (port_count < 1) {
errormsg("Please specify at least one port with --port.");
return 1;
}
}
if (optind >= argc) {
errormsg("Please specify a file to play.");
return 1;
}
create_source_port();
create_queue();
connect_ports();
for (; optind < argc; optind++)
play_file(argv[optind]);
snd_seq_close(seq);
return 0;
}
+110
View File
@@ -0,0 +1,110 @@
.TH ARECORDMIDI2 1 "4 July 2024"
.SH NAME
arecordmidi2 \- record a MIDI Clip file
.SH SYNOPSIS
.B arecordmidi2
[options] midi2file
.SH DESCRIPTION
.B arecordmidi2
is a command-line utility that records a MIDI Clip file from one or
more ALSA sequencer ports.
To stop recording, press Ctrl+C.
When \fB\-\fP is passed to the MIDI Clip file argument,
it's recorded to stdout. It implies \fI\-s\fP option, too.
.SH OPTIONS
.TP
.I \-h,\-\-help
Prints a list of options.
.TP
.I \-V,\-\-version
Prints the current version.
.TP
.I \-p,\-\-port=client:port,...
Sets the sequencer port(s) from which events are recorded.
A client can be specified by its number, its name, or a prefix of its
name. A port is specified by its number; for port 0 of a client, the
":0" part of the port specification can be omitted.
\fBarecordmidi2\fP creates a UMP Endpoint containing the same number
of Function Blocks as specified by this option, each of which is
connected to the specified port as a source.
When no source ports are specified with \fI\-p\fP option,
\fBarecordmidi2\fP creates a UMP Endpoint with full 16 Function Blocks
and records from those inputs. User can connect the sequencer ports
freely via \fBaconnect\fP, for example. This mode can be used
together with the interactive mode via \fI\-r\fP option.
.TP
.I \-b,\-\-bpm=beats
Sets the musical tempo of the MIDI file, in beats per minute.
The default value is 120 BPM.
.TP
.I \-t,\-\-ticks=ticks
Sets the resolution of timestamps (ticks) in the MIDI file,
in ticks per beat.
The default value is 384 ticks/beat.
.TP
.I \-i,\-\-timesig=numerator:denominator
Sets the time signature for the MIDI file.
The time signature is specified as usual with two numbers, representing
the numerator and denominator of the time signature as it would be
notated. The denominator must be a power of two. Both numbers should be
separated by a colon. The time signature is 4:4 by default.
.TP
.I \-n,\-\-num-events=events
Stops the recording after receiving the given number of events.
.TP
.I \-u,\-\-ump=version
Sets the UMP MIDI protocol version. Either 1 or 2 has to be given for
MIDI 1.0 and MIDI 2.0 protocol, respectively.
Default is 1.
.TP
.I \-r,\-\-interactive
Run in the interactive mode. \fBarecordmidi2\fP waits for a RETURN
key input from the terminal to start the recording. After starting,
the recording ends when another RETURN key is input from the
terminal. The received events before the start of recording are
discarded.
.TP
.I \-s,\-\-silent
Don't print messages to stdout.
.TP
.I \-P,\-\-profile=file
Read the UMP data from the given file and put them into the
configuration section of the recorded output.
The file must contain only valid UMP data encoded in big-endian.
.TP
.I \-\-song=text, \-\-clip=text, \-\-copyright=text, \-\-composer=text, \
\-\-lyricist=text, \-\-arranger=text, \-\-publisher=text, \
\-\-performer=text \-\-accompany=text, \-\-date=text, \-\-location=text
Put the given meta data text in the configuration section.
.SH SEE ALSO
arecordmidi(1)
.br
aplaymidi2(1)
.SH AUTHOR
Takashi Iwai <tiwai@suse.de>
+717
View File
@@ -0,0 +1,717 @@
/*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <getopt.h>
#include <poll.h>
#include <alsa/asoundlib.h>
#include <alsa/ump_msg.h>
#include "aconfig.h"
#include "version.h"
static snd_seq_t *seq;
static int client;
static int port_count;
static snd_seq_addr_t *ports;
static int queue;
static int midi_version = 1;
static int beats = 120;
static int ticks = 384;
static int tempo_base = 10;
static volatile sig_atomic_t stop;
static int ts_num = 4; /* time signature: numerator */
static int ts_div = 4; /* time signature: denominator */
static int last_tick;
static int silent;
static const char *profile_ump_file;
#define MAX_METADATA 16
static int metadata_num;
static unsigned int metadata_types[MAX_METADATA];
static const char *metadata_texts[MAX_METADATA];
/* Parse a decimal number from a command line argument. */
static long arg_parse_decimal_num(const char *str, int *err)
{
long val;
char *endptr;
errno = 0;
val = strtol(str, &endptr, 0);
if (errno > 0) {
*err = -errno;
return 0;
}
if (*endptr != '\0') {
*err = -EINVAL;
return 0;
}
return val;
}
/* prints an error message to stderr, and dies */
static void fatal(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
/* memory allocation error handling */
static void check_mem(void *p)
{
if (!p)
fatal("Out of memory");
}
/* error handling for ALSA functions */
static void check_snd(const char *operation, int err)
{
if (err < 0)
fatal("Cannot %s - %s", operation, snd_strerror(err));
}
/* open a sequencer client */
static void init_seq(void)
{
int err;
/* open sequencer */
err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0);
check_snd("open sequencer", err);
/* find out our client's id */
client = snd_seq_client_id(seq);
check_snd("get client id", client);
}
/* set up UMP virtual client/port */
static void create_ump_client(void)
{
snd_ump_endpoint_info_t *ep;
snd_ump_block_info_t *blk;
snd_seq_port_info_t *pinfo;
int num_groups;
int i, err;
/* in passive mode, create full 16 groups */
if (port_count)
num_groups = port_count;
else
num_groups = 16;
/* create a UMP Endpoint */
snd_ump_endpoint_info_alloca(&ep);
snd_ump_endpoint_info_set_name(ep, "arecordmidi2");
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_groups);
err = snd_seq_create_ump_endpoint(seq, ep, num_groups);
check_snd("create UMP endpoint", err);
/* create UMP Function Blocks */
snd_ump_block_info_alloca(&blk);
for (i = 0; i < num_groups; i++) {
char blkname[32];
sprintf(blkname, "Group %d", i + 1);
snd_ump_block_info_set_name(blk, blkname);
snd_ump_block_info_set_direction(blk, SND_UMP_DIR_INPUT);
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_RECEIVER);
err = snd_seq_create_ump_block(seq, i, blk);
check_snd("create UMP block", err);
}
/* toggle timestamping for all input ports */
snd_seq_port_info_alloca(&pinfo);
for (i = 0; i <= num_groups; i++) {
err = snd_seq_get_port_info(seq, i, pinfo);
check_snd("get port info", err);
snd_seq_port_info_set_timestamping(pinfo, 1);
snd_seq_port_info_set_timestamp_queue(pinfo, queue);
snd_seq_set_port_info(seq, i, pinfo);
check_snd("set port info", err);
}
}
/* parses one or more port addresses from the string */
static void parse_ports(const char *arg)
{
char *buf, *s, *port_name;
int err;
/* make a copy of the string because we're going to modify it */
buf = strdup(arg);
check_mem(buf);
for (port_name = s = buf; s; port_name = s + 1) {
/* Assume that ports are separated by commas. We don't use
* spaces because those are valid in client names.
*/
s = strchr(port_name, ',');
if (s)
*s = '\0';
++port_count;
ports = realloc(ports, port_count * sizeof(snd_seq_addr_t));
check_mem(ports);
err = snd_seq_parse_address(seq, &ports[port_count - 1], port_name);
if (err < 0)
fatal("Invalid port %s - %s", port_name, snd_strerror(err));
}
free(buf);
}
/* parses time signature specification */
static void time_signature(const char *arg)
{
long x = 0;
char *sep;
x = strtol(arg, &sep, 10);
if (x < 1 || x > 64 || *sep != ':')
fatal("Invalid time signature (%s)", arg);
ts_num = x;
x = strtol(++sep, NULL, 10);
if (x < 1 || x > 64)
fatal("Invalid time signature (%s)", arg);
ts_div = x;
}
/* create a queue, set up the default tempo */
static void create_queue(void)
{
snd_seq_queue_tempo_t *tempo;
if (!snd_seq_has_queue_tempo_base(seq))
tempo_base = 1000;
queue = snd_seq_alloc_named_queue(seq, "arecordmidi2");
check_snd("create queue", queue);
snd_seq_queue_tempo_alloca(&tempo);
if (tempo_base == 1000)
snd_seq_queue_tempo_set_tempo(tempo, 60000000 / beats);
else
snd_seq_queue_tempo_set_tempo(tempo, (unsigned int)(6000000000ULL / beats));
snd_seq_queue_tempo_set_ppq(tempo, ticks);
snd_seq_queue_tempo_set_tempo_base(tempo, tempo_base);
if (snd_seq_set_queue_tempo(seq, queue, tempo) < 0)
fatal("Cannot set queue tempo (%d)", queue);
}
/* connect to the input ports */
static void connect_ports(void)
{
int i, err;
for (i = 0; i < port_count; ++i) {
err = snd_seq_connect_from(seq, i + 1,
ports[i].client, ports[i].port);
check_snd("port connection", err);
}
}
/* write the given UMP packet */
static void write_ump(FILE *file, const void *src)
{
const snd_ump_msg_hdr_t *h = src;
const uint32_t *p = src;
uint32_t v;
int len;
len = snd_ump_packet_length(h->type);
while (len-- > 0) {
v = htobe32(*p++);
fwrite(&v, 4, 1, file);
}
}
/* write a DC message */
static void write_dcs(FILE *file, unsigned int t)
{
snd_ump_msg_dc_t d = {};
d.type = SND_UMP_MSG_TYPE_UTILITY;
d.status = SND_UMP_UTILITY_MSG_STATUS_DC;
d.ticks = t;
write_ump(file, &d);
}
/* write a DCTPQ message */
static void write_dctpq(FILE *file)
{
snd_ump_msg_dctpq_t d = {};
d.type = SND_UMP_MSG_TYPE_UTILITY;
d.status = SND_UMP_UTILITY_MSG_STATUS_DCTPQ;
d.ticks = ticks;
write_ump(file, &d);
}
/* write a Start Clip message */
static void write_start_clip(FILE *file)
{
snd_ump_msg_stream_gen_t d = {};
d.type = SND_UMP_MSG_TYPE_STREAM;
d.status = SND_UMP_STREAM_MSG_STATUS_START_CLIP;
write_ump(file, &d);
}
/* write an End Clip message */
static void write_end_clip(FILE *file)
{
snd_ump_msg_stream_gen_t d = {};
d.type = SND_UMP_MSG_TYPE_STREAM;
d.status = SND_UMP_STREAM_MSG_STATUS_END_CLIP;
write_ump(file, &d);
}
/* write a Set Tempo message */
static void write_tempo(FILE *file)
{
snd_ump_msg_set_tempo_t d = {};
d.type = SND_UMP_MSG_TYPE_FLEX_DATA;
d.group = 0;
d.format = SND_UMP_FLEX_DATA_MSG_FORMAT_SINGLE;
d.addrs = SND_UMP_FLEX_DATA_MSG_ADDR_GROUP;
d.status_bank = SND_UMP_FLEX_DATA_MSG_BANK_SETUP;
d.status = SND_UMP_FLEX_DATA_MSG_STATUS_SET_TEMPO;
d.tempo = (unsigned int)(6000000000ULL / beats);
write_ump(file, &d);
}
/* write a Set Time Signature message */
static void write_time_sig(FILE *file)
{
snd_ump_msg_set_time_sig_t d = {};
d.type = SND_UMP_MSG_TYPE_FLEX_DATA;
d.group = 0;
d.format = SND_UMP_FLEX_DATA_MSG_FORMAT_SINGLE;
d.addrs = SND_UMP_FLEX_DATA_MSG_ADDR_GROUP;
d.status_bank = SND_UMP_FLEX_DATA_MSG_BANK_SETUP;
d.status = SND_UMP_FLEX_DATA_MSG_STATUS_SET_TIME_SIGNATURE;
d.numerator = ts_num;
d.denominator = ts_div;
d.num_notes = 8;
write_ump(file, &d);
}
/* record the delta time from the last event */
static void delta_time(FILE *file, const snd_seq_ump_event_t *ev)
{
int diff = ev->time.tick - last_tick;
if (diff <= 0)
return;
write_dcs(file, diff);
last_tick = ev->time.tick;
}
static void record_event(FILE *file, const snd_seq_ump_event_t *ev)
{
/* ignore events without proper timestamps */
if (ev->queue != queue || !snd_seq_ev_is_tick(ev) ||
!snd_seq_ev_is_ump(ev))
return;
delta_time(file, ev);
write_ump(file, ev->ump);
}
/* read a UMP raw (big-endian) packet, return the packet length in words */
static int read_ump_raw(FILE *file, uint32_t *buf)
{
uint32_t v;
int i, num;
if (fread(buf, 4, 1, file) != 1)
return 0;
v = be32toh(*buf);
num = snd_ump_packet_length(snd_ump_msg_hdr_type(v));
for (i = 1; i < num; i++) {
if (fread(buf + i, 4, 1, file) != 1)
return 0;
}
return num;
}
/* read the profile UMP data and write to the configuration */
static void write_profiles(FILE *file)
{
FILE *fp;
uint32_t ump[4];
int len;
if (!profile_ump_file)
return;
fp = fopen(profile_ump_file, "rb");
if (!fp)
fatal("cannot open the profile '%s'", profile_ump_file);
while (!feof(fp)) {
len = read_ump_raw(fp, ump);
if (!len)
break;
fwrite(ump, 4, len, file);
}
fclose(fp);
}
/* write Flex Data metadata text given by command lines */
static void write_metadata(FILE *file, unsigned int type, const char *text)
{
int len = strlen(text), size;
unsigned int format = SND_UMP_FLEX_DATA_MSG_FORMAT_START;
while (len > 0) {
snd_ump_msg_flex_data_t d = {};
if (len <= 12) {
if (format == SND_UMP_FLEX_DATA_MSG_FORMAT_CONTINUE)
format = SND_UMP_FLEX_DATA_MSG_FORMAT_END;
else
format = SND_UMP_FLEX_DATA_MSG_FORMAT_SINGLE;
size = len;
} else {
size = 12;
}
d.meta.type = SND_UMP_MSG_TYPE_FLEX_DATA;
d.meta.addrs = SND_UMP_FLEX_DATA_MSG_ADDR_GROUP;
d.meta.status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA;
d.meta.status = type;
d.meta.format = format;
/* keep the data in big endian */
d.raw[0] = htobe32(d.raw[0]);
/* strings are copied as-is in big-endian */
memcpy(d.meta.data, text, size);
fwrite(d.raw, 4, 4, file);
len -= size;
text += size;
format = SND_UMP_FLEX_DATA_MSG_FORMAT_CONTINUE;
}
}
/* write MIDI Clip file header and the configuration packets */
static void write_file_header(FILE *file)
{
int i;
/* header id */
fwrite("SMF2CLIP", 1, 8, file);
/* clip configuration header */
write_profiles(file);
for (i = 0; i < metadata_num; i++)
write_metadata(file, metadata_types[i], metadata_texts[i]);
/* first DCS */
write_dcs(file, 0);
write_dctpq(file);
}
/* write start bar */
static void start_bar(FILE *file)
{
int err;
/* start the queue */
err = snd_seq_start_queue(seq, queue, NULL);
check_snd("start queue", err);
snd_seq_drain_output(seq);
write_start_clip(file);
write_tempo(file);
write_time_sig(file);
}
static void help(const char *argv0)
{
fprintf(stderr, "Usage: %s [options] outputfile\n"
"\nAvailable options:\n"
" -h,--help this help\n"
" -V,--version show version\n"
" -p,--port=client:port,... source port(s)\n"
" -b,--bpm=beats tempo in beats per minute\n"
" -t,--ticks=ticks resolution in ticks per beat or frame\n"
" -i,--timesig=nn:dd time signature\n"
" -n,--num-events=events fixed number of events to record, then exit\n"
" -u,--ump=version UMP MIDI version (1 or 2)\n"
" -r,--interactive Interactive mode\n"
" -s,--silent don't print messages\n"
" -P,--profile=file configuration profile UMP\n"
" --project=text put project name meta data text\n"
" --song=text put song name meta data text\n"
" --clip=text put MIDI clip name meta data text\n"
" --copyright=text put copyright notice meta data text\n"
" --composer=text put composer name meta data text\n"
" --lyricist=text put lyricist name meta data text\n"
" --arranger=text put arranger name meta data text\n"
" --publisher=text put publisher name meta data text\n"
" --publisher=text put publisher name meta data text\n"
" --publisher=text put publisher name meta data text\n"
" --performer=text put performer name meta data text\n"
" --accompany=text put accompany performer name meta data text\n"
" --date=text put recording date meta data text\n"
" --location=text put recording location meta data text\n",
argv0);
}
static void version(void)
{
fputs("arecordmidi version " SND_UTIL_VERSION_STR "\n", stderr);
}
static void sighandler(int sig ATTRIBUTE_UNUSED)
{
stop = 1;
}
#define OPT_META_BIT 0x1000
enum {
OPT_META_PROJECT = 0x1001,
OPT_META_SONG = 0x1002,
OPT_META_CLIP = 0x1003,
OPT_META_COPYRIGHT = 0x1004,
OPT_META_COMPOSER = 0x1005,
OPT_META_LYRICIST = 0x1006,
OPT_META_ARRANGER = 0x1007,
OPT_META_PUBLISHER = 0x1008,
OPT_META_PERFORMER = 0x1009,
OPT_META_ACCOMPANY = 0x100a,
OPT_META_DATE = 0x100b,
OPT_META_LOCATION = 0x100c,
};
int main(int argc, char *argv[])
{
static const char short_options[] = "hVp:b:t:n:u:rsP:";
static const struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'V'},
{"port", 1, NULL, 'p'},
{"bpm", 1, NULL, 'b'},
{"ticks", 1, NULL, 't'},
{"timesig", 1, NULL, 'i'},
{"num-events", 1, NULL, 'n'},
{"ump", 1, NULL, 'u'},
{"interactive", 0, NULL, 'r'},
{"silent", 0, NULL, 's'},
{"profile", 1, NULL, 'P'},
/* meta data texts */
{"project", 1, NULL, OPT_META_PROJECT},
{"song", 1, NULL, OPT_META_SONG},
{"clip", 1, NULL, OPT_META_CLIP},
{"copyright", 1, NULL, OPT_META_COPYRIGHT},
{"composer", 1, NULL, OPT_META_COMPOSER},
{"lyricist", 1, NULL, OPT_META_LYRICIST},
{"arranger", 1, NULL, OPT_META_ARRANGER},
{"publisher", 1, NULL, OPT_META_PUBLISHER},
{"performer", 1, NULL, OPT_META_PERFORMER},
{"accompany", 1, NULL, OPT_META_ACCOMPANY},
{"date", 1, NULL, OPT_META_DATE},
{"location", 1, NULL, OPT_META_LOCATION},
{0}
};
char *filename;
FILE *file;
struct pollfd *pfds;
int npfds;
int c, err;
/* If |num_events| isn't specified, leave it at 0. */
long num_events = 0;
long events_received = 0;
int start = 0;
int interactive = 0;
init_seq();
while ((c = getopt_long(argc, argv, short_options,
long_options, NULL)) != -1) {
switch (c) {
case 'h':
help(argv[0]);
return 0;
case 'V':
version();
return 0;
case 'p':
parse_ports(optarg);
break;
case 'b':
beats = atoi(optarg);
if (beats < 4 || beats > 6000)
fatal("Invalid tempo");
break;
case 't':
ticks = atoi(optarg);
if (ticks < 1 || ticks > 0x7fff)
fatal("Invalid number of ticks");
break;
case 'i':
time_signature(optarg);
break;
case 'n':
err = 0;
num_events = arg_parse_decimal_num(optarg, &err);
if (err != 0) {
fatal("Couldn't parse num_events argument: %s\n",
strerror(-err));
}
if (num_events <= 0)
fatal("num_events must be greater than 0");
break;
case 'u':
midi_version = atoi(optarg);
if (midi_version != 1 && midi_version != 2)
fatal("Invalid MIDI version %d\n", midi_version);
break;
case 'r':
interactive = 1;
break;
case 's':
silent = 1;
break;
case 'P':
profile_ump_file = optarg;
break;
default:
if (c & OPT_META_BIT) {
if (metadata_num >= MAX_METADATA)
fatal("Too many metadata given");
metadata_types[metadata_num] = c & 0x0f;
metadata_texts[metadata_num] = optarg;
metadata_num++;
break;
}
help(argv[0]);
return 1;
}
}
if (optind >= argc) {
fputs("Please specify a file to record to.\n", stderr);
return 1;
}
create_queue();
create_ump_client();
if (port_count)
connect_ports();
filename = argv[optind];
if (!strcmp(filename, "-")) {
file = stdout;
silent = 1; // imply silent mode
} else {
file = fopen(filename, "wb");
if (!file)
fatal("Cannot open %s - %s", filename, strerror(errno));
}
write_file_header(file);
if (interactive) {
if (!silent) {
printf("Press RETURN to start recording:");
fflush(stdout);
}
} else {
start_bar(file);
start = 1;
}
err = snd_seq_nonblock(seq, 1);
check_snd("set nonblock mode", err);
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
npfds = snd_seq_poll_descriptors_count(seq, POLLIN);
pfds = alloca(sizeof(*pfds) * (npfds + 1));
for (;;) {
snd_seq_poll_descriptors(seq, pfds, npfds, POLLIN);
if (interactive) {
pfds[npfds].fd = STDIN_FILENO;
pfds[npfds].events = POLLIN | POLLERR | POLLNVAL;
if (poll(pfds, npfds + 1, -1) < 0)
break;
if (pfds[npfds].revents & POLLIN) {
while (!feof(stdin) && getchar() != '\n')
;
if (!start) {
start_bar(file);
start = 1;
if (!silent) {
printf("Press RETURN to stop recording:");
fflush(stdout);
}
continue;
} else {
stop = 1;
}
}
} else {
if (poll(pfds, npfds, -1) < 0)
break;
}
do {
snd_seq_ump_event_t *event;
err = snd_seq_ump_event_input(seq, &event);
if (err < 0)
break;
if (start && event) {
record_event(file, event);
events_received++;
}
} while (err > 0);
if (stop)
break;
if (num_events && (events_received >= num_events))
break;
}
if (num_events && events_received < num_events) {
if (!silent)
fputs("Warning: Received signal before num_events\n", stdout);
}
write_end_clip(file);
if (file != stdout)
fclose(file);
snd_seq_close(seq);
return 0;
}
+449 -28
View File
@@ -29,9 +29,7 @@
#include <poll.h>
#include <alsa/asoundlib.h>
#include "version.h"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
#include <alsa/ump_msg.h>
#endif
enum {
VIEW_RAW, VIEW_NORMALIZED, VIEW_PERCENT
@@ -41,11 +39,7 @@ static snd_seq_t *seq;
static int port_count;
static snd_seq_addr_t *ports;
static volatile sig_atomic_t stop = 0;
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
static int ump_version;
#else
#define ump_version 0
#endif
static int view_mode = VIEW_RAW;
/* prints an error message to stderr, and dies */
@@ -368,7 +362,6 @@ static void dump_event(const snd_seq_event_t *ev)
}
}
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
static int group_number(unsigned char c)
{
if (view_mode != VIEW_RAW)
@@ -400,8 +393,8 @@ static void dump_ump_midi1_event(const unsigned int *ump)
break;
case SND_UMP_MSG_NOTE_ON:
printf("Note on %2d, note %d, velocity %s",
channel, m->note_off.note,
midi1_data(m->note_off.velocity));
channel, m->note_on.note,
midi1_data(m->note_on.velocity));
break;
case SND_UMP_MSG_POLY_PRESSURE:
printf("Poly pressure %2d, note %d, value %s",
@@ -442,7 +435,7 @@ static const char *midi2_velocity(unsigned int v)
snprintf(tmp, sizeof(tmp), "%.2f",
((double)v * 64.0) / 0x8000);
else
snprintf(tmp, sizeof(tmp), ".2%f",
snprintf(tmp, sizeof(tmp), "%.2f",
((double)(v - 0x8000) * 63.0) / 0x7fff + 64.0);
return tmp;
} else if (view_mode == VIEW_PERCENT) {
@@ -552,9 +545,9 @@ static void dump_ump_midi2_event(const unsigned int *ump)
break;
case SND_UMP_MSG_NOTE_ON:
printf("Note on %2d, note %d, velocity %s, attr type = %d, data = 0x%x",
channel, m->note_off.note,
midi2_velocity(m->note_off.velocity),
m->note_off.attr_type, m->note_off.attr_data);
channel, m->note_on.note,
midi2_velocity(m->note_on.velocity),
m->note_on.attr_type, m->note_on.attr_data);
break;
case SND_UMP_MSG_POLY_PRESSURE:
printf("Poly pressure %2d, note %d, value %s",
@@ -579,7 +572,7 @@ static void dump_ump_midi2_event(const unsigned int *ump)
midi2_data(m->channel_pressure.data));
break;
case SND_UMP_MSG_PITCHBEND:
printf("Channel pressure %2d, value %s",
printf("Pitchbend %2d, value %s",
channel,
midi2_pitchbend(m->channel_pressure.data));
break;
@@ -595,6 +588,427 @@ static void dump_ump_midi2_event(const unsigned int *ump)
printf("\n");
}
static void dump_ump_utility_event(const unsigned int *ump)
{
unsigned char status = snd_ump_msg_status(ump);
unsigned int val = *ump & 0xfffff;
printf(" ");
switch (status) {
case SND_UMP_UTILITY_MSG_STATUS_NOOP:
printf("Noop\n");
break;
case SND_UMP_UTILITY_MSG_STATUS_JR_CLOCK:
printf("JR Clock value %d\n", val);
break;
case SND_UMP_UTILITY_MSG_STATUS_JR_TSTAMP:
printf("JR Timestamp value %d\n", val);
break;
case SND_UMP_UTILITY_MSG_STATUS_DCTPQ:
printf("DCTPQ value %d\n", val);
break;
case SND_UMP_UTILITY_MSG_STATUS_DC:
printf("DC Ticks value %d\n", val);
break;
default:
printf("UMP Utility event: status = %d, 0x%08x\n",
status, *ump);
break;
}
}
static void dump_ump_system_event(const unsigned int *ump)
{
const snd_ump_msg_system_t *m = (const snd_ump_msg_system_t *)ump;
printf("Group %2d, ", group_number(m->group));
switch (m->status) {
case SND_UMP_MSG_MIDI_TIME_CODE:
printf("MIDI Time Code value %d\n", m->parm1);
break;
case SND_UMP_MSG_SONG_POSITION:
printf("Song position pointer value %d\n",
((unsigned int)m->parm2 << 7) | m->parm1);
break;
case SND_UMP_MSG_SONG_SELECT:
printf("Song select value %d\n", m->parm1);
break;
case SND_UMP_MSG_TUNE_REQUEST:
printf("Tune request\n");
break;
case SND_UMP_MSG_TIMING_CLOCK:
printf("Timing clock\n");
break;
case SND_UMP_MSG_START:
printf("Start\n");
break;
case SND_UMP_MSG_CONTINUE:
printf("Continue\n");
break;
case SND_UMP_MSG_STOP:
printf("Stop\n");
break;
case SND_UMP_MSG_ACTIVE_SENSING:
printf("Active sensing\n");
break;
case SND_UMP_MSG_RESET:
printf("Reset\n");
break;
default:
printf("UMP System event: status = %d, 0x%08x\n",
m->status, *ump);
break;
}
}
static unsigned char ump_sysex7_data(const unsigned int *ump,
unsigned int offset)
{
return snd_ump_get_byte(ump, offset + 2);
}
static void dump_ump_sysex_status(const char *prefix, unsigned int status)
{
printf("%s ", prefix);
switch (status) {
case SND_UMP_SYSEX_STATUS_SINGLE:
printf("Single ");
break;
case SND_UMP_SYSEX_STATUS_START:
printf("Start ");
break;
case SND_UMP_SYSEX_STATUS_CONTINUE:
printf("Continue");
break;
case SND_UMP_SYSEX_STATUS_END:
printf("End ");
break;
default:
printf("(0x%04x)", status);
break;
}
}
static void dump_ump_sysex_event(const unsigned int *ump)
{
int i, length;
printf("Group %2d, ", group_number(snd_ump_msg_group(ump)));
dump_ump_sysex_status("SysEx", snd_ump_sysex_msg_status(ump));
length = snd_ump_sysex_msg_length(ump);
printf(" length %d ", length);
if (length > 6)
length = 6;
for (i = 0; i < length; i++)
printf("%s%02x", i ? ":" : "", ump_sysex7_data(ump, i));
printf("\n");
}
static unsigned char ump_sysex8_data(const unsigned int *ump,
unsigned int offset)
{
return snd_ump_get_byte(ump, offset + 3);
}
static void dump_ump_sysex8_event(const unsigned int *ump)
{
int i, length;
printf("Group %2d, ", group_number(snd_ump_msg_group(ump)));
dump_ump_sysex_status("SysEx8", snd_ump_sysex_msg_status(ump));
length = snd_ump_sysex_msg_length(ump);
printf(" length %d ", length);
printf(" stream %d ", (ump[0] >> 8) & 0xff);
if (length > 13)
length = 13;
for (i = 0; i < length; i++)
printf("%s%02x", i ? ":" : "", ump_sysex8_data(ump, i));
printf("\n");
}
static void dump_ump_mixed_data_event(const unsigned int *ump)
{
const snd_ump_msg_mixed_data_t *m =
(const snd_ump_msg_mixed_data_t *)ump;
int i;
printf("Group %2d, ", group_number(snd_ump_msg_group(ump)));
switch (snd_ump_sysex_msg_status(ump)) {
case SND_UMP_MIXED_DATA_SET_STATUS_HEADER:
printf("MDS Header id=0x%x, bytes=%d, chunk=%d/%d, manufacturer=0x%04x, device=0x%04x, sub_id=0x%04x 0x%04x\n",
m->header.mds_id, m->header.bytes,
m->header.chunk, m->header.chunks,
m->header.manufacturer, m->header.device,
m->header.sub_id_1, m->header.sub_id_2);
break;
case SND_UMP_MIXED_DATA_SET_STATUS_PAYLOAD:
printf("MDS Payload id=0x%x, ", m->payload.mds_id);
for (i = 0; i < 14; i++)
printf("%s%02x", i ? ":" : "",
snd_ump_get_byte(ump, i + 2));
printf("\n");
break;
default:
printf("Extended Data (status 0x%x)\n",
snd_ump_sysex_msg_status(ump));
break;
}
}
static void dump_ump_extended_data_event(const unsigned int *ump)
{
unsigned char status = snd_ump_sysex_msg_status(ump);
if (status < 4)
dump_ump_sysex8_event(ump);
else
dump_ump_mixed_data_event(ump);
}
static void print_ump_string(const unsigned int *ump, unsigned int fmt,
unsigned int offset, int maxlen)
{
static const char *fmtstr[4] = { "Single", "Start", "Cont", "End" };
unsigned char buf[32];
int i = 0;
do {
buf[i] = snd_ump_get_byte(ump, offset);
if (!buf[i])
break;
if (buf[i] < 0x20)
buf[i] = '.';
offset++;
} while (++i < maxlen);
buf[i] = 0;
printf("%6s: %s", fmtstr[fmt], buf);
}
static void dump_ump_stream_event(const unsigned int *ump)
{
const snd_ump_msg_stream_t *s = (const snd_ump_msg_stream_t *)ump;
printf(" "); /* stream message is groupless */
switch (s->gen.status) {
case SND_UMP_STREAM_MSG_STATUS_EP_DISCOVERY:
printf("EP Discovery ver=%d/%d, filter=0x%x\n",
(ump[0] >> 8) & 0xff, ump[0] & 0xff, ump[1] & 0xff);
break;
case SND_UMP_STREAM_MSG_STATUS_EP_INFO:
printf("EP Info ver=%d/%d, static=%d, fb#=%d, midi2=%d, midi1=%d, rxjr=%d, txjr=%d\n",
(ump[0] >> 8) & 0xff, ump[0] & 0xff, (ump[1] >> 31),
(ump[1] >> 24) & 0x7f,
(ump[1] >> 9) & 1, (ump[1] >> 8) & 1,
(ump[1] >> 1) & 1, ump[1] & 1);
break;
case SND_UMP_STREAM_MSG_STATUS_DEVICE_INFO:
printf("Device Info sysid=%02x:%02x:%02x, family=%02x:%02x, model=%02x:%02x, rev=%02x:%02x:%02x:%02x\n",
(ump[1] >> 16) & 0x7f, (ump[1] >> 8) & 0x7f, ump[1] & 0x7f,
(ump[2] >> 16) & 0x7f, (ump[2] >> 24) & 0x7f,
ump[2] & 0x7f, (ump[2] >> 8) & 0x7f,
(ump[3] >> 24) & 0x7f, (ump[3] >> 16) & 0x7f,
(ump[3] >> 8) & 0x7f, ump[3] & 0x7f);
break;
case SND_UMP_STREAM_MSG_STATUS_EP_NAME:
printf("EP Name ");
print_ump_string(ump, (ump[0] >> 26) & 3, 2, 14);
printf("\n");
break;
case SND_UMP_STREAM_MSG_STATUS_PRODUCT_ID:
printf("Product Id ");
print_ump_string(ump, (ump[0] >> 26) & 3, 2, 14);
printf("\n");
break;
case SND_UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST:
printf("Stream Cfg Req protocl=%d, rxjr=%d, txjr=%d\n",
(ump[0] >> 8) & 0xff, (ump[0] >> 1) & 1, ump[0] & 1);
break;
case SND_UMP_STREAM_MSG_STATUS_STREAM_CFG:
printf("Stream Cfg protocl=%d, rxjr=%d, txjr=%d\n",
(ump[0] >> 8) & 0xff, (ump[0] >> 1) & 1, ump[0] & 1);
break;
case SND_UMP_STREAM_MSG_STATUS_FB_DISCOVERY:
printf("FB Discovery fb#=%d, filter=0x%x\n",
(ump[0] >> 8) & 0xff, ump[0] & 0xff);
break;
case SND_UMP_STREAM_MSG_STATUS_FB_INFO:
printf("FB Info fb#=%d, active=%d, ui=%d, MIDI1=%d, dir=%d, group=%d-%d, MIDI-CI=%d, SysEx8=%d\n",
(ump[0] >> 8) & 0x7f, (ump[0] >> 15) & 1,
(ump[0] >> 4) & 3, (ump[0] >> 2) & 3, ump[0] & 3,
(ump[1] >> 24) & 0xff, (ump[1] >> 16) & 0xff,
(ump[1] >> 8) * 0xff, ump[1] & 0xff);
break;
case SND_UMP_STREAM_MSG_STATUS_FB_NAME:
printf("Product Id ");
printf("FB Name #%02d ", (ump[0] >> 8) & 0xff);
print_ump_string(ump, (ump[0] >> 26) & 3, 3, 13);
printf("\n");
break;
case SND_UMP_STREAM_MSG_STATUS_START_CLIP:
printf("Start Clip\n");
break;
case SND_UMP_STREAM_MSG_STATUS_END_CLIP:
printf("End Clip\n");
break;
default:
printf("UMP Stream event: status = %d, 0x%08x:0x%08x:0x%08x:0x%08x\n",
s->gen.status, ump[0], ump[1], ump[2], ump[3]);
break;
}
}
struct flexdata_text_prefix {
unsigned char status_bank;
unsigned char status;
const char *prefix;
};
static struct flexdata_text_prefix text_prefix[] = {
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_PROJECT_NAME,
.prefix = "Project" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_SONG_NAME,
.prefix = "Song" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_MIDI_CLIP_NAME,
.prefix = "MIDI Clip" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_COPYRIGHT_NOTICE,
.prefix = "Copyright" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_COMPOSER_NAME,
.prefix = "Composer" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_LYRICIST_NAME,
.prefix = "Lyricist" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_ARRANGER_NAME,
.prefix = "Arranger" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_PUBLISHER_NAME,
.prefix = "Publisher" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_PRIMARY_PERFORMER,
.prefix = "Performer" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_ACCOMPANY_PERFORMAER,
.prefix = "Accompany Performer" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RECORDING_DATE,
.prefix = "Recording Date" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_METADATA,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RECORDING_LOCATION,
.prefix = "Recording Location" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_LYRICS,
.prefix = "Lyrics" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_LYRICS_LANGUAGE,
.prefix = "Lyrics Language" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RUBY,
.prefix = "Ruby" },
{ .status_bank = SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT,
.status = SND_UMP_FLEX_DATA_MSG_STATUS_RUBY_LANGUAGE,
.prefix = "Ruby Language" },
{}
};
static const char *ump_meta_prefix(const snd_ump_msg_flex_data_t *fh)
{
static char buf[32];
int i;
for (i = 0; text_prefix[i].status_bank; i++) {
if (text_prefix[i].status_bank == fh->meta.status_bank &&
text_prefix[i].status == fh->meta.status)
return text_prefix[i].prefix;
}
sprintf(buf, "(%d:%d)", fh->meta.status_bank, fh->meta.status);
return buf;
}
static void dump_ump_flex_data_event(const unsigned int *ump)
{
const snd_ump_msg_flex_data_t *fh =
(const snd_ump_msg_flex_data_t *)ump;
printf("Group %2d, ", group_number(snd_ump_msg_group(ump)));
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_SETUP &&
fh->meta.status == SND_UMP_FLEX_DATA_MSG_STATUS_SET_TEMPO) {
printf("UMP Set Tempo value %d\n", fh->set_tempo.tempo);
return;
}
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_SETUP &&
fh->meta.status == SND_UMP_FLEX_DATA_MSG_STATUS_SET_TIME_SIGNATURE) {
printf("UMP Set Time Signature value %d / %d, num_notes %d\n",
fh->set_time_sig.numerator, fh->set_time_sig.denominator,
fh->set_time_sig.num_notes);
return;
}
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_SETUP &&
fh->meta.status == SND_UMP_FLEX_DATA_MSG_STATUS_SET_METRONOME) {
printf("UMP Set Metronome clock %d, bar %d/%d/%d, sub %d/%d\n",
fh->set_metronome.clocks_primary,
fh->set_metronome.bar_accent_1,
fh->set_metronome.bar_accent_2,
fh->set_metronome.bar_accent_3,
fh->set_metronome.subdivision_1,
fh->set_metronome.subdivision_2);
return;
}
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_SETUP &&
fh->meta.status == SND_UMP_FLEX_DATA_MSG_STATUS_SET_KEY_SIGNATURE) {
printf("UMP Set Key Signature sharps/flats %d, tonic %d\n",
fh->set_key_sig.sharps_flats,
fh->set_key_sig.tonic_note);
return;
}
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_SETUP &&
fh->meta.status == SND_UMP_FLEX_DATA_MSG_STATUS_SET_CHORD_NAME) {
printf("UMP Set Chord Name tonic %d %d %d, alt1 %d/%d, alt2 %d/%d, alt3 %d/%d, alt4 %d/%d, bass %d %d %d, alt1 %d/%d alt2 %d/%d\n",
fh->set_chord_name.tonic_sharp,
fh->set_chord_name.chord_tonic,
fh->set_chord_name.chord_type,
fh->set_chord_name.alter1_type,
fh->set_chord_name.alter1_degree,
fh->set_chord_name.alter2_type,
fh->set_chord_name.alter2_degree,
fh->set_chord_name.alter3_type,
fh->set_chord_name.alter3_degree,
fh->set_chord_name.alter4_type,
fh->set_chord_name.alter4_degree,
fh->set_chord_name.bass_sharp,
fh->set_chord_name.bass_note,
fh->set_chord_name.bass_type,
fh->set_chord_name.bass_alter1_type,
fh->set_chord_name.bass_alter1_type,
fh->set_chord_name.bass_alter2_degree,
fh->set_chord_name.bass_alter2_degree);
return;
}
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_METADATA ||
fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT) {
printf("Meta (%s) ", ump_meta_prefix(fh));
print_ump_string(ump, fh->meta.format, 4, 12);
printf("\n");
return;
}
printf("Flex Data: channel = %d, format = %d, addrs = %d, status_bank = %d, status = %d\n",
fh->meta.channel, fh->meta.format, fh->meta.addrs,
fh->meta.status_bank, fh->meta.status);
}
static void dump_ump_event(const snd_seq_ump_event_t *ev)
{
if (!snd_seq_ev_is_ump(ev)) {
@@ -605,12 +1019,30 @@ static void dump_ump_event(const snd_seq_ump_event_t *ev)
printf("%3d:%-3d ", ev->source.client, ev->source.port);
switch (snd_ump_msg_type(ev->ump)) {
case SND_UMP_MSG_TYPE_UTILITY:
dump_ump_utility_event(ev->ump);
break;
case SND_UMP_MSG_TYPE_SYSTEM:
dump_ump_system_event(ev->ump);
break;
case SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE:
dump_ump_midi1_event(ev->ump);
break;
case SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE:
dump_ump_midi2_event(ev->ump);
break;
case SND_UMP_MSG_TYPE_DATA:
dump_ump_sysex_event(ev->ump);
break;
case SND_UMP_MSG_TYPE_EXTENDED_DATA:
dump_ump_extended_data_event(ev->ump);
break;
case SND_UMP_MSG_TYPE_FLEX_DATA:
dump_ump_flex_data_event(ev->ump);
break;
case SND_UMP_MSG_TYPE_STREAM:
dump_ump_stream_event(ev->ump);
break;
default:
printf("UMP event: type = %d, group = %d, status = %d, 0x%08x\n",
snd_ump_msg_type(ev->ump),
@@ -620,7 +1052,6 @@ static void dump_ump_event(const snd_seq_ump_event_t *ev)
break;
}
}
#endif /* HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION */
static void list_ports(void)
{
@@ -663,10 +1094,8 @@ static void help(const char *argv0)
" -N,--normalized-view show normalized values\n"
" -P,--percent-view show percent values\n"
" -R,--raw-view show raw values (default)\n"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
" -u,--ump=version set client MIDI version (0=legacy, 1= UMP MIDI 1.0, 2=UMP MIDI2.0)\n"
" -r,--raw do not convert UMP and legacy events\n"
#endif
" -p,--port=client:port,... source port(s)\n",
argv0);
}
@@ -683,11 +1112,7 @@ static void sighandler(int sig ATTRIBUTE_UNUSED)
int main(int argc, char *argv[])
{
static const char short_options[] = "hVlp:NPR"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
"u:r"
#endif
;
static const char short_options[] = "hVlp:NPRu:r";
static const struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'V'},
@@ -696,10 +1121,8 @@ int main(int argc, char *argv[])
{"normalized-view", 0, NULL, 'N'},
{"percent-view", 0, NULL, 'P'},
{"raw-view", 0, NULL, 'R'},
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
{"ump", 1, NULL, 'u'},
{"raw", 0, NULL, 'r'},
#endif
{0}
};
@@ -734,15 +1157,15 @@ int main(int argc, char *argv[])
case 'N':
view_mode = VIEW_NORMALIZED;
break;
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
case 'u':
ump_version = atoi(optarg);
if (ump_version < 0 || ump_version > 2)
fatal("Invalid UMP version %d", ump_version);
snd_seq_set_client_midi_version(seq, ump_version);
break;
case 'r':
snd_seq_set_client_ump_conversion(seq, 0);
break;
#endif
default:
help(argv[0]);
return 1;
@@ -784,7 +1207,6 @@ int main(int argc, char *argv[])
break;
for (;;) {
snd_seq_event_t *event;
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
snd_seq_ump_event_t *ump_ev;
if (ump_version > 0) {
err = snd_seq_ump_event_input(seq, &ump_ev);
@@ -794,7 +1216,6 @@ int main(int argc, char *argv[])
dump_ump_event(ump_ev);
continue;
}
#endif
err = snd_seq_event_input(seq, &event);
if (err < 0)
+5
View File
@@ -0,0 +1,5 @@
AM_CPPFLAGS = -I$(top_srcdir)/include
EXTRA_DIST = aseqsend.1
bin_PROGRAMS = aseqsend
man_MANS = aseqsend.1
+77
View File
@@ -0,0 +1,77 @@
.TH ASEQSEND 1 "11 Mar 2024"
.SH NAME
aseqsend \- send arbitrary messages to selected ALSA MIDI seqencer port
.SH SYNOPSIS
\fBaseqsend\fP \-p client:port -s file-name
.br
\fBaseqsend\fP \-p client:port "hex encoded byte-string"
.SH DESCRIPTION
\fBaseqsend\fP is a command-line utility which allows one to send
SysEx (system exclusive) data to ALSA MIDI sequencer port.
It can also send any other MIDI commands.
Messages to be sent can be given in the last argument as hex encoded
byte string or can be read from raw binary file.
When sending several SysEx messages at once there is a delay of 1ms
after each message as default and can be set to different value with
option \-i.
A client can be specified by its number, its name, or a prefix of its
name. A port is specified by its number; for port 0 of a client, the
":0" part of the port specification can be omitted.
\fBaseqsend\fP can send UMP packets as MIDI 2.0 device by specifying
via \-u option as well, while the default operation is the legacy MIDI
1.0 byte stream.
.SH OPTIONS
.TP
\fI\-h, \-\-help\fP
Prints a list of options.
.TP
\fI\-V, \-\-version\fP
Prints the current version.
.TP
\fI\-l, \-\-list\FP
Prints a list of possible output ports.
.TP
\fI\-v, \-\-verbose\fP
Prints number of bytes actually sent
.TP
\fI\-p, -\-port=client:port\fP
Target port by number or name
.TP
\fI\-s, \-\-file=filename\fP
Send raw binary data from given file name
.TP
\fI\-i, \-\-interval=msec\fP
Interval between SysEx messages in milliseconds
.TP
\fI\-u, \-\-ump=version\fP
Specify the MIDI version. 0 for the legacy MIDI 1.0 (default),
1 for UMP MIDI 1.0 protocol and 2 for UMP MIDI 2.0 protocol.
When UMP MIDI 1.0 or MIDI 2.0 protocol is specified, \fBaseqsend\fP
reads the input as raw UMP packets, 4 each byte in big endian.
.SH EXAMPLES
\fBaseqsend -p 128:0 "F0 41 10 00 00 64 12 18 00 21 06 59 41 59 4E F7"\fP
\fBaseqsend -p 128:0 -s I7BulkDump.syx\fP
.SH SEE ALSO
\fBaseqdump(1)\fP
.SH AUTHOR
Miroslav Kovac <mixxoo@gmail.com>
+458
View File
@@ -0,0 +1,458 @@
/*
* aseqsend.c - send arbitrary MIDI messages to selected ALSA MIDI seqencer port
*
* Copyright (c) 2005 Clemens Ladisch <clemens@ladisch.de>
* Copyright (c) 2024 Miroslav Kovac <mixxoo@gmail.com>
*
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include "aconfig.h"
#include "version.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <alsa/asoundlib.h>
#include <alsa/ump_msg.h>
typedef unsigned char mbyte_t;
static snd_seq_t *seq;
static char *port_name = NULL;
static char *send_file_name = NULL;
static char *send_hex;
static mbyte_t *send_data;
static snd_seq_addr_t addr;
static int send_data_length;
static int sent_data_c;
static int ump_version;
static int sysex_interval = 1000; //us
static snd_midi_event_t *edev;
static void error(const char *format, ...)
{
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
putc('\n', stderr);
}
/* prints an error message to stderr, and dies */
static void fatal(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
static void usage(void)
{
printf(
"\nUsage: aseqsend -p target-port -s file-name|\"hex encoded bytes\"\n\n"
" -h,--help this help\n"
" -V,--version print current version\n"
" -v,--verbose verbose mode\n"
" -l,--list list all sequencer ports\n"
" -p,--port=c:p target port by number or name\n"
" -s,--file=name send binary data from given file name\n"
" -i,--interval=v interval between SysEx messages in miliseconds\n"
" -u,--ump=version MIDI version: 0=legacy (default), 1=MIDI1, 2=MIDI2\n\n");
}
static void version(void)
{
puts("aseqsend version " SND_UTIL_VERSION_STR);
}
static void *my_malloc(size_t size)
{
void *p = malloc(size);
if (!p) {
fatal("out of memory");
exit(EXIT_FAILURE);
}
return p;
}
static int hex_value(char c)
{
if ('0' <= c && c <= '9')
return c - '0';
if ('A' <= c && c <= 'F')
return c - 'A' + 10;
if ('a' <= c && c <= 'f')
return c - 'a' + 10;
error("invalid character %c", c);
return -1;
}
static void parse_data(void)
{
const char *p;
int i, value;
send_data = my_malloc(strlen(send_hex));
i = 0;
value = -1; /* value is >= 0 when the first hex digit of a byte has been read */
for (p = send_hex; *p; ++p) {
int digit;
if (isspace((unsigned char)*p)) {
if (value >= 0) {
send_data[i++] = value;
value = -1;
}
continue;
}
digit = hex_value(*p);
if (digit < 0) {
exit(EXIT_FAILURE);
}
if (value < 0) {
value = digit;
} else {
send_data[i++] = (value << 4) | digit;
value = -1;
}
}
if (value >= 0)
send_data[i++] = value;
send_data_length = i;
}
static void add_send_hex_data(const char *str)
{
int length;
char *s;
length = (send_hex ? strlen(send_hex) + 1 : 0) + strlen(str) + 1;
s = my_malloc(length);
if (send_hex) {
strcpy(s, send_hex);
strcat(s, " ");
} else {
s[0] = '\0';
}
strcat(s, str);
free(send_hex);
send_hex = s;
}
static void load_file(void)
{
int fd;
off_t length;
fd = open(send_file_name, O_RDONLY);
if (fd == -1) {
error("cannot open %s - %s", send_file_name, strerror(errno));
return;
}
length = lseek(fd, 0, SEEK_END);
if (length == (off_t)-1) {
error("cannot determine length of %s: %s", send_file_name, strerror(errno));
goto _error;
}
send_data = my_malloc(length);
lseek(fd, 0, SEEK_SET);
if (read(fd, send_data, length) != length) {
error("cannot read from %s: %s", send_file_name, strerror(errno));
goto _error;
}
if (length >= 4 && !memcmp(send_data, "MThd", 4)) {
error("%s is a Standard MIDI File; use aplaymidi to send it", send_file_name);
goto _error;
}
send_data_length = length;
goto _exit;
_error:
free(send_data);
send_data = NULL;
_exit:
close(fd);
}
/* error handling for ALSA functions */
static void check_snd(const char *operation, int err)
{
if (err < 0)
fatal("Cannot %s - %s", operation, snd_strerror(err));
}
static void init_seq(void)
{
int err;
/* open sequencer */
err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0);
check_snd("open sequencer", err);
/* set our client's name */
err = snd_seq_set_client_name(seq, "aseqsend");
check_snd("set client name", err);
err = snd_seq_set_client_midi_version(seq, ump_version);
check_snd("set client midi version", err);
}
static void create_port(void)
{
int err;
err = snd_seq_create_simple_port(seq, "aseqsend",
SND_SEQ_PORT_CAP_READ,
SND_SEQ_PORT_TYPE_MIDI_GENERIC |
SND_SEQ_PORT_TYPE_APPLICATION);
check_snd("create port", err);
}
static void init_midi_event_encoder(void)
{
int err;
err = snd_midi_event_new(256, &edev);
check_snd("create midi event encoder", err);
}
static void list_ports(void)
{
snd_seq_client_info_t *cinfo;
snd_seq_port_info_t *pinfo;
snd_seq_client_info_alloca(&cinfo);
snd_seq_port_info_alloca(&pinfo);
puts(" Port Client name Port name");
snd_seq_client_info_set_client(cinfo, -1);
while (snd_seq_query_next_client(seq, cinfo) >= 0) {
int client = snd_seq_client_info_get_client(cinfo);
snd_seq_port_info_set_client(pinfo, client);
snd_seq_port_info_set_port(pinfo, -1);
while (snd_seq_query_next_port(seq, pinfo) >= 0) {
if ((snd_seq_port_info_get_capability(pinfo)
& SND_SEQ_PORT_CAP_WRITE)
!= SND_SEQ_PORT_CAP_WRITE)
continue;
printf("%3d:%-3d %-32.32s %s\n",
snd_seq_port_info_get_client(pinfo),
snd_seq_port_info_get_port(pinfo),
snd_seq_client_info_get_name(cinfo),
snd_seq_port_info_get_name(pinfo));
}
}
}
/* compose a UMP event, submit it, return the next data position */
static int send_ump(int pos)
{
int ump_len = 0, offset = 0;
unsigned int ump[4];
snd_seq_ump_event_t ev;
snd_seq_ump_ev_clear(&ev);
snd_seq_ev_set_source(&ev, 0);
snd_seq_ev_set_dest(&ev, addr.client, addr.port);
snd_seq_ev_set_direct(&ev);
do {
const mbyte_t *data = send_data + pos;
if (pos >= send_data_length)
return pos;
ump[offset] = (data[0] << 24) | (data[1] << 16) |
(data[2] << 8) | data[3];
if (!offset)
ump_len = snd_ump_packet_length(snd_ump_msg_type(ump));
offset++;
pos += 4;
} while (offset < ump_len);
snd_seq_ev_set_ump_data(&ev, ump, ump_len * 4);
snd_seq_ump_event_output(seq, &ev);
snd_seq_drain_output(seq);
sent_data_c += ump_len * 4;
return pos;
}
/* compose an event, submit it, return the next data position */
static int send_midi_bytes(int pos)
{
const mbyte_t *data = send_data + pos;
snd_seq_event_t ev;
int is_sysex = 0;
int end;
snd_seq_ev_clear(&ev);
snd_seq_ev_set_source(&ev, 0);
snd_seq_ev_set_dest(&ev, addr.client, addr.port);
snd_seq_ev_set_direct(&ev);
if (send_data[pos] == 0xf0) {
is_sysex = 1;
for (end = pos + 1; end < send_data_length; end++) {
if (send_data[end] == 0xf7)
break;
}
if (end == send_data_length)
fatal("SysEx is missing terminating byte (0xF7)");
end++;
snd_seq_ev_set_sysex(&ev, end - pos, send_data + pos);
} else {
end = pos;
while (!snd_midi_event_encode_byte(edev, *data++, &ev)) {
if (++end >= send_data_length)
return end;
}
end++;
}
snd_seq_event_output(seq, &ev);
snd_seq_drain_output(seq);
if (is_sysex)
usleep(sysex_interval);
sent_data_c += end - pos;
return end;
}
int main(int argc, char *argv[])
{
static const struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'V'},
{"verbose", 0, NULL, 'v'},
{"list", 0, NULL, 'l'},
{"port", 1, NULL, 'p'},
{"file", 1, NULL, 's'},
{"interval", 1, NULL, 'i'},
{"ump", 1, NULL, 'u'},
{0}
};
int c = 0;
char do_send_file = 0;
char do_port_list = 0;
char verbose = 0;
int k;
while ((c = getopt_long(argc, argv, "hi:Vvlp:s:u:", long_options, NULL)) != -1) {
switch (c) {
case 'h':
usage();
return 0;
case 'V':
version();
return 0;
case 'v':
verbose = 1;
break;
case 'l':
do_port_list = 1;
break;
case 'p':
port_name = optarg;
break;
case 's':
send_file_name = optarg;
do_send_file = 1;
break;
case 'i':
sysex_interval = atoi(optarg) * 1000; //ms--->us
break;
case 'u':
ump_version = atoi(optarg);
break;
default:
error("Try 'aseqsend -h' for more information.");
exit(EXIT_FAILURE);
}
}
if (argc < 2) {
usage();
exit(EXIT_FAILURE);
}
if (do_port_list){
init_seq();
list_ports();
exit(EXIT_SUCCESS);
}
if (port_name == NULL)
fatal("Output port must be specified!");
if (do_send_file) {
load_file();
} else {
/* no file specified ---> send hex bytes from cmd arguments*/
/* data for send can be specified as multiple arguments */
for (; argv[optind]; ++optind) {
add_send_hex_data(argv[optind]);
}
if (send_hex)
parse_data();
}
if (!send_data)
exit(EXIT_SUCCESS);
if (ump_version && (send_data_length % 4) != 0)
fatal("UMP data must be aligned to 4 bytes");
init_seq();
create_port();
if (!ump_version)
init_midi_event_encoder();
if (snd_seq_parse_address(seq, &addr, port_name) < 0) {
error("Unable to parse port name!");
exit(EXIT_FAILURE);
}
sent_data_c = 0; //counter of actually sent bytes
k = 0;
while (k < send_data_length) {
if (ump_version)
k = send_ump(k);
else
k = send_midi_bytes(k);
}
if (verbose)
printf("Sent : %u bytes\n", sent_data_c);
snd_seq_close(seq);
exit(EXIT_SUCCESS);
}
+1 -5
View File
@@ -10,11 +10,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alsa/global.h>
#include <alsa/input.h>
#include <alsa/output.h>
#include <alsa/conf.h>
#include <alsa/error.h>
#include <alsa/asoundlib.h>
#include "dmic-nhlt.h"
#include "dmic/dmic-process.h"
+1 -1
View File
@@ -8,7 +8,7 @@
#include "aconfig.h"
#include <stdio.h>
#include <stdint.h>
#include <alsa/global.h>
#include <alsa/asoundlib.h>
#include "dmic-debug.h"
#ifdef NHLT_DEBUG
+62 -38
View File
@@ -11,10 +11,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alsa/input.h>
#include <alsa/output.h>
#include <alsa/conf.h>
#include <alsa/error.h>
#include <alsa/asoundlib.h>
#include "../intel-nhlt.h"
#include "dmic-process.h"
#include "dmic-internal.h"
@@ -159,28 +156,33 @@ static void find_modes(struct intel_dmic_params *dmic, struct dmic_calc_decim_mo
/* Check for sane pdm clock, min 100 kHz, max ioclk/2 */
if (dmic->dmic_prm[di].pdmclk_max < DMIC_HW_PDM_CLK_MIN ||
dmic->dmic_prm[di].pdmclk_max > dmic->dmic_prm[di].io_clk / 2) {
fprintf(stderr, "find_modes(): pdm clock max not in range\n");
fprintf(stderr, "%s: pdm clock max %d not in range\n", __func__,
dmic->dmic_prm[di].pdmclk_max);
return;
}
if (dmic->dmic_prm[di].pdmclk_min < DMIC_HW_PDM_CLK_MIN ||
dmic->dmic_prm[di].pdmclk_min > dmic->dmic_prm[di].pdmclk_max) {
fprintf(stderr, "find_modes(): pdm clock min not in range\n");
fprintf(stderr, "%s: pdm clock min %d not in range\n", __func__,
dmic->dmic_prm[di].pdmclk_min);
return;
}
/* Check for sane duty cycle */
if (dmic->dmic_prm[di].duty_min > dmic->dmic_prm[di].duty_max) {
fprintf(stderr, "find_modes(): duty cycle min > max\n");
fprintf(stderr, "%s: duty cycle min > max: %d > %d\n", __func__,
dmic->dmic_prm[di].duty_min, dmic->dmic_prm[di].duty_max);
return;
}
if (dmic->dmic_prm[di].duty_min < DMIC_HW_DUTY_MIN ||
dmic->dmic_prm[di].duty_min > DMIC_HW_DUTY_MAX) {
fprintf(stderr, "find_modes(): pdm clock min not in range\n");
fprintf(stderr, "%s: pdm clock min %d not in range\n", __func__,
dmic->dmic_prm[di].duty_min);
return;
}
if (dmic->dmic_prm[di].duty_max < DMIC_HW_DUTY_MIN ||
dmic->dmic_prm[di].duty_max > DMIC_HW_DUTY_MAX) {
fprintf(stderr, "find_modes(): pdm clock max not in range\n");
fprintf(stderr, "%s: pdm clock max %d not in range\n", __func__,
dmic->dmic_prm[di].duty_max);
return;
}
@@ -428,7 +430,7 @@ static int select_mode(struct intel_dmic_params *dmic, struct dmic_calc_configur
* candidates should be sufficient.
*/
if (modes->num_of_modes == 0) {
fprintf(stderr, "select_mode(): no modes available\n");
fprintf(stderr, "%s: no modes available\n", __func__);
return -EINVAL;
}
@@ -451,7 +453,7 @@ static int select_mode(struct intel_dmic_params *dmic, struct dmic_calc_configur
}
if (!found) {
fprintf(stderr, "select_mode(): No filter for decimation found\n");
fprintf(stderr, "%s: No filter for decimation found\n", __func__);
return -EINVAL;
}
n = idx[found - 1]; /* Option with highest clock divisor and lowest mic clock rate */
@@ -468,8 +470,8 @@ static int select_mode(struct intel_dmic_params *dmic, struct dmic_calc_configur
if (cfg->mfir_a > 0) {
cfg->fir_a = get_fir(dmic, cfg, cfg->mfir_a);
if (!cfg->fir_a) {
fprintf(stderr, "select_mode(): can't find FIR coefficients, mfir_a = %d\n",
cfg->mfir_a);
fprintf(stderr, "%s: can't find FIR coefficients, mfir_a = %d\n",
__func__, cfg->mfir_a);
return -EINVAL;
}
}
@@ -477,8 +479,8 @@ static int select_mode(struct intel_dmic_params *dmic, struct dmic_calc_configur
if (cfg->mfir_b > 0) {
cfg->fir_b = get_fir(dmic, cfg, cfg->mfir_b);
if (!cfg->fir_b) {
fprintf(stderr, "select_mode(): can't find FIR coefficients, mfir_b = %d\n",
cfg->mfir_b);
fprintf(stderr, "%s: can't find FIR coefficients, mfir_b = %d\n",
__func__, cfg->mfir_b);
return -EINVAL;
}
}
@@ -490,7 +492,7 @@ static int select_mode(struct intel_dmic_params *dmic, struct dmic_calc_configur
g_cic = mcic * mcic * mcic * mcic * mcic;
if (g_cic < 0) {
/* Erroneous decimation factor and CIC gain */
fprintf(stderr, "select_mode(): erroneous decimation factor and CIC gain\n");
fprintf(stderr, "%s: erroneous decimation factor and CIC gain\n", __func__);
return -EINVAL;
}
@@ -515,7 +517,7 @@ static int select_mode(struct intel_dmic_params *dmic, struct dmic_calc_configur
cfg->fir_a->length, gain_to_fir);
if (ret < 0) {
/* Invalid coefficient set found, should not happen. */
fprintf(stderr, "select_mode(): invalid coefficient set found\n");
fprintf(stderr, "%s: invalid coefficient set found\n", __func__);
return -EINVAL;
}
} else {
@@ -531,7 +533,7 @@ static int select_mode(struct intel_dmic_params *dmic, struct dmic_calc_configur
cfg->fir_b->length, gain_to_fir);
if (ret < 0) {
/* Invalid coefficient set found, should not happen. */
fprintf(stderr, "select_mode(): invalid coefficient set found\n");
fprintf(stderr, "%s: invalid coefficient set found\n", __func__);
return -EINVAL;
}
} else {
@@ -608,13 +610,24 @@ static void ipm_helper2(struct intel_dmic_params *dmic, int source[], int *ipm)
* operate as stereo or mono left (A) or mono right (B) mode. Mono right mode is setup as channel
* swapped mono left.
*/
static int stereo_helper(struct intel_dmic_params *dmic, int stereo[], int swap[])
static int stereo_helper(struct intel_dmic_params *dmic, int stereo[], int swap[], int ipmsm[])
{
int cnt;
int i;
int i, j;
int swap_check;
int ret = 0;
/* Find FIFOs those need stereo mode, ipmsm 0 is mono, 1 is stereo */
for (i = 0; i < DMIC_HW_FIFOS; i++) {
ipmsm[i] = 0;
for (j = 0; j < DMIC_HW_CONTROLLERS; j++) {
if (dmic->dmic_prm[i].pdm[j].enable_mic_a && dmic->dmic_prm[i].pdm[j].enable_mic_b) {
ipmsm[i] = 1;
break;
}
}
}
for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
cnt = 0;
if (dmic->dmic_prm[0].pdm[i].enable_mic_a ||
@@ -647,6 +660,7 @@ static int stereo_helper(struct intel_dmic_params *dmic, int stereo[], int swap[
static int configure_registers(struct intel_dmic_params *dmic, struct dmic_calc_configuration *cfg)
{
int ipmsm[DMIC_HW_FIFOS];
int stereo[DMIC_HW_CONTROLLERS];
int swap[DMIC_HW_CONTROLLERS];
uint32_t val = 0;
@@ -757,6 +771,12 @@ static int configure_registers(struct intel_dmic_params *dmic, struct dmic_calc_
of0 = (dmic->dmic_prm[0].fifo_bits == 32) ? 2 : 0;
of1 = (dmic->dmic_prm[1].fifo_bits == 32) ? 2 : 0;
ret = stereo_helper(dmic, stereo, swap, ipmsm);
if (ret < 0) {
fprintf(stderr, "%s: Microphones enable conflict for DMIC0 and DMIC1 FIFOs\n", __func__);
return ret;
}
if (dmic->dmic_prm[di].driver_version == 1) {
if (di == 0) {
ipm_helper1(dmic, &ipm);
@@ -781,7 +801,10 @@ static int configure_registers(struct intel_dmic_params *dmic, struct dmic_calc_
}
}
if (dmic->dmic_prm[di].driver_version == 2 || dmic->dmic_prm[di].driver_version == 3) {
if (dmic->dmic_prm[di].driver_version >= 2) {
if (dmic->dmic_prm[di].driver_version >= 4)
bfth = 0;
if (di == 0) {
ipm_helper2(dmic, source, &ipm);
val = OUTCONTROL0_TIE(0) |
@@ -795,7 +818,7 @@ static int configure_registers(struct intel_dmic_params *dmic, struct dmic_calc_
OUTCONTROL0_IPM_SOURCE_2(source[1]) |
OUTCONTROL0_IPM_SOURCE_3(source[2]) |
OUTCONTROL0_IPM_SOURCE_4(source[3]) |
OUTCONTROL0_IPM_SOURCE_MODE(1) |
OUTCONTROL0_IPM_SOURCE_MODE(ipmsm[0]) |
OUTCONTROL0_TH(th);
} else {
ipm_helper2(dmic, source, &ipm);
@@ -810,19 +833,13 @@ static int configure_registers(struct intel_dmic_params *dmic, struct dmic_calc_
OUTCONTROL1_IPM_SOURCE_2(source[1]) |
OUTCONTROL1_IPM_SOURCE_3(source[2]) |
OUTCONTROL1_IPM_SOURCE_4(source[3]) |
OUTCONTROL1_IPM_SOURCE_MODE(1) |
OUTCONTROL1_IPM_SOURCE_MODE(ipmsm[1]) |
OUTCONTROL1_TH(th);
}
}
dmic->dmic_blob.chan_ctrl_cfg[di] = val;
ret = stereo_helper(dmic, stereo, swap);
if (ret < 0) {
fprintf(stderr, "configure_registers(): enable conflict\n");
return ret;
}
for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
/* CIC */
val = CIC_CONTROL_SOFT_RESET(soft_reset) |
@@ -975,13 +992,14 @@ int dmic_calculate(struct intel_nhlt_params *nhlt)
di = dmic->dmic_dai_index;
if (di >= DMIC_HW_FIFOS) {
fprintf(stderr, "dmic_set_config(): dai->index exceeds number of FIFOs\n");
fprintf(stderr, "%s: dai->index %d exceeds number of FIFOs\n", __func__, di);
ret = -EINVAL;
goto out;
}
if (dmic->dmic_prm[di].num_pdm_active > DMIC_HW_CONTROLLERS) {
fprintf(stderr, "dmic_set_config():controller count exceeds platform capability\n");
fprintf(stderr, "%s: controller count %d exceeds platform capability\n",
__func__, dmic->dmic_prm[di].num_pdm_active);
ret = -EINVAL;
goto out;
}
@@ -993,7 +1011,8 @@ int dmic_calculate(struct intel_nhlt_params *nhlt)
case 32:
break;
default:
fprintf(stderr, "dmic_set_config(): fifo_bits EINVAL\n");
fprintf(stderr, "%s: Bad fifo_bits %d\n", __func__,
dmic->dmic_prm[di].fifo_bits);
ret = -EINVAL;
goto out;
}
@@ -1005,14 +1024,14 @@ int dmic_calculate(struct intel_nhlt_params *nhlt)
*/
find_modes(dmic, &modes_a, dmic->dmic_prm[0].fifo_fs);
if (modes_a.num_of_modes == 0 && dmic->dmic_prm[0].fifo_fs > 0) {
fprintf(stderr, "dmic_set_config(): No modes found for FIFO A\n");
fprintf(stderr, "%s: No modes found for FIFO A\n", __func__);
ret = -EINVAL;
goto out;
}
find_modes(dmic, &modes_b, dmic->dmic_prm[1].fifo_fs);
if (modes_b.num_of_modes == 0 && dmic->dmic_prm[1].fifo_fs > 0) {
fprintf(stderr, "dmic_set_config(): No modes found for FIFO B\n");
fprintf(stderr, "%s: No modes found for FIFO B\n", __func__);
ret = -EINVAL;
goto out;
}
@@ -1020,7 +1039,7 @@ int dmic_calculate(struct intel_nhlt_params *nhlt)
match_modes(&modes_ab, &modes_a, &modes_b);
ret = select_mode(dmic, &cfg, &modes_ab);
if (ret < 0) {
fprintf(stderr, "dmic_set_config(): select_mode() failed\n");
fprintf(stderr, "%s: select_mode() failed %d\n", __func__, ret);
ret = -EINVAL;
goto out;
}
@@ -1030,7 +1049,7 @@ int dmic_calculate(struct intel_nhlt_params *nhlt)
*/
ret = configure_registers(dmic, &cfg);
if (ret < 0) {
fprintf(stderr, "dmic_set_config(): cannot configure registers\n");
fprintf(stderr, "%s: cannot configure registers %d\n", __func__, ret);
ret = -EINVAL;
goto out;
}
@@ -1232,7 +1251,12 @@ int dmic_set_params(struct intel_nhlt_params *nhlt, int dai_index, int driver_ve
return -EINVAL;
if (dai_index >= DMIC_HW_FIFOS) {
fprintf(stderr, "set_dmic_data illegal dai index\n");
fprintf(stderr, "%s: illegal dai index %d \n", __func__, dai_index);
return -EINVAL;
}
if (driver_version < 1 || driver_version > 5) {
fprintf(stderr, "%s: illegal driver version %d\n", __func__, driver_version);
return -EINVAL;
}
@@ -1261,7 +1285,7 @@ int dmic_set_pdm_params(struct intel_nhlt_params *nhlt, int pdm_index, int enabl
return -EINVAL;
if (pdm_index >= DMIC_HW_CONTROLLERS) {
fprintf(stderr, "set_pdm_data illegal pdm_index\n");
fprintf(stderr, "%s: illegal pdm_index %d\n", __func__, pdm_index);
return -EINVAL;
}
+1 -4
View File
@@ -12,10 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alsa/input.h>
#include <alsa/output.h>
#include <alsa/conf.h>
#include <alsa/error.h>
#include <alsa/asoundlib.h>
#define MIN(a, b) ({ \
typeof(a) __a = (a); \
+1 -4
View File
@@ -10,10 +10,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alsa/input.h>
#include <alsa/output.h>
#include <alsa/conf.h>
#include <alsa/error.h>
#include <alsa/asoundlib.h>
#include "intel-nhlt.h"
#include "ssp-nhlt.h"
#include "ssp/ssp-process.h"
+104 -55
View File
@@ -7,20 +7,110 @@
#include "aconfig.h"
#include <stdio.h>
#include <stdint.h>
#include <alsa/global.h>
#include <alsa/asoundlib.h>
#include "ssp-debug.h"
#include "../intel-nhlt.h"
#ifdef NHLT_DEBUG
void ssp_print_calculated(struct intel_ssp_params *ssp)
static void ssp_print_blob10(struct intel_ssp_params *ssp, int i)
{
struct ssp_intel_config_data *blob;
int ssp_index = ssp->ssp_count;
int j;
blob = &ssp->ssp_blob[ssp_index][i];
fprintf(stdout, "gateway_attributes %u\n", blob->gateway_attributes);
for (j = 0; j < SSP_TS_GROUP_SIZE; j++)
fprintf(stdout, "ts_group[%d] 0x%08x\n", j, blob->ts_group[j]);
fprintf(stdout, "ssc0 0x%08x\n", blob->ssc0);
fprintf(stdout, "ssc1 0x%08x\n", blob->ssc1);
fprintf(stdout, "sscto 0x%08x\n", blob->sscto);
fprintf(stdout, "sspsp 0x%08x\n", blob->sspsp);
fprintf(stdout, "sstsa 0x%08x\n", blob->sstsa);
fprintf(stdout, "ssrsa 0x%08x\n", blob->ssrsa);
fprintf(stdout, "ssc2 0x%08x\n", blob->ssc2);
fprintf(stdout, "sspsp2 0x%08x\n", blob->sspsp2);
fprintf(stdout, "ssc3 0x%08x\n", blob->ssc3);
fprintf(stdout, "ssioc 0x%08x\n", blob->ssioc);
fprintf(stdout, "mdivc 0x%08x\n", blob->mdivc);
fprintf(stdout, "mdivr 0x%08x\n", blob->mdivr);
}
static void ssp_print_blob15(struct intel_ssp_params *ssp, int i)
{
struct ssp_intel_config_data_1_5 *blob15;
int ssp_index = ssp->ssp_count;
uint32_t j;
blob15 = &ssp->ssp_blob_1_5[ssp_index][i];
fprintf(stdout, "gateway_attributes %u\n", blob15->gateway_attributes);
fprintf(stdout, "version %u\n", blob15->version);
fprintf(stdout, "size %u\n", blob15->size);
for (j = 0; j < SSP_TS_GROUP_SIZE; j++)
fprintf(stdout, "ts_group[%d] 0x%08x\n", j, blob15->ts_group[j]);
fprintf(stdout, "ssc0 0x%08x\n", blob15->ssc0);
fprintf(stdout, "ssc1 0x%08x\n", blob15->ssc1);
fprintf(stdout, "sscto 0x%08x\n", blob15->sscto);
fprintf(stdout, "sspsp 0x%08x\n", blob15->sspsp);
fprintf(stdout, "sstsa 0x%08x\n", blob15->sstsa);
fprintf(stdout, "ssrsa 0x%08x\n", blob15->ssrsa);
fprintf(stdout, "ssc2 0x%08x\n", blob15->ssc2);
fprintf(stdout, "sspsp2 0x%08x\n", blob15->sspsp2);
fprintf(stdout, "ssc3 0x%08x\n", blob15->ssc3);
fprintf(stdout, "ssioc 0x%08x\n", blob15->ssioc);
fprintf(stdout, "mdivc 0x%08x\n", blob15->mdivctlr);
fprintf(stdout, "mdivr count 0x%08x\n", blob15->mdivrcnt);
for (j = 0; j < blob15->mdivrcnt; j++)
fprintf(stdout, "mdivr 0x%08x\n",
ssp->ssp_prm[ssp_index].mdivr[i].mdivrs[j]);
}
static void ssp_print_blob30(struct intel_ssp_params *ssp, int i)
{
struct ssp_intel_config_data_3_0 *blob30;
int ssp_index = ssp->ssp_count;
uint32_t j;
blob30 = &ssp->ssp_blob_3_0[ssp_index][i];
fprintf(stdout, "gateway_attributes %u\n", blob30->gateway_attributes);
fprintf(stdout, "version %u\n", blob30->version);
fprintf(stdout, "size %u\n", blob30->size);
for (j = 0; j < SSP_TS_GROUP_SIZE; j++)
fprintf(stdout, "ts_group[%d] 0x%08x\n", j, blob30->ts_group[j]);
fprintf(stdout, "ssc0 0x%08x\n", blob30->ssc0);
fprintf(stdout, "ssc1 0x%08x\n", blob30->ssc1);
fprintf(stdout, "sscto 0x%08x\n", blob30->sscto);
fprintf(stdout, "sspsp 0x%08x\n", blob30->sspsp);
fprintf(stdout, "ssc2 0x%08x\n", blob30->ssc2);
fprintf(stdout, "sspsp2 0x%08x\n", blob30->sspsp2);
fprintf(stdout, "rsvd2 0x%08x\n", blob30->rsvd2);
fprintf(stdout, "ssioc 0x%08x\n", blob30->ssioc);
for (j = 0; j < I2SIPCMC; j++) {
fprintf(stdout, "rx_dir[%d] 0x%08x\n", j * 2, (uint32_t)(blob30->rx_dir[j].ssmidytsa & 0xffffffff));
fprintf(stdout, "rx_dir[%d] 0x%08x\n", j * 2 + 1, (uint32_t)(blob30->rx_dir[j].ssmidytsa >> 32));
}
for (j = 0; j < I2SOPCMC; j++) {
fprintf(stdout, "tx_dir[%d] 0x%08x\n", j * 2, (uint32_t)(blob30->tx_dir[j].ssmodytsa & 0xffffffff));
fprintf(stdout, "tx_dir[%d] 0x%08x\n", j * 2 + 1, (uint32_t)(blob30->tx_dir[j].ssmodytsa >> 32));
}
fprintf(stdout, "mdivc 0x%08x\n", blob30->mdivctlr);
fprintf(stdout, "mdivr count 0x%08x\n", blob30->mdivrcnt);
for (j = 0; j < blob30->mdivrcnt; j++)
fprintf(stdout, "mdivr 0x%08x\n",
ssp->ssp_prm[ssp_index].mdivr[i].mdivrs[j]);
}
void ssp_print_calculated(struct intel_ssp_params *ssp)
{
struct ssp_aux_blob *blob_aux;
int ssp_index = ssp->ssp_count;
uint32_t *ptr;
int i, j;
uint32_t i, j;
fprintf(stdout, "printing ssp nhlt calculated data:\n");
@@ -38,58 +128,17 @@ void ssp_print_calculated(struct intel_ssp_params *ssp)
for (i = 0; i < ssp->ssp_hw_config_count[ssp_index]; i++) {
fprintf(stdout, "ssp blob %d hw_config %d\n", ssp->ssp_count, i);
if (ssp->ssp_prm[ssp_index].version == SSP_BLOB_VER_1_5) {
blob15 = &ssp->ssp_blob_1_5[ssp_index][i];
fprintf(stdout, "gateway_attributes %u\n", blob15->gateway_attributes);
fprintf(stdout, "version %u\n", blob15->version);
fprintf(stdout, "size %u\n", blob15->size);
fprintf(stdout, "ts_group[0] 0x%08x\n", blob15->ts_group[0]);
fprintf(stdout, "ts_group[1] 0x%08x\n", blob15->ts_group[1]);
fprintf(stdout, "ts_group[2] 0x%08x\n", blob15->ts_group[2]);
fprintf(stdout, "ts_group[3] 0x%08x\n", blob15->ts_group[3]);
fprintf(stdout, "ts_group[4] 0x%08x\n", blob15->ts_group[4]);
fprintf(stdout, "ts_group[5] 0x%08x\n", blob15->ts_group[5]);
fprintf(stdout, "ts_group[6] 0x%08x\n", blob15->ts_group[6]);
fprintf(stdout, "ts_group[7] 0x%08x\n", blob15->ts_group[7]);
fprintf(stdout, "ssc0 0x%08x\n", blob15->ssc0);
fprintf(stdout, "ssc1 0x%08x\n", blob15->ssc1);
fprintf(stdout, "sscto 0x%08x\n", blob15->sscto);
fprintf(stdout, "sspsp 0x%08x\n", blob15->sspsp);
fprintf(stdout, "sstsa 0x%08x\n", blob15->sstsa);
fprintf(stdout, "ssrsa 0x%08x\n", blob15->ssrsa);
fprintf(stdout, "ssc2 0x%08x\n", blob15->ssc2);
fprintf(stdout, "sspsp2 0x%08x\n", blob15->sspsp2);
fprintf(stdout, "ssc3 0x%08x\n", blob15->ssc3);
fprintf(stdout, "ssioc 0x%08x\n", blob15->ssioc);
fprintf(stdout, "mdivc 0x%08x\n", blob15->mdivctlr);
fprintf(stdout, "mdivr count 0x%08x\n", blob15->mdivrcnt);
for (j = 0; j < blob15->mdivrcnt; j++)
fprintf(stdout, "mdivr 0x%08x\n",
ssp->ssp_prm[ssp_index].mdivr[i].mdivrs[j]);
} else {
blob = &ssp->ssp_blob[ssp_index][i];
fprintf(stdout, "gateway_attributes %u\n", blob->gateway_attributes);
fprintf(stdout, "ts_group[0] 0x%08x\n", blob->ts_group[0]);
fprintf(stdout, "ts_group[1] 0x%08x\n", blob->ts_group[1]);
fprintf(stdout, "ts_group[2] 0x%08x\n", blob->ts_group[2]);
fprintf(stdout, "ts_group[3] 0x%08x\n", blob->ts_group[3]);
fprintf(stdout, "ts_group[4] 0x%08x\n", blob->ts_group[4]);
fprintf(stdout, "ts_group[5] 0x%08x\n", blob->ts_group[5]);
fprintf(stdout, "ts_group[6] 0x%08x\n", blob->ts_group[6]);
fprintf(stdout, "ts_group[7] 0x%08x\n", blob->ts_group[7]);
fprintf(stdout, "ssc0 0x%08x\n", blob->ssc0);
fprintf(stdout, "ssc1 0x%08x\n", blob->ssc1);
fprintf(stdout, "sscto 0x%08x\n", blob->sscto);
fprintf(stdout, "sspsp 0x%08x\n", blob->sspsp);
fprintf(stdout, "sstsa 0x%08x\n", blob->sstsa);
fprintf(stdout, "ssrsa 0x%08x\n", blob->ssrsa);
fprintf(stdout, "ssc2 0x%08x\n", blob->ssc2);
fprintf(stdout, "sspsp2 0x%08x\n", blob->sspsp2);
fprintf(stdout, "ssc3 0x%08x\n", blob->ssc3);
fprintf(stdout, "ssioc 0x%08x\n", blob->ssioc);
fprintf(stdout, "mdivc 0x%08x\n", blob->mdivc);
fprintf(stdout, "mdivr 0x%08x\n", blob->mdivr);
switch (ssp->ssp_prm[ssp_index].version) {
case SSP_BLOB_VER_1_5:
ssp_print_blob15(ssp, i);
break;
case SSP_BLOB_VER_3_0:
ssp_print_blob30(ssp, i);
break;
default:
ssp_print_blob10(ssp, i);
}
blob_aux = (struct ssp_aux_blob *)&(ssp->ssp_blob_ext[ssp_index][i]);
fprintf(stdout, "aux_blob size %u\n", blob_aux->size);
for (j = 0; j < blob_aux->size; j += 4) {
@@ -115,7 +164,7 @@ void ssp_print_internal(struct intel_ssp_params *ssp)
struct ssp_config_dai *dai;
struct ssp_config_hw *hw_conf;
uint32_t enabled;
int i, j;
uint32_t i, j;
dai = &ssp->ssp_prm[ssp->ssp_count];
+36 -2
View File
@@ -12,10 +12,12 @@
#include <stdint.h>
#define SSP_TS_GROUP_SIZE 8
/* struct for intel ssp nhlt vendor specific blob generation */
struct ssp_intel_config_data {
uint32_t gateway_attributes;
uint32_t ts_group[8];
uint32_t ts_group[SSP_TS_GROUP_SIZE];
uint32_t ssc0;
uint32_t ssc1;
uint32_t sscto;
@@ -31,12 +33,13 @@ struct ssp_intel_config_data {
} __attribute__((packed));
#define SSP_BLOB_VER_1_5 0xEE000105
#define SSP_BLOB_VER_3_0 0xEE000300
struct ssp_intel_config_data_1_5 {
uint32_t gateway_attributes;
uint32_t version;
uint32_t size;
uint32_t ts_group[8];
uint32_t ts_group[SSP_TS_GROUP_SIZE];
uint32_t ssc0;
uint32_t ssc1;
uint32_t sscto;
@@ -52,6 +55,37 @@ struct ssp_intel_config_data_1_5 {
uint32_t mdivr[];
} __attribute__((packed));
#define I2SIPCMC 8
#define I2SOPCMC 8
struct ssp_rx_dir {
uint64_t ssmidytsa;
} __attribute__((packed));
struct ssp_tx_dir {
uint64_t ssmodytsa;
} __attribute__((packed));
struct ssp_intel_config_data_3_0 {
uint32_t gateway_attributes;
uint32_t version;
uint32_t size;
uint32_t ts_group[SSP_TS_GROUP_SIZE];
uint32_t ssc0;
uint32_t ssc1;
uint32_t sscto;
uint32_t sspsp;
uint32_t ssc2;
uint32_t sspsp2;
uint32_t rsvd2;
uint32_t ssioc;
struct ssp_rx_dir rx_dir[I2SIPCMC];
struct ssp_tx_dir tx_dir[I2SOPCMC];
uint32_t mdivctlr;
uint32_t mdivrcnt;
uint32_t mdivr[];
} __attribute__((packed));
struct ssp_intel_aux_tlv {
uint32_t type;
uint32_t size;
+1
View File
@@ -147,6 +147,7 @@ struct intel_ssp_params {
/* ssp vendor blob structs */
struct ssp_intel_config_data ssp_blob[SSP_MAX_DAIS][SSP_MAX_HW_CONFIG];
struct ssp_intel_config_data_1_5 ssp_blob_1_5[SSP_MAX_DAIS][SSP_MAX_HW_CONFIG];
struct ssp_intel_config_data_3_0 ssp_blob_3_0[SSP_MAX_DAIS][SSP_MAX_HW_CONFIG];
struct ssp_aux_blob ssp_blob_ext[SSP_MAX_DAIS][SSP_MAX_HW_CONFIG];
};
+93 -15
View File
@@ -14,11 +14,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <alsa/global.h>
#include <alsa/input.h>
#include <alsa/output.h>
#include <alsa/conf.h>
#include <alsa/error.h>
#include <alsa/asoundlib.h>
#include "../intel-nhlt.h"
#include "../../nhlt.h"
#include "ssp-process.h"
@@ -38,10 +34,51 @@ static int popcount(uint32_t value)
return bits_set;
}
static void ssp_calculate_intern_v30(struct intel_nhlt_params *nhlt, int hwi)
{
struct intel_ssp_params *ssp = (struct intel_ssp_params *)nhlt->ssp_params;
int di = ssp->ssp_count;
struct ssp_intel_config_data_3_0 *blob30 = &ssp->ssp_blob_3_0[di][hwi];
struct ssp_intel_config_data *blob = &ssp->ssp_blob[di][hwi];
int i;
blob30->gateway_attributes = blob->gateway_attributes;
blob30->version = SSP_BLOB_VER_3_0;
for (i = 0; i < SSP_TS_GROUP_SIZE; i++)
blob30->ts_group[i] = blob->ts_group[i];
blob30->ssc0 = blob->ssc0;
blob30->ssc1 = blob->ssc1;
blob30->sscto = blob->sscto;
blob30->sspsp = blob->sspsp;
blob30->ssc2 = blob->ssc2;
blob30->sspsp2 = blob->sspsp2;
blob30->rsvd2 = 0;
blob30->ssioc = blob->ssioc;
blob30->rx_dir[0].ssmidytsa = ssp->ssp_prm[di].hw_cfg[hwi].rx_slots;
for (i = 1; i < I2SIPCMC; i++)
blob30->rx_dir[i].ssmidytsa = 0;
blob30->tx_dir[0].ssmodytsa = ssp->ssp_prm[di].hw_cfg[hwi].tx_slots;
for (i = 1; i < I2SOPCMC; i++)
blob30->tx_dir[i].ssmodytsa = 0;
/* mdivr count is always 1 */
blob30->mdivctlr = blob->mdivc;
ssp->ssp_prm[di].mdivr[hwi].count = 1;
blob30->mdivrcnt = ssp->ssp_prm[di].mdivr[hwi].count;
ssp->ssp_prm[di].mdivr[hwi].mdivrs[0] = blob->mdivr;
blob30->size = sizeof(struct ssp_intel_config_data_3_0) +
blob30->mdivrcnt * sizeof(uint32_t) +
ssp->ssp_blob_ext[di][hwi].size;
}
static void ssp_calculate_intern_v15(struct intel_nhlt_params *nhlt, int hwi)
{
struct intel_ssp_params *ssp = (struct intel_ssp_params *)nhlt->ssp_params;
int di = ssp->ssp_count;;
int di = ssp->ssp_count;
struct ssp_intel_config_data_1_5 *blob15 = &ssp->ssp_blob_1_5[di][hwi];
struct ssp_intel_config_data *blob = &ssp->ssp_blob[di][hwi];
int i;
@@ -49,7 +86,7 @@ static void ssp_calculate_intern_v15(struct intel_nhlt_params *nhlt, int hwi)
blob15->gateway_attributes = ssp->ssp_blob[di][hwi].gateway_attributes;
blob15->version = SSP_BLOB_VER_1_5;
for (i = 0; i < 8; i++)
for (i = 0; i < SSP_TS_GROUP_SIZE; i++)
blob15->ts_group[i] = blob->ts_group[i];
blob15->ssc0 = blob->ssc0;
@@ -115,11 +152,17 @@ static int ssp_calculate_intern(struct intel_nhlt_params *nhlt, int hwi)
/* reset SSP settings */
/* sscr0 dynamic settings are DSS, EDSS, SCR, FRDC, ECS */
ssp->ssp_blob[di][hwi].ssc0 = SSCR0_PSP | SSCR0_RIM | SSCR0_TIM;
ssp->ssp_blob[di][hwi].ssc0 = SSCR0_MOD | SSCR0_PSP | SSCR0_RIM | SSCR0_TIM;
/* sscr1 dynamic settings are SFRMDIR, SCLKDIR, SCFR */
ssp->ssp_blob[di][hwi].ssc1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_TRAIL | SSCR1_RSRE |
SSCR1_TSRE;
if (ssp->ssp_prm[di].version == SSP_BLOB_VER_3_0)
/* bits 21 and 20, TSRE and RSRE do not exist in ACE3.x
* Note: Assuming SSP_BLOB_VER_3_0 is ACE3.x
*/
ssp->ssp_blob[di][hwi].ssc1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_TRAIL;
else
ssp->ssp_blob[di][hwi].ssc1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_TRAIL |
SSCR1_RSRE | SSCR1_TSRE;
/* sscr2 dynamic setting is LJDFD */
ssp->ssp_blob[di][hwi].ssc2 = SSCR2_SDFD | SSCR2_TURM1;
@@ -197,7 +240,13 @@ static int ssp_calculate_intern(struct intel_nhlt_params *nhlt, int hwi)
ssp->ssp_blob[di][hwi].sspsp |= SSPSP_SCMODE(inverted_bclk);
}
ssp->ssp_blob[di][hwi].ssc0 |= SSCR0_MOD | SSCR0_ACS;
/* Note: ACS as SSCR0(30) does not exist in any ACE version, or cAVS2.x. This disables
* it for ACE3.x. It might be good to fix later for other platforms. In cAVS this is 30:29
* reserved, in ACE1.x this is DLE as 30:29, in ACE2.x this is RSVD30 as 30:29, in ACE3.x this
* is DLE as 30:29.
*/
if (ssp->ssp_prm[di].version != SSP_BLOB_VER_3_0)
ssp->ssp_blob[di][hwi].ssc0 |= SSCR0_ACS;
/* Additional hardware settings */
@@ -538,13 +587,23 @@ static int ssp_calculate_intern(struct intel_nhlt_params *nhlt, int hwi)
ssp->ssp_blob[di][hwi].mdivr = clk_div;
/* clock will always go through the divider */
/* Note: There is no SSC0(6) ECS in ACE3.x but RSVD6 in same bit position
* that must be set to one.
*/
ssp->ssp_blob[di][hwi].ssc0 |= SSCR0_ECS;
/* enable divider for this clock id */
ssp->ssp_blob[di][hwi].mdivc |= BIT(ssp->ssp_prm[di].mclk_id);
/* set mclk source always for audio cardinal clock */
ssp->ssp_blob[di][hwi].mdivc |= MCDSS(SSP_CLOCK_AUDIO_CARDINAL);
/* set bclk source for audio cardinal clock */
ssp->ssp_blob[di][hwi].mdivc |= MNDSS(SSP_CLOCK_AUDIO_CARDINAL);
/* set bclk source for audio cardinal clock
* Note: There is no MDIVXCTRL(21:20) MNDSS in any ACE version 1.x - 3.x. MNDSS
* exists in cAVS2.x. This removes the set for ACE3.x. May need to address other
* ACE platforms later.
*/
if (ssp->ssp_prm[di].version != SSP_BLOB_VER_3_0)
ssp->ssp_blob[di][hwi].mdivc |= MNDSS(SSP_CLOCK_AUDIO_CARDINAL);
return 0;
}
@@ -748,6 +807,9 @@ int ssp_calculate(struct intel_nhlt_params *nhlt)
return -EINVAL;
/* v15 blob is made from legacy blob, so it can't fail */
ssp_calculate_intern_v15(nhlt, i);
/* v30 blob is made from legacy blob, so it can't fail */
ssp_calculate_intern_v30(nhlt, i);
}
ssp_print_internal(ssp);
@@ -826,6 +888,8 @@ int ssp_get_vendor_blob_size(struct intel_nhlt_params *nhlt, int dai_index,
/* set size for the blob */
if (ssp->ssp_prm[dai_index].version == SSP_BLOB_VER_1_5)
*size = ssp->ssp_blob_1_5[dai_index][hw_config_index].size;
else if (ssp->ssp_prm[dai_index].version == SSP_BLOB_VER_3_0)
*size = ssp->ssp_blob_3_0[dai_index][hw_config_index].size;
else
/* legacy */
*size = sizeof(struct ssp_intel_config_data) +
@@ -867,8 +931,19 @@ int ssp_get_vendor_blob(struct intel_nhlt_params *nhlt, uint8_t *vendor_blob,
memcpy(vendor_blob + basic_len + clock_len,
ssp->ssp_blob_ext[dai_index][hw_config_index].aux_blob,
ssp->ssp_blob_ext[dai_index][hw_config_index].size);
}
else {
} else if (ssp->ssp_prm[dai_index].version == SSP_BLOB_VER_3_0) {
basic_len = sizeof(struct ssp_intel_config_data_3_0);
clock_len = sizeof(uint32_t) * ssp->ssp_prm[dai_index].mdivr[hw_config_index].count;
/* basic data */
memcpy(vendor_blob, &ssp->ssp_blob_3_0[dai_index][hw_config_index], basic_len);
/* clock data */
memcpy(vendor_blob + basic_len,
&ssp->ssp_prm[dai_index].mdivr[hw_config_index].mdivrs[0], clock_len);
/* ext data */
memcpy(vendor_blob + basic_len + clock_len,
ssp->ssp_blob_ext[dai_index][hw_config_index].aux_blob,
ssp->ssp_blob_ext[dai_index][hw_config_index].size);
} else {
basic_len = sizeof(struct ssp_intel_config_data);
/*basic data */
memcpy(vendor_blob, &ssp->ssp_blob[dai_index][hw_config_index], basic_len);
@@ -914,6 +989,9 @@ int ssp_set_params(struct intel_nhlt_params *nhlt, const char *dir, int dai_inde
/* let's compare the lower 16 bits as we don't send the signature from topology */
if (version == (SSP_BLOB_VER_1_5 & ((1 << 16) - 1)))
ssp->ssp_prm[ssp->ssp_count].version = SSP_BLOB_VER_1_5;
else if (version == (SSP_BLOB_VER_3_0 & ((1 << 16) - 1)))
ssp->ssp_prm[ssp->ssp_count].version = SSP_BLOB_VER_3_0;
if (tdm_padding_per_slot && !strcmp(tdm_padding_per_slot, "true"))
ssp->ssp_prm[ssp->ssp_count].tdm_per_slot_padding_flag = 1;
else
+1 -5
View File
@@ -11,11 +11,7 @@
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include <alsa/global.h>
#include <alsa/input.h>
#include <alsa/output.h>
#include <alsa/conf.h>
#include <alsa/error.h>
#include <alsa/asoundlib.h>
#include "pre-process-external.h"
#include "nhlt.h"
#include "intel/intel-nhlt.h"
+174 -4
View File
@@ -697,13 +697,11 @@ static int pre_process_create_items(struct tplg_pre_processor *tplg_pp,
snd_config_iterator_t i, next;
snd_config_type_t type;
const char *class_id;
char *class_id_local;
int attr_count = 0;
int object_count = *object_count_offset;
int ret;
snd_config_get_id(top, &class_id);
class_id_local = strdup(class_id);
snd_config_for_each(i, next, cfg) {
snd_config_iterator_t i2, next2;
@@ -719,7 +717,7 @@ static int pre_process_create_items(struct tplg_pre_processor *tplg_pp,
if (snd_config_get_id(n, &attribute) < 0)
continue;
ret = snd_config_make(&local_top, class_id_local, SND_CONFIG_TYPE_COMPOUND);
ret = snd_config_make(&local_top, class_id, SND_CONFIG_TYPE_COMPOUND);
snd_config_for_each(i2, next2, n) {
snd_config_t *n2, *new, *new_obj;
@@ -787,7 +785,7 @@ static int pre_process_create_items(struct tplg_pre_processor *tplg_pp,
ret = pre_process_add_objects(tplg_pp, &object_count, top,
local_top, new);
if (ret < 0) {
SNDERR("failed to add objects of type %s\n", class_id_local);
SNDERR("failed to add objects of type %s\n", class_id);
goto err;
}
@@ -921,6 +919,172 @@ static int pre_process_arrays(struct tplg_pre_processor *tplg_pp, snd_config_t *
return 0;
}
static int pre_process_subtree_copy(struct tplg_pre_processor *tplg_pp, snd_config_t *curr,
snd_config_t *top)
{
snd_config_iterator_t i, next;
snd_config_t *subtrees;
int ret;
ret = snd_config_search(curr, "SubTreeCopy", &subtrees);
if (ret < 0)
return 0;
snd_config_for_each(i, next, subtrees) {
snd_config_t *n, *source_cfg, *target_cfg, *type_cfg;
snd_config_t *source_tree, *target_tree, *tmp, *subtree_cfg;
const char *id, *source_id;
const char *type = NULL;
const char *target_id = NULL;
bool override = false;
n = snd_config_iterator_entry(i);
ret = snd_config_get_id(n, &id);
if (ret < 0) {
SNDERR("Failed to get ID for subtree copy\n");
return ret;
}
/* get the type of copy ex: override/extend if set, by default override is false */
ret = snd_config_search(n, "type", &type_cfg);
if (ret >= 0) {
ret = snd_config_get_string(type_cfg, &type);
if (ret >= 0) {
if (strcmp(type, "override") && strcmp(type, "extend")) {
SNDERR("Invalid value for sub tree copy type %s\n", type);
return ret;
}
if (!strcmp(type, "override"))
override = true;
}
}
ret = snd_config_search(n, "source", &source_cfg);
if (ret < 0) {
SNDERR("failed to get source config for subtree %s\n", id);
return ret;
}
/* if the target node is not set, the subtree will be copied to current node */
ret = snd_config_search(n, "target", &target_cfg);
if (ret >= 0) {
snd_config_t *temp_target;
char *s;
ret = snd_config_get_string(target_cfg, &target_id);
if (ret < 0) {
SNDERR("Invalid target id for subtree %s\n", id);
return ret;
}
/*
* create a temporary node with target_id and merge with top to avoid
* failure in case the target node ID doesn't exist already
*/
s = tplg_snprintf("%s {}", target_id);
if (!s)
return -ENOMEM;
ret = snd_config_load_string(&temp_target, s, 0);
free(s);
if (ret < 0) {
SNDERR("Cannot create temp node with target id %s\n", target_id);
return ret;
}
ret = snd_config_merge(top, temp_target, false);
if (ret < 0) {
SNDERR("Cannot merge temp node with target id %s\n", target_id);
return ret;
}
ret = snd_config_search(top, target_id, &target_tree);
if (ret < 0) {
SNDERR("failed to get target tree %s\n", target_id);
return ret;
}
} else {
target_tree = curr;
}
/* get the source tree node */
ret = snd_config_get_string(source_cfg, &source_id);
if (ret < 0) {
SNDERR("Invalid source node id for subtree %s\n", id);
return ret;
}
ret = snd_config_search(top, source_id, &source_tree);
if (ret < 0) {
SNDERR("failed to get source tree %s\n", source_id);
return ret;
}
/* get the subtree to be merged */
ret = snd_config_search(n, "tree", &subtree_cfg);
if (ret < 0)
subtree_cfg = NULL;
/* make a temp copy of the source tree */
ret = snd_config_copy(&tmp, source_tree);
if (ret < 0) {
SNDERR("failed to copy source tree for subtreecopy %s\n", id);
return ret;
}
/* merge the current block with the source tree */
ret = snd_config_merge(tmp, subtree_cfg, override);
if (ret < 0) {
snd_config_delete(tmp);
SNDERR("Failed to merge source tree w/ subtree %s\n", id);
return ret;
}
/* merge the merged block to the target tree */
ret = snd_config_merge(target_tree, tmp, override);
if (ret < 0) {
snd_config_delete(tmp);
SNDERR("Failed to merge subtree %s w/ target\n", id);
return ret;
}
}
/* all subtree copies have been processed, remove the node */
snd_config_delete(subtrees);
return 0;
}
static int pre_process_subtree_copies(struct tplg_pre_processor *tplg_pp, snd_config_t *top,
snd_config_t *curr)
{
snd_config_iterator_t i, next;
int ret;
if (snd_config_get_type(curr) != SND_CONFIG_TYPE_COMPOUND)
return 0;
/* process subtreecopies at this node */
ret = pre_process_subtree_copy(tplg_pp, curr, top);
if (ret < 0)
return ret;
/* process subtreecopies at all child nodes */
snd_config_for_each(i, next, curr) {
snd_config_t *n;
n = snd_config_iterator_entry(i);
/* process subtreecopies at this node */
ret = pre_process_subtree_copies(tplg_pp, top, n);
if (ret < 0)
return ret;
}
return 0;
}
#endif /* version < 1.2.6 */
int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size,
@@ -980,6 +1144,12 @@ int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_
fprintf(stderr, "Failed to process object arrays in input config\n");
goto err;
}
err = pre_process_subtree_copies(tplg_pp, tplg_pp->input_cfg, tplg_pp->input_cfg);
if (err < 0) {
SNDERR("Failed to process subtree copies in input config\n");
goto err;
}
#endif
err = pre_process_config(tplg_pp, tplg_pp->input_cfg);
+5 -2
View File
@@ -398,9 +398,12 @@ static int add_define(char **defs, char *d)
size_t len = (*defs ? strlen(*defs) : 0) + strlen(d) + 2;
char *m = realloc(*defs, len);
if (m) {
if (*defs)
if (*defs) {
strcat(m, ",");
strcat(m, d);
strcat(m, d);
} else {
strcpy(m, d);
}
*defs = m;
return 0;
}