Compare commits

...

48 Commits

Author SHA1 Message Date
Alexandre Ratchov c4b354abd7 Fix off-by-one array access when 64 channel stream is resampled 2021-07-05 14:18:53 +02:00
Alexandre Ratchov aac840d9f9 oss: Fix sio_setpar() when channel number is not set
The trap value set by sio_initpar() was interpreted as a channel
number, causing sio_setpar() to fail.

Found and analysed by Roman Bogorodskiy <bogorodskiy at gmail.com>,
thanks
2021-06-16 09:32:46 +02:00
Alexandre Ratchov 4d63a3b185 Merge remote-tracking branch 'refs/remotes/origin/master' 2021-05-31 15:09:49 +02:00
Alexandre Ratchov 1f42a21368 Drop assembly for 24-bit fixed-point operations
Now i386 compiler is smart enough to generate a single imul
instruction per fixed-point multiplication. This change also allows
24-bit precision to be used on all archs (if -DADATA_BITS=24 is used).

Tested on arm64 by Doug Moss <dougmoss710 at yahoo.com>, thanks

Suggested and ok naddy@
2021-05-31 15:08:02 +02:00
Alexandre Ratchov 23e9ebebc4 Use ${version} macro instead of hardcoded number 2021-05-25 06:38:12 +02:00
Alexandre Ratchov fde5ad8a68 Bump version to v1.8.0 2021-05-07 09:49:58 +02:00
Alexandre Ratchov 0cb3defc3c sndiod: Use proper mode to setup conversions
Now we need to use opt's mode instead of slot's mode as this is the
one used by dev_cycle(). Fixes monitoring mode not working in case
dev->rchan != dev->pchan
2021-04-29 15:15:33 +02:00
Alexandre Ratchov 12b31d021a sndiod: If mode not allowed in opt structure just play/record silence
This is similar to what we do when device is open and its mode doesn't
match requested mode.
2021-04-26 10:08:11 +02:00
Alexandre Ratchov e450bab99e configure: Generate .pc file with variable-prefixed paths 2021-04-23 14:22:47 +02:00
Alexandre Ratchov a26f7fdf36 configure: add autoconf-style --exec-prefix option 2021-04-23 13:42:05 +02:00
Alexandre Ratchov 90684ad458 sndiod: Allocate (free) conversion buffers in slot_attach (slot_detach)
Besides being better coding style, this allows a client to be detached
and then attached to a device with different parameters.
2021-04-23 11:12:35 +02:00
Alexandre Ratchov 9a5092b08e sndiod: Make play-only and rec-only start at the same position
This is necessary to make rec-only device properly work in
full-duplex. Fixes full-duplex clients stuck on rec-only device.
2021-04-23 11:01:05 +02:00
Alexandre Ratchov 5a20439ec8 Add forgotten sndio.pc.in template 2021-04-20 11:43:40 +02:00
Alexandre Ratchov ce7e998772 Add initial pkg-config support, from brad@ 2021-04-20 11:02:47 +02:00
Alexandre Ratchov 1649f75532 sndiod: Use chronological order for {dev,port}_list 2021-03-08 10:25:31 +01:00
Alexandre Ratchov d6a1624f29 sndiod: Initialize dev->master_enabled earlier 2021-03-08 07:14:16 +01:00
Alexandre Ratchov f712cdb11b sndiod: Style tweak: uniformize pattern to access ctlslot_array 2021-03-08 07:04:32 +01:00
Alexandre Ratchov 6869540f3b sndiod: When a slot structure is recycled allocate new control
Trying to rename the program level control is not needed anymore.
When a slot is given to another program, the new ctl_{new,del}()
functions can be used to delete the control of the old program and
create a new one for the new program. Cleaner, simpler.
2021-03-08 06:38:48 +01:00
Alexandre Ratchov 302865ddd8 sndiod: Style tweak: swap if/else code blocks in slot_new() 2021-03-08 06:06:12 +01:00
Alexandre Ratchov 5451c3c55e Move MTC/MMC to its own structure 2021-03-03 10:49:28 +01:00
Alexandre Ratchov ada702087d Move MIDI control endpoint to opt structure 2021-03-03 10:49:28 +01:00
Alexandre Ratchov bb27629080 Move controls list out of the device structure
Use a unique dynamically allocated ctl->addr key and a "scope"
enum to identify controls that belong to different devices.
2021-03-03 10:49:28 +01:00
Alexandre Ratchov 0adedb2a89 sndiod.8: Some article fixes; from eddie youseph and grep through jmc@ 2021-03-03 10:35:54 +01:00
Alexandre Ratchov a6a6dcb57c sndiod: Fix use-after-free causing crash when devices is disconnected 2021-03-03 10:31:20 +01:00
Alexandre Ratchov e5766cbfc0 sndiod: Allow alternative devices to support different modes
Currently sndiod does not allow you to use alternative devices (-F
devices) which support only a subset of the modes of the main (-f)
device.

For example, if you do `sndiod -f rsnd/0 -F rsnd/1` and:
- rsnd/0 is full-duplex (rec + play).
- rsnd/1 is play-only.

Then you will be unable to use rsnd/1 as sndiod deems it incompatible
and refuses to use it (similarly if rsnd/1 is record-only).

This is annoying. It means if you want to use a record-only or play-only
device, you will either have to kill sndiod and restart it specifying
only that device (`sndiod -f rsnd/1` for the above example), or failing
that, downgrade the functionality of the main device (`-m play`).

This diff (a joint effort between ratchov@ and myself) makes mixing
devices with different modes possible. It does this by making both
recording and playing available for all devices, even if the underlying
hardware doesn't support both modes.

For example, if I try to record from a play-only device, then recording
will succeed, but the captured PCM data will be pure silence. Similarly,
if I try to play to a record-only device, then the audio stream will
disappear into the ether.

This is mostly a no-op for sndiod in the default configuration (except
that play-only devices now accept recording clients). If you use
alternative devices (-F), then it's possible for a record-only device to
be found first, which may be confusing if you just want to hear sound.
We can only assume that if you deviate from defaults, then you know what
you are doing.

From edd@
2021-03-03 10:22:38 +01:00
Alexandre Ratchov 350db6ce41 sndiod: Disable autovol by default and set default volume to 127
The previous behavior of automatically decreasing playback volume
when new programs start playing can be achieved with '-w on'.
2021-03-03 10:16:12 +01:00
Alexandre Ratchov 7534418e78 Make audio clients use struct opt to reach the device 2021-03-03 07:43:34 +01:00
Alexandre Ratchov a78ec25e07 Make control clients use struct opt to reach the device 2021-03-03 07:43:34 +01:00
Alexandre Ratchov 1e1589d70e Rename ctlslot->mask to ctlslot->self to match the rest of the code 2021-03-03 07:43:34 +01:00
Alexandre Ratchov e891595ec7 Order opt_list in opt_new() call order, limit items to OPT_NMAX 2021-03-03 07:43:34 +01:00
Alexandre Ratchov 8e99cca36c Move the options list out of the device structure 2021-03-03 07:43:34 +01:00
Alexandre Ratchov 4710960bbe Move the control client state out of the device structure 2021-03-03 07:43:34 +01:00
Alexandre Ratchov 8bb6267fdb Move the audio clients state out of the device structure 2021-03-03 07:43:34 +01:00
Michael Forney 7551d499a8 alsa: remove existing hardware config before setting a new one
In Linux 5.11, a behavior change for USB audio devices was introduced[0]
that prevents changing parameters such as sample rate or period
size once they are initially set without a snd_pcm_hw_free in
between. This causes sndio to get forced to 48000 Hz and and a
period size of 4800, since those are the parameters set during
sio_open.

To fix this, call snd_pcm_hw_free at the start of sio_alsa_setpar_hw
to remove any previous configuration before setting a new one.

[0] https://bugzilla.kernel.org/show_bug.cgi?id=211941
2021-03-01 09:09:51 +01:00
Alexandre Ratchov ae7637d044 Add example program that generates resampling coefficients 2021-02-12 11:37:56 +01:00
Alexandre Ratchov 2cd5b8e0ab Disconnect MIDI clients controlling disconnected audio device 2021-01-28 11:11:23 +01:00
Alexandre Ratchov ea692625ea Dont attempt to drain disconnected clients 2021-01-28 11:11:23 +01:00
Alexandre Ratchov 6f741dab61 In slot_xxx(), store dev pointer in local variable 2021-01-28 11:11:23 +01:00
Alexandre Ratchov fd4c58aae7 Use everywhere the same pattern to handle fractional clock ticks
No behavior change; the code change is to make the maths easier to
proofread
2021-01-28 11:11:23 +01:00
Alexandre Ratchov 63d4639f9a Make slot_{attach,detach}() the opposite of each other
No bahavior change, but this makes the code is much easier.

Now, slot_attach() moves slot's clock forward and puts the slot on
device list; slot_detach() does the opposite: remove from device list
and move clock backwards.
2021-01-28 11:11:23 +01:00
Alexandre Ratchov ae76a1f941 Drop/move unused prototypes for slot_xxx() 2021-01-28 11:11:23 +01:00
Alexandre Ratchov ac7ddc5301 Drop unused dev_nctl() function 2021-01-27 11:57:35 +01:00
Alexandre Ratchov b1c2df5d9c aucat, sndiod: const-ify lookup tables
From naddy@
2021-01-13 17:30:19 +01:00
Alexandre Ratchov 21580f56b5 Clip 16-bit resampled signal as there's no room for filter overshoots 2021-01-11 14:46:32 +01:00
Alexandre Ratchov 2996dcdc03 Use a 8-th order FIR low-pass filter for resampling 2020-12-20 11:01:27 +01:00
Alexandre Ratchov 40cdc42e22 Check sample signedness when setting up format conversions.
Fixes confusion between s16 and u16 found in aucat and analysed by
James Cook <falsifian at falsifian.org>. Thanks.
2020-12-10 22:05:56 +01:00
Alexandre Ratchov d1c4d40cb9 Sync to OpenBSD.
Mostly the new AUDIO{REC,PLAY}DEVICE envoronment variables
and many man-page improvements.
2020-11-29 13:04:18 +01:00
Alexandre Ratchov 62bc69d71c sndioctl: Flush stdout before polling
If stdout is not flushed, output is not seen by programs using
sndioctl through a pipe (ex. "sndioctl -m | cat" displays nothing).

Patch from Érico Nogueira <ericonr@disroot.org>, idea
from Duncan Overbruck <mail@duncano.de>
2020-09-07 07:14:39 +02:00
31 changed files with 1864 additions and 1160 deletions

View File

@ -31,6 +31,7 @@ distclean: clean
rm -f \
Makefile aucat/Makefile midicat/Makefile sndiod/Makefile \
libsndio/Makefile \
libsndio/sndio.pc \
sndioctl/Makefile \
examples/Makefile \
contrib/init.d.sndiod \

View File

