Skip to content

Commit 6c96dbc

Browse files
committed
Add codec output codec buffering and event handling threads.
...plus lots of small changes related to the above. The output codec buffer allows better control over buffer length as well as making it possible to implement short fades when pausing, seeking, etc. without excessive response delay (a possible future addition). Some other changes: * Faster (and slightly better) pm_rand() and dither routines. * ao.c: Set dev, buffer_time, and client_name. * alsa.c: Set a reasonable minimum start threshold (helps prevent underruns in some cases).
1 parent fda16e3 commit 6c96dbc

35 files changed

+1181
-555
lines changed

GNUmakefile

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ LADSPA_DSP_OBJDIR := ${OBJDIR}/ladspa_dsp
55
DSP_OBJ := dsp.o \
66
effect.o \
77
codec.o \
8+
codec_buf.o \
89
sampleconv.o \
910
util.o \
1011
biquad.o \
@@ -50,9 +51,9 @@ BASE_LIBS := -lm
5051
include config.mk
5152

5253
DEPFLAGS := -MMD -MP
53-
DSP_CFLAGS := ${DEPFLAGS} ${BASE_CFLAGS} ${DSP_EXTRA_CFLAGS} ${CFLAGS} ${CPPFLAGS}
54-
DSP_CXXFLAGS := ${DEPFLAGS} ${BASE_CXXFLAGS} ${DSP_EXTRA_CFLAGS} ${CXXFLAGS} ${CPPFLAGS}
55-
DSP_LDFLAGS := ${BASE_LDFLAGS} ${LDFLAGS}
54+
DSP_CFLAGS := ${DEPFLAGS} ${BASE_CFLAGS} -pthread ${DSP_EXTRA_CFLAGS} ${CFLAGS} ${CPPFLAGS}
55+
DSP_CXXFLAGS := ${DEPFLAGS} ${BASE_CXXFLAGS} -pthread ${DSP_EXTRA_CFLAGS} ${CXXFLAGS} ${CPPFLAGS}
56+
DSP_LDFLAGS := ${BASE_LDFLAGS} -pthread ${LDFLAGS}
5657
DSP_LIBS := ${DSP_EXTRA_LIBS} ${BASE_LIBS}
5758
LADSPA_DSP_CFLAGS := ${DEPFLAGS} ${BASE_CFLAGS} -fPIC -DPIC -DLADSPA_FRONTEND -DSYMMETRIC_IO ${LADSPA_DSP_EXTRA_CFLAGS} ${CFLAGS} ${CPPFLAGS}
5859
LADSPA_DSP_CXXFLAGS := ${DEPFLAGS} ${BASE_CXXFLAGS} -fPIC -DPIC -DLADSPA_FRONTEND -DSYMMETRIC_IO ${LADSPA_DSP_EXTRA_CFLAGS} ${CXXFLAGS} ${CPPFLAGS}

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ options. Run `./configure --help` to see all available options.
4545
Flag | Description
4646
----------- | --------------------------------------------------------------------------
4747
`-h` | Show help text.
48-
`-b frames` | Set buffer size (must be given before the first input).
49-
`-R ratio` | Set codec maximum buffer ratio (must be given before the first input).
48+
`-b frames` | Block size (must be given before the first input).
5049
`-i` | Force interactive mode.
5150
`-I` | Disable interactive mode.
5251
`-q` | Disable progress display.
@@ -56,7 +55,7 @@ Flag | Description
5655
`-D` | Disable dithering.
5756
`-E` | Don't drain effects chain before rebuilding.
5857
`-p` | Plot effects chain instead of processing audio.
59-
`-V` | Enable verbose progress display.
58+
`-V` | Verbose progress display.
6059
`-S` | Use "sequence" input combining mode.
6160

6261
#### Input/output options
@@ -69,6 +68,7 @@ Flag | Description
6968
`-B/L/N` | Big/little/native endian.
7069
`-r frequency[k]` | Sample rate.
7170
`-c channels` | Number of channels.
71+
`-R ratio` | Buffer ratio.
7272
`-n` | Equivalent to `-t null null`.
7373

