- moved iecset from alsa-lib/test.

- added man page.
This commit is contained in:
Takashi Iwai 2003-10-23 16:54:32 +00:00
parent 4a8060d6a8
commit 5f8a137a7a
6 changed files with 693 additions and 2 deletions

View file

@ -4,7 +4,7 @@ ALSAMIXER_DIR=alsamixer
else
ALSAMIXER_DIR=
endif
SUBDIRS=include alsactl $(ALSAMIXER_DIR) amixer aplay seq utils
SUBDIRS=include alsactl $(ALSAMIXER_DIR) amixer aplay iecset seq utils
EXTRA_DIST=ChangeLog INSTALL TODO README configure cvscompile depcomp
rpm: dist

View file

@ -65,5 +65,5 @@ AC_PROG_GCC_TRADITIONAL
SAVE_UTIL_VERSION
AC_OUTPUT(Makefile alsactl/Makefile alsamixer/Makefile amixer/Makefile aplay/Makefile \
include/Makefile utils/Makefile utils/alsa-utils.spec \
include/Makefile iecset/Makefile utils/Makefile utils/alsa-utils.spec \
seq/Makefile seq/aconnect/Makefile seq/aseqnet/Makefile)

9
iecset/Makefile.am Normal file
View file

@ -0,0 +1,9 @@
INCLUDES = -I$(top_srcdir)/include
LDADD = -lm
# LDFLAGS = -static
# CFLAGS += -g -Wall
bin_PROGRAMS = iecset
amixer_SOURCES = iecset.c iecbits.c
man_MANS = iecset.1
EXTRA_DIST = iecset.1

213
iecset/iecbits.c Normal file
View file