@ -131,28 +131,28 @@ struct au_hdr {
/* followed by optional desc[] continuation */
};
char wav_id_riff[4] = {'R', 'I', 'F', 'F'};
char wav_id_wave[4] = {'W', 'A', 'V', 'E'};
char wav_id_data[4] = {'d', 'a', 't', 'a'};
char wav_id_fmt[4] = {'f', 'm', 't', ' '};
char wav_guid[14] = {
const char wav_id_riff[4] = {'R', 'I', 'F', 'F'};
const char wav_id_wave[4] = {'W', 'A', 'V', 'E'};
const char wav_id_data[4] = {'d', 'a', 't', 'a'};
const char wav_id_fmt[4] = {'f', 'm', 't', ' '};
const char wav_guid[14] = {
0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x80, 0x00,
0x00, 0xAA, 0x00, 0x38,
0x9B, 0x71
};
char aiff_id_form[4] = {'F', 'O', 'R', 'M'};
char aiff_id_aiff[4] = {'A', 'I', 'F', 'F'};
char aiff_id_aifc[4] = {'A', 'I', 'F', 'C'};
char aiff_id_data[4] = {'S', 'S', 'N', 'D'};
char aiff_id_comm[4] = {'C', 'O', 'M', 'M'};
char aiff_id_none[4] = {'N', 'O', 'N', 'E'};
char aiff_id_fl32[4] = {'f', 'l', '3', '2'};
char aiff_id_ulaw[4] = {'u', 'l', 'a', 'w'};
char aiff_id_alaw[4] = {'a', 'l', 'a', 'w'};
const char aiff_id_form[4] = {'F', 'O', 'R', 'M'};
const char aiff_id_aiff[4] = {'A', 'I', 'F', 'F'};
const char aiff_id_aifc[4] = {'A', 'I', 'F', 'C'};
const char aiff_id_data[4] = {'S', 'S', 'N', 'D'};
const char aiff_id_comm[4] = {'C', 'O', 'M', 'M'};
const char aiff_id_none[4] = {'N', 'O', 'N', 'E'};
const char aiff_id_fl32[4] = {'f', 'l', '3', '2'};
const char aiff_id_ulaw[4] = {'u', 'l', 'a', 'w'};
const char aiff_id_alaw[4] = {'a', 'l', 'a', 'w'};
char au_id[4] = {'.', 's', 'n', 'd'};
const char au_id[4] = {'.', 's', 'n', 'd'};
static inline unsigned int
le16_get(le16_t *p)

View File

@ -132,8 +132,8 @@ struct slot *slot_list = NULL;
/*
* length of voice and common MIDI messages (status byte included)
*/
unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
const unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
const unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
char usagestr[] = "usage: aucat [-dn] [-b size] "
"[-c min:max] [-e enc] [-f device] [-g position]\n\t"

View File

@ -18,7 +18,7 @@
#include "dsp.h"
#include "utils.h"
int aparams_ctltovol[128] = {
const int aparams_ctltovol[128] = {
0,
256, 266, 276, 287, 299, 310, 323, 335,
348, 362, 376, 391, 406, 422, 439, 456,
@ -38,7 +38,7 @@ int aparams_ctltovol[128] = {
26008, 27029, 28090, 29193, 30339, 31530, 32768
};
short dec_ulawmap[256] = {
const short dec_ulawmap[256] = {
-32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
-23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
-15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
@ -73,7 +73,7 @@ short dec_ulawmap[256] = {
56, 48, 40, 32, 24, 16, 8, 0
};
short dec_alawmap[256] = {
const short dec_alawmap[256] = {
-5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
-7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
-2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
@ -108,6 +108,75 @@ short dec_alawmap[256] = {
944, 912, 1008, 976, 816, 784, 880, 848
};
const int resamp_filt[RESAMP_LENGTH / RESAMP_STEP + 1] = {
0, 0, 3, 9, 22, 42, 73, 116,
174, 248, 341, 454, 589, 749, 934, 1148,
1392, 1666, 1974, 2316, 2693, 3107, 3560, 4051,
4582, 5154, 5766, 6420, 7116, 7853, 8632, 9451,
10311, 11210, 12148, 13123, 14133, 15178, 16253, 17359,
18491, 19647, 20824, 22018, 23226, 24443, 25665, 26888,
28106, 29315, 30509, 31681, 32826, 33938, 35009, 36033,
37001, 37908, 38744, 39502, 40174, 40750, 41223, 41582,
41819, 41925, 41890, 41704, 41358, 40842, 40147, 39261,
38176, 36881, 35366, 33623, 31641, 29411, 26923, 24169,
21140, 17827, 14222, 10317, 6105, 1580, -3267, -8440,
-13944, -19785, -25967, -32492, -39364, -46584, -54153, -62072,
-70339, -78953, -87911, -97209, -106843, -116806, -127092, -137692,
-148596, -159795, -171276, -183025, -195029, -207271, -219735, -232401,
-245249, -258259, -271407, -284670, -298021, -311434, -324880, -338329,
-351750, -365111, -378378, -391515, -404485, -417252, -429775, -442015,
-453930, -465477, -476613, -487294, -497472, -507102, -516137, -524527,
-532225, -539181, -545344, -550664, -555090, -558571, -561055, -562490,
-562826, -562010, -559990, -556717, -552139, -546205, -538866, -530074,
-519779, -507936, -494496, -479416, -462652, -444160, -423901, -401835,
-377923, -352132, -324425, -294772, -263143, -229509, -193847, -156134,
-116348, -74474, -30494, 15601, 63822, 114174, 166661, 221283,
278037, 336916, 397911, 461009, 526194, 593446, 662741, 734054,
807354, 882608, 959779, 1038826, 1119706, 1202370, 1286768, 1372846,
1460546, 1549808, 1640566, 1732753, 1826299, 1921130, 2017169, 2114336,
2212550, 2311723, 2411770, 2512598, 2614116, 2716228, 2818836, 2921841,
3025142, 3128636, 3232218, 3335782, 3439219, 3542423, 3645282, 3747687,
3849526, 3950687, 4051059, 4150530, 4248987, 4346320, 4442415, 4537163,
4630453, 4722177, 4812225, 4900493, 4986873, 5071263, 5153561, 5233668,
5311485, 5386917, 5459872, 5530259, 5597992, 5662986, 5725160, 5784436,
5840739, 5893999, 5944148, 5991122, 6034862, 6075313, 6112422, 6146142,
6176430, 6203247, 6226559, 6246335, 6262551, 6275185, 6284220, 6289647,
6291456, 6289647, 6284220, 6275185, 6262551, 6246335, 6226559, 6203247,
6176430, 6146142, 6112422, 6075313, 6034862, 5991122, 5944148, 5893999,
5840739, 5784436, 5725160, 5662986, 5597992, 5530259, 5459872, 5386917,
5311485, 5233668, 5153561, 5071263, 4986873, 4900493, 4812225, 4722177,
4630453, 4537163, 4442415, 4346320, 4248987, 4150530, 4051059, 3950687,
3849526, 3747687, 3645282, 3542423, 3439219, 3335782, 3232218, 3128636,
3025142, 2921841, 2818836, 2716228, 2614116, 2512598, 2411770, 2311723,
2212550, 2114336, 2017169, 1921130, 1826299, 1732753, 1640566, 1549808,
1460546, 1372846, 1286768, 1202370, 1119706, 1038826, 959779, 882608,
807354, 734054, 662741, 593446, 526194, 461009, 397911, 336916,
278037, 221283, 166661, 114174, 63822, 15601, -30494, -74474,
-116348, -156134, -193847, -229509, -263143, -294772, -324425, -352132,
-377923, -401835, -423901, -444160, -462652, -479416, -494496, -507936,
-519779, -530074, -538866, -546205, -552139, -556717, -559990, -562010,
-562826, -562490, -561055, -558571, -555090, -550664, -545344, -539181,
-532225, -524527, -516137, -507102, -497472, -487294, -476613, -465477,
-453930, -442015, -429775, -417252, -404485, -391515, -378378, -365111,
-351750, -338329, -324880, -311434, -298021, -284670, -271407, -258259,
-245249, -232401, -219735, -207271, -195029, -183025, -171276, -159795,
-148596, -137692, -127092, -116806, -106843, -97209, -87911, -78953,
-70339, -62072, -54153, -46584, -39364, -32492, -25967, -19785,
-13944, -8440, -3267, 1580, 6105, 10317, 14222, 17827,
21140, 24169, 26923, 29411, 31641, 33623, 35366, 36881,
38176, 39261, 40147, 40842, 41358, 41704, 41890, 41925,
41819, 41582, 41223, 40750, 40174, 39502, 38744, 37908,
37001, 36033, 35009, 33938, 32826, 31681, 30509, 29315,
28106, 26888, 25665, 24443, 23226, 22018, 20824, 19647,
18491, 17359, 16253, 15178, 14133, 13123, 12148, 11210,
10311, 9451, 8632, 7853, 7116, 6420, 5766, 5154,
4582, 4051, 3560, 3107, 2693, 2316, 1974, 1666,
1392, 1148, 934, 749, 589, 454, 341, 248,
174, 116, 73, 42, 22, 9, 3, 0,
0
};
/*
* Generate a string corresponding to the encoding in par,
* return the length of the resulting string.
@ -262,7 +331,9 @@ aparams_log(struct aparams *par)
int
aparams_native(struct aparams *par)
{
return par->bps == sizeof(adata_t) && par->bits == ADATA_BITS &&
return par->sig &&
par->bps == sizeof(adata_t) &&
par->bits == ADATA_BITS &&
(par->bps == 1 || par->le == ADATA_LE) &&
(par->bits == par->bps * 8 || !par->msb);
}
@ -307,8 +378,10 @@ resamp_do(struct resamp *p, adata_t *in, adata_t *out, int icnt, int ocnt)
unsigned int iblksz;
unsigned int ofr;
unsigned int c;
int64_t f[NCHAN_MAX];
adata_t *ctxbuf, *ctx;
unsigned int ctx_start;
int q, qi, qf, n;
/*
* Partially copy structures into local variables, to avoid
@ -344,7 +417,7 @@ resamp_do(struct resamp *p, adata_t *in, adata_t *out, int icnt, int ocnt)
if (diff >= oblksz) {
if (ifr == 0)
break;
ctx_start ^= 1;
ctx_start = (ctx_start - 1) & (RESAMP_NCTX - 1);
ctx = ctxbuf + ctx_start;
for (c = nch; c > 0; c--) {
*ctx = *idata++;
@ -355,13 +428,48 @@ resamp_do(struct resamp *p, adata_t *in, adata_t *out, int icnt, int ocnt)
} else {
if (ofr == 0)
break;
ctx = ctxbuf;
for (c = nch; c > 0; c--) {
s = ctx[ctx_start ^ 1];
ds = ctx[ctx_start] - s;
ctx += RESAMP_NCTX;
*odata++ = s + ADATA_MULDIV(ds, diff, oblksz);
for (c = 0; c < nch; c++)
f[c] = 0;
q = diff * p->filt_step;
n = ctx_start;
while (q < RESAMP_LENGTH) {
qi = q >> RESAMP_STEP_BITS;
qf = q & (RESAMP_STEP - 1);
s = resamp_filt[qi];
ds = resamp_filt[qi + 1] - s;
s += (int64_t)qf * ds >> RESAMP_STEP_BITS;
ctx = ctxbuf;
for (c = 0; c < nch; c++) {
f[c] += (int64_t)ctx[n] * s;
ctx += RESAMP_NCTX;
}
q += p->filt_cutoff;
n = (n + 1) & (RESAMP_NCTX - 1);
}
for (c = 0; c < nch; c++) {
s = f[c] >> RESAMP_BITS;
s = (int64_t)s * p->filt_cutoff >> RESAMP_BITS;
#if ADATA_BITS == 16
/*
* In 16-bit mode, we've no room for filter
* overshoots, so we need to clip the signal
* to avoid 16-bit integers to wrap around.
* In 24-bit mode, samples may exceed the
* [-1:1] range. Later, cmap_add() will clip
* them, so no need to clip them here as well.
*/
if (s >= ADATA_UNIT)
s = ADATA_UNIT - 1;
else if (s < -ADATA_UNIT)
s = -ADATA_UNIT;
#endif
*odata++ = s;
}
diff += iblksz;
ofr--;
}
@ -427,6 +535,13 @@ resamp_init(struct resamp *p, unsigned int iblksz,
p->nch = nch;
p->ctx_start = 0;
memset(p->ctx, 0, sizeof(p->ctx));
if (p->iblksz < p->oblksz) {
p->filt_cutoff = RESAMP_UNIT;
p->filt_step = RESAMP_UNIT / p->oblksz;
} else {
p->filt_cutoff = (int64_t)RESAMP_UNIT * p->oblksz / p->iblksz;
p->filt_step = RESAMP_UNIT / p->iblksz;
}
#ifdef DEBUG
if (log_level >= 3) {
log_puts("resamp: ");
@ -727,7 +842,7 @@ dec_do_ulaw(struct conv *p, unsigned char *in,
unsigned int f;
unsigned char *idata;
adata_t *odata;
short *map;
const short *map;
#ifdef DEBUG
if (log_level >= 4) {

View File

@ -40,57 +40,43 @@ typedef short adata_t;
#elif ADATA_BITS == 24
#if defined(__i386__) && defined(__GNUC__)
static inline int
fp24_mul(int x, int a)
{
int res;
asm volatile (
"imull %2\n\t"
"shrdl $23, %%edx, %%eax\n\t"
: "=a" (res)
: "a" (x), "r" (a)
: "%edx"
);
return res;
}
static inline int
fp24_muldiv(int x, int a, int b)
{
int res;
asm volatile (
"imull %2\n\t"
"idivl %3\n\t"
: "=a" (res)
: "a" (x), "d" (a), "r" (b)
);
return res;
}
#define ADATA_MUL(x,y) fp24_mul(x, y)
#define ADATA_MULDIV(x,y,z) fp24_muldiv(x, y, z);
#elif defined(__amd64__) || defined(__sparc64__)
#define ADATA_MUL(x,y) \
((int)(((long long)(x) * (long long)(y)) >> (ADATA_BITS - 1)))
#define ADATA_MULDIV(x,y,z) \
((int)((long long)(x) * (long long)(y) / (long long)(z)))
#else
#error "no 24-bit code for this architecture"
#endif
typedef int adata_t;
#else
#error "only 16-bit and 24-bit precisions are supported"
#endif
/*
* The FIR is sampled and stored in a table of fixed-point numbers
* with 23 fractional bits. For convenience, we use the same fixed-point
* numbers to represent time and to walk through the table.
*/
#define RESAMP_BITS 23
#define RESAMP_UNIT (1 << RESAMP_BITS)
/*
* Filter window length (the time unit is RESAMP_UNIT)
*/
#define RESAMP_LENGTH (8 * RESAMP_UNIT)
/*
* Time between samples of the FIR (the time unit is RESAMP_UNIT)
*/
#define RESAMP_STEP_BITS (RESAMP_BITS - 6)
#define RESAMP_STEP (1 << RESAMP_STEP_BITS)
/*
* Maximum downsample/upsample ratio we support, must be a power of two.
* The ratio between the max and the min sample rates is 192kHz / 4kHz = 48,
* so we can use 64
*/
#define RESAMP_RATIO 64
/*
* Maximum size of the encording string (the longest possible
* encoding is ``s24le3msb'').
@ -111,9 +97,10 @@ struct aparams {
};
struct resamp {
#define RESAMP_NCTX 2
#define RESAMP_NCTX (RESAMP_LENGTH / RESAMP_UNIT * RESAMP_RATIO)
unsigned int ctx_start;
adata_t ctx[NCHAN_MAX * RESAMP_NCTX];
int filt_cutoff, filt_step;
unsigned int iblksz, oblksz;
int diff;
int nch;
@ -138,7 +125,7 @@ struct cmap {
};
#define MIDI_TO_ADATA(m) (aparams_ctltovol[m] << (ADATA_BITS - 16))
extern int aparams_ctltovol[128];
extern const int aparams_ctltovol[128];
void aparams_init(struct aparams *);
void aparams_log(struct aparams *);

46
configure vendored
View File

@ -6,11 +6,13 @@
help() {
cat << END
Usage: configure [options]
--prefix=DIR set install prefix to DIR [$prefix]
--bindir=DIR install executables in DIR [\$prefix/bin]
--prefix=DIR set arch independent install prefix to DIR [$prefix]
--exec-prefix=DIR set arch dependent install prefix to DIR [\$prefix]
--bindir=DIR install executables in DIR [\$exec_prefix/bin]
--datadir=DIR install read-only data in DIR [\$prefix/share]
--includedir=DIR install header files in DIR [\$prefix/include]
--libdir=DIR install libraries in DIR [\$prefix/lib]
--libdir=DIR install libraries in DIR [\$exec_prefix/lib]
--pkgconfdir=DIR install pkg-config file in DIR [\$libdir/pkgconfig]
--mandir=DIR install man pages in DIR [\$prefix/man]
--precision=NUMBER aucat/sndiod processing precision [$precision]
--privsep-user=USER non-privileged user for sndio daemon [$user]
@ -31,6 +33,7 @@ END
#
# defaults
#
version=1.8.0 # package version (used by pkg-config)
prefix=/usr/local # where to install sndio
so="libsndio.so.\${MAJ}.\${MIN}" # shared libs to build
alsa=no # do we want alsa support ?
@ -43,11 +46,13 @@ user=_sndio # non-privileged user for sndio daemon
libbsd=no # use libbsd?
so_ldflags= # extra linker flags for shared libs
unset vars # variables passed as arguments
unset exec_prefix # prefix for arch. independent files
unset bindir # path where to install binaries
unset datadir # path where to install doc
unset mandir # path where to install man pages
unset includedir # path where to install header file
unset libdir # path where to install library
unset pkgconfdir # path where to install pkg-config file
unset defs # no extra #defines
unset ldadd # no extra libraries (-l options)
unset dev
@ -112,6 +117,9 @@ for i; do
--prefix=*)
prefix="${i#--prefix=}"
shift;;
--exec-prefix=*)
exec_prefix="${i#--exec-prefix=}"
shift;;
--bindir=*)
bindir="${i#--bindir=}"
shift;;
@ -124,6 +132,9 @@ for i; do
--libdir=*)
libdir="${i#--libdir=}"
shift;;
--pkgconfdir=*)
pkgconfdir="${i#--pkgconfdir=}"
shift;;
--mandir=*)
mandir="${i#--mandir=}"
shift;;
@ -189,10 +200,12 @@ done
#
# if $xxxdir is not specified, define it to $prefix/xxx
#
bindir="${bindir:-$prefix/bin}"
exec_prefix="${exec_prefix:-$prefix}"
bindir="${bindir:-$exec_prefix/bin}"
datadir="${datadir:-$prefix/share}"
includedir="${includedir:-$prefix/include}"
libdir="${libdir:-$prefix/lib}"
libdir="${libdir:-$exec_prefix/lib}"
pkgconfdir="${pkgconfdir:-$prefix/lib/pkgconfig}"
mandir="${mandir:-$prefix/share/man}"
#
@ -264,7 +277,9 @@ do
-e "s:@datadir@:$datadir:" \
-e "s:@includedir@:$includedir:" \
-e "s:@libdir@:$libdir:" \
-e "s:@pkgconfdir@:$pkgconfdir:" \
-e "s:@mandir@:$mandir:" \
-e "s:@version@:$version:" \
-e "s:@defs@:$defs:" \
-e "s:@ldadd@:$ldadd:" \
-e "s:@so@:$so:" \
@ -278,14 +293,35 @@ do
< $f.in > $f
done
#
# Generate sndio.pc. Substitute path prefixes with ${prefix} or
# ${exec_prefix} variable names, so that they can be overriden by the
# user
#
cat <<EOF >libsndio/sndio.pc
prefix=${prefix}
exec_prefix=`echo $exec_prefix | sed -e "s:^${prefix}:\\${prefix}:"`
libdir=`echo $libdir | sed -e "s:^${exec_prefix}:\\${exec_prefix}:"`
includedir=`echo $includedir | sed -e "s:^${prefix}:\\${prefix}:"`
Name: sndio
Description: sndio library
Version: ${version}
Requires:
Libs: -L\${libdir} -lsndio
Cflags: -I\${includedir}
EOF
chmod +x contrib/init.d.sndiod
cat <<EOF
version.................. $version
bindir................... $bindir
datadir.................. $datadir
includedir............... $includedir
libdir................... $libdir
pkgconfdir............... $pkgconfdir
mandir................... $mandir
user..................... $user
libbsd................... $libbsd

View File

@ -13,7 +13,7 @@ LDADD = -lsndio @ldadd@
# variables defined on configure script command line (if any)
@vars@
PROG = play rec fd vol cap
PROG = play rec fd vol cap gen-fir
all: ${PROG}
@ -32,6 +32,9 @@ vol: vol.o tools.o
cap: cap.o tools.o
${CC} ${LDFLAGS} ${LIB} -o cap cap.o tools.o ${LDADD}
gen-fir: gen-fir.c
${CC} ${LDFLAGS} ${LIB} -o gen-fir gen-fir.c -lm
.c.o:
${CC} ${CFLAGS} ${INCLUDE} ${DEFS} -c $<

119
examples/gen-fir.c Normal file
View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 2021 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This program generates coefficients for the resampling low-pass
* filter used in sndiod and aucat.
*
* The coefficients correspond to the filter impulse response sampled
* with 64 points per time unit; they are represented as 24-bit
* fixed-point numbers.
*
* The filter is an ideal low-pass (sinc function), multiplied by a 8
* time units long Blackman window. The filter cut-off frequency is
* set at 0.75 of the Nyquist frequency (for instance, at 48kHz, the
* cut-off will be at 18kHz, beyond audible frequencies).
*
* References:
* https://en.wikipedia.org/wiki/Sinc_filter
* https://en.wikipedia.org/wiki/Window_function
* https://ccrma.stanford.edu/~jos/resample/
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int order = 8, factor = 64, bits = 24;
/*
* convert double to fixed-point integer
*/
int
mkfp(double x)
{
long long r, unit;
unit = 1LL << (bits - 1);
r = x * unit + ((x >= 0) ? 0.5 : -0.5);
if (r < -unit || r >= unit) {
fprintf(stderr, "%g: sample out of range\n", x);
exit(1);
}
return r;
}
/*
* Blackman window function
*/
double
win_blackman(double a, double x)
{
if (x < 0)
x = -x;
return (x >= 0.5) ? 0 :
0.5 * (1 - a) +
0.5 * cos(2 * M_PI * x) +
0.5 * a * cos(4 * M_PI * x);
}
/*
* Ideal low-pass filter response
*/
double
lowpass_sinc(double cutoff, double t)
{
double arg;
if (t == 0)
return cutoff;
arg = M_PI * t;
return sin(cutoff * arg) / arg;
}
/*
* Convert table index to time
*/
double
timeof(int i)
{
return (double)(i - order * factor / 2) / factor;
}
int
main(void)
{
double t;
double h;
double cutoff = 3. / 4.;
double param = 0.16;
int i;
printf("{");
for (i = 0; i <= order * factor; i++) {
t = timeof(i);
h = lowpass_sinc(cutoff, t) * win_blackman(param, t / order);
if (i != 0)
printf(",");
printf("%s", (i % 8 == 0) ? "\n\t" : " ");
printf("%7d", mkfp(h));
}
printf("\n};\n");
return 0;
}

View File

@ -24,6 +24,7 @@ SO_LDFLAGS = -shared @so_ldflags@
#
INCLUDE_DIR = @includedir@
LIB_DIR = @libdir@
PKGCONF_DIR = @pkgconfdir@
MAN3_DIR = @mandir@/man3
MAN7_DIR = @mandir@/man7
@ -57,10 +58,12 @@ all: ${SO} ${SO_LINK} ${SO_LINK_MAJ}
install:
mkdir -p ${DESTDIR}${INCLUDE_DIR}
mkdir -p ${DESTDIR}${LIB_DIR}
mkdir -p ${DESTDIR}${PKGCONF_DIR}
mkdir -p ${DESTDIR}${MAN3_DIR}
mkdir -p ${DESTDIR}${MAN7_DIR}
cp sndio.h ${DESTDIR}${INCLUDE_DIR}
cp -R ${SO} ${SO_LINK} ${SO_LINK_MAJ} ${DESTDIR}${LIB_DIR}
cp sndio.pc ${DESTDIR}${PKGCONF_DIR}
cp sio_open.3 ${DESTDIR}${MAN3_DIR}
ln -sf sio_open.3 ${DESTDIR}${MAN3_DIR}/sio_close.3
ln -sf sio_open.3 ${DESTDIR}${MAN3_DIR}/sio_setpar.3
@ -100,6 +103,7 @@ install:
uninstall:
rm -f ${DESTDIR}${INCLUDE_DIR}/sndio.h
cd ${DESTDIR}${LIB_DIR} && rm -f ${SO} ${SO_LINK}
cd ${DESTDIR}${PKGCONF_DIR} && rm -f sndio.pc
cd ${DESTDIR}${MAN3_DIR} && rm -f ${MAN3}
cd ${DESTDIR}${MAN7_DIR} && rm -f ${MAN7}

View File

@ -53,7 +53,12 @@ sio_open(const char *str, unsigned int mode, int nbio)
if (str == NULL) /* backward compat */
str = devany;
if (strcmp(str, devany) == 0 && !issetugid()) {
str = getenv("AUDIODEVICE");
if ((mode & SIO_PLAY) == 0)
str = getenv("AUDIORECDEVICE");
if ((mode & SIO_REC) == 0)
str = getenv("AUDIOPLAYDEVICE");
if (mode == (SIO_PLAY | SIO_REC) || str == NULL)
str = getenv("AUDIODEVICE");
if (str == NULL)
str = devany;
}

View File

@ -554,6 +554,11 @@ sio_alsa_setpar_hw(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwp,
req_rate = *rate;
err = snd_pcm_hw_free(pcm);
if (err < 0) {
DALSA("couldn't reset hw configuration", err);
return 0;
}
err = snd_pcm_hw_params_any(pcm, hwp);
if (err < 0) {
DALSA("couldn't init pars", err);

View File

@ -34,7 +34,8 @@
.Nm sio_eof ,
.Nm sio_setvol ,
.Nm sio_onvol ,
.Nm sio_initpar
.Nm sio_initpar ,
.Nm SIO_BPS
.Nd sndio interface to audio devices
.Sh SYNOPSIS
.In sndio.h
@ -80,8 +81,8 @@
.Fc
.Ft "void"
.Fn sio_initpar "struct sio_par *par"
.\"Fd #define SIO_BPS(bits)
.\"Fd #define SIO_LE_NATIVE
.Ft unsigned int
.Fn SIO_BPS "unsigned int bits"
.Sh DESCRIPTION
The
.Nm sndio
@ -270,7 +271,7 @@ To ease filling the
structure, the
following macros can be used:
.Bl -tag -width "SIO_BPS(bits)"
.It Dv SIO_BPS Ns Pq Fa bits
.It Fn SIO_BPS bits
Return the smallest value for
.Fa bps
that is a power of two and that is large enough to
@ -280,6 +281,7 @@ hold
Can be used to set the
.Fa le
parameter when native byte order is required.
It is 1 if the native byte order is little endian or 0 otherwise.
.El
.Ss Getting device capabilities
There's no way to get an exhaustive list of all parameter
@ -385,17 +387,17 @@ bitmasks should always be used.
.Ss Starting and stopping the device
The
.Fn sio_start
function puts the device in a waiting state:
the device will wait for playback data to be provided
(using the
.Fn sio_write
function).
Once enough data is queued to ensure that play buffers
will not underrun, actual playback is started automatically.
If record mode only is selected, then recording starts
immediately.
function prepares the device to start.
Once the play buffer is full, i.e.\&
.Fa sio_par.bufsz
samples are queued with
.Fn sio_write ,
playback starts automatically.
If record-only mode is selected, then
.Fn sio_start
starts recording immediately.
In full-duplex mode, playback and recording will start
synchronously as soon as enough data to play is available.
synchronously as soon as the play buffer is full.
.Pp
The
.Fn sio_stop
@ -579,7 +581,7 @@ parameter of the
structure, the audio subsystem will behave as follows:
.Bl -tag -width "SIO_IGNORE"
.It Dv SIO_IGNORE
The devices pauses during overruns and underruns,
The device pauses during overruns and underruns,
thus the current position (obtained through
.Fn sio_onmove )
stops being incremented.
@ -735,10 +737,17 @@ The debug level:
may be a value between 0 and 2.
.El
.Sh SEE ALSO
.Xr mio_open 3 ,
.Xr sioctl_open 3 ,
.Xr audio 4 ,
.Xr sndio 7 ,
.Xr sndiod 8 ,
.Xr audio 9
.Sh HISTORY
These functions first appeared in
.Ox 4.5 .
.Sh AUTHORS
.An Alexandre Ratchov Aq Mt ratchov@openbsd.org
.Sh BUGS
The
.Xr audio 4

View File

@ -436,9 +436,9 @@ sio_oss_setpar(struct sio_hdl *sh, struct sio_par *par)
if (hdl->rate > 192000)
hdl->rate = 192000;
if (hdl->sio.mode & SIO_PLAY)
if ((hdl->sio.mode & SIO_PLAY) && par->pchan != ~0U)
hdl->chan = par->pchan;
else if (hdl->sio.mode & SIO_REC)
else if ((hdl->sio.mode & SIO_REC) && par->rchan != ~0U)
hdl->chan = par->rchan;
if (ioctl(hdl->fd, SNDCTL_DSP_SETFMT, &hdl->fmt) == -1) {

View File

@ -19,7 +19,7 @@
.Os
.Sh NAME
.Nm sndio
.Nd interface to audio and MIDI
.Nd audio and MIDI device descriptors
.Sh DESCRIPTION
Programs access audio and MIDI hardware using the
.Nm sndio
@ -62,8 +62,9 @@ Additionally,
exposes a MIDI port used to control audio programs using
standard MIDI Machine Control (MMC), MIDI Time Code (MTC),
and master volume messages.
.Sh AUDIO AND MIDI DESCRIPTORS
From the user's perspective every audio device or MIDI port
.Ss Server device descriptors
From the user's perspective, every audio device or MIDI port exposed by
.Xr sndiod 8
has a descriptor of the form:
.Bd -literal -offset center
type[@hostname][,servnum]/devnum[.option]
@ -77,14 +78,6 @@ The type of the audio device or MIDI port.
Possible values are:
.Pp
.Bl -tag -width "midithru" -offset 3n -compact
.It Cm rsnd
Raw
.Xr audio 4
device.
.It Cm rmidi
Raw
.Xr midi 4
port.
.It Cm snd
Audio device exposed by
.Xr sndiod 8 .
@ -94,8 +87,6 @@ MIDI thru port created with
.It Cm midi
MIDI port exposed by
.Xr sndiod 8 .
.It Cm default
Default audio device or MIDI port (see below).
.El
.It Ar hostname
The hostname or address where the remote
@ -113,47 +104,49 @@ Useful only if multiple
servers are running on the same system.
.It Ar devnum
Device number.
For hardware audio or MIDI ports, this corresponds to
the character device minor number.
For audio devices or MIDI ports created with
It corresponds to the number of the corresponding
.Fl f
or
.Fl q
option on the
.Xr sndiod 8
it corresponds to the number of the corresponding
.Fl fq
option on the command line.
command line.
.It Ar option
Corresponds to the sub-device string registered using the
.Fl s
option of
.Xr sndiod 8 .
.El
.Ss Raw device descriptors
Every raw audio device or MIDI port has a descriptor of the form:
.Pp
For example:
.D1 Ar type Ns / Ns Ar devnum
.Pp
.Bl -tag -width "snd/0.rear" -offset 3n -compact
.It Li rsnd/0
Raw access to first audio device.
.It Li rmidi/5
Raw access to MIDI port number 5.
.It Li snd/0
Audio device referred by first
.Fl f
option of
.Xr sndiod 8 .
.It Li snd/0.rear
Sub-device registered with
.Fl s Fa rear .
.It Li midithru/0
First MIDI thru port created with
.Xr sndiod 8 .
.El
.Sh DEFAULTS
If
The
.Ar type
can be either
.Cm rsnd
or
.Cm rmidi .
The rsnd/0 device descriptor accesses the
.Pa /dev/audio0
device, rsnd/1 accesses
.Pa /dev/audio1 ,
and so on.
Similarly, rmidi/0 accesses
.Pa /dev/rmidi0
and so on.
.Ss Default Audio and MIDI devices
When no audio device descriptor is provided to a program
or when the reserved word
.Cm default
is used as the audio device, the program will use the
one specified in the
.Ev AUDIODEVICE
environment variable.
If it is not set, the program first tries to connect to
.Ev AUDIODEVICE , AUDIOPLAYDEVICE
and/or
.Ev AUDIORECDEVICE
environment variables.
If they are not set, the program first tries to connect to
.Li snd/0 .
If that fails, it then tries to use
.Li rsnd/0 .
@ -175,7 +168,7 @@ is running, this allows programs to exchange MIDI data on
machines with no MIDI hardware by default, e.g. a MIDI player
could use a software synthesizer with no manual configuration
required.
.Sh AUTHENTICATION
.Ss Authentication
For privacy reasons only one user may have connections to
.Xr sndiod 8
at a given time.
@ -190,10 +183,20 @@ and contains 128 bits of raw random data.
If a session needs to be shared between multiple users, they
can connect to the server using the same cookie.
.Sh ENVIRONMENT
.Bl -tag -width "AUDIODEVICEXXX" -compact
.Bl -tag -width "AUDIOPLAYDEVICE" -compact
.It Ev AUDIODEVICE
Audio device descriptor to use
when no descriptor is explicitly specified to a program.
.It Ev AUDIOPLAYDEVICE
Audio device descriptor to use for play-only mode
when no descriptor is explicitly specified to a program.
Overrides
.Ev AUDIODEVICE .
.It Ev AUDIORECDEVICE
Audio device descriptor to use for record-only mode
when no descriptor is explicitly specified to a program.
Overrides
.Ev AUDIODEVICE .
.It Ev MIDIDEVICE
MIDI port descriptor to use
when no descriptor is explicitly specified to a program.
@ -207,9 +210,31 @@ if the program has the set-user-ID or set-group-ID bits set.
.It Pa ~/.sndio/cookie
User's session authentication cookie.
.It Pa /dev/audioN
Audio devices.
Raw audio devices.
.It Pa /dev/rmidiN
MIDI ports.
Raw MIDI ports.
.El
.Sh EXAMPLES
.Bl -tag -width "snd/0.rear" -compact
.It Li snd/0
Audio device referred to by the first
.Fl f
option of
.Xr sndiod 8 .
.It Li snd/0.rear
Sub-device registered with
.Dq -s rear .
.It Li midithru/0
First MIDI thru port created with
.Xr sndiod 8 .
.It Li default
Default audio or MIDI device.
.It Li rsnd/0
Direct hardware access to
.Pa /dev/audio0 .
.It Li rmidi/5
Direct hardware access to
.Pa /dev/rmidi5 .
.El
.Sh SEE ALSO
.Xr aucat 1 ,

View File

@ -72,8 +72,8 @@ The set of available controls depends on the audio device.
Commands use the following two formats to display and change
controls respectively:
.Pp
.Dl group/stream[channel].function
.Dl group/stream[channel].function=value
.Dl [group/]stream[channel].function
.Dl [group/]stream[channel].function=value
.Pp
On the left-hand side are specified the control group (if any),
the affected stream name, and the optional channel number.
@ -119,5 +119,13 @@ Toggle the above
control:
.Pp
.Dl $ sndioctl output.mute=!
.Pp
Allow audio recording and set all
.Cm input
channels to 50%:
.Bd -literal -offset indent
# sysctl kern.audio.record=1
$ sndioctl input.mute=0 input.level=0.5
.Ed
.Sh SEE ALSO
.Xr sioctl_open 3

View File

@ -1015,6 +1015,7 @@ main(int argc, char **argv)
exit(1);
}
for (;;) {
fflush(stdout);
nfds = sioctl_pollfd(hdl, pfds, POLLIN);
if (nfds == 0)
break;

View File

@ -55,23 +55,26 @@ sndiod: ${OBJS}
abuf.o: abuf.c abuf.h utils.h
dev.o: dev.c ../bsd-compat/bsd-compat.h abuf.h defs.h dev.h \
dsp.h siofile.h file.h midi.h miofile.h sysex.h utils.h
dsp.h siofile.h file.h dev_sioctl.h opt.h midi.h \
miofile.h sysex.h utils.h
dev_sioctl.o: dev_sioctl.c abuf.h defs.h dev.h dsp.h siofile.h file.h \
utils.h
dev_sioctl.h opt.h utils.h ../bsd-compat/bsd-compat.h
dsp.o: dsp.c dsp.h defs.h utils.h
file.o: file.c file.h utils.h
file.o: file.c ../bsd-compat/bsd-compat.h file.h utils.h
listen.o: listen.c listen.h file.h sock.h ../libsndio/amsg.h \
utils.h ../bsd-compat/bsd-compat.h
midi.o: midi.c abuf.h defs.h dev.h dsp.h siofile.h file.h midi.h \
miofile.h sysex.h utils.h ../bsd-compat/bsd-compat.h
midi.o: midi.c abuf.h defs.h dev.h dsp.h siofile.h file.h \
dev_sioctl.h opt.h midi.h miofile.h sysex.h utils.h \
../bsd-compat/bsd-compat.h
miofile.o: miofile.c defs.h file.h midi.h abuf.h miofile.h utils.h
opt.o: opt.c dev.h abuf.h dsp.h defs.h siofile.h file.h opt.h \
utils.h
opt.o: opt.c dev.h abuf.h dsp.h defs.h siofile.h file.h \
dev_sioctl.h opt.h midi.h miofile.h sysex.h utils.h
siofile.o: siofile.c abuf.h defs.h dev.h dsp.h siofile.h file.h \
utils.h
dev_sioctl.h opt.h utils.h
sndiod.o: sndiod.c ../libsndio/amsg.h defs.h dev.h abuf.h dsp.h \
siofile.h file.h listen.h midi.h miofile.h opt.h sock.h \
utils.h ../bsd-compat/bsd-compat.h
sock.o: sock.c abuf.h defs.h dev.h dsp.h siofile.h file.h midi.h \
miofile.h opt.h sock.h ../libsndio/amsg.h utils.h
siofile.h file.h dev_sioctl.h opt.h listen.h midi.h \
miofile.h sock.h utils.h ../bsd-compat/bsd-compat.h
sock.o: sock.c abuf.h defs.h dev.h dsp.h siofile.h file.h \
dev_sioctl.h opt.h midi.h miofile.h sock.h \
../libsndio/amsg.h utils.h ../bsd-compat/bsd-compat.h
utils.o: utils.c utils.h

File diff suppressed because it is too large Load Diff

View File

@ -21,11 +21,17 @@
#include "dsp.h"
#include "siofile.h"
#include "dev_sioctl.h"
#include "opt.h"
#define CTLADDR_SLOT_LEVEL(n) (n)
#define CTLADDR_MASTER (DEV_NSLOT)
#define CTLADDR_ALT_SEL (CTLADDR_MASTER + 1)
#define CTLADDR_END (CTLADDR_ALT_SEL + DEV_NMAX)
/*
* preallocated audio clients
*/
#define DEV_NSLOT 8
/*
* preallocated control clients
*/
#define DEV_NCTLSLOT 8
/*
* audio stream state structure
@ -50,7 +56,6 @@ struct ctlops
struct slot {
struct slotops *ops; /* client callbacks */
struct slot *next; /* next on the play list */
struct dev *dev; /* device this belongs to */
struct opt *opt; /* config used */
void *arg; /* user data for callbacks */
struct aparams par; /* socket side params */
@ -82,7 +87,7 @@ struct slot {
int xrun; /* underrun policy */
int skip; /* cycles to skip (for xrun) */
#define SLOT_BUFSZ(s) \
((s)->appbufsz + (s)->dev->bufsz / (s)->dev->round * (s)->round)
((s)->appbufsz + (s)->opt->dev->bufsz / (s)->opt->dev->round * (s)->round)
int appbufsz; /* slot-side buffer size */
int round; /* slot-side block size */
int rate; /* slot-side sample rate */
@ -104,24 +109,13 @@ struct slot {
unsigned int id; /* process id */
};
struct opt {
struct opt *next;
#define OPT_NAMEMAX 11
char name[OPT_NAMEMAX + 1];
int maxweight; /* max dynamic range for clients */
int pmin, pmax; /* play channels */
int rmin, rmax; /* recording channels */
int mmc; /* true if MMC control enabled */
int dup; /* true if join/expand enabled */
int mode; /* bitmap of MODE_XXX */
};
/*
* subset of channels of a stream
*/
struct ctl {
struct ctl *next;
#define CTL_NONE 0 /* deleted */
#define CTL_NUM 2 /* number (aka integer value) */
#define CTL_SW 3 /* on/off switch, only bit 7 counts */
@ -129,7 +123,34 @@ struct ctl {
#define CTL_LIST 5 /* switch, element of a list */
#define CTL_SEL 6 /* element of a selector */
unsigned int type; /* one of above */
unsigned int addr; /* control address */
#define CTL_HW 0
#define CTL_DEV_MASTER 1
#define CTL_DEV_ALT 2
#define CTL_SLOT_LEVEL 3
unsigned int scope;
union {
struct {
void *arg0;
void *arg1;
} any;
struct {
struct dev *dev;
unsigned int addr;
} hw;
struct {
struct dev *dev;
} dev_master;
struct {
struct dev *dev;
unsigned int idx;
} dev_alt;
struct {
struct slot *slot;
} slot_level;
} u;
unsigned int addr; /* slot side control address */
#define CTL_NAMEMAX 16 /* max name lenght */
char func[CTL_NAMEMAX]; /* parameter function name */
char group[CTL_NAMEMAX]; /* group aka namespace */
@ -150,19 +171,50 @@ struct ctl {
struct ctlslot {
struct ctlops *ops;
void *arg;
struct dev *dev;
unsigned int mask;
struct opt *opt;
unsigned int self; /* equal to (1 << index) */
unsigned int mode;
};
/*
* MIDI time code (MTC)
*/
struct mtc {
/*
* MIDI time code (MTC) states
*/
#define MTC_STOP 1 /* stopped, can't start */
#define MTC_START 2 /* attempting to start */
#define MTC_RUN 3 /* started */
unsigned int tstate; /* one of MTC_* constants */
struct dev *dev;
unsigned int origin; /* MTC start time */
unsigned int fps; /* MTC frames per second */
#define MTC_FPS_24 0
#define MTC_FPS_25 1
#define MTC_FPS_30 3
unsigned int fps_id; /* one of above */
unsigned int hr; /* MTC hours */
unsigned int min; /* MTC minutes */
unsigned int sec; /* MTC seconds */
unsigned int fr; /* MTC frames */
unsigned int qfr; /* MTC quarter frames */
int delta; /* rel. to the last MTC tick */
int refs;
};
/*
* audio device with plenty of slots
*/
struct dev {
struct dev *next;
struct slot *slot_list; /* audio streams attached */
struct opt *opt_list;
struct midi *midi;
/*
* name used for various controls
*/
char name[CTL_NAMEMAX];
/*
* audio device (while opened)
@ -181,13 +233,6 @@ struct dev {
unsigned char *encbuf; /* buffer for encoding */
unsigned char *decbuf; /* buffer for decoding */
/*
* preallocated audio sub-devices
*/
#define DEV_NSLOT 8
struct slot slot[DEV_NSLOT];
unsigned int serial; /* for slot allocation */
/*
* current position, relative to the current cycle
*/
@ -225,46 +270,17 @@ struct dev {
unsigned int bufsz, round, rate;
unsigned int prime;
/*
* MIDI time code (MTC)
*/
struct {
unsigned int origin; /* MTC start time */
unsigned int fps; /* MTC frames per second */
#define MTC_FPS_24 0
#define MTC_FPS_25 1
#define MTC_FPS_30 3
unsigned int fps_id; /* one of above */
unsigned int hr; /* MTC hours */
unsigned int min; /* MTC minutes */
unsigned int sec; /* MTC seconds */
unsigned int fr; /* MTC frames */
unsigned int qfr; /* MTC quarter frames */
int delta; /* rel. to the last MTC tick */
int refs;
} mtc;
/*
* MIDI machine control (MMC)
*/
#define MMC_STOP 1 /* stopped, can't start */
#define MMC_START 2 /* attempting to start */
#define MMC_RUN 3 /* started */
unsigned int tstate; /* one of above */
unsigned int master; /* software vol. knob */
unsigned int master_enabled; /* 1 if h/w has no vo. knob */
/*
* control
*/
struct ctl *ctl_list;
#define DEV_NCTLSLOT 8
struct ctlslot ctlslot[DEV_NCTLSLOT];
};
extern struct dev *dev_list;
extern struct ctl *ctl_list;
extern struct slot slot_array[DEV_NSLOT];
extern struct ctlslot ctlslot_array[DEV_NCTLSLOT];
extern struct mtc mtc_array[1];
void slot_array_init(void);
void dev_log(struct dev *);
void dev_abort(struct dev *);
@ -291,39 +307,56 @@ void dev_cycle(struct dev *);
/*
* midi & midi call-backs
*/
void dev_mmcstart(struct dev *);
void dev_mmcstop(struct dev *);
void dev_mmcloc(struct dev *, unsigned int);
void dev_master(struct dev *, unsigned int);
void dev_midi_send(struct dev *, void *, int);
void dev_midi_vol(struct dev *, struct slot *);
void dev_midi_master(struct dev *);
void dev_midi_slotdesc(struct dev *, struct slot *);
void dev_midi_dump(struct dev *);
void mtc_midi_qfr(struct mtc *, int);
void mtc_midi_full(struct mtc *);
void mtc_trigger(struct mtc *);
void mtc_start(struct mtc *);
void mtc_stop(struct mtc *);
void mtc_loc(struct mtc *, unsigned int);
void mtc_setdev(struct mtc *, struct dev *);
/*
* sio_open(3) like interface for clients
*/
void slot_log(struct slot *);
struct slot *slot_new(struct dev *, struct opt *, unsigned int, char *,
struct slot *slot_new(struct opt *, unsigned int, char *,
struct slotops *, void *, int);
void slot_del(struct slot *);
void slot_setvol(struct slot *, unsigned int);
void slot_start(struct slot *);
void slot_stop(struct slot *);
void slot_stop(struct slot *, int);
void slot_read(struct slot *);
void slot_write(struct slot *);
void slot_initconv(struct slot *);
void slot_attach(struct slot *);
void slot_detach(struct slot *);
/*
* control related functions
*/
struct ctl *ctl_new(int, void *, void *,
int, char *, char *, int, char *, char *, int, int, int);
void ctl_del(int, void *, void *);
void ctl_log(struct ctl *);
struct ctlslot *ctlslot_new(struct dev *, struct ctlops *, void *);
int ctl_setval(struct ctl *c, int val);
int ctl_match(struct ctl *, int, void *, void *);
struct ctl *ctl_find(int, void *, void *);
void ctl_update(struct ctl *);
int ctl_onval(int, void *, void *, int);
struct ctlslot *ctlslot_new(struct opt *, struct ctlops *, void *);
void ctlslot_del(struct ctlslot *);
int dev_setctl(struct dev *, int, int);
int dev_onval(struct dev *, int, int);
int dev_nctl(struct dev *);
int ctlslot_visible(struct ctlslot *, struct ctl *);
struct ctl *ctlslot_lookup(struct ctlslot *, int);
void dev_label(struct dev *, int);
struct ctl *dev_addctl(struct dev *, char *, int, int,
char *, int, char *, char *, int, int, int);
void dev_rmctl(struct dev *, int);
int dev_makeunit(struct dev *, char *);
void dev_ctlsync(struct dev *);
#endif /* !defined(DEV_H) */

View File

@ -51,34 +51,27 @@ struct fileops dev_sioctl_ops = {
void
dev_sioctl_ondesc(void *arg, struct sioctl_desc *desc, int val)
{
#define GROUP_PREFIX "hw"
char group_buf[CTL_NAMEMAX], *group;
struct dev *d = arg;
int addr;
char *group, group_buf[CTL_NAMEMAX];
if (desc == NULL) {
dev_ctlsync(d);
return;
}
addr = CTLADDR_END + desc->addr;
dev_rmctl(d, addr);
ctl_del(CTL_HW, d, &desc->addr);
/*
* prefix with "hw/" group names of controls we expose, to
* ensure that all controls have unique names when multiple
* sndiod's are chained
*/
if (strcmp(desc->group, "app") == 0 || (desc->group[0] == 0 &&
strcmp(desc->node0.name, "server") == 0)) {
group = group_buf;
if (snprintf(group_buf, CTL_NAMEMAX, GROUP_PREFIX "/%s",
desc->group) >= CTL_NAMEMAX)
if (desc->group[0] == 0)
group = d->name;
else {
if (snprintf(group_buf, CTL_NAMEMAX, "%s/%s",
d->name, desc->group) >= CTL_NAMEMAX)
return;
} else
group = desc->group;
group = group_buf;
}
dev_addctl(d, group, desc->type, addr,
ctl_new(CTL_HW, d, &desc->addr,
desc->type, group,
desc->node0.name, desc->node0.unit, desc->func,
desc->node1.name, desc->node1.unit, desc->maxval, val);
}
@ -89,8 +82,6 @@ dev_sioctl_onval(void *arg, unsigned int addr, unsigned int val)
struct dev *d = arg;
struct ctl *c;
addr += CTLADDR_END;
dev_log(d);
log_puts(": onctl: addr = ");
log_putu(addr);
@ -98,8 +89,8 @@ dev_sioctl_onval(void *arg, unsigned int addr, unsigned int val)
log_putu(val);
log_puts("\n");
for (c = d->ctl_list; c != NULL; c = c->next) {
if (c->addr != addr)
for (c = ctl_list; c != NULL; c = c->next) {
if (c->scope != CTL_HW || c->u.hw.addr != addr)
continue;
ctl_log(c);
log_puts(": new value -> ");
@ -139,9 +130,9 @@ dev_sioctl_close(struct dev *d)
struct ctl *c, **pc;
/* remove controls */
pc = &d->ctl_list;
pc = &ctl_list;
while ((c = *pc) != NULL) {
if (c->addr >= CTLADDR_END) {
if (c->scope == CTL_HW && c->u.hw.dev == d) {
c->refs_mask &= ~CTL_DEVMASK;
if (c->refs_mask == 0) {
*pc = c->next;
@ -163,8 +154,8 @@ dev_sioctl_pollfd(void *arg, struct pollfd *pfd)
struct ctl *c;
int events = 0;
for (c = d->ctl_list; c != NULL; c = c->next) {
if (c->dirty)
for (c = ctl_list; c != NULL; c = c->next) {
if (c->scope == CTL_HW && c->u.hw.dev == d && c->dirty)
events |= POLLOUT;
}
return sioctl_pollfd(d->sioctl.hdl, pfd, events);
@ -196,11 +187,10 @@ dev_sioctl_out(void *arg)
* we've finished iterating on it.
*/
cnt = 0;
for (c = d->ctl_list; c != NULL; c = c->next) {
if (!c->dirty)
for (c = ctl_list; c != NULL; c = c->next) {
if (c->scope != CTL_HW || c->u.hw.dev != d || !c->dirty)
continue;
if (!sioctl_setval(d->sioctl.hdl,
c->addr - CTLADDR_END, c->curval)) {
if (!sioctl_setval(d->sioctl.hdl, c->u.hw.addr, c->curval)) {
ctl_log(c);
log_puts(": set failed\n");
break;

View File

@ -18,7 +18,7 @@
#include "dsp.h"
#include "utils.h"
int aparams_ctltovol[128] = {
const int aparams_ctltovol[128] = {
0,
256, 266, 276, 287, 299, 310, 323, 335,
348, 362, 376, 391, 406, 422, 439, 456,
@ -38,6 +38,75 @@ int aparams_ctltovol[128] = {
26008, 27029, 28090, 29193, 30339, 31530, 32768
};
const int resamp_filt[RESAMP_LENGTH / RESAMP_STEP + 1] = {
0, 0, 3, 9, 22, 42, 73, 116,
174, 248, 341, 454, 589, 749, 934, 1148,
1392, 1666, 1974, 2316, 2693, 3107, 3560, 4051,
4582, 5154, 5766, 6420, 7116, 7853, 8632, 9451,
10311, 11210, 12148, 13123, 14133, 15178, 16253, 17359,
18491, 19647, 20824, 22018, 23226, 24443, 25665, 26888,
28106, 29315, 30509, 31681, 32826, 33938, 35009, 36033,
37001, 37908, 38744, 39502, 40174, 40750, 41223, 41582,
41819, 41925, 41890, 41704, 41358, 40842, 40147, 39261,
38176, 36881, 35366, 33623, 31641, 29411, 26923, 24169,
21140, 17827, 14222, 10317, 6105, 1580, -3267, -8440,
-13944, -19785, -25967, -32492, -39364, -46584, -54153, -62072,
-70339, -78953, -87911, -97209, -106843, -116806, -127092, -137692,
-148596, -159795, -171276, -183025, -195029, -207271, -219735, -232401,
-245249, -258259, -271407, -284670, -298021, -311434, -324880, -338329,
-351750, -365111, -378378, -391515, -404485, -417252, -429775, -442015,
-453930, -465477, -476613, -487294, -497472, -507102, -516137, -524527,
-532225, -539181, -545344, -550664, -555090, -558571, -561055, -562490,
-562826, -562010, -559990, -556717, -552139, -546205, -538866, -530074,
-519779, -507936, -494496, -479416, -462652, -444160, -423901, -401835,
-377923, -352132, -324425, -294772, -263143, -229509, -193847, -156134,
-116348, -74474, -30494, 15601, 63822, 114174, 166661, 221283,
278037, 336916, 397911, 461009, 526194, 593446, 662741, 734054,
807354, 882608, 959779, 1038826, 1119706, 1202370, 1286768, 1372846,
1460546, 1549808, 1640566, 1732753, 1826299, 1921130, 2017169, 2114336,
2212550, 2311723, 2411770, 2512598, 2614116, 2716228, 2818836, 2921841,
3025142, 3128636, 3232218, 3335782, 3439219, 3542423, 3645282, 3747687,
3849526, 3950687, 4051059, 4150530, 4248987, 4346320, 4442415, 4537163,
4630453, 4722177, 4812225, 4900493, 4986873, 5071263, 5153561, 5233668,
5311485, 5386917, 5459872, 5530259, 5597992, 5662986, 5725160, 5784436,
5840739, 5893999, 5944148, 5991122, 6034862, 6075313, 6112422, 6146142,
6176430, 6203247, 6226559, 6246335, 6262551, 6275185, 6284220, 6289647,
6291456, 6289647, 6284220, 6275185, 6262551, 6246335, 6226559, 6203247,
6176430, 6146142, 6112422, 6075313, 6034862, 5991122, 5944148, 5893999,
5840739, 5784436, 5725160, 5662986, 5597992, 5530259, 5459872, 5386917,
5311485, 5233668, 5153561, 5071263, 4986873, 4900493, 4812225, 4722177,
4630453, 4537163, 4442415, 4346320, 4248987, 4150530, 4051059, 3950687,
3849526, 3747687, 3645282, 3542423, 3439219, 3335782, 3232218, 3128636,
3025142, 2921841, 2818836, 2716228, 2614116, 2512598, 2411770, 2311723,
2212550, 2114336, 2017169, 1921130, 1826299, 1732753, 1640566, 1549808,
1460546, 1372846, 1286768, 1202370, 1119706, 1038826, 959779, 882608,
807354, 734054, 662741, 593446, 526194, 461009, 397911, 336916,
278037, 221283, 166661, 114174, 63822, 15601, -30494, -74474,
-116348, -156134, -193847, -229509, -263143, -294772, -324425, -352132,
-377923, -401835, -423901, -444160, -462652, -479416, -494496, -507936,
-519779, -530074, -538866, -546205, -552139, -556717, -559990, -562010,
-562826, -562490, -561055, -558571, -555090, -550664, -545344, -539181,
-532225, -524527, -516137, -507102, -497472, -487294, -476613, -465477,
-453930, -442015, -429775, -417252, -404485, -391515, -378378, -365111,
-351750, -338329, -324880, -311434, -298021, -284670, -271407, -258259,
-245249, -232401, -219735, -207271, -195029, -183025, -171276, -159795,
-148596, -137692, -127092, -116806, -106843, -97209, -87911, -78953,
-70339, -62072, -54153, -46584, -39364, -32492, -25967, -19785,
-13944, -8440, -3267, 1580, 6105, 10317, 14222, 17827,
21140, 24169, 26923, 29411, 31641, 33623, 35366, 36881,
38176, 39261, 40147, 40842, 41358, 41704, 41890, 41925,
41819, 41582, 41223, 40750, 40174, 39502, 38744, 37908,
37001, 36033, 35009, 33938, 32826, 31681, 30509, 29315,
28106, 26888, 25665, 24443, 23226, 22018, 20824, 19647,
18491, 17359, 16253, 15178, 14133, 13123, 12148, 11210,
10311, 9451, 8632, 7853, 7116, 6420, 5766, 5154,
4582, 4051, 3560, 3107, 2693, 2316, 1974, 1666,
1392, 1148, 934, 749, 589, 454, 341, 248,
174, 116, 73, 42, 22, 9, 3, 0,
0
};
/*
* Generate a string corresponding to the encoding in par,
* return the length of the resulting string.
@ -192,7 +261,9 @@ aparams_log(struct aparams *par)
int
aparams_native(struct aparams *par)
{
return par->bps == sizeof(adata_t) && par->bits == ADATA_BITS &&
return par->sig &&
par->bps == sizeof(adata_t) &&
par->bits == ADATA_BITS &&
(par->bps == 1 || par->le == ADATA_LE) &&
(par->bits == par->bps * 8 || !par->msb);
}
@ -210,8 +281,10 @@ resamp_do(struct resamp *p, adata_t *in, adata_t *out, int todo)
adata_t *odata;
unsigned int iblksz;
unsigned int c;
int64_t f[NCHAN_MAX];
adata_t *ctxbuf, *ctx;
unsigned int ctx_start;
int q, qi, qf, n;
#ifdef DEBUG
if (todo % p->iblksz != 0) {
@ -238,7 +311,7 @@ resamp_do(struct resamp *p, adata_t *in, adata_t *out, int todo)
if (diff >= oblksz) {
if (todo == 0)
break;
ctx_start ^= 1;
ctx_start = (ctx_start - 1) & (RESAMP_NCTX - 1);
ctx = ctxbuf + ctx_start;
for (c = nch; c > 0; c--) {
*ctx = *idata++;
@ -247,12 +320,46 @@ resamp_do(struct resamp *p, adata_t *in, adata_t *out, int todo)
diff -= oblksz;
todo--;
} else {
ctx = ctxbuf;
for (c = nch; c > 0; c--) {
s = ctx[ctx_start ^ 1];
ds = ctx[ctx_start] - s;
ctx += RESAMP_NCTX;
*odata++ = s + ADATA_MULDIV(ds, diff, oblksz);
for (c = 0; c < nch; c++)
f[c] = 0;
q = diff * p->filt_step;
n = ctx_start;
while (q < RESAMP_LENGTH) {
qi = q >> RESAMP_STEP_BITS;
qf = q & (RESAMP_STEP - 1);
s = resamp_filt[qi];
ds = resamp_filt[qi + 1] - s;
s += (int64_t)qf * ds >> RESAMP_STEP_BITS;
ctx = ctxbuf;
for (c = 0; c < nch; c++) {
f[c] += (int64_t)ctx[n] * s;
ctx += RESAMP_NCTX;
}
q += p->filt_cutoff;
n = (n + 1) & (RESAMP_NCTX - 1);
}
for (c = 0; c < nch; c++) {
s = f[c] >> RESAMP_BITS;
s = (int64_t)s * p->filt_cutoff >> RESAMP_BITS;
#if ADATA_BITS == 16
/*
* In 16-bit mode, we've no room for filter
* overshoots, so we need to clip the signal
* to avoid 16-bit integers to wrap around.
* In 24-bit mode, samples may exceed the
* [-1:1] range. Later, cmap_add() will clip
* them, so no need to clip them here as well.
*/
if (s >= ADATA_UNIT)
s = ADATA_UNIT - 1;
else if (s < -ADATA_UNIT)
s = -ADATA_UNIT;
#endif
*odata++ = s;
}
diff += iblksz;
}
@ -273,6 +380,13 @@ resamp_init(struct resamp *p, unsigned int iblksz,
p->nch = nch;
p->ctx_start = 0;
memset(p->ctx, 0, sizeof(p->ctx));
if (p->iblksz < p->oblksz) {
p->filt_cutoff = RESAMP_UNIT;
p->filt_step = RESAMP_UNIT / p->oblksz;
} else {
p->filt_cutoff = (int64_t)RESAMP_UNIT * p->oblksz / p->iblksz;
p->filt_step = RESAMP_UNIT / p->iblksz;
}
#ifdef DEBUG
if (log_level >= 3) {
log_puts("resamp: ");

View File

@ -40,57 +40,43 @@ typedef short adata_t;
#elif ADATA_BITS == 24
#if defined(__i386__) && defined(__GNUC__)
static inline int
fp24_mul(int x, int a)
{
int res;
asm volatile (
"imull %2\n\t"
"shrdl $23, %%edx, %%eax\n\t"
: "=a" (res)
: "a" (x), "r" (a)
: "%edx"
);
return res;
}
static inline int
fp24_muldiv(int x, int a, int b)
{
int res;
asm volatile (
"imull %2\n\t"
"idivl %3\n\t"
: "=a" (res)
: "a" (x), "d" (a), "r" (b)
);
return res;
}
#define ADATA_MUL(x,y) fp24_mul(x, y)
#define ADATA_MULDIV(x,y,z) fp24_muldiv(x, y, z);
#elif defined(__amd64__) || defined(__sparc64__)
#define ADATA_MUL(x,y) \
((int)(((long long)(x) * (long long)(y)) >> (ADATA_BITS - 1)))
#define ADATA_MULDIV(x,y,z) \
((int)((long long)(x) * (long long)(y) / (long long)(z)))
#else
#error "no 24-bit code for this architecture"
#endif
typedef int adata_t;
#else
#error "only 16-bit and 24-bit precisions are supported"
#endif
/*
* The FIR is sampled and stored in a table of fixed-point numbers
* with 23 fractional bits. For convenience, we use the same fixed-point
* numbers to represent time and to walk through the table.
*/
#define RESAMP_BITS 23
#define RESAMP_UNIT (1 << RESAMP_BITS)
/*
* Filter window length (the time unit is RESAMP_UNIT)
*/
#define RESAMP_LENGTH (8 * RESAMP_UNIT)
/*
* Time between samples of the FIR (the time unit is RESAMP_UNIT)
*/
#define RESAMP_STEP_BITS (RESAMP_BITS - 6)
#define RESAMP_STEP (1 << RESAMP_STEP_BITS)
/*
* Maximum downsample/upsample ratio we support, must be a power of two.
* The ratio between the max and the min sample rates is 192kHz / 4kHz = 48,
* so we can use 64
*/
#define RESAMP_RATIO 64
/*
* Maximum size of the encording string (the longest possible
* encoding is ``s24le3msb'').
@ -111,9 +97,10 @@ struct aparams {
};
struct resamp {
#define RESAMP_NCTX 2
#define RESAMP_NCTX (RESAMP_LENGTH / RESAMP_UNIT * RESAMP_RATIO)
unsigned int ctx_start;
adata_t ctx[NCHAN_MAX * RESAMP_NCTX];
int filt_cutoff, filt_step;
unsigned int iblksz, oblksz;
int nch;
};
@ -137,7 +124,7 @@ struct cmap {
};
#define MIDI_TO_ADATA(m) (aparams_ctltovol[m] << (ADATA_BITS - 16))
extern int aparams_ctltovol[128];
extern const int aparams_ctltovol[128];
void aparams_init(struct aparams *);
void aparams_log(struct aparams *);

View File

@ -55,8 +55,8 @@ struct midithru {
/*
* length of voice and common messages (status byte included)
*/
unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
const unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
const unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
void
midi_log(struct midi *ep)
@ -418,6 +418,22 @@ midi_out(struct midi *oep, unsigned char *idata, int icount)
}
}
/*
* disconnect clients attached to this end-point
*/
void
midi_abort(struct midi *p)
{
int i;
struct midi *ep;
for (i = 0; i < MIDI_NEP; i++) {
ep = midi_ep + i;
if ((ep->txmask & p->self) || (p->txmask & ep->self))
ep->ops->exit(ep->arg);
}
}
void
port_log(struct port *p)
{
@ -467,7 +483,7 @@ port_exit(void *arg)
struct port *
port_new(char *path, unsigned int mode, int hold)
{
struct port *c;
struct port *c, **pc;
c = xmalloc(sizeof(struct port));
c->path_list = NULL;
@ -476,8 +492,10 @@ port_new(char *path, unsigned int mode, int hold)
c->hold = hold;
c->midi = midi_new(&port_midiops, c, mode);
c->num = midi_portnum++;
c->next = port_list;
port_list = c;
for (pc = &port_list; *pc != NULL; pc = &(*pc)->next)
;
c->next = *pc;
*pc = c;
return c;
}
@ -563,23 +581,6 @@ port_open(struct port *c)
return 1;
}
void
port_abort(struct port *c)
{
int i;
struct midi *ep;
for (i = 0; i < MIDI_NEP; i++) {
ep = midi_ep + i;
if ((ep->txmask & c->midi->self) ||
(c->midi->txmask & ep->self))
ep->ops->exit(ep->arg);
}
if (c->state != PORT_CFG)
port_close(c);
}
int
port_close(struct port *c)
{

View File

@ -112,6 +112,7 @@ void midi_fill(struct midi *);
void midi_tag(struct midi *, unsigned int);
unsigned int midi_tags(struct midi *);
void midi_link(struct midi *, struct midi *);
void midi_abort(struct midi *);
void port_log(struct port *);
struct port *port_new(char *, unsigned int, int);
@ -124,6 +125,5 @@ void port_done(struct port *);
void port_drain(struct port *);
int port_close(struct port *);
int port_reopen(struct port *);
void port_abort(struct port *);
#endif /* !defined(MIDI_H) */

View File

@ -186,6 +186,9 @@ port_mio_hup(void *arg)
{
struct port *p = arg;
if (!port_reopen(p))
port_abort(p);
if (!port_reopen(p)) {
midi_abort(p->midi);
if (p->state != PORT_CFG)
port_close(p);
}
}

View File

@ -17,9 +17,154 @@
#include <string.h>
#include "dev.h"
#include "midi.h"
#include "opt.h"
#include "sysex.h"
#include "utils.h"
struct opt *opt_list;
void opt_midi_imsg(void *, unsigned char *, int);
void opt_midi_omsg(void *, unsigned char *, int);
void opt_midi_fill(void *, int);
void opt_midi_exit(void *);
struct midiops opt_midiops = {
opt_midi_imsg,
opt_midi_omsg,
opt_midi_fill,
opt_midi_exit
};
void
opt_midi_imsg(void *arg, unsigned char *msg, int len)
{
#ifdef DEBUG
struct opt *o = arg;
log_puts(o->name);
log_puts(": can't receive midi messages\n");
panic();
#endif
}
void
opt_midi_omsg(void *arg, unsigned char *msg, int len)
{
struct opt *o = arg;
struct sysex *x;
unsigned int fps, chan;
if ((msg[0] & MIDI_CMDMASK) == MIDI_CTL && msg[1] == MIDI_CTL_VOL) {
chan = msg[0] & MIDI_CHANMASK;
if (chan >= DEV_NSLOT)
return;
if (slot_array[chan].opt == NULL ||
slot_array[chan].opt->dev != o->dev)
return;
slot_setvol(slot_array + chan, msg[2]);
ctl_onval(CTL_SLOT_LEVEL, slot_array + chan, NULL, msg[2]);
return;
}
x = (struct sysex *)msg;
if (x->start != SYSEX_START)
return;
if (len < SYSEX_SIZE(empty))
return;
switch (x->type) {
case SYSEX_TYPE_RT:
if (x->id0 == SYSEX_CONTROL && x->id1 == SYSEX_MASTER) {
if (len == SYSEX_SIZE(master)) {
dev_master(o->dev, x->u.master.coarse);
if (o->dev->master_enabled) {
ctl_onval(CTL_DEV_MASTER, o->dev, NULL,
x->u.master.coarse);
}
}
return;
}
if (x->id0 != SYSEX_MMC)
return;
switch (x->id1) {
case SYSEX_MMC_STOP:
if (len != SYSEX_SIZE(stop))
return;
if (o->mtc == NULL)
return;
if (log_level >= 2) {
log_puts(o->name);
log_puts(": mmc stop\n");
}
mtc_stop(o->mtc);
break;
case SYSEX_MMC_START:
if (len != SYSEX_SIZE(start))
return;
if (o->mtc == NULL)
return;
if (log_level >= 2) {
log_puts(o->name);
log_puts(": mmc start\n");
}
mtc_start(o->mtc);
break;
case SYSEX_MMC_LOC:
if (len != SYSEX_SIZE(loc) ||
x->u.loc.len != SYSEX_MMC_LOC_LEN ||
x->u.loc.cmd != SYSEX_MMC_LOC_CMD)
return;
if (o->mtc == NULL)
return;
switch (x->u.loc.hr >> 5) {
case MTC_FPS_24:
fps = 24;
break;
case MTC_FPS_25:
fps = 25;
break;
case MTC_FPS_30:
fps = 30;
break;
default:
mtc_stop(o->mtc);
return;
}
mtc_loc(o->mtc,
(x->u.loc.hr & 0x1f) * 3600 * MTC_SEC +
x->u.loc.min * 60 * MTC_SEC +
x->u.loc.sec * MTC_SEC +
x->u.loc.fr * (MTC_SEC / fps));
break;
}
break;
case SYSEX_TYPE_EDU:
if (x->id0 != SYSEX_AUCAT || x->id1 != SYSEX_AUCAT_DUMPREQ)
return;
if (len != SYSEX_SIZE(dumpreq))
return;
dev_midi_dump(o->dev);
break;
}
}
void
opt_midi_fill(void *arg, int count)
{
/* nothing to do */
}
void
opt_midi_exit(void *arg)
{
struct opt *o = arg;
if (log_level >= 1) {
log_puts(o->name);
log_puts(": midi end point died\n");
panic();
}
}
/*
* create a new audio sub-device "configuration"
*/
@ -28,17 +173,10 @@ opt_new(struct dev *d, char *name,
int pmin, int pmax, int rmin, int rmax,
int maxweight, int mmc, int dup, unsigned int mode)
{
struct opt *o;
unsigned int len;
struct opt *o, **po;
unsigned int len, num;
char c;
if (opt_byname(d, name)) {
dev_log(d);
log_puts(".");
log_puts(name);
log_puts(": already defined\n");
return NULL;
}
for (len = 0; name[len] != '\0'; len++) {
if (len == OPT_NAMEMAX) {
log_puts(name);
@ -53,7 +191,49 @@ opt_new(struct dev *d, char *name,
return NULL;
}
}
num = 0;
for (po = &opt_list; *po != NULL; po = &(*po)->next)
num++;
if (num >= OPT_NMAX) {
log_puts(name);
log_puts(": too many opts\n");
return NULL;
}
if (opt_byname(d, name)) {
dev_log(d);
log_puts(".");
log_puts(name);
log_puts(": already defined\n");
return NULL;
}
if (mmc) {
if (mtc_array[0].dev != NULL && mtc_array[0].dev != d) {
log_puts(name);
log_puts(": MTC already setup for another device\n");
return NULL;
}
mtc_array[0].dev = d;
if (log_level >= 2) {
dev_log(d);
log_puts(": initial MTC source, controlled by MMC\n");
}
}
o = xmalloc(sizeof(struct opt));
o->num = num;
o->dev = d;
/*
* XXX: below, we allocate a midi input buffer, since we don't
* receive raw midi data, so no need to allocate a input
* ibuf. Possibly set imsg & fill callbacks to NULL and
* use this to in midi_new() to check if buffers need to be
* allocated
*/
o->midi = midi_new(&opt_midiops, o, MODE_MIDIIN | MODE_MIDIOUT);
midi_tag(o->midi, o->num);
if (mode & MODE_PLAY) {
o->pmin = pmin;
o->pmax = pmax;
@ -63,12 +243,12 @@ opt_new(struct dev *d, char *name,
o->rmax = rmax;
}
o->maxweight = maxweight;
o->mmc = mmc;
o->mtc = mmc ? &mtc_array[0] : NULL;
o->dup = dup;
o->mode = mode;
memcpy(o->name, name, len + 1);
o->next = d->opt_list;
d->opt_list = o;
o->next = *po;
*po = o;
if (log_level >= 2) {
dev_log(d);
log_puts(".");
@ -95,8 +275,8 @@ opt_new(struct dev *d, char *name,
log_putu(o->rmax);
}
if (o->mode & (MODE_RECMASK | MODE_PLAY)) {
if (o->mmc)
log_puts(" mmc");
if (o->mtc)
log_puts(" mtc");
if (o->dup)
log_puts(" dup");
}
@ -110,7 +290,9 @@ opt_byname(struct dev *d, char *name)
{
struct opt *o;
for (o = d->opt_list; o != NULL; o = o->next) {
for (o = opt_list; o != NULL; o = o->next) {
if (d != NULL && o->dev != d)
continue;
if (strcmp(name, o->name) == 0)
return o;
}
@ -118,11 +300,11 @@ opt_byname(struct dev *d, char *name)
}
void
opt_del(struct dev *d, struct opt *o)
opt_del(struct opt *o)
{
struct opt **po;
for (po = &d->opt_list; *po != o; po = &(*po)->next) {
for (po = &opt_list; *po != o; po = &(*po)->next) {
#ifdef DEBUG
if (*po == NULL) {
log_puts("opt_del: not on list\n");
@ -130,6 +312,7 @@ opt_del(struct dev *d, struct opt *o)
}
#endif
}
midi_del(o->midi);
*po = o->next;
xfree(o);
}

View File

@ -17,11 +17,31 @@
#ifndef OPT_H
#define OPT_H
#define OPT_NMAX 16
struct dev;
struct opt {
struct opt *next;
struct dev *dev;
struct midi *midi;
struct mtc *mtc; /* if set, MMC-controlled MTC source */
int num;
#define OPT_NAMEMAX 11
char name[OPT_NAMEMAX + 1];
int maxweight; /* max dynamic range for clients */
int pmin, pmax; /* play channels */
int rmin, rmax; /* recording channels */
int dup; /* true if join/expand enabled */
int mode; /* bitmap of MODE_XXX */
};
extern struct opt *opt_list;
struct opt *opt_new(struct dev *, char *, int, int, int, int,
int, int, int, unsigned int);
void opt_del(struct dev *, struct opt *);
void opt_del(struct opt *);
struct opt *opt_byname(struct dev *, char *);
#endif /* !defined(OPT_H) */

View File

@ -86,76 +86,24 @@ dev_sio_timeout(void *arg)
dev_abort(d);
}
/*
* open the device using one of the provided paths
*/
static struct sio_hdl *
dev_sio_openlist(struct dev *d, unsigned int mode, struct sioctl_hdl **rctlhdl)
static int
dev_sio_openalt(struct dev *d, struct dev_alt *n,
struct sio_hdl **rhdl, struct sioctl_hdl **rctlhdl, unsigned int *rmode)
{
struct dev_alt *n;
struct sio_hdl *hdl;
struct sioctl_hdl *ctlhdl;
struct ctl *c;
int val;
unsigned int mode = d->reqmode & (MODE_PLAY | MODE_REC);
for (n = d->alt_list; n != NULL; n = n->next) {
if (d->alt_num == n->idx)
continue;
hdl = sio_open(n->name, mode, 1);
if (hdl != NULL) {
if (log_level >= 2) {
dev_log(d);
log_puts(": using ");
log_puts(n->name);
log_puts("\n");
}
ctlhdl = sioctl_open(n->name,
SIOCTL_READ | SIOCTL_WRITE, 0);
if (ctlhdl == NULL) {
if (log_level >= 1) {
dev_log(d);
log_puts(": no control device\n");
}
}
d->alt_num = n->idx;
for (c = d->ctl_list; c != NULL; c = c->next) {
if (c->addr < CTLADDR_ALT_SEL ||
c->addr >= CTLADDR_ALT_SEL + DEV_NMAX)
continue;
val = (c->addr - CTLADDR_ALT_SEL) == n->idx;
if (c->curval == val)
continue;
c->curval = val;
if (val)
c->val_mask = ~0U;
}
*rctlhdl = ctlhdl;
return hdl;
}
}
return NULL;
}
/*
* open the device.
*/
int
dev_sio_open(struct dev *d)
{
struct sio_par par;
unsigned int mode = d->mode & (MODE_PLAY | MODE_REC);
d->sio.hdl = dev_sio_openlist(d, mode, &d->sioctl.hdl);
if (d->sio.hdl == NULL) {
hdl = sio_open(n->name, mode, 1);
if (hdl == NULL) {
if (mode != (SIO_PLAY | SIO_REC))
return 0;
d->sio.hdl = dev_sio_openlist(d, SIO_PLAY, &d->sioctl.hdl);
if (d->sio.hdl != NULL)
hdl = sio_open(n->name, SIO_PLAY, 1);
if (hdl != NULL)
mode = SIO_PLAY;
else {
d->sio.hdl = dev_sio_openlist(d,
SIO_REC, &d->sioctl.hdl);
if (d->sio.hdl != NULL)
hdl = sio_open(n->name, SIO_REC, 1);
if (hdl != NULL)
mode = SIO_REC;
else
return 0;
@ -166,15 +114,85 @@ dev_sio_open(struct dev *d)
log_puts(" mode\n");
}
}
ctlhdl = sioctl_open(n->name, SIOCTL_READ | SIOCTL_WRITE, 0);
if (ctlhdl == NULL) {
if (log_level >= 1) {
dev_log(d);
log_puts(": no control device\n");
}
}
*rhdl = hdl;
*rctlhdl = ctlhdl;
*rmode = mode;
return 1;
}
/*
* open the device using one of the provided paths
*/
static int
dev_sio_openlist(struct dev *d,
struct sio_hdl **rhdl, struct sioctl_hdl **rctlhdl, unsigned int *rmode)
{
struct dev_alt *n;
struct ctl *c;
int val;
for (n = d->alt_list; n != NULL; n = n->next) {
if (d->alt_num == n->idx)
continue;
if (log_level >= 2) {
dev_log(d);
log_puts(": trying ");
log_puts(n->name);
log_puts("\n");
}
if (dev_sio_openalt(d, n, rhdl, rctlhdl, rmode)) {
if (log_level >= 2) {
dev_log(d);
log_puts(": using ");
log_puts(n->name);
log_puts("\n");
}
d->alt_num = n->idx;
for (c = ctl_list; c != NULL; c = c->next) {
if (!ctl_match(c, CTL_DEV_ALT, d, NULL))
continue;
val = c->u.dev_alt.idx == n->idx;
if (c->curval == val)
continue;
c->curval = val;
if (val)
c->val_mask = ~0U;
}
return 1;
}
}
return 0;
}
/*
* open the device.
*/
int
dev_sio_open(struct dev *d)
{
struct sio_par par;
if (!dev_sio_openlist(d, &d->sio.hdl, &d->sioctl.hdl, &d->mode))
return 0;
sio_initpar(&par);
par.bits = d->par.bits;
par.bps = d->par.bps;
par.sig = d->par.sig;
par.le = d->par.le;
par.msb = d->par.msb;
if (mode & SIO_PLAY)
if (d->mode & SIO_PLAY)
par.pchan = d->pchan;
if (mode & SIO_REC)
if (d->mode & SIO_REC)
par.rchan = d->rchan;
if (d->bufsz)
par.appbufsz = d->bufsz;
@ -210,14 +228,14 @@ dev_sio_open(struct dev *d)
log_puts(": unsupported sample size\n");
goto bad_close;
}
if ((mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
if ((d->mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
dev_log(d);
log_puts(": ");
log_putu(par.pchan);
log_puts(": unsupported number of play channels\n");
goto bad_close;
}
if ((mode & SIO_REC) && par.rchan > NCHAN_MAX) {
if ((d->mode & SIO_REC) && par.rchan > NCHAN_MAX) {
dev_log(d);
log_puts(": ");
log_putu(par.rchan);
@ -253,17 +271,15 @@ dev_sio_open(struct dev *d)
d->par.sig = par.sig;
d->par.le = par.le;
d->par.msb = par.msb;
if (mode & SIO_PLAY)
if (d->mode & SIO_PLAY)
d->pchan = par.pchan;
if (mode & SIO_REC)
if (d->mode & SIO_REC)
d->rchan = par.rchan;
d->bufsz = par.bufsz;
d->round = par.round;
d->rate = par.rate;
if (!(mode & MODE_PLAY))
d->mode &= ~(MODE_PLAY | MODE_MON);
if (!(mode & MODE_REC))
d->mode &= ~MODE_REC;
if (d->mode & MODE_PLAY)
d->mode |= MODE_MON;
sio_onmove(d->sio.hdl, dev_sio_onmove, d);
d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(d->sio.hdl));
if (d->sioctl.hdl) {
@ -290,18 +306,13 @@ dev_sio_open(struct dev *d)
int
dev_sio_reopen(struct dev *d)
{
struct sioctl_hdl *ctlhdl;
struct sio_par par;
struct sio_hdl *hdl;
struct sioctl_hdl *ctlhdl;
unsigned int mode;
hdl = dev_sio_openlist(d, d->mode & (MODE_PLAY | MODE_REC), &ctlhdl);
if (hdl == NULL) {
if (log_level >= 1) {
dev_log(d);
log_puts(": couldn't open an alternate device\n");
}
if (!dev_sio_openlist(d, &hdl, &ctlhdl, &mode))
return 0;
}
sio_initpar(&par);
par.bits = d->par.bits;
@ -309,10 +320,10 @@ dev_sio_reopen(struct dev *d)
par.sig = d->par.sig;
par.le = d->par.le;
par.msb = d->par.msb;
if (d->mode & SIO_PLAY)
par.pchan = d->pchan;
if (d->mode & SIO_REC)
par.rchan = d->rchan;
if (mode & SIO_PLAY)
par.pchan = d->reqpchan;
if (mode & SIO_REC)
par.rchan = d->reqrchan;
par.appbufsz = d->bufsz;
par.round = d->round;
par.rate = d->rate;
@ -342,6 +353,7 @@ dev_sio_reopen(struct dev *d)
}
/* update parameters */
d->mode = mode;
d->par.bits = par.bits;
d->par.bps = par.bps;
d->par.sig = par.sig;

View File

@ -329,7 +329,7 @@ to audio programs.
Unit number.
Each
.Nm
server instance has an unique unit number,
server instance has a unique unit number,
used in
.Xr sndio 7
device names.
@ -343,7 +343,7 @@ Reducing the volume in advance allows a client's volume to stay independent
from the number of clients as long as their number is small enough.
18 volume units (i.e. \-6dB attenuation) allows the number
of playback programs to be doubled.
The default is 118 i.e. \-3dB.
The default is 127.
.It Fl w Ar flag
Control
.Nm
@ -355,11 +355,8 @@ option.
If the flag is
.Va on ,
then the master volume is automatically adjusted to avoid clipping.
Using
.Va off
makes sense in the rare situation where all programs lower their volumes.
The default is
.Va on .
.Va off .
.It Fl z Ar nframes
The audio device block size in frames.
This is the number of frames between audio clock ticks,

View File

@ -366,7 +366,7 @@ mkopt(char *path, struct dev *d,
int
main(int argc, char **argv)
{
int c, i, background, unit, devindex;
int c, i, background, unit;
int pmin, pmax, rmin, rmax;
char base[SOCKPATH_MAX], path[SOCKPATH_MAX];
unsigned int mode, dup, mmc, vol;
@ -387,11 +387,11 @@ main(int argc, char **argv)
/*
* global options defaults
*/
vol = 118;
vol = 127;
dup = 1;
mmc = 0;
hold = 0;
autovol = 1;
autovol = 0;
bufsz = 0;
round = 0;
rate = DEFAULT_RATE;
@ -404,7 +404,10 @@ main(int argc, char **argv)
aparams_init(&par);
mode = MODE_PLAY | MODE_REC;
tcpaddr_list = NULL;
devindex = 0;
d = NULL;
p = NULL;
slot_array_init();
while ((c = getopt(argc, argv,
"a:b:c:C:de:F:f:j:L:m:Q:q:r:s:t:U:v:w:x:z:")) != -1) {
@ -453,21 +456,24 @@ main(int argc, char **argv)
errx(1, "%s: volume is %s", optarg, str);
break;
case 's':
if ((d = dev_list) == NULL) {
d = mkdev(default_devs[devindex++], &par, 0,
bufsz, round, rate, hold, autovol);
if (d == NULL) {
for (i = 0; default_devs[i] != NULL; i++) {
mkdev(default_devs[i], &par, 0,
bufsz, round, rate, 0, autovol);
}
d = dev_list;
}
if (mkopt(optarg, d, pmin, pmax, rmin, rmax,
mode, vol, mmc, dup) == NULL)
return 1;
break;
case 'q':
mkport(optarg, hold);
p = mkport(optarg, hold);
break;
case 'Q':
if (port_list == NULL)
if (p == NULL)
errx(1, "-Q %s: no ports defined", optarg);
namelist_add(&port_list->path_list, optarg);
namelist_add(&p->path_list, optarg);
break;
case 'a':
hold = opt_onoff();
@ -486,12 +492,11 @@ main(int argc, char **argv)
errx(1, "%s: block size is %s", optarg, str);
break;
case 'f':
mkdev(optarg, &par, 0, bufsz, round,
d = mkdev(optarg, &par, 0, bufsz, round,
rate, hold, autovol);
devindex = -1;
break;
case 'F':
if ((d = dev_list) == NULL)
if (d == NULL)
errx(1, "-F %s: no devices defined", optarg);
if (!dev_addname(d, optarg))
exit(1);
@ -511,8 +516,8 @@ main(int argc, char **argv)
for (i = 0; default_ports[i] != NULL; i++)
mkport(default_ports[i], 0);
}
if (devindex != -1) {
for (i = devindex; default_devs[i] != NULL; i++) {
if (dev_list == NULL) {
for (i = 0; default_devs[i] != NULL; i++) {
mkdev(default_devs[i], &par, 0,
bufsz, round, rate, 0, autovol);
}
@ -593,6 +598,8 @@ main(int argc, char **argv)
; /* nothing */
midi_done();
while (opt_list)
opt_del(opt_list);
while (dev_list)
dev_del(dev_list);
while (port_list)

View File

@ -114,7 +114,7 @@ sock_log(struct sock *f)
midi_log(f->midi);
else if (f->ctlslot) {
log_puts("ctlslot");
log_putu(f->ctlslot - f->ctlslot->dev->ctlslot);
log_putu(f->ctlslot - ctlslot_array);
} else
log_puts("sock");
#ifdef DEBUG
@ -616,7 +616,7 @@ int
sock_setpar(struct sock *f)
{
struct slot *s = f->slot;
struct dev *d = s->dev;
struct dev *d = s->opt->dev;
struct amsg_par *p = &f->rmsg.u.par;
unsigned int min, max;
uint32_t rate, appbufsz;
@ -748,7 +748,7 @@ sock_setpar(struct sock *f)
return 0;
}
s->xrun = p->xrun;
if (s->opt->mmc && s->xrun == XRUN_IGNORE)
if (s->opt->mtc != NULL && s->xrun == XRUN_IGNORE)
s->xrun = XRUN_SYNC;
#ifdef DEBUG
if (log_level >= 3) {
@ -869,9 +869,12 @@ sock_hello(struct sock *f)
d = dev_bynum(p->devnum);
if (d == NULL)
return 0;
opt = opt_byname(d, p->opt);
if (opt == NULL)
return 0;
if (!dev_ref(d))
return 0;
midi_tag(f->midi, p->devnum);
midi_tag(f->midi, opt->num);
} else if (p->devnum < 32) {
midi_tag(f->midi, p->devnum);
} else if (p->devnum < 48) {
@ -895,7 +898,10 @@ sock_hello(struct sock *f)
}
return 0;
}
f->ctlslot = ctlslot_new(d, &sock_ctlops, f);
opt = opt_byname(d, p->opt);
if (opt == NULL)
return 0;
f->ctlslot = ctlslot_new(opt, &sock_ctlops, f);
if (f->ctlslot == NULL) {
if (log_level >= 2) {
sock_log(f);
@ -915,7 +921,7 @@ sock_hello(struct sock *f)
opt = opt_byname(d, p->opt);
if (opt == NULL)
return 0;
f->slot = slot_new(d, opt, id, p->who, &sock_slotops, f, mode);
f->slot = slot_new(opt, id, p->who, &sock_slotops, f, mode);
if (f->slot == NULL)
return 0;
f->midi = NULL;
@ -1130,7 +1136,7 @@ sock_execmsg(struct sock *f)
f->ralign = s->round * s->mix.bpf;
}
}
slot_stop(s);
slot_stop(s, 1);
break;
case AMSG_SETPAR:
#ifdef DEBUG
@ -1225,9 +1231,8 @@ sock_execmsg(struct sock *f)
f->rstate = SOCK_RMSG;
f->lastvol = ctl; /* dont trigger feedback message */
slot_setvol(s, ctl);
dev_midi_vol(s->dev, s);
dev_onval(s->dev,
CTLADDR_SLOT_LEVEL(f->slot - s->dev->slot), ctl);
dev_midi_vol(s->opt->dev, s);
ctl_onval(CTL_SLOT_LEVEL, s, NULL, ctl);
break;
case AMSG_CTLSUB:
#ifdef DEBUG
@ -1252,10 +1257,11 @@ sock_execmsg(struct sock *f)
}
if (m->u.ctlsub.desc) {
if (!(f->ctlops & SOCK_CTLDESC)) {
ctl = f->ctlslot->mask;
c = f->ctlslot->dev->ctl_list;
ctl = f->ctlslot->self;
c = ctl_list;
while (c != NULL) {
c->desc_mask |= ctl;
if (ctlslot_visible(f->ctlslot, c))
c->desc_mask |= ctl;
c = c->next;
}
f->ctlops |= SOCK_CTLDESC;
@ -1287,13 +1293,23 @@ sock_execmsg(struct sock *f)
sock_close(f);
return 0;
}
if (!dev_setctl(f->ctlslot->dev,
ntohs(m->u.ctlset.addr),
ntohs(m->u.ctlset.val))) {
c = ctlslot_lookup(f->ctlslot, ntohs(m->u.ctlset.addr));
if (c == NULL) {
#ifdef DEBUG
if (log_level >= 1) {
sock_log(f);
log_puts(": CTLSET, wrong addr/val\n");
log_puts(": CTLSET, wrong addr\n");
}
#endif
sock_close(f);
return 0;
}
if (!ctl_setval(c, ntohs(m->u.ctlset.val))) {
#ifdef DEBUG
if (log_level >= 1) {
sock_log(f);
log_puts(": CTLSET, bad value\n");
}
#endif
sock_close(f);
@ -1389,7 +1405,7 @@ sock_execmsg(struct sock *f)
int
sock_buildmsg(struct sock *f)
{
unsigned int size, mask;
unsigned int size, type, mask;
struct amsg_ctl_desc *desc;
struct ctl *c, **pc;
@ -1539,9 +1555,9 @@ sock_buildmsg(struct sock *f)
*/
if (f->ctlslot && (f->ctlops & SOCK_CTLDESC)) {
desc = f->ctldesc;
mask = f->ctlslot->mask;
mask = f->ctlslot->self;
size = 0;
pc = &f->ctlslot->dev->ctl_list;
pc = &ctl_list;
while ((c = *pc) != NULL) {
if ((c->desc_mask & mask) == 0 ||
(c->refs_mask & mask) == 0) {
@ -1553,7 +1569,11 @@ sock_buildmsg(struct sock *f)
break;
c->desc_mask &= ~mask;
c->val_mask &= ~mask;
strlcpy(desc->group, c->group,
type = ctlslot_visible(f->ctlslot, c) ?
c->type : CTL_NONE;
strlcpy(desc->group, (f->ctlslot->opt == NULL ||
strcmp(c->group, f->ctlslot->opt->dev->name) != 0) ?
c->group : "",
AMSG_CTL_NAMEMAX);
strlcpy(desc->node0.name, c->node0.name,
AMSG_CTL_NAMEMAX);
@ -1561,7 +1581,7 @@ sock_buildmsg(struct sock *f)
strlcpy(desc->node1.name, c->node1.name,
AMSG_CTL_NAMEMAX);
desc->node1.unit = ntohs(c->node1.unit);
desc->type = c->type;
desc->type = type;
strlcpy(desc->func, c->func, AMSG_CTL_NAMEMAX);
desc->addr = htons(c->addr);
desc->maxval = htons(c->maxval);
@ -1570,7 +1590,7 @@ sock_buildmsg(struct sock *f)
desc++;
/* if this is a deleted entry unref it */
if (c->type == CTL_NONE) {
if (type == CTL_NONE) {
c->refs_mask &= ~mask;
if (c->refs_mask == 0) {
*pc = c->next;
@ -1597,8 +1617,10 @@ sock_buildmsg(struct sock *f)
}
}
if (f->ctlslot && (f->ctlops & SOCK_CTLVAL)) {
mask = f->ctlslot->mask;
for (c = f->ctlslot->dev->ctl_list; c != NULL; c = c->next) {
mask = f->ctlslot->self;
for (c = ctl_list; c != NULL; c = c->next) {
if (!ctlslot_visible(f->ctlslot, c))
continue;
if ((c->val_mask & mask) == 0)
continue;
c->val_mask &= ~mask;