7474
### Inputs and Outputs

alsa.c

+74-35
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "util.h"
2626
#include "sampleconv.h"
2727

28+
#define DUMP_PCM_INFO 0
29+
2830
struct alsa_enc_info {
2931
const char *name;
3032
snd_pcm_format_t fmt;
@@ -132,8 +134,10 @@ ssize_t alsa_delay(struct codec *c)
132134
void alsa_drop(struct codec *c)
133135
{
134136
struct alsa_state *state = (struct alsa_state *) c->data;
135-
snd_pcm_drop(state->dev);
136-
state->delay = 0;
137+
if (c->write) {
138+
snd_pcm_drop(state->dev);
139+
state->delay = 0;
140+
}
137141
}
138142

139143
void alsa_pause(struct codec *c, int p)
@@ -175,96 +179,131 @@ static struct alsa_enc_info * alsa_get_enc_info(const char *enc)
175179
return NULL;
176180
}
177181

178-
struct codec * alsa_codec_init(const char *path, const char *type, const char *enc, int fs, int channels, int endian, int mode)
182+
struct codec * alsa_codec_init(const struct codec_params *p)
179183
{
180184
int err;
181185
snd_pcm_t *dev = NULL;
182-
snd_pcm_hw_params_t *p = NULL;
186+
snd_pcm_hw_params_t *hw_p = NULL;
187+
snd_pcm_sw_params_t *sw_p = NULL;
183188
struct codec *c = NULL;
184189
struct alsa_state *state = NULL;
185-
snd_pcm_uframes_t buf_frames;
190+
snd_pcm_uframes_t buf_frames_min, buf_frames_max, buf_frames;
186191
struct alsa_enc_info *enc_info;
187192

188-
if ((err = snd_pcm_open(&dev, path, (mode == CODEC_MODE_WRITE) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, 0)) < 0) {
193+
if ((err = snd_pcm_open(&dev, p->path, (p->mode == CODEC_MODE_WRITE) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, 0)) < 0) {
189194
LOG_FMT(LL_OPEN_ERROR, "%s: error: failed to open device: %s", codec_name, snd_strerror(err));
190195
goto fail;
191196
}
192-
if ((enc_info = alsa_get_enc_info(enc)) == NULL) {
193-
LOG_FMT(LL_ERROR, "%s: error: bad encoding: %s", codec_name, enc);
197+
if ((enc_info = alsa_get_enc_info(p->enc)) == NULL) {
198+
LOG_FMT(LL_ERROR, "%s: error: bad encoding: %s", codec_name, p->enc);
194199
goto fail;
195200
}
196-
if ((err = snd_pcm_hw_params_malloc(&p)) < 0) {
201+
202+
if ((err = snd_pcm_hw_params_malloc(&hw_p)) < 0) {
197203
LOG_FMT(LL_ERROR, "%s: error: failed to allocate hw params: %s", codec_name, snd_strerror(err));
198204
goto fail;
199205
}
200-
if ((err = snd_pcm_hw_params_any(dev, p)) < 0) {
206+
if ((err = snd_pcm_hw_params_any(dev, hw_p)) < 0) {
201207
LOG_FMT(LL_ERROR, "%s: error: failed to initialize hw params: %s", codec_name, snd_strerror(err));
202208
goto fail;
203209
}
204-
if ((err = snd_pcm_hw_params_set_access(dev, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
210+
if ((err = snd_pcm_hw_params_set_access(dev, hw_p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
205211
LOG_FMT(LL_ERROR, "%s: error: failed to set access: %s", codec_name, snd_strerror(err));
206212
goto fail;
207213
}
208-
if ((err = snd_pcm_hw_params_set_format(dev, p, enc_info->fmt)) < 0) {
214+
if ((err = snd_pcm_hw_params_set_format(dev, hw_p, enc_info->fmt)) < 0) {
209215
LOG_FMT(LL_ERROR, "%s: error: failed to set format: %s", codec_name, snd_strerror(err));
210216
goto fail;
211217
}
212-
if ((err = snd_pcm_hw_params_set_rate(dev, p, (unsigned int) fs, 0)) < 0) {
218+
if ((err = snd_pcm_hw_params_set_rate(dev, hw_p, (unsigned int) p->fs, 0)) < 0) {
213219
LOG_FMT(LL_ERROR, "%s: error: failed to set rate: %s", codec_name, snd_strerror(err));
214220
goto fail;
215221
}
216-
if ((err = snd_pcm_hw_params_set_channels(dev, p, channels)) < 0) {
222+
if ((err = snd_pcm_hw_params_set_channels(dev, hw_p, p->channels)) < 0) {
217223
LOG_FMT(LL_ERROR, "%s: error: failed to set channels: %s", codec_name, snd_strerror(err));
218224
goto fail;
219225
}
220-
buf_frames = dsp_globals.buf_frames;
221-
if ((err = snd_pcm_hw_params_set_buffer_size_min(dev, p, &buf_frames)) < 0) {
222-
LOG_FMT(LL_ERROR, "%s: error: failed to set buffer size minimum: %s", codec_name, snd_strerror(err));
226+
if ((err = snd_pcm_hw_params_get_buffer_size_min(hw_p, &buf_frames_min)) < 0) {
227+
LOG_FMT(LL_ERROR, "%s: error: failed to get minimum buffer size: %s", codec_name, snd_strerror(err));
228+
goto fail;
229+
}
230+
if ((err = snd_pcm_hw_params_get_buffer_size_max(hw_p, &buf_frames_max)) < 0) {
231+
LOG_FMT(LL_ERROR, "%s: error: failed to get maximum buffer size: %s", codec_name, snd_strerror(err));
232+
goto fail;
233+
}
234+
buf_frames = MINIMUM(MAXIMUM(p->block_frames, buf_frames_min), buf_frames_max);
235+
if ((err = snd_pcm_hw_params_set_buffer_size_min(dev, hw_p, &buf_frames)) < 0) {
236+
LOG_FMT(LL_ERROR, "%s: error: failed to set minimum buffer size: %s", codec_name, snd_strerror(err));
223237
goto fail;
224238
}
225-
buf_frames = dsp_globals.buf_frames * dsp_globals.max_buf_ratio;
226-
if ((err = snd_pcm_hw_params_set_buffer_size_max(dev, p, &buf_frames)) < 0) {
227-
LOG_FMT(LL_ERROR, "%s: error: failed to set buffer size maximum: %s", codec_name, snd_strerror(err));
239+
buf_frames = MINIMUM(MAXIMUM(p->block_frames * p->buf_ratio, buf_frames), buf_frames_max);
240+
if ((err = snd_pcm_hw_params_set_buffer_size_near(dev, hw_p, &buf_frames)) < 0) {
241+
LOG_FMT(LL_ERROR, "%s: error: failed to set buffer size: %s", codec_name, snd_strerror(err));
228242
goto fail;
229243
}
230-
if ((err = snd_pcm_hw_params(dev, p)) < 0) {
231-
LOG_FMT(LL_ERROR, "%s: error: failed to set params: %s", codec_name, snd_strerror(err));
244+
LOG_FMT(LL_VERBOSE, "%s: info: buffer size: %lu frames [%lu %lu]", codec_name, buf_frames, buf_frames_min, buf_frames_max);
245+
if ((err = snd_pcm_hw_params(dev, hw_p)) < 0) {
246+
LOG_FMT(LL_ERROR, "%s: error: failed to set hw params: %s", codec_name, snd_strerror(err));
232247
goto fail;
233248
}
234249

250+
if (p->mode == CODEC_MODE_WRITE) {
251+
if ((err = snd_pcm_sw_params_malloc(&sw_p)) < 0) {
252+
LOG_FMT(LL_ERROR, "%s: error: failed to allocate sw params: %s", codec_name, snd_strerror(err));
253+
goto fail;
254+
}
255+
if ((err = snd_pcm_sw_params_current(dev, sw_p)) < 0) {
256+
LOG_FMT(LL_ERROR, "%s: error: failed to get current sw params: %s", codec_name, snd_strerror(err));
257+
goto fail;
258+
}
259+
if ((err = snd_pcm_sw_params_set_start_threshold(dev, sw_p, MINIMUM(p->block_frames * 2, buf_frames))) < 0) {
260+
LOG_FMT(LL_ERROR, "%s: error: failed to set start threshold: %s", codec_name, snd_strerror(err));
261+
goto fail;
262+
}
263+
if ((err = snd_pcm_sw_params(dev, sw_p)) < 0) {
264+
LOG_FMT(LL_ERROR, "%s: error: failed to set sw params: %s", codec_name, snd_strerror(err));
265+
goto fail;
266+
}
267+
}
268+
269+
#if DUMP_PCM_INFO
270+
snd_output_t *output = NULL;
271+
if (snd_output_stdio_attach(&output, stderr, 0) == 0)
272+
snd_pcm_dump(dev, output);
273+
#endif
274+
235275
state = calloc(1, sizeof(struct alsa_state));
236276
state->dev = dev;
237277
state->enc_info = enc_info;
238278
state->delay = 0;
239279

240280
c = calloc(1, sizeof(struct codec));
241-
c->path = path;
242-
c->type = type;
281+
c->path = p->path;
282+
c->type = p->type;
243283
c->enc = enc_info->name;
244-
c->fs = fs;
245-
c->channels = channels;
284+
c->fs = p->fs;
285+
c->channels = p->channels;
246286
c->prec = enc_info->prec;
247-
c->can_dither = enc_info->can_dither;
248-
c->interactive = (mode == CODEC_MODE_WRITE) ? 1 : 0;
287+
if (enc_info->can_dither) c->hints |= CODEC_HINT_CAN_DITHER;
288+
if (p->mode == CODEC_MODE_WRITE) c->hints |= CODEC_HINT_INTERACTIVE;
249289
c->frames = -1;
250-
c->read = alsa_read;
251-
c->write = alsa_write;
290+
if (p->mode == CODEC_MODE_READ) c->read = alsa_read;
291+
else c->write = alsa_write;
252292
c->seek = alsa_seek;
253293
c->delay = alsa_delay;
254294
c->drop = alsa_drop;
255295
c->pause = alsa_pause;
256296
c->destroy = alsa_destroy;
257297
c->data = state;
258298

259-
snd_pcm_hw_params_free(p);
299+
snd_pcm_hw_params_free(hw_p);
260300

261301
return c;
262302

263303
fail:
264-
if (p != NULL)
265-
snd_pcm_hw_params_free(p);
266-
if (dev != NULL)
267-
snd_pcm_close(dev);
304+
if (hw_p) snd_pcm_hw_params_free(hw_p);
305+
if (sw_p) snd_pcm_sw_params_free(sw_p);
306+
if (dev) snd_pcm_close(dev);
268307
return NULL;
269308
}
270309

alsa.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
#include "dsp.h"
2323
#include "codec.h"
2424

25-
struct codec * alsa_codec_init(const char *, const char *, const char *, int, int, int, int);
25+
struct codec * alsa_codec_init(const struct codec_params *);
2626
void alsa_codec_print_encodings(const char *);
2727

2828
#endif

ao.c

+24-18
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,6 @@ static struct ao_enc_info * ao_get_enc_info(const char *enc)
5555
return NULL;
5656
}
5757

58-
ssize_t ao_read(struct codec *c, sample_t *buf, ssize_t frames)
59-
{
60-
return 0;
61-
}
62-
6358
ssize_t ao_write(struct codec *c, sample_t *buf, ssize_t frames)
6459
{
6560
struct ao_state *state = (struct ao_state *) c->data;
@@ -102,50 +97,60 @@ void ao_destroy(struct codec *c)
10297
free(state);
10398
}
10499

105-
struct codec * ao_codec_init(const char *path, const char *type, const char *enc, int fs, int channels, int endian, int mode)
100+
struct codec * ao_codec_init(const struct codec_params *p)
106101
{
107102
int driver;
108103
struct ao_enc_info *enc_info;
109104
struct ao_sample_format format;
110105
ao_device *dev = NULL;
111106
struct ao_state *state = NULL;
107+
struct ao_option *opts = NULL;
112108
struct codec *c = NULL;
109+
char buf_time_str[12];
113110

114111
if (ao_open_count == 0)
115112
ao_initialize();
116113
if ((driver = ao_default_driver_id()) == -1) {
117114
LOG_FMT(LL_OPEN_ERROR, "%s: error: failed get default driver id", codec_name);
118115
goto fail;
119116
}
120-
if ((enc_info = ao_get_enc_info(enc)) == NULL) {
121-
LOG_FMT(LL_ERROR, "%s: error: bad encoding: %s", codec_name, enc);
117+
if ((enc_info = ao_get_enc_info(p->enc)) == NULL) {
118+
LOG_FMT(LL_ERROR, "%s: error: bad encoding: %s", codec_name, p->enc);
122119
goto fail;
123120
}
124121
format.bits = enc_info->prec;
125-
format.rate = fs;
126-
format.channels = channels;
122+
format.rate = p->fs;
123+
format.channels = p->channels;
127124
format.byte_format = AO_FMT_NATIVE;
128125
format.matrix = NULL;
129-
if ((dev = ao_open_live(driver, &format, NULL)) == NULL) {
126+
const double buf_time = MINIMUM(1000.0 * p->block_frames * p->buf_ratio / p->fs, 1000.0);
127+
snprintf(buf_time_str, sizeof(buf_time_str), "%.2f", buf_time);
128+
if (LOGLEVEL(LL_VERBOSE))
129+
ao_append_option(&opts, "verbose", "");
130+
if (strcmp(p->path, CODEC_DEFAULT_DEVICE) != 0)
131+
ao_append_option(&opts, "dev", p->path);
132+
ao_append_option(&opts, "client_name", dsp_globals.prog_name);
133+
ao_append_option(&opts, "buffer_time", buf_time_str);
134+
if ((dev = ao_open_live(driver, &format, opts)) == NULL) {
130135
LOG_FMT(LL_OPEN_ERROR, "%s: error: could not open device", codec_name);
131136
goto fail;
132137
}
138+
ao_free_options(opts);
133139

134140
state = calloc(1, sizeof(struct ao_state));
135141
state->dev = dev;
136142
state->enc_info = enc_info;
137143

138144
c = calloc(1, sizeof(struct codec));
139-
c->path = path;
140-
c->type = type;
145+
c->path = p->path;
146+
c->type = p->type;
141147
c->enc = enc_info->name;
142-
c->fs = fs;
143-
c->channels = channels;
148+
c->fs = p->fs;
149+
c->channels = p->channels;
144150
c->prec = enc_info->prec;
145-
c->can_dither = 1; /* all formats are fixed-point LPCM */
146-
c->interactive = 1;
151+
c->hints |= CODEC_HINT_CAN_DITHER; /* all formats are fixed-point LPCM */
152+
c->hints |= CODEC_HINT_INTERACTIVE;
147153
c->frames = -1;
148-
c->read = ao_read;
149154
c->write = ao_write;
150155
c->seek = ao_seek;
151156
c->delay = ao_delay;
@@ -159,6 +164,7 @@ struct codec * ao_codec_init(const char *path, const char *type, const char *enc
159164
return c;
160165

161166
fail:
167+
ao_free_options(opts);
162168
if (ao_open_count == 0)
163169
ao_shutdown();
164170
return NULL;

ao.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
#include "dsp.h"
2323
#include "codec.h"
2424

25-
struct codec * ao_codec_init(const char *, const char *, const char *, int, int, int, int);
25+
struct codec * ao_codec_init(const struct codec_params *);
2626
void ao_codec_print_encodings(const char *);
2727

2828
#endif

0 commit comments

Comments
 (0)