@ -0,0 +1,213 @@
/*
iecdump - dump IEC958 status bits on ALSA
Copyright (C) 2003 by Takashi Iwai <tiwai@suse.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <alsa/asoundlib.h>
struct category_str {
int val;
const char *name;
};
static struct category_str con_category[] = {
{ IEC958_AES1_CON_DAT, "DAT" },
{ IEC958_AES1_CON_VCR, "VCR" },
{ IEC958_AES1_CON_MICROPHONE, "microphone" },
{ IEC958_AES1_CON_SYNTHESIZER, "synthesizer" },
{ IEC958_AES1_CON_RATE_CONVERTER, "rate converter" },
{ IEC958_AES1_CON_MIXER, "mixer" },
{ IEC958_AES1_CON_SAMPLER, "sampler" },
{ IEC958_AES1_CON_PCM_CODER, "PCM coder" },
{ IEC958_AES1_CON_IEC908_CD, "CD" },
{ IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" },
{ IEC958_AES1_CON_GENERAL, "general" },
};
#define ARRAY_SIZE(x) (int)(sizeof(x)/sizeof(x[0]))
void dump_iec958(snd_aes_iec958_t *iec)
{
int i;
if (! (iec->status[0] & IEC958_AES0_PROFESSIONAL)) {
/* consumer */
printf("Mode: consumer\n");
printf("Data: ");
if (!(iec->status[0] & IEC958_AES0_NONAUDIO)) {
printf("audio\n");
} else {
printf("non-audio\n");
}
printf("Rate: ");
switch (iec->status[3] & IEC958_AES3_CON_FS) {
case IEC958_AES3_CON_FS_44100:
printf("44100 Hz\n");
break;
case IEC958_AES3_CON_FS_48000:
printf("48000 Hz\n");
break;
case IEC958_AES3_CON_FS_32000:
printf("32000 Hz\n");
break;
default:
printf("unknown\n");
break;
}
printf("Copyright: ");
if (iec->status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) {
printf("permitted\n");
} else {
printf("protected\n");
}
printf("Emphasis: ");
if ((iec->status[0] & IEC958_AES0_CON_EMPHASIS) != IEC958_AES0_CON_EMPHASIS_5015) {
printf("none\n");
} else {
printf("50/15us\n");
}
printf("Category: ");
for (i = 0; i < ARRAY_SIZE(con_category); i++) {
if ((iec->status[1] & IEC958_AES1_CON_CATEGORY) == con_category[i].val) {
printf("%s\n", con_category[i].name);
break;
}
}
if (i >= ARRAY_SIZE(con_category)) {
printf("unknown 0x%x\n", iec->status[1] & IEC958_AES1_CON_CATEGORY);
}
printf("Original: ");
if (iec->status[1] & IEC958_AES1_CON_ORIGINAL) {
printf("original\n");
} else {
printf("1st generation\n");
}
printf("Clock: ");
switch (iec->status[3] & IEC958_AES3_CON_CLOCK) {
case IEC958_AES3_CON_CLOCK_1000PPM:
printf("1000 ppm\n");
break;
case IEC958_AES3_CON_CLOCK_50PPM:
printf("50 ppm\n");
break;
case IEC958_AES3_CON_CLOCK_VARIABLE:
printf("variable pitch\n");
break;
default:
printf("unknown\n");
break;
}
} else {
printf("Mode: professional\n");
printf("Data: ");
if (!(iec->status[0] & IEC958_AES0_NONAUDIO)) {
printf("audio\n");
} else {
printf("non-audio\n");
}
printf("Rate: ");
switch (iec->status[0] & IEC958_AES0_PRO_FS) {
case IEC958_AES0_PRO_FS_44100:
printf("44100 Hz\n");
break;
case IEC958_AES0_PRO_FS_48000:
printf("48000 Hz\n");
break;
case IEC958_AES0_PRO_FS_32000:
printf("32000 Hz\n");
break;
default:
printf("unknown\n");
break;
}
printf("Rate Locked: ");
if (iec->status[0] & IEC958_AES0_PRO_FREQ_UNLOCKED)
printf("no\n");
else
printf("yes\n");
printf("Emphasis: ");
switch (iec->status[0] & IEC958_AES0_PRO_EMPHASIS) {
case IEC958_AES0_PRO_EMPHASIS_CCITT:
printf("CCITT J.17\n");
break;
case IEC958_AES0_PRO_EMPHASIS_NONE:
printf("none\n");
break;
case IEC958_AES0_PRO_EMPHASIS_5015:
printf("50/15us\n");
break;
case IEC958_AES0_PRO_EMPHASIS_NOTID:
default:
printf("unknown\n");
break;
}
printf("Stereophonic: ");
if ((iec->status[1] & IEC958_AES1_PRO_MODE) == IEC958_AES1_PRO_MODE_STEREOPHONIC) {
printf("stereo\n");
} else {
printf("not indicated\n");
}
printf("Userbits: ");
switch (iec->status[1] & IEC958_AES1_PRO_USERBITS) {
case IEC958_AES1_PRO_USERBITS_192:
printf("192bit\n");
break;
case IEC958_AES1_PRO_USERBITS_UDEF:
printf("user-defined\n");
break;
default:
printf("unkown\n");
break;
}
printf("Sample Bits: ");
switch (iec->status[2] & IEC958_AES2_PRO_SBITS) {
case IEC958_AES2_PRO_SBITS_20:
printf("20 bit\n");
break;
case IEC958_AES2_PRO_SBITS_24:
printf("24 bit\n");
break;
case IEC958_AES2_PRO_SBITS_UDEF:
printf("user defined\n");
break;
default:
printf("unknown\n");
break;
}
printf("Word Length: ");
switch (iec->status[2] & IEC958_AES2_PRO_WORDLEN) {
case IEC958_AES2_PRO_WORDLEN_22_18:
printf("22 bit or 18 bit\n");
break;
case IEC958_AES2_PRO_WORDLEN_23_19:
printf("23 bit or 19 bit\n");
break;
case IEC958_AES2_PRO_WORDLEN_24_20:
printf("24 bit or 20 bit\n");
break;
case IEC958_AES2_PRO_WORDLEN_20_16:
printf("20 bit or 16 bit\n");
break;
default:
printf("unknown\n");
break;
}
}
}

100
iecset/iecset.1 Normal file
View file

