977 lines
31 KiB
Diff
977 lines
31 KiB
Diff
diff --git a/configure.in b/configure.in
|
|
index 9a71d95..ea6a8a0 100644
|
|
--- a/configure.in
|
|
+++ b/configure.in
|
|
@@ -367,6 +367,7 @@ AC_ARG_ENABLE(seq,
|
|
AC_ARG_ENABLE(alisp,
|
|
AS_HELP_STRING([--disable-alisp], [disable the alisp component]),
|
|
[build_alisp="$enableval"], [build_alisp="yes"])
|
|
+test "$softfloat" = "yes" && build_alisp="no"
|
|
AC_ARG_ENABLE(old-symbols,
|
|
AS_HELP_STRING([--disable-old-symbols], [disable old obsoleted symbols]),
|
|
[keep_old_symbols="$enableval"], [keep_old_symbols="yes"])
|
|
@@ -474,6 +475,7 @@ fi
|
|
|
|
if test "$softfloat" = "yes"; then
|
|
build_pcm_lfloat="no"
|
|
+ build_pcm_ladspa="no"
|
|
fi
|
|
|
|
AM_CONDITIONAL(BUILD_PCM_PLUGIN, test x$build_pcm_plugin = xyes)
|
|
diff --git a/include/sound/asound.h b/include/sound/asound.h
|
|
index 977b2d6..62d1e57 100644
|
|
--- a/include/sound/asound.h
|
|
+++ b/include/sound/asound.h
|
|
@@ -402,7 +402,7 @@ struct sndrv_pcm_sw_params {
|
|
|
|
struct sndrv_pcm_channel_info {
|
|
unsigned int channel;
|
|
- off_t offset; /* mmap offset */
|
|
+ long offset; /* mmap offset */
|
|
unsigned int first; /* offset to first sample in bits */
|
|
unsigned int step; /* samples distance in bits */
|
|
};
|
|
diff --git a/modules/mixer/simple/Makefile.am b/modules/mixer/simple/Makefile.am
|
|
index f73871f..bad0944 100644
|
|
--- a/modules/mixer/simple/Makefile.am
|
|
+++ b/modules/mixer/simple/Makefile.am
|
|
@@ -21,11 +21,11 @@ smixer_sbase_la_LIBADD = ../../../src/libasound.la
|
|
|
|
smixer_ac97_la_SOURCES = ac97.c sbasedl.c
|
|
smixer_ac97_la_LDFLAGS = -module -avoid-version $(LDFLAGS_NOUNDEFINED)
|
|
-smixer_ac97_la_LIBADD = ../../../src/libasound.la
|
|
+smixer_ac97_la_LIBADD = ../../../src/libasound.la -ldl
|
|
|
|
smixer_hda_la_SOURCES = hda.c sbasedl.c
|
|
smixer_hda_la_LDFLAGS = -module -avoid-version $(LDFLAGS_NOUNDEFINED)
|
|
-smixer_hda_la_LIBADD = ../../../src/libasound.la
|
|
+smixer_hda_la_LIBADD = ../../../src/libasound.la -ldl
|
|
|
|
if BUILD_PYTHON
|
|
smixer_python_la_SOURCES = python.c
|
|
diff --git a/src/conf/cards/CMI8788.conf b/src/conf/cards/CMI8788.conf
|
|
index 26910d5..0ca71e9 100644
|
|
--- a/src/conf/cards/CMI8788.conf
|
|
+++ b/src/conf/cards/CMI8788.conf
|
|
@@ -13,7 +13,7 @@ CMI8788.pcm.front.0 {
|
|
card $CARD
|
|
}
|
|
|
|
-# default with dmix+softvol & dsnoop
|
|
+# default with dmix & dsnoop
|
|
CMI8788.pcm.default {
|
|
@args [ CARD ]
|
|
@args.CARD {
|
|
@@ -23,15 +23,8 @@ CMI8788.pcm.default {
|
|
playback.pcm {
|
|
type plug
|
|
slave.pcm {
|
|
- type softvol
|
|
- slave.pcm {
|
|
- @func concat
|
|
- strings [ "dmix:" $CARD ",FORMAT=S32_LE" ]
|
|
- }
|
|
- control {
|
|
- name "PCM Playback Volume"
|
|
- card $CARD
|
|
- }
|
|
+ @func concat
|
|
+ strings [ "dmix:" $CARD ",FORMAT=S32_LE" ]
|
|
}
|
|
}
|
|
capture.pcm {
|
|
diff --git a/src/conf/cards/GUS.conf b/src/conf/cards/GUS.conf
|
|
index 80e3058..d744c54 100644
|
|
--- a/src/conf/cards/GUS.conf
|
|
+++ b/src/conf/cards/GUS.conf
|
|
@@ -17,50 +17,3 @@ GUS.pcm.front.0 {
|
|
card $CARD
|
|
}
|
|
}
|
|
-
|
|
-#
|
|
-# It's a temporary solution.
|
|
-#
|
|
-
|
|
-!pcm.hw {
|
|
- @args [ CARD DEV SUBDEV ]
|
|
- @args.CARD {
|
|
- type string
|
|
- default {
|
|
- @func getenv
|
|
- vars [
|
|
- ALSA_PCM_CARD
|
|
- ALSA_CARD
|
|
- ]
|
|
- default {
|
|
- @func refer
|
|
- name defaults.pcm.card
|
|
- }
|
|
- }
|
|
- }
|
|
- @args.DEV {
|
|
- type integer
|
|
- default {
|
|
- @func igetenv
|
|
- vars [
|
|
- ALSA_PCM_DEVICE
|
|
- ]
|
|
- default {
|
|
- @func refer
|
|
- name defaults.pcm.device
|
|
- }
|
|
- }
|
|
- }
|
|
- @args.SUBDEV {
|
|
- type integer
|
|
- default {
|
|
- @func refer
|
|
- name defaults.pcm.subdevice
|
|
- }
|
|
- }
|
|
- type hw
|
|
- card $CARD
|
|
- device $DEV
|
|
- subdevice $SUBDEV
|
|
- mmap_emulation on
|
|
-}
|
|
diff --git a/src/conf/cards/HDA-Intel.conf b/src/conf/cards/HDA-Intel.conf
|
|
index bcbcb9b..800281e 100644
|
|
--- a/src/conf/cards/HDA-Intel.conf
|
|
+++ b/src/conf/cards/HDA-Intel.conf
|
|
@@ -58,6 +58,7 @@ HDA-Intel.pcm.default {
|
|
resolution 121
|
|
}
|
|
}
|
|
+ hint.device 0
|
|
}
|
|
|
|
<confdir:pcm/surround40.conf>
|
|
diff --git a/src/control/control_ext.c b/src/control/control_ext.c
|
|
index a8675c1..d1fe8ea 100644
|
|
--- a/src/control/control_ext.c
|
|
+++ b/src/control/control_ext.c
|
|
@@ -107,6 +107,7 @@ static int snd_ctl_ext_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list)
|
|
ret = ext->callback->elem_list(ext, offset, ids);
|
|
if (ret < 0)
|
|
return ret;
|
|
+ ids->numid = offset + 1; /* fake number */
|
|
list->used++;
|
|
offset++;
|
|
ids++;
|
|
@@ -114,13 +115,24 @@ static int snd_ctl_ext_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list)
|
|
return 0;
|
|
}
|
|
|
|
+static snd_ctl_ext_key_t get_elem(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id)
|
|
+{
|
|
+ int numid = id->numid;
|
|
+ if (numid > 0) {
|
|
+ ext->callback->elem_list(ext, numid - 1, id);
|
|
+ id->numid = numid;
|
|
+ } else
|
|
+ id->numid = 0;
|
|
+ return ext->callback->find_elem(ext, id);
|
|
+}
|
|
+
|
|
static int snd_ctl_ext_elem_info(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
|
|
{
|
|
snd_ctl_ext_t *ext = handle->private_data;
|
|
snd_ctl_ext_key_t key;
|
|
int type, ret;
|
|
|
|
- key = ext->callback->find_elem(ext, &info->id);
|
|
+ key = get_elem(ext, &info->id);
|
|
if (key == SND_CTL_EXT_KEY_NOT_FOUND)
|
|
return -ENOENT;
|
|
ret = ext->callback->get_attribute(ext, key, &type, &info->access, &info->count);
|
|
@@ -200,7 +212,7 @@ static int snd_ctl_ext_elem_read(snd_ctl_t *handle, snd_ctl_elem_value_t *contro
|
|
int type, ret;
|
|
unsigned int access, count;
|
|
|
|
- key = ext->callback->find_elem(ext, &control->id);
|
|
+ key = get_elem(ext, &control->id);
|
|
if (key == SND_CTL_EXT_KEY_NOT_FOUND)
|
|
return -ENOENT;
|
|
ret = ext->callback->get_attribute(ext, key, &type, &access, &count);
|
|
@@ -254,7 +266,7 @@ static int snd_ctl_ext_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *contr
|
|
int type, ret;
|
|
unsigned int access, count;
|
|
|
|
- key = ext->callback->find_elem(ext, &control->id);
|
|
+ key = get_elem(ext, &control->id);
|
|
if (key == SND_CTL_EXT_KEY_NOT_FOUND)
|
|
return -ENOENT;
|
|
ret = ext->callback->get_attribute(ext, key, &type, &access, &count);
|
|
diff --git a/src/mixer/simple_none.c b/src/mixer/simple_none.c
|
|
index 0f4dd3a..4802200 100644
|
|
--- a/src/mixer/simple_none.c
|
|
+++ b/src/mixer/simple_none.c
|
|
@@ -1450,7 +1450,14 @@ static int simple_add1(snd_mixer_class_t *class, const char *name,
|
|
}
|
|
if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN)
|
|
return 0;
|
|
+#ifdef HAVE_SOFT_FLOAT
|
|
+ /* up to 256 channels */
|
|
+ for (n = 1; n < 256; n++)
|
|
+ if (n * n == values)
|
|
+ break;
|
|
+#else
|
|
n = sqrt((double)values);
|
|
+#endif
|
|
if (n * n != values)
|
|
return 0;
|
|
values = n;
|
|
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
|
|
index 74d1d1a..209c5bb 100644
|
|
--- a/src/pcm/pcm.c
|
|
+++ b/src/pcm/pcm.c
|
|
@@ -1430,7 +1430,7 @@ int snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int s
|
|
* \param pcm PCM handle
|
|
* \param pfds array of poll descriptors
|
|
* \param nfds count of poll descriptors
|
|
- * \param revents returned events
|
|
+ * \param revents pointer to the returned (single) event
|
|
* \return zero if success, otherwise a negative error code
|
|
*
|
|
* This function does "demangling" of the revents mask returned from
|
|
@@ -1440,6 +1440,9 @@ int snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int s
|
|
* syscall returned that some events are waiting, this function might
|
|
* return empty set of events. In this case, application should
|
|
* do next event waiting using poll() or select().
|
|
+ *
|
|
+ * Note: Even if multiple poll descriptors are used (i.e. pfds > 1),
|
|
+ * this function returns only a single event.
|
|
*/
|
|
int snd_pcm_poll_descriptors_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
|
|
{
|
|
@@ -2338,8 +2341,8 @@ int snd_pcm_wait(snd_pcm_t *pcm, int timeout)
|
|
int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout)
|
|
{
|
|
struct pollfd *pfd;
|
|
- unsigned short *revents;
|
|
- int i, npfds, pollio, err, err_poll;
|
|
+ unsigned short revents = 0;
|
|
+ int npfds, err, err_poll;
|
|
|
|
npfds = snd_pcm_poll_descriptors_count(pcm);
|
|
if (npfds <= 0 || npfds >= 16) {
|
|
@@ -2347,7 +2350,6 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout)
|
|
return -EIO;
|
|
}
|
|
pfd = alloca(sizeof(*pfd) * npfds);
|
|
- revents = alloca(sizeof(*revents) * npfds);
|
|
err = snd_pcm_poll_descriptors(pcm, pfd, npfds);
|
|
if (err < 0)
|
|
return err;
|
|
@@ -2356,7 +2358,6 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout)
|
|
return -EIO;
|
|
}
|
|
do {
|
|
- pollio = 0;
|
|
err_poll = poll(pfd, npfds, timeout);
|
|
if (err_poll < 0) {
|
|
if (errno == EINTR)
|
|
@@ -2365,28 +2366,23 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout)
|
|
}
|
|
if (! err_poll)
|
|
break;
|
|
- err = snd_pcm_poll_descriptors_revents(pcm, pfd, npfds, revents);
|
|
+ err = snd_pcm_poll_descriptors_revents(pcm, pfd, npfds, &revents);
|
|
if (err < 0)
|
|
return err;
|
|
- for (i = 0; i < npfds; i++) {
|
|
- if (revents[i] & (POLLERR | POLLNVAL)) {
|
|
- /* check more precisely */
|
|
- switch (snd_pcm_state(pcm)) {
|
|
- case SND_PCM_STATE_XRUN:
|
|
- return -EPIPE;
|
|
- case SND_PCM_STATE_SUSPENDED:
|
|
- return -ESTRPIPE;
|
|
- case SND_PCM_STATE_DISCONNECTED:
|
|
- return -ENODEV;
|
|
- default:
|
|
- return -EIO;
|
|
- }
|
|
+ if (revents & (POLLERR | POLLNVAL)) {
|
|
+ /* check more precisely */
|
|
+ switch (snd_pcm_state(pcm)) {
|
|
+ case SND_PCM_STATE_XRUN:
|
|
+ return -EPIPE;
|
|
+ case SND_PCM_STATE_SUSPENDED:
|
|
+ return -ESTRPIPE;
|
|
+ case SND_PCM_STATE_DISCONNECTED:
|
|
+ return -ENODEV;
|
|
+ default:
|
|
+ return -EIO;
|
|
}
|
|
- if ((revents[i] & (POLLIN | POLLOUT)) == 0)
|
|
- continue;
|
|
- pollio++;
|
|
}
|
|
- } while (! pollio);
|
|
+ } while (!(revents & (POLLIN | POLLOUT)));
|
|
#if 0 /* very useful code to test poll related problems */
|
|
{
|
|
snd_pcm_sframes_t avail_update;
|
|
@@ -7261,7 +7257,7 @@ int snd_pcm_recover(snd_pcm_t *pcm, int err, int silent)
|
|
* \param channels required PCM channels
|
|
* \param rate required sample rate in Hz
|
|
* \param soft_resample 0 = disallow alsa-lib resample stream, 1 = allow resampling
|
|
- * \param latency required overall latency in us (0 = optimum latency for players)
|
|
+ * \param latency required overall latency in us
|
|
* \return 0 on success otherwise a negative error code
|
|
*/
|
|
int snd_pcm_set_params(snd_pcm_t *pcm,
|
|
diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c
|
|
index 82823a0..bfa1cc8 100644
|
|
--- a/src/pcm/pcm_file.c
|
|
+++ b/src/pcm/pcm_file.c
|
|
@@ -29,6 +29,7 @@
|
|
#include <endian.h>
|
|
#include <byteswap.h>
|
|
#include <ctype.h>
|
|
+#include <string.h>
|
|
#include "pcm_local.h"
|
|
#include "pcm_plugin.h"
|
|
|
|
@@ -39,6 +40,16 @@ const char *_snd_module_pcm_file = "";
|
|
|
|
#ifndef DOC_HIDDEN
|
|
|
|
+/* keys to be replaced by real values in the filename */
|
|
+#define LEADING_KEY '%' /* i.e. %r, %c, %b ... */
|
|
+#define RATE_KEY 'r'
|
|
+#define CHANNELS_KEY 'c'
|
|
+#define BWIDTH_KEY 'b'
|
|
+#define FORMAT_KEY 'f'
|
|
+
|
|
+/* maximum length of a value */
|
|
+#define VALUE_MAXLEN 64
|
|
+
|
|
typedef enum _snd_pcm_file_format {
|
|
SND_PCM_FILE_FORMAT_RAW,
|
|
SND_PCM_FILE_FORMAT_WAV
|
|
@@ -57,6 +68,9 @@ struct wav_fmt {
|
|
typedef struct {
|
|
snd_pcm_generic_t gen;
|
|
char *fname;
|
|
+ char *final_fname;
|
|
+ int trunc;
|
|
+ int perm;
|
|
int fd;
|
|
char *ifname;
|
|
int ifd;
|
|
@@ -84,6 +98,175 @@ typedef struct {
|
|
#define TO_LE16(x) bswap_16(x)
|
|
#endif
|
|
|
|
+static int snd_pcm_file_append_value(char **string_p, char **index_ch_p,
|
|
+ int *len_p, const char *value)
|
|
+{
|
|
+ char *string, *index_ch;
|
|
+ int index, len, value_len;
|
|
+ /* input pointer values */
|
|
+ len = *(len_p);
|
|
+ string = *(string_p);
|
|
+ index_ch = *(index_ch_p);
|
|
+
|
|
+ value_len = strlen(value);
|
|
+ /* reallocation to accommodate the value */
|
|
+ index = index_ch - string;
|
|
+ len += value_len;
|
|
+ string = realloc(string, len + 1);
|
|
+ if (!string)
|
|
+ return -ENOMEM;
|
|
+ index_ch = string + index;
|
|
+ /* concatenating the new value */
|
|
+ strcpy(index_ch, value);
|
|
+ index_ch += value_len;
|
|
+ /* return values */
|
|
+ *(len_p) = len;
|
|
+ *(string_p) = string;
|
|
+ *(index_ch_p) = index_ch;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int snd_pcm_file_replace_fname(snd_pcm_file_t *file, char **new_fname_p)
|
|
+{
|
|
+ char value[VALUE_MAXLEN];
|
|
+ char *fname = file->fname;
|
|
+ char *new_fname = NULL;
|
|
+ char *old_last_ch, *old_index_ch, *new_index_ch;
|
|
+ int old_len, new_len, err;
|
|
+
|
|
+ snd_pcm_t *pcm = file->gen.slave;
|
|
+
|
|
+ /* we want to keep fname, const */
|
|
+ old_len = new_len = strlen(fname);
|
|
+ old_last_ch = fname + old_len - 1;
|
|
+ new_fname = malloc(new_len + 1);
|
|
+ if (!new_fname)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ old_index_ch = fname; /* first character of the old name */
|
|
+ new_index_ch = new_fname; /* first char of the new name */
|
|
+
|
|
+ while (old_index_ch <= old_last_ch) {
|
|
+ if (*(old_index_ch) == LEADING_KEY &&
|
|
+ old_index_ch != old_last_ch) {
|
|
+ /* is %, not last char, skipping and checking
|
|
+ next char */
|
|
+ switch (*(++old_index_ch)) {
|
|
+ case RATE_KEY:
|
|
+ snprintf(value, sizeof(value), "%d",
|
|
+ pcm->rate);
|
|
+ err = snd_pcm_file_append_value(&new_fname,
|
|
+ &new_index_ch, &new_len, value);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+ break;
|
|
+
|
|
+ case CHANNELS_KEY:
|
|
+ snprintf(value, sizeof(value), "%d",
|
|
+ pcm->channels);
|
|
+ err = snd_pcm_file_append_value(&new_fname,
|
|
+ &new_index_ch, &new_len, value);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+ break;
|
|
+
|
|
+ case BWIDTH_KEY:
|
|
+ snprintf(value, sizeof(value), "%d",
|
|
+ pcm->frame_bits/pcm->channels);
|
|
+ err = snd_pcm_file_append_value(&new_fname,
|
|
+ &new_index_ch, &new_len, value);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+ break;
|
|
+
|
|
+ case FORMAT_KEY:
|
|
+ err = snd_pcm_file_append_value(&new_fname,
|
|
+ &new_index_ch, &new_len,
|
|
+ snd_pcm_format_name(pcm->format));
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ /* non-key char, just copying */
|
|
+ *(new_index_ch++) = *(old_index_ch);
|
|
+ }
|
|
+ /* next old char */
|
|
+ old_index_ch++;
|
|
+ } else {
|
|
+ /* plain copying, shifting both strings to next chars */
|
|
+ *(new_index_ch++) = *(old_index_ch++);
|
|
+ }
|
|
+ }
|
|
+ /* closing the new string */
|
|
+ *(new_index_ch) = '\0';
|
|
+ *(new_fname_p) = new_fname;
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+static int snd_pcm_file_open_output_file(snd_pcm_file_t *file)
|
|
+{
|
|
+ int err, fd;
|
|
+
|
|
+ /* fname can contain keys, generating final_fname */
|
|
+ err = snd_pcm_file_replace_fname(file, &(file->final_fname));
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+ /*printf("DEBUG - original fname: %s, final fname: %s\n",
|
|
+ file->fname, file->final_fname);*/
|
|
+
|
|
+ if (file->final_fname[0] == '|') {
|
|
+ /* pipe mode */
|
|
+ FILE *pipe;
|
|
+ /* clearing */
|
|
+ pipe = popen(file->final_fname + 1, "w");
|
|
+ if (!pipe) {
|
|
+ SYSERR("running %s for writing failed",
|
|
+ file->final_fname);
|
|
+ return -errno;
|
|
+ }
|
|
+ fd = fileno(pipe);
|
|
+ } else {
|
|
+ if (file->trunc)
|
|
+ fd = open(file->final_fname, O_WRONLY|O_CREAT|O_TRUNC,
|
|
+ file->perm);
|
|
+ else {
|
|
+ fd = open(file->final_fname, O_WRONLY|O_CREAT|O_EXCL,
|
|
+ file->perm);
|
|
+ if (fd < 0) {
|
|
+ char *tmpfname = NULL;
|
|
+ int idx, len;
|
|
+ len = strlen(file->final_fname) + 6;
|
|
+ tmpfname = malloc(len);
|
|
+ if (!tmpfname)
|
|
+ return -ENOMEM;
|
|
+ for (idx = 1; idx < 10000; idx++) {
|
|
+ snprintf(tmpfname, len,
|
|
+ "%s.%04d", file->final_fname,
|
|
+ idx);
|
|
+ fd = open(tmpfname,
|
|
+ O_WRONLY|O_CREAT|O_EXCL,
|
|
+ file->perm);
|
|
+ if (fd >= 0) {
|
|
+ free(file->final_fname);
|
|
+ file->final_fname = tmpfname;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (fd < 0) {
|
|
+ SYSERR("open %s for writing failed",
|
|
+ file->final_fname);
|
|
+ free(tmpfname);
|
|
+ return -errno;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ file->fd = fd;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static void setup_wav_header(snd_pcm_t *pcm, struct wav_fmt *fmt)
|
|
{
|
|
fmt->fmt = TO_LE16(0x01);
|
|
@@ -152,6 +335,8 @@ static void fixup_wav_header(snd_pcm_t *pcm)
|
|
}
|
|
#endif /* DOC_HIDDEN */
|
|
|
|
+
|
|
+
|
|
static void snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes)
|
|
{
|
|
snd_pcm_file_t *file = pcm->private_data;
|
|
@@ -442,6 +627,13 @@ static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
|
|
a->first = slave->sample_bits * channel;
|
|
a->step = slave->frame_bits;
|
|
}
|
|
+ if (file->fd < 0) {
|
|
+ err = snd_pcm_file_open_output_file(file);
|
|
+ if (err < 0) {
|
|
+ SYSERR("failed opening output file %s", file->fname);
|
|
+ return err;
|
|
+ }
|
|
+ }
|
|
return 0;
|
|
}
|
|
|
|
@@ -452,6 +644,10 @@ static void snd_pcm_file_dump(snd_pcm_t *pcm, snd_output_t *out)
|
|
snd_output_printf(out, "File PCM (file=%s)\n", file->fname);
|
|
else
|
|
snd_output_printf(out, "File PCM (fd=%d)\n", file->fd);
|
|
+ if (file->final_fname)
|
|
+ snd_output_printf(out, "Final file PCM (file=%s)\n",
|
|
+ file->final_fname);
|
|
+
|
|
if (pcm->setup) {
|
|
snd_output_printf(out, "Its setup is:\n");
|
|
snd_pcm_dump_setup(pcm, out);
|
|
@@ -533,7 +729,6 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
|
|
snd_pcm_file_t *file;
|
|
snd_pcm_file_format_t format;
|
|
struct timespec timespec;
|
|
- char *tmpname = NULL;
|
|
int err;
|
|
|
|
assert(pcmp);
|
|
@@ -546,58 +741,27 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
|
|
SNDERR("file format %s is unknown", fmt);
|
|
return -EINVAL;
|
|
}
|
|
- if (fname) {
|
|
- if (trunc)
|
|
- fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, perm);
|
|
- else {
|
|
- fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, perm);
|
|
- if (fd < 0) {
|
|
- int idx, len;
|
|
- len = strlen(fname) + 6;
|
|
- tmpname = malloc(len);
|
|
- if (!tmpname)
|
|
- return -ENOMEM;
|
|
- for (idx = 1; idx < 10000; idx++) {
|
|
- snprintf(tmpname, len,
|
|
- "%s.%04d", fname, idx);
|
|
- fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, perm);
|
|
- if (fd >= 0) {
|
|
- fname = tmpname;
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
- if (fd < 0) {
|
|
- SYSERR("open %s for writing failed", fname);
|
|
- free(tmpname);
|
|
- return -errno;
|
|
- }
|
|
- }
|
|
file = calloc(1, sizeof(snd_pcm_file_t));
|
|
if (!file) {
|
|
- if (fname)
|
|
- close(fd);
|
|
- free(tmpname);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
+ /* opening output fname is delayed until writing,
|
|
+ when PCM params are known */
|
|
+ if (fname)
|
|
+ file->fname = strdup(fname);
|
|
+ file->trunc = trunc;
|
|
+ file->perm = perm;
|
|
+
|
|
if (ifname) {
|
|
ifd = open(ifname, O_RDONLY); /* TODO: mind blocking mode */
|
|
if (ifd < 0) {
|
|
SYSERR("open %s for reading failed", ifname);
|
|
- if (fname)
|
|
- close(fd);
|
|
free(file);
|
|
- free(tmpname);
|
|
return -errno;
|
|
}
|
|
- }
|
|
-
|
|
- if (fname)
|
|
- file->fname = strdup(fname);
|
|
- if (ifname)
|
|
file->ifname = strdup(ifname);
|
|
+ }
|
|
file->fd = fd;
|
|
file->ifd = ifd;
|
|
file->format = format;
|
|
@@ -608,7 +772,6 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
|
|
if (err < 0) {
|
|
free(file->fname);
|
|
free(file);
|
|
- free(tmpname);
|
|
return err;
|
|
}
|
|
pcm->ops = &snd_pcm_file_ops;
|
|
@@ -625,8 +788,6 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
|
|
snd_pcm_link_hw_ptr(pcm, slave);
|
|
snd_pcm_link_appl_ptr(pcm, slave);
|
|
*pcmp = pcm;
|
|
-
|
|
- free(tmpname);
|
|
return 0;
|
|
}
|
|
|
|
@@ -634,8 +795,9 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
|
|
|
|
\section pcm_plugins_file Plugin: File
|
|
|
|
-This plugin stores contents of a PCM stream to file, and optionally
|
|
-uses an existing file as an input data source (i.e., "virtual mic")
|
|
+This plugin stores contents of a PCM stream to file or pipes the stream
|
|
+to a command, and optionally uses an existing file as an input data source
|
|
+(i.e., "virtual mic")
|
|
|
|
\code
|
|
pcm.name {
|
|
@@ -647,7 +809,17 @@ pcm.name {
|
|
# or
|
|
pcm { } # Slave PCM definition
|
|
}
|
|
- file STR # Output filename
|
|
+ file STR # Output filename (or shell command the stream
|
|
+ # will be piped to if STR starts with the pipe
|
|
+ # char).
|
|
+ # STR can contain format keys, replaced by
|
|
+ # real values corresponding to the stream:
|
|
+ # %r rate (replaced with: 48000)
|
|
+ # %c channels (replaced with: 2)
|
|
+ # %b bits per sample (replaced with: 16)
|
|
+ # %f sample format string
|
|
+ # (replaced with: S16_LE)
|
|
+ # %% replaced with %
|
|
or
|
|
file INT # Output file descriptor number
|
|
infile STR # Input filename - only raw format
|
|
@@ -773,7 +945,7 @@ int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
|
|
err = snd_pcm_slave_conf(root, slave, &sconf, 0);
|
|
if (err < 0)
|
|
return err;
|
|
- if (!fname && fd < 0 && !ifname) {
|
|
+ if ((!fname || strlen(fname) == 0) && fd < 0 && !ifname) {
|
|
snd_config_delete(sconf);
|
|
SNDERR("file is not defined");
|
|
return -EINVAL;
|
|
diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c
|
|
index abd3d43..967cf46 100644
|
|
--- a/src/pcm/pcm_plug.c
|
|
+++ b/src/pcm/pcm_plug.c
|
|
@@ -605,7 +605,16 @@ static int snd_pcm_plug_change_mmap(snd_pcm_t *pcm, snd_pcm_t **new,
|
|
plug->gen.slave != plug->req_slave);
|
|
if (err < 0)
|
|
return err;
|
|
- slv->access = clt->access;
|
|
+ switch (slv->access) {
|
|
+ case SND_PCM_ACCESS_RW_INTERLEAVED:
|
|
+ slv->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
|
|
+ break;
|
|
+ case SND_PCM_ACCESS_RW_NONINTERLEAVED:
|
|
+ slv->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
return 1;
|
|
}
|
|
#endif
|
|
@@ -743,19 +752,29 @@ static int check_access_change(snd_pcm_hw_params_t *cparams,
|
|
return 0; /* OK, we have mmap support */
|
|
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
|
|
/* no mmap support - we need mmap emulation */
|
|
+
|
|
+ if (!snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
|
|
+ !snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
|
|
+ return -EINVAL; /* even no RW access? no way! */
|
|
+
|
|
cmask = (const snd_pcm_access_mask_t *)
|
|
snd_pcm_hw_param_get_mask(cparams,
|
|
SND_PCM_HW_PARAM_ACCESS);
|
|
snd_mask_none(&mask);
|
|
if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
|
|
- snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
|
|
- snd_pcm_access_mask_set(&mask,
|
|
- SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
+ snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
|
|
+ if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED))
|
|
+ snd_pcm_access_mask_set(&mask,
|
|
+ SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
+ }
|
|
if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
|
|
- snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
|
|
- snd_pcm_access_mask_set(&mask,
|
|
- SND_PCM_ACCESS_RW_NONINTERLEAVED);
|
|
- *smask = mask;
|
|
+ snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
|
|
+ if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
|
|
+ snd_pcm_access_mask_set(&mask,
|
|
+ SND_PCM_ACCESS_RW_NONINTERLEAVED);
|
|
+ }
|
|
+ if (!snd_mask_empty(&mask))
|
|
+ *smask = mask; /* prefer the straight conversion */
|
|
return 0;
|
|
#else
|
|
return -EINVAL;
|
|
diff --git a/src/seq/seq.c b/src/seq/seq.c
|
|
index feb9733..7b777a1 100644
|
|
--- a/src/seq/seq.c
|
|
+++ b/src/seq/seq.c
|
|
@@ -30,7 +30,7 @@
|
|
|
|
/*! \page seq Sequencer interface
|
|
|
|
-\section seq_general Genral
|
|
+\section seq_general General
|
|
|
|
The ALSA sequencer interface is designed to deliver the MIDI-like
|
|
events between clients/ports.
|
|
@@ -76,10 +76,10 @@ A client can have one or more <i>ports</i> to communicate between other
|
|
clients. A port is corresponding to the MIDI port in the case of MIDI device,
|
|
but in general it is nothing but the access point between other clients.
|
|
Each port may have capability flags, which specify the read/write
|
|
-accessbility and subscription permissions of the port.
|
|
+accessibility and subscription permissions of the port.
|
|
For creation of a port, call #snd_seq_create_port()
|
|
-with the appropirate port attribute specified in #snd_seq_port_info_t
|
|
-reocrd.
|
|
+with the appropriate port attribute specified in #snd_seq_port_info_t
|
|
+record.
|
|
|
|
For creating a port for the normal use, there is a helper function
|
|
#snd_seq_create_simple_port(). An example with this function is like below.
|
|
@@ -102,7 +102,7 @@ Here, input and output mean
|
|
input (read) from other clients and output (write) to others, respectively.
|
|
Since memory pool of each client is independent from others,
|
|
it avoids such a situation that a client eats the whole events pool
|
|
-and interfere other clients' responce.
|
|
+and interfere other clients' response.
|
|
|
|
The all scheduled output events or input events from dispatcher are stored
|
|
on these pools until delivered to other clients or extracted to user space.
|
|
@@ -171,7 +171,7 @@ the MIDI events like program, velocity or chorus effects.
|
|
This application can accept arbitrary MIDI input
|
|
and send to arbitrary port, just like a Unix pipe application using
|
|
stdin and stdout files.
|
|
-We can even connect several filter applictions which work individually
|
|
+We can even connect several filter applications which work individually
|
|
in order to process the MIDI events.
|
|
Subscription can be used for this purpose.
|
|
The connection between ports can be done also by the "third" client.
|
|
@@ -199,7 +199,7 @@ All the sequencer events are stored in a sequencer event record,
|
|
#snd_seq_event_t type.
|
|
Application can send and receive these event records to/from other
|
|
clients via sequencer.
|
|
-An event has several stroage types according to its usage.
|
|
+An event has several storage types according to its usage.
|
|
For example, a SYSEX message is stored on the variable length event,
|
|
and a large synth sample data is delivered using a user-space data pointer.
|
|
|
|
@@ -227,7 +227,7 @@ The type field contains the type of the event
|
|
(1 byte).
|
|
The flags field consists of bit flags which
|
|
describe several conditions of the event (1 byte).
|
|
-It includes the time-stamp mode, data storage type, and scheduling prority.
|
|
+It includes the time-stamp mode, data storage type, and scheduling priority.
|
|
The tag field is an arbitrary tag.
|
|
This tag can used for removing a distinct event from the event queue
|
|
via #snd_seq_remove_events().
|
|
@@ -240,7 +240,7 @@ The data field is a union of event data.
|
|
An event can be delivered either on scheduled or direct dispatch mode.
|
|
On the scheduling mode, an event is once stored on the priority queue
|
|
and delivered later (or even immediately) to the destination,
|
|
-whereas on the direct disatch mode, an event is passed to the destination
|
|
+whereas on the direct dispatch mode, an event is passed to the destination
|
|
without any queue.
|
|
|
|
For a scheduled delivery, a queue to process the event must exist.
|
|
@@ -284,7 +284,7 @@ The time stored in an event record is a union of these two different
|
|
time values.
|
|
|
|
Note that the time format used for real time events is very similar to
|
|
-timeval struct used for unix system time.
|
|
+timeval struct used for Unix system time.
|
|
The absurd resolution of the timestamps allows us to perform very accurate
|
|
conversions between songposition and real time. Round-off errors can be
|
|
neglected.
|
|
@@ -299,7 +299,7 @@ counted from the moment when the queue started.
|
|
An client that relies on these relative timestamps is the MIDI input port.
|
|
As each sequencer queue has it's own clock the only way to deliver events at
|
|
the right time is by using the relative timestamp format. When the event
|
|
-arrives at the queue it is normalised to absolute format.
|
|
+arrives at the queue it is normalized to absolute format.
|
|
|
|
The timestamp format is specified in the flag bitfield masked by
|
|
#SND_SEQ_TIME_STAMP_MASK.
|
|
@@ -320,7 +320,7 @@ fill the port id of source.port and
|
|
both client and port of dest field.
|
|
|
|
If an existing address is set to the destination,
|
|
-the event is simplly delivered to it.
|
|
+the event is simply delivered to it.
|
|
When #SND_SEQ_ADDRESS_SUBSCRIBERS is set to the destination client id,
|
|
the event is delivered to all the clients connected to the source port.
|
|
|
|
@@ -346,7 +346,7 @@ an announcement is sent to subscribers from this port.
|
|
|
|
Some events like SYSEX message, however, need larger data space
|
|
than the standard data.
|
|
-For such events, ALSA sequencer provides seveal different data storage types.
|
|
+For such events, ALSA sequencer provides several different data storage types.
|
|
The data type is specified in the flag bits masked by #SND_SEQ_EVENT_LENGTH_MASK.
|
|
The following data types are available:
|
|
|
|
@@ -359,7 +359,7 @@ A macro #snd_seq_ev_set_fixed() is provided to set this type.
|
|
\par Variable length data
|
|
SYSEX or a returned error use this type.
|
|
The actual data is stored on an extra allocated space.
|
|
-On sequecer kernel, the whole extra-data is duplicated, so that the event
|
|
+On sequencer kernel, the whole extra-data is duplicated, so that the event
|
|
can be scheduled on queue.
|
|
The data contains only the length and the
|
|
pointer of extra-data.
|
|
@@ -430,7 +430,7 @@ Note that PPQ cannot be changed while the queue is running.
|
|
It must be set before the queue is started.
|
|
|
|
On the other hand, in the case of <i>realtime</i> queue, the
|
|
-time resolution is fixed to nanosecononds. There is, however,
|
|
+time resolution is fixed to nanoseconds. There is, however,
|
|
a parameter to change the speed of this queue, called <i>skew</i>.
|
|
You can make the queue faster or slower by setting the skew value
|
|
bigger or smaller. In the API, the skew is defined by two values,
|
|
@@ -488,7 +488,7 @@ special settings.
|
|
In the above example, the tempo is changed immediately after
|
|
the buffer is flushed by #snd_seq_drain_output() call.
|
|
You can schedule the event in a certain queue so that the tempo
|
|
-change happes at the scheduled time, too.
|
|
+change happens at the scheduled time, too.
|
|
|
|
\subsection seq_ev_start Starting and stopping a queue
|
|
|
|
@@ -515,7 +515,7 @@ Each ALSA port can have capability flags.
|
|
The most basic capability flags are
|
|
#SND_SEQ_PORT_CAP_READ and #SND_SEQ_PORT_CAP_WRITE.
|
|
The former means that the port allows to send events to other ports,
|
|
-whereas the latter capability menas
|
|
+whereas the latter capability means
|
|
that the port allows to receive events from other ports.
|
|
You may have noticed that meanings of \c READ and \c WRITE
|
|
are permissions of the port from the viewpoint of other ports.
|
|
@@ -536,7 +536,7 @@ Obviously, these flags have no influence
|
|
if \c READ or \c WRITE> capability is not set.
|
|
|
|
Note that these flags are not necessary if the client subscribes itself
|
|
-to the spcified port.
|
|
+to the specified port.
|
|
For example, when a port makes READ subscription
|
|
to MIDI input port, this port must have #SND_SEQ_PORT_CAP_WRITE capability,
|
|
but no #SND_SEQ_PORT_CAP_SUBS_WRITE capability is required.
|
|
@@ -610,7 +610,7 @@ if #SND_SEQ_PORT_CAP_NO_EXPORT capability is set in either sender or receiver po
|
|
|
|
Assume MIDI input port = 64:0, application port = 128:0, and
|
|
queue for timestamp = 1 with real-time stamp.
|
|
-The application port must have capabilty #SND_SEQ_PORT_CAP_WRITE.
|
|
+The application port must have capability #SND_SEQ_PORT_CAP_WRITE.
|
|
\code
|
|
void capture_keyboard(snd_seq_t *seq)
|
|
{
|
|
@@ -633,7 +633,7 @@ void capture_keyboard(snd_seq_t *seq)
|
|
\subsection seq_subs_ex_out Output to MIDI device
|
|
|
|
Assume MIDI output port = 65:1 and application port = 128:0.
|
|
-The application port must have capabilty #SND_SEQ_PORT_CAP_READ.
|
|
+The application port must have capability #SND_SEQ_PORT_CAP_READ.
|
|
\code
|
|
void subscribe_output(snd_seq_t *seq)
|
|
{
|
|
@@ -790,7 +790,7 @@ void event_filter(snd_seq_t *seq, snd_seq_event_t *ev)
|
|
/**
|
|
* \brief get identifier of sequencer handle
|
|
* \param seq sequencer handle
|
|
- * \return ascii identifier of sequencer handle
|
|
+ * \return ASCII identifier of sequencer handle
|
|
*
|
|
* Returns the ASCII identifier of the given sequencer handle. It's the same
|
|
* identifier specified in snd_seq_open().
|
|
@@ -1610,7 +1610,7 @@ void snd_seq_client_info_event_filter_del(snd_seq_client_info_t *info, int event
|
|
* \param event_type event type to be checked
|
|
* \return 1 if the event type is present, 0 otherwise
|
|
*
|
|
- * Test if the event type is in the filter bitamp of this client_info container.
|
|
+ * Test if the event type is in the filter bitmap of this client_info container.
|
|
*
|
|
* \sa snd_seq_get_client_info(),
|
|
* snd_seq_set_client_info(),
|
|
diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c
|
|
index 3b0960d..86a4970 100644
|
|
--- a/src/seq/seqmid.c
|
|
+++ b/src/seq/seqmid.c
|
|
@@ -317,7 +317,7 @@ int snd_seq_set_client_pool_input(snd_seq_t *seq, size_t size)
|
|
* \param seq sequencer handle
|
|
* \return 0 on success or negative error code
|
|
*
|
|
- * So far, this works ideically like #snd_seq_drop_output().
|
|
+ * So far, this works identically like #snd_seq_drop_output().
|
|
*/
|
|
int snd_seq_reset_pool_output(snd_seq_t *seq)
|
|
{
|
|
@@ -329,7 +329,7 @@ int snd_seq_reset_pool_output(snd_seq_t *seq)
|
|
* \param seq sequencer handle
|
|
* \return 0 on success or negative error code
|
|
*
|
|
- * So far, this works ideically like #snd_seq_drop_input().
|
|
+ * So far, this works identically like #snd_seq_drop_input().
|
|
*/
|
|
int snd_seq_reset_pool_input(snd_seq_t *seq)
|
|
{
|