@ -0,0 +1,100 @@
.TH iecset 1 "23 Oct 2003"
.SH NAME
iecset \- Set or dump IEC958 status bits
.SH SYNOPSIS
\fBiecset\fP [\fIoptions\fP] [\fIcmd\fP \fIarg\fP...]
.SH DESCRIPTION
\fBiecset\fP is a small utility to set or dump the IEC958 (or so-called
"S/PDIF") status bits of the specified sound card via ALSA control API.
When \fBiecset\fP is started without arguments except for options,
it will show the current IEC958 status in a human-readable form.
When the commands are given in the arguments, they are parsed
and the IEC958 status bits are updated. The resultant status is
shown as well.
The commands consist of the command directive and the argument.
As the boolean argument, \fIyes\fP, \fIno\fP, \fItrue\fP, \fIfalse\fI,
or a digit number is allowed.
.SH EXAMPLES
.TP
.BI iecset\ \-Dhw:1
Displays the current IEC958 status bits on the second card.
This is equivalent with \fI-c 1\fP.
.TP
.BI iecset\ \-x
Displays the current IEC958 status bits in a style of the arguments
for the PCM stream. The output string can be passed to the \fIiec958\fP
(or \fIspdif\fP) PCM type as the optional argument.
.TP
.BI iecset\ pro\ off\ audio\ off
Sets the current status to the consumer-mode and turns on the
non-audio bit. The modified status will be shown, too.
.SH OPTIONS
.TP
\fI-D\fP device
Specifies the device name of the control to open
.TP
\fI-c\fP card
Specifies the card index to open. Equivalent with \fI-Dhw:x\fP.
.TP
\fI-x\fP
Dumps the status in the form of AESx bytes.
.TP
\fI-i\fP
Reads the command sequences from stdin.
Each line has single command.
.SH COMMANDS
.TP
\fIprofessional\fP <bool>
The professional mode (true) or consumer mode (false).
.TP
\fIaudio\fP <bool>
The audio mode (true) or non-audio mode (false).
.TP
\fIrate\fP <int>
The sample rate in Hz.
.TP
\fIemphasis\fP <int>
The emphasis: 0 = none, 1 = 50/15us, 2 = CCITT.
.TP
\fIlock\fP <bool>
Rate lock: locked (true), unlocked (false).
This command is for the professional mode only.
.TP
\fIsbits\fP <int>
Sample bits: 2 = 20bit, 4 = 24bit, 6 = undefined.
This command is for the professional mode only.
.TP
\fIwordlength\fP <int>
Wordlength: 0 = No, 2 = 22-18 bit, 4 = 23-19 bit, 5 = 24-20 bit, 6 = 20-16 bit.
This command is for the professional mode only.
.TP
\fIcategory\fP <int>
Category: the value is from 0 to 0x7f.
This command is for the consumer mode only.
.TP
\fIcopyright\fP <bool>
Copyright: copyrighted (true), non-copyrighted (false).
This command is for the consumer mode only.
.TP
\fIoriginal\fP <boo>
Original flag: original (true), 1st generation (false).
This command is for the consumer mode only.
.SH AUTHOR
Takashi Iwai <tiwai@suse.de>

369
iecset/iecset.c Normal file
View file

@ -0,0 +1,369 @@
/*
iecset - change IEC958 status bits on ALSA
Copyright (C) 2003 by Takashi Iwai <tiwai@suse.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <ctype.h>
#include <alsa/asoundlib.h>
#include "iecbits.c"
static int get_bool(const char *str)
{
if (strncmp(str, "yes", 3) == 0 ||
strncmp(str, "YES", 3) == 0 ||
strncmp(str, "on", 2) == 0 ||
strncmp(str, "ON", 2) == 0 ||
strncmp(str, "true", 4) == 0 ||
strncmp(str, "TRUE", 4) == 0 ||
*str == '1')
return 1;
return 0;
}
enum {
CMD_BOOL, CMD_BOOL_INV, CMD_INT
};
enum {
IDX_PRO, IDX_NOAUDIO, IDX_RATE, IDX_UNLOCK, IDX_SBITS, IDX_WORD, IDX_EMP, IDX_CAT, IDX_NOCOPY, IDX_ORIG,
IDX_LAST
};
struct cmdtbl {
const char *name;
int idx;
int type;
const char *desc;
};
static struct cmdtbl cmds[] = {
{ "pro", IDX_PRO, CMD_BOOL,
"professional (common)\n\toff = consumer mode, on = professional mode" },
{ "aud", IDX_NOAUDIO, CMD_BOOL_INV,
"audio (common)\n\toff = audio mode, on = non-audio mode" },
{ "rat", IDX_RATE, CMD_INT,
"sample rate (common)\n\tsample rate in Hz" },
{ "emp", IDX_EMP, CMD_INT,
"emphasis (common)\n\t0 = none, 1 = 50/15us, 2 = CCITT" },
{ "loc", IDX_UNLOCK, CMD_BOOL_INV,
"lock (prof.)\n\toff = rate unlocked, on = rate locked" },
{ "sbi", IDX_SBITS, CMD_INT,
"sbits (prof.)\n\tsample bits 2 = 20bit, 4 = 24bit, 6 = undef" },
{ "wor", IDX_WORD, CMD_INT,
"wordlength (prof.)\n\t0=no, 2=22-18bit, 4=23-19bit, 5=24-20bit, 6=20-16bit" },
{ "cat", IDX_CAT, CMD_INT,
"category (consumer)\n\t0-0x7f" },
{ "cop", IDX_NOCOPY, CMD_BOOL_INV,
"copyright (consumer)\n\toff = non-copyright, on = copyright" },
{ "ori", IDX_ORIG, CMD_BOOL,
"original (consumer)\n\toff = 1st-gen, on = original" },
};
static void usage(void)
{
int i;
printf("Usage: iecset [options] [cmd arg...]\n");
printf("Options:\n");
printf(" -D device specifies the control device to use\n");
printf(" -c card specifies the card number to use (equiv. with -Dhw:#)\n");
printf(" -x dump the dump the AESx hex code for IEC958 PCM parameters\n");
printf(" -i read commands from stdin\n");
printf("Commands:\n");
for (i = 0; i < (int)(sizeof(cmds)/sizeof(cmds[0])); i++) {
printf(" %s\n", cmds[i].desc);
}
}
/*
* parse iecset commands
*/
static void parse_command(int *parms, const char *c, const char *arg)
{
int i;
for (i = 0; i < (int)(sizeof(cmds)/sizeof(cmds[0])); i++) {
if (strncmp(c, cmds[i].name, strlen(cmds[i].name)) == 0) {
int val;
switch (cmds[i].type) {
case CMD_BOOL:
val = get_bool(arg);
break;
case CMD_BOOL_INV:
val = !get_bool(arg);
break;
case CMD_INT:
default:
val = (int)strtol(arg, NULL, 0);
break;
}
parms[cmds[i].idx] = val;
return;
}
}
}
static char *skipspace(char *line)
{
char *p;
for (p = line; *p && isspace(*p); p++)
;
return p;
}
/*
* parse iecset commands from the file
*/
static void parse_file(int *parms, FILE *fp)
{
char line[1024], *cmd, *arg;
while (fgets(line, sizeof(line), fp) != NULL) {
cmd = skipspace(line);
if (*cmd == '#' || ! *cmd)
continue;
for (arg = cmd; *arg && !isspace(*arg); arg++)
;
if (! *arg)
continue;
*arg++ = 0;
arg = skipspace(arg);
if (! *arg)
continue;
parse_command(parms, cmd, arg);
}
}
/* update iec958 status values
* return non-zero if the values are modified
*/
static int update_iec958_status(snd_aes_iec958_t *iec958, int *parms)
{
int changed = 0;
if (parms[IDX_PRO] >= 0) {
if (parms[IDX_PRO])
iec958->status[0] |= IEC958_AES0_PROFESSIONAL;
else
iec958->status[0] &= ~IEC958_AES0_PROFESSIONAL;
changed = 1;
}
if (parms[IDX_NOAUDIO] >= 0) {
if (parms[IDX_NOAUDIO])
iec958->status[0] |= IEC958_AES0_NONAUDIO;
else
iec958->status[0] &= ~IEC958_AES0_NONAUDIO;
changed = 1;
}
if (parms[IDX_RATE] >= 0) {
if (iec958->status[0] & IEC958_AES0_PROFESSIONAL) {
iec958->status[0] &= ~IEC958_AES0_PRO_FS;
switch (parms[IDX_RATE]) {
case 44100:
iec958->status[0] |= IEC958_AES0_PRO_FS_44100;
break;
case 48000:
iec958->status[0] |= IEC958_AES0_PRO_FS_48000;
break;
case 3200:
iec958->status[0] |= IEC958_AES0_PRO_FS_32000;
break;
}
} else {
iec958->status[3] &= ~IEC958_AES3_CON_FS;
switch (parms[IDX_RATE]) {
case 44100:
iec958->status[3] |= IEC958_AES3_CON_FS_44100;
break;
case 48000:
iec958->status[3] |= IEC958_AES3_CON_FS_48000;
break;
case 3200:
iec958->status[3] |= IEC958_AES3_CON_FS_32000;
break;
}
}
changed = 1;
}
if (parms[IDX_NOCOPY] >= 0) {
if (! (iec958->status[0] & IEC958_AES0_PROFESSIONAL)) {
if (parms[IDX_NOCOPY])
iec958->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT;
else
iec958->status[0] &= ~IEC958_AES0_CON_NOT_COPYRIGHT;
}
changed = 1;
}
if (parms[IDX_ORIG] >= 0) {
if (! (iec958->status[0] & IEC958_AES0_PROFESSIONAL)) {
if (parms[IDX_ORIG])
iec958->status[1] |= IEC958_AES1_CON_ORIGINAL;
else
iec958->status[1] &= ~IEC958_AES1_CON_ORIGINAL;
}
changed = 1;
}
if (parms[IDX_EMP] >= 0) {
if (iec958->status[0] & IEC958_AES0_PROFESSIONAL) {
iec958->status[0] &= ~IEC958_AES0_PRO_EMPHASIS;
switch (parms[IDX_EMP]) {
case 0:
iec958->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE;
break;
case 1:
iec958->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015;
break;
case 2:
iec958->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT;
break;
}
} else {
if (parms[IDX_EMP])
iec958->status[0] |= IEC958_AES0_CON_EMPHASIS_5015;
else
iec958->status[0] &= ~IEC958_AES0_CON_EMPHASIS_5015;
}
changed = 1;
}
if (parms[IDX_UNLOCK] >= 0) {
if (iec958->status[0] & IEC958_AES0_PROFESSIONAL) {
if (parms[IDX_UNLOCK])
iec958->status[0] |= IEC958_AES0_PRO_FREQ_UNLOCKED;
else
iec958->status[0] &= ~IEC958_AES0_PRO_FREQ_UNLOCKED;
}
changed = 1;
}
if (parms[IDX_SBITS] >= 0) {
if (iec958->status[0] & IEC958_AES0_PROFESSIONAL) {
iec958->status[2] &= ~IEC958_AES2_PRO_SBITS;
iec958->status[2] |= parms[IDX_SBITS] & 7;
}
changed = 1;
}
if (parms[IDX_WORD] >= 0) {
if (iec958->status[0] & IEC958_AES0_PROFESSIONAL) {
iec958->status[2] &= ~IEC958_AES2_PRO_WORDLEN;
iec958->status[2] |= (parms[IDX_WORD] & 7) << 3;
}
changed = 1;
}
if (parms[IDX_CAT] >= 0) {
if (! (iec958->status[0] & IEC958_AES0_PROFESSIONAL)) {
iec958->status[1] &= ~IEC958_AES1_CON_CATEGORY;
iec958->status[1] |= parms[IDX_CAT] & 0x7f;
}
changed = 1;
}
return changed;
}
int main(int argc, char **argv)
{
const char *dev = "default";
const char *spdif_str = "IEC958 Playback Default";
snd_ctl_t *ctl;
snd_ctl_elem_value_t *cval;
snd_aes_iec958_t iec958;
int from_stdin = 0;
int dumphex = 0;
int i, c;
char tmpname[32];
int parms[IDX_LAST];
for (i = 0; i < IDX_LAST; i++)
parms[i] = -1; /* not set */
while ((c = getopt(argc, argv, "D:c:xhi")) != -1) {
switch (c) {
case 'D':
dev = optarg;
break;
case 'c':
i = atoi(optarg);
if (i < 0 || i >= 7) {
fprintf(stderr, "invalid card index %d\n", i);
return 1;
}
sprintf(tmpname, "hw:%d", i);
dev = tmpname;
break;
case 'x':
dumphex = 1;
break;
case 'i':
from_stdin = 1;
break;
default:
usage();
return 1;
}
}
if (snd_ctl_open(&ctl, dev, 0) < 0) {
perror("snd_ctl_open");
return 1;
}
snd_ctl_elem_value_alloca(&cval);
snd_ctl_elem_value_set_interface(cval, SND_CTL_ELEM_IFACE_MIXER);
snd_ctl_elem_value_set_name(cval, spdif_str);
if (snd_ctl_elem_read(ctl, cval) < 0) {
snd_ctl_elem_value_set_interface(cval, SND_CTL_ELEM_IFACE_PCM);
if (snd_ctl_elem_read(ctl, cval) < 0) {
perror("snd_ctl_elem_read");
return 1;
}
}
snd_ctl_elem_value_get_iec958(cval, &iec958);
/* parse from stdin */
if (from_stdin)
parse_file(parms, stdin);
/* parse commands */
for (c = optind; c < argc - 1; c += 2)
parse_command(parms, argv[c], argv[c + 1]);
if (update_iec958_status(&iec958, parms)) {
/* store the values */
snd_ctl_elem_value_set_iec958(cval, &iec958);
if (snd_ctl_elem_write(ctl, cval) < 0) {
perror("snd_ctl_elem_write");
return 1;
}
if (snd_ctl_elem_read(ctl, cval) < 0) {
perror("snd_ctl_elem_write");
return 1;
}
snd_ctl_elem_value_get_iec958(cval, &iec958);
}
if (dumphex)
printf("AES0=0x%02x,AES1=0x%02x,AES2=0x%02x,AES3=0x%02x\n",
iec958.status[0], iec958.status[1], iec958.status[2], iec958.status[3]);
else
dump_iec958(&iec958);
snd_ctl_close(ctl);
return 0;
}