From bf60281ffb6f47986fa6ef9ee559689be0075050 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 10 Nov 2013 23:15:02 +0100 Subject: audio/out: reject non-interleaved formats No AO can handle these, so it would be a problem if they get added later, and non-interleaved formats get accepted erroneously. Let them gracefully fall back to other formats. Most AOs actually would fall back, but to an unrelated formats. This is covered by this commit too, and if possible they should pick the interleaved variant if a non-interleaved format is requested. --- audio/out/ao_alsa.c | 2 ++ audio/out/ao_coreaudio.c | 2 ++ audio/out/ao_dsound.c | 2 +- audio/out/ao_lavc.c | 1 + audio/out/ao_null.c | 2 ++ audio/out/ao_oss.c | 2 ++ audio/out/ao_pcm.c | 3 +++ audio/out/ao_portaudio.c | 2 ++ audio/out/ao_pulse.c | 2 ++ audio/out/ao_rsound.c | 2 ++ audio/out/ao_sdl.c | 2 ++ audio/out/ao_sndio.c | 3 +++ audio/out/ao_wasapi.c | 1 + 13 files changed, 25 insertions(+), 1 deletion(-) (limited to 'audio/out') diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c index 5816ee5f5f..0d1c589e34 100644 --- a/audio/out/ao_alsa.c +++ b/audio/out/ao_alsa.c @@ -436,6 +436,8 @@ static int init(struct ao *ao) (p->alsa, alsa_hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); CHECK_ALSA_ERROR("Unable to set access type"); + ao->format = af_fmt_from_planar(ao->format); + p->alsa_fmt = find_alsa_format(ao->format); if (p->alsa_fmt == SND_PCM_FORMAT_UNKNOWN) { p->alsa_fmt = SND_PCM_FORMAT_S16; diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c index da526ada20..4823eb43e1 100644 --- a/audio/out/ao_coreaudio.c +++ b/audio/out/ao_coreaudio.c @@ -285,6 +285,8 @@ static int init(struct ao *ao) // Save selected device id p->device = selected_device; + ao->format = af_fmt_from_planar(ao->format); + bool supports_digital = false; /* Probe whether device support S/PDIF stream output if input is AC3 stream. */ if (AF_FORMAT_IS_AC3(ao->format)) { diff --git a/audio/out/ao_dsound.c b/audio/out/ao_dsound.c index 464947c0dc..58812d80e6 100644 --- a/audio/out/ao_dsound.c +++ b/audio/out/ao_dsound.c @@ -388,7 +388,7 @@ static int init(struct ao *ao) WAVEFORMATEXTENSIBLE wformat; DSBUFFERDESC dsbpridesc; DSBUFFERDESC dsbdesc; - int format = ao->format; + int format = af_fmt_from_planar(ao->format); int rate = ao->samplerate; if (AF_FORMAT_IS_AC3(format)) diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c index 4364b9054d..b849f9b2b4 100644 --- a/audio/out/ao_lavc.c +++ b/audio/out/ao_lavc.c @@ -105,6 +105,7 @@ static int init(struct ao *ao) ac->stream->codec->channel_layout = mp_chmap_to_lavc(&ao->channels); ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE; + ao->format = af_fmt_from_planar(ao->format); { // first check if the selected format is somewhere in the list of diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c index ff6b12a1a6..75e812b238 100644 --- a/audio/out/ao_null.c +++ b/audio/out/ao_null.c @@ -51,6 +51,8 @@ static int init(struct ao *ao) struct priv *priv = talloc_zero(ao, struct priv); ao->priv = priv; + ao->format = af_fmt_from_planar(ao->format); + struct mp_chmap_sel sel = {0}; mp_chmap_sel_add_any(&sel); if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c index db9847851d..a97424c1cb 100644 --- a/audio/out/ao_oss.c +++ b/audio/out/ao_oss.c @@ -261,6 +261,8 @@ static int init(struct ao *ao) fcntl(p->audio_fd, F_SETFD, FD_CLOEXEC); #endif + ao->format = af_fmt_from_planar(ao->format); + if (AF_FORMAT_IS_AC3(ao->format)) { ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate); } diff --git a/audio/out/ao_pcm.c b/audio/out/ao_pcm.c index f7d793700d..e94e6b569a 100644 --- a/audio/out/ao_pcm.c +++ b/audio/out/ao_pcm.c @@ -118,6 +118,9 @@ static int init(struct ao *ao) if (!priv->outputfilename) priv->outputfilename = talloc_strdup(priv, priv->waveheader ? "audiodump.wav" : "audiodump.pcm"); + + ao->format = af_fmt_from_planar(ao->format); + if (priv->waveheader) { // WAV files must have one of the following formats diff --git a/audio/out/ao_portaudio.c b/audio/out/ao_portaudio.c index 8b235f8806..d75fad0aca 100644 --- a/audio/out/ao_portaudio.c +++ b/audio/out/ao_portaudio.c @@ -242,6 +242,8 @@ static int init(struct ao *ao) = Pa_GetDeviceInfo(pa_device)->defaultHighOutputLatency, }; + ao->format = af_fmt_from_planar(ao->format); + const struct format_map *fmt = format_maps; while (fmt->pa_format) { if (fmt->mp_format == ao->format) { diff --git a/audio/out/ao_pulse.c b/audio/out/ao_pulse.c index a4da2a179b..1d7fcdd382 100644 --- a/audio/out/ao_pulse.c +++ b/audio/out/ao_pulse.c @@ -286,6 +286,8 @@ static int init(struct ao *ao) ss.channels = ao->channels.num; ss.rate = ao->samplerate; + ao->format = af_fmt_from_planar(ao->format); + const struct format_map *fmt_map = format_maps; while (fmt_map->mp_format != ao->format) { if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) { diff --git a/audio/out/ao_rsound.c b/audio/out/ao_rsound.c index 162fb21feb..95fadf7188 100644 --- a/audio/out/ao_rsound.c +++ b/audio/out/ao_rsound.c @@ -114,6 +114,8 @@ static int init(struct ao *ao) rsd_set_param(priv->rd, RSD_SAMPLERATE, &ao->samplerate); rsd_set_param(priv->rd, RSD_CHANNELS, &ao->channels.num); + ao->format = af_fmt_from_planar(ao->format); + int rsd_format = set_format(ao); rsd_set_param(priv->rd, RSD_FORMAT, &rsd_format); diff --git a/audio/out/ao_sdl.c b/audio/out/ao_sdl.c index a42c0812cb..d95a82615b 100644 --- a/audio/out/ao_sdl.c +++ b/audio/out/ao_sdl.c @@ -144,6 +144,8 @@ static int init(struct ao *ao) return -1; } + ao->format = af_fmt_from_planar(ao->format); + SDL_AudioSpec desired, obtained; switch (ao->format) { diff --git a/audio/out/ao_sndio.c b/audio/out/ao_sndio.c index ab9eaf8197..1786c0158d 100644 --- a/audio/out/ao_sndio.c +++ b/audio/out/ao_sndio.c @@ -130,6 +130,9 @@ static int init(struct ao *ao) MP_ERR(ao, "can't open sndio %s\n", p->dev); goto error; } + + ao->format = af_fmt_from_planar(ao->format); + sio_initpar(&p->par); for (i = 0, ap = af_to_par;; i++, ap++) { if (i == sizeof(af_to_par) / sizeof(struct af_to_par)) { diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c index c605e1cd5d..5d9f33278c 100644 --- a/audio/out/ao_wasapi.c +++ b/audio/out/ao_wasapi.c @@ -1231,6 +1231,7 @@ static void uninit(struct ao *ao, bool immed) static int init(struct ao *ao) { mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: init!\n"); + ao->format = af_fmt_from_planar(ao->format); struct mp_chmap_sel sel = {0}; mp_chmap_sel_add_waveext(&sel); if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) -- cgit v1.2.3 From 380fc765e4ad4e3ff828c9b0bd4a565ea2ba79ed Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 10 Nov 2013 23:24:21 +0100 Subject: audio/out: prepare for non-interleaved audio This comes with two internal AO API changes: 1. ao_driver.play now can take non-interleaved audio. For this purpose, the data pointer is changed to void **data, where data[0] corresponds to the pointer in the old API. Also, the len argument as well as the return value are now in samples, not bytes. "Sample" in this context means the unit of the smallest possible audio frame, i.e. sample_size * channels. 2. ao_driver.get_space now returns samples instead of bytes. (Similar to the play function.) Change all AOs to use the new API. The AO API as exposed to the rest of the player still uses the old API. It's emulated in ao.c. This is purely to split the commits changing all AOs and the commits adding actual support for outputting N-I audio. --- audio/out/ao.c | 17 ++++++++++------- audio/out/ao.h | 4 +++- audio/out/ao_alsa.c | 16 +++++----------- audio/out/ao_coreaudio.c | 8 +++++--- audio/out/ao_dsound.c | 7 ++++--- audio/out/ao_jack.c | 7 ++++--- audio/out/ao_lavc.c | 23 ++++++++++++----------- audio/out/ao_null.c | 7 ++++--- audio/out/ao_openal.c | 9 +++++---- audio/out/ao_oss.c | 16 ++++++++-------- audio/out/ao_pcm.c | 7 ++++--- audio/out/ao_portaudio.c | 8 ++++---- audio/out/ao_pulse.c | 12 ++++++------ audio/out/ao_rsound.c | 6 +++--- audio/out/ao_sdl.c | 9 +++++---- audio/out/ao_sndio.c | 8 ++++---- audio/out/ao_wasapi.c | 13 ++++++------- 17 files changed, 92 insertions(+), 85 deletions(-) (limited to 'audio/out') diff --git a/audio/out/ao.c b/audio/out/ao.c index 55db34becb..fd20270160 100644 --- a/audio/out/ao.c +++ b/audio/out/ao.c @@ -159,7 +159,10 @@ static struct ao *ao_create(bool probing, struct mpv_global *global, talloc_free(chmap); if (ao->driver->init(ao) < 0) goto error; - ao->bps = ao->channels.num * ao->samplerate * af_fmt2bits(ao->format) / 8; + ao->sstride = af_fmt2bits(ao->format) / 8; + if (!af_fmt_is_planar(ao->format)) + ao->sstride *= ao->channels.num; + ao->bps = ao->samplerate * ao->sstride; return ao; error: talloc_free(ao); @@ -208,7 +211,8 @@ void ao_uninit(struct ao *ao, bool cut_audio) int ao_play(struct ao *ao, void *data, int len, int flags) { - return ao->driver->play(ao, data, len, flags); + int r = ao->driver->play(ao, &data, len / ao->sstride, flags); + return r < 0 ? r : r * ao->sstride; } int ao_control(struct ao *ao, enum aocontrol cmd, void *arg) @@ -229,7 +233,7 @@ double ao_get_delay(struct ao *ao) int ao_get_space(struct ao *ao) { - return ao->driver->get_space(ao); + return ao->driver->get_space(ao) * ao->sstride; } void ao_reset(struct ao *ao) @@ -254,10 +258,9 @@ int ao_play_silence(struct ao *ao, int samples) { if (samples <= 0 || AF_FORMAT_IS_SPECIAL(ao->format)) return 0; - int s = ao->channels.num * (af_fmt2bits(ao->format) / 8); - char *p = talloc_size(NULL, samples * s); - af_fill_silence(p, samples * s, ao->format); - int r = ao_play(ao, p, samples * s, 0); + char *p = talloc_size(NULL, samples * ao->sstride); + af_fill_silence(p, samples * ao->sstride, ao->format); + int r = ao_play(ao, p, samples * ao->sstride, 0); talloc_free(p); return r; } diff --git a/audio/out/ao.h b/audio/out/ao.h index 159a6adc0f..eb1af990ff 100644 --- a/audio/out/ao.h +++ b/audio/out/ao.h @@ -56,7 +56,7 @@ struct ao_driver { void (*uninit)(struct ao *ao, bool cut_audio); void (*reset)(struct ao*ao); int (*get_space)(struct ao *ao); - int (*play)(struct ao *ao, void *data, int len, int flags); + int (*play)(struct ao *ao, void **data, int samples, int flags); float (*get_delay)(struct ao *ao); void (*pause)(struct ao *ao); void (*resume)(struct ao *ao); @@ -73,6 +73,8 @@ struct ao { struct mp_chmap channels; int format; int bps; // bytes per second + int sstride; // size of a sample on each plane + // (format_size*num_channels/num_planes) double pts; // some mplayer.c state (why is this here?) struct bstr buffer; int buffer_playable_size; // part of the part of the buffer the AO hasn't diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c index 0d1c589e34..97c8b5acfd 100644 --- a/audio/out/ao_alsa.c +++ b/audio/out/ao_alsa.c @@ -636,18 +636,12 @@ static void reset(struct ao *ao) alsa_error: ; } -/* - plays 'len' bytes of 'data' - returns: number of bytes played - modified last at 29.06.02 by jp - thanxs for marius for giving us the light ;) - */ - -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *p = ao->priv; int num_frames; snd_pcm_sframes_t res = 0; + int len = samples * p->bytes_per_sample; if (!(flags & AOPLAY_FINAL_CHUNK)) len = len / p->outburst * p->outburst; num_frames = len / p->bytes_per_sample; @@ -661,7 +655,7 @@ static int play(struct ao *ao, void *data, int len, int flags) return 0; do { - res = snd_pcm_writei(p->alsa, data, num_frames); + res = snd_pcm_writei(p->alsa, data[0], num_frames); if (res == -EINTR) { /* nothing to do */ @@ -680,7 +674,7 @@ static int play(struct ao *ao, void *data, int len, int flags) } } while (res == 0); - return res < 0 ? -1 : res * p->bytes_per_sample; + return res < 0 ? -1 : res; alsa_error: return -1; @@ -701,7 +695,7 @@ static int get_space(struct ao *ao) unsigned space = snd_pcm_status_get_avail(status) * p->bytes_per_sample; if (space > p->buffersize) // Buffer underrun? space = p->buffersize; - return space; + return space / p->bytes_per_sample; alsa_error: return 0; diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c index 4823eb43e1..d61ce63d01 100644 --- a/audio/out/ao_coreaudio.c +++ b/audio/out/ao_coreaudio.c @@ -578,10 +578,12 @@ coreaudio_error: return CONTROL_ERROR; } -static int play(struct ao *ao, void *output_samples, int num_bytes, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *p = ao->priv; struct priv_d *d = p->digital; + void *output_samples = data[0]; + int num_bytes = samples * ao->sstride; // Check whether we need to reset the digital output stream. if (p->is_digital && d->stream_asbd_changed) { @@ -599,7 +601,7 @@ static int play(struct ao *ao, void *output_samples, int num_bytes, int flags) int wrote = mp_ring_write(p->buffer, output_samples, num_bytes); audio_resume(ao); - return wrote; + return wrote / ao->sstride; } static void reset(struct ao *ao) @@ -612,7 +614,7 @@ static void reset(struct ao *ao) static int get_space(struct ao *ao) { struct priv *p = ao->priv; - return mp_ring_available(p->buffer); + return mp_ring_available(p->buffer) / ao->sstride; } static float get_delay(struct ao *ao) diff --git a/audio/out/ao_dsound.c b/audio/out/ao_dsound.c index 58812d80e6..f828a210dc 100644 --- a/audio/out/ao_dsound.c +++ b/audio/out/ao_dsound.c @@ -596,7 +596,7 @@ static int get_space(struct ao *ao) int space = check_free_buffer_size(ao); if (space < p->min_free_space) return 0; - return space - p->min_free_space; + return (space - p->min_free_space) / ao->sstride; } /** @@ -606,9 +606,10 @@ static int get_space(struct ao *ao) \param flags currently unused \return number of played bytes */ -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *p = ao->priv; + int len = samples * ao->sstride; int space = check_free_buffer_size(ao); if (space < len) @@ -616,7 +617,7 @@ static int play(struct ao *ao, void *data, int len, int flags) if (!(flags & AOPLAY_FINAL_CHUNK)) len = (len / p->outburst) * p->outburst; - return write_buffer(ao, data, len); + return write_buffer(ao, data[0], len) / ao->sstride; } /** diff --git a/audio/out/ao_jack.c b/audio/out/ao_jack.c index 20dd0d4aab..f69c6c928d 100644 --- a/audio/out/ao_jack.c +++ b/audio/out/ao_jack.c @@ -319,19 +319,20 @@ static void audio_resume(struct ao *ao) static int get_space(struct ao *ao) { struct priv *p = ao->priv; - return mp_ring_available(p->ring); + return mp_ring_available(p->ring) / ao->sstride; } /** * \brief write data into buffer and reset underrun flag */ -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *p = ao->priv; + int len = samples * ao->sstride; if (!(flags & AOPLAY_FINAL_CHUNK)) len -= len % p->outburst; p->underrun = 0; - return mp_ring_write(p->ring, data, len); + return mp_ring_write(p->ring, data[0], len) / ao->sstride; } #define OPT_BASE_STRUCT struct priv diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c index b849f9b2b4..9be3a7b6ae 100644 --- a/audio/out/ao_lavc.c +++ b/audio/out/ao_lavc.c @@ -296,8 +296,6 @@ static void fill_with_padding(void *buf, int cnt, int sz, const void *padding) } // close audio device -static int encode(struct ao *ao, double apts, void *data); -static int play(struct ao *ao, void *data, int len, int flags); static void uninit(struct ao *ao, bool cut_audio) { struct encode_lavc_context *ectx = ao->encode_lavc_ctx; @@ -315,7 +313,7 @@ static int get_space(struct ao *ao) { struct priv *ac = ao->priv; - return ac->aframesize * ac->sample_size * ao->channels.num * ac->framecount; + return ac->aframesize * ac->framecount; } // must get exactly ac->aframesize amount of data @@ -444,10 +442,10 @@ static int encode(struct ao *ao, double apts, void *data) return packet.size; } -// plays 'len' bytes of 'data' +// plays 'samples' samples of 'ni_data[0]' // it should round it down to frame sizes -// return: number of bytes played -static int play(struct ao *ao, void *data, int len, int flags) +// return: number of samples played +static int play(struct ao *ao, void **ni_data, int samples, int flags) { struct priv *ac = ao->priv; struct encode_lavc_context *ectx = ao->encode_lavc_ctx; @@ -457,6 +455,8 @@ static int play(struct ao *ao, void *data, int len, int flags) double nextpts; double pts = ao->pts; double outpts; + void *data = ni_data[0]; + int len = samples * ao->sstride; int bytelen = len; len /= ac->sample_size * ao->channels.num; @@ -477,8 +477,9 @@ static int play(struct ao *ao, void *data, int len, int flags) extralen / ac->sample_size, ac->sample_size, ac->sample_padding); // No danger of recursion, because AOPLAY_FINAL_CHUNK not set - written = play(ao, paddingbuf, bytelen + extralen, 0); - if (written < bytelen) { + written = + play(ao, &paddingbuf, (bytelen + extralen) / ao->sstride, 0); + if (written * ao->sstride < bytelen) { MP_ERR(ao, "did not write enough data at the end\n"); } talloc_free(paddingbuf); @@ -492,7 +493,7 @@ static int play(struct ao *ao, void *data, int len, int flags) while (encode(ao, outpts, NULL) > 0) ; - return FFMIN(written, bytelen); + return (FFMIN(written, bytelen)) / ao->sstride; } if (pts == MP_NOPTS_VALUE) { @@ -559,7 +560,7 @@ static int play(struct ao *ao, void *data, int len, int flags) if (ac->offset_left <= -len) { // skip whole frame ac->offset_left += len; - return len * ac->sample_size * ao->channels.num; + return len; } else { // skip part of this frame, buffer/encode the rest bufpos -= ac->offset_left; @@ -632,7 +633,7 @@ static int play(struct ao *ao, void *data, int len, int flags) ectx->next_in_pts = nextpts; } - return bufpos * ac->sample_size * ao->channels.num; + return bufpos; } const struct ao_driver audio_out_lavc = { diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c index 75e812b238..7470d9a824 100644 --- a/audio/out/ao_null.c +++ b/audio/out/ao_null.c @@ -96,18 +96,19 @@ static int get_space(struct ao *ao) struct priv *priv = ao->priv; drain(ao); - return priv->buffersize - priv->buffered_bytes; + return (priv->buffersize - priv->buffered_bytes) / ao->sstride; } -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *priv = ao->priv; + int len = samples * ao->sstride; int maxbursts = (priv->buffersize - priv->buffered_bytes) / priv->outburst; int playbursts = len / priv->outburst; int bursts = playbursts > maxbursts ? maxbursts : playbursts; priv->buffered_bytes += bursts * priv->outburst; - return bursts * priv->outburst; + return (bursts * priv->outburst) / ao->sstride; } static float get_delay(struct ao *ao) diff --git a/audio/out/ao_openal.c b/audio/out/ao_openal.c index 6fdc388711..256e50f281 100644 --- a/audio/out/ao_openal.c +++ b/audio/out/ao_openal.c @@ -251,18 +251,19 @@ static int get_space(struct ao *ao) queued = NUM_BUF - queued - 3; if (queued < 0) return 0; - return queued * CHUNK_SIZE * ao->channels.num; + return (queued * CHUNK_SIZE * ao->channels.num) / ao->sstride; } /** * \brief write data into buffer and reset underrun flag */ -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { ALint state; int i, j, k; int ch; - int16_t *d = data; + int16_t *d = data[0]; + int len = samples * ao->sstride; len /= ao->channels.num * CHUNK_SIZE; for (i = 0; i < len; i++) { for (ch = 0; ch < ao->channels.num; ch++) { @@ -278,7 +279,7 @@ static int play(struct ao *ao, void *data, int len, int flags) alGetSourcei(sources[0], AL_SOURCE_STATE, &state); if (state != AL_PLAYING) // checked here in case of an underrun alSourcePlayv(ao->channels.num, sources); - return len * ao->channels.num * CHUNK_SIZE; + return len * ao->channels.num * CHUNK_SIZE / ao->sstride; } static float get_delay(struct ao *ao) diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c index a97424c1cb..09a2951629 100644 --- a/audio/out/ao_oss.c +++ b/audio/out/ao_oss.c @@ -457,7 +457,7 @@ static int get_space(struct ao *ao) if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &p->zz) != -1) { // calculate exact buffer space: playsize = p->zz.fragments * p->zz.fragsize; - return playsize; + return playsize / ao->sstride; } #endif @@ -475,14 +475,14 @@ static int get_space(struct ao *ao) } #endif - return p->outburst; + return p->outburst / ao->sstride; } // stop playing, keep buffers (for pause) static void audio_pause(struct ao *ao) { struct priv *p = ao->priv; - p->prepause_space = get_space(ao); + p->prepause_space = get_space(ao) * ao->sstride; #ifdef SNDCTL_DSP_RESET ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL); #else @@ -493,17 +493,18 @@ static void audio_pause(struct ao *ao) // plays 'len' bytes of 'data' // it should round it down to outburst*n // return: number of bytes played -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *p = ao->priv; + int len = samples * ao->sstride; if (len == 0) return len; if (len > p->outburst || !(flags & AOPLAY_FINAL_CHUNK)) { len /= p->outburst; len *= p->outburst; } - len = write(p->audio_fd, data, len); - return len; + len = write(p->audio_fd, data[0], len); + return len / ao->sstride; } // resume playing, after audio_pause() @@ -513,8 +514,7 @@ static void audio_resume(struct ao *ao) #ifndef SNDCTL_DSP_RESET reset(ao); #endif - int fillframes = (get_space(ao) - p->prepause_space) / - (af_fmt2bits(ao->format) / 8 * ao->channels.num); + int fillframes = get_space(ao) - p->prepause_space / ao->sstride; if (fillframes > 0) ao_play_silence(ao, fillframes); } diff --git a/audio/out/ao_pcm.c b/audio/out/ao_pcm.c index e94e6b569a..1d88fc6665 100644 --- a/audio/out/ao_pcm.c +++ b/audio/out/ao_pcm.c @@ -196,13 +196,14 @@ static int get_space(struct ao *ao) return 65536; } -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *priv = ao->priv; + int len = samples * ao->sstride; - fwrite(data, len, 1, priv->fp); + fwrite(data[0], len, 1, priv->fp); priv->data_length += len; - return len; + return len / ao->sstride; } #define OPT_BASE_STRUCT struct priv diff --git a/audio/out/ao_portaudio.c b/audio/out/ao_portaudio.c index d75fad0aca..9c0d7804f8 100644 --- a/audio/out/ao_portaudio.c +++ b/audio/out/ao_portaudio.c @@ -280,13 +280,13 @@ error_exit: return -1; } -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *priv = ao->priv; pthread_mutex_lock(&priv->ring_mutex); - int write_len = mp_ring_write(priv->ring, data, len); + int write_len = mp_ring_write(priv->ring, data[0], samples * ao->sstride); if (flags & AOPLAY_FINAL_CHUNK) priv->play_remaining = true; @@ -295,7 +295,7 @@ static int play(struct ao *ao, void *data, int len, int flags) if (Pa_IsStreamStopped(priv->stream) == 1) check_pa_ret(Pa_StartStream(priv->stream)); - return write_len; + return write_len / ao->sstride; } static int get_space(struct ao *ao) @@ -308,7 +308,7 @@ static int get_space(struct ao *ao) pthread_mutex_unlock(&priv->ring_mutex); - return free; + return free / ao->sstride; } static float get_delay(struct ao *ao) diff --git a/audio/out/ao_pulse.c b/audio/out/ao_pulse.c index 1d7fcdd382..f1800f279c 100644 --- a/audio/out/ao_pulse.c +++ b/audio/out/ao_pulse.c @@ -377,14 +377,14 @@ static void cork(struct ao *ao, bool pause) } // Play the specified data to the pulseaudio server -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *priv = ao->priv; pa_threaded_mainloop_lock(priv->mainloop); - if (pa_stream_write(priv->stream, data, len, NULL, 0, + if (pa_stream_write(priv->stream, data[0], samples * ao->sstride, NULL, 0, PA_SEEK_RELATIVE) < 0) { GENERIC_ERR_MSG("pa_stream_write() failed"); - len = -1; + samples = -1; } if (flags & AOPLAY_FINAL_CHUNK) { // Force start in case the stream was too short for prebuf @@ -392,7 +392,7 @@ static int play(struct ao *ao, void *data, int len, int flags) pa_operation_unref(op); } pa_threaded_mainloop_unlock(priv->mainloop); - return len; + return samples; } // Reset the audio stream, i.e. flush the playback buffer on the server side @@ -427,14 +427,14 @@ static void resume(struct ao *ao) cork(ao, false); } -// Return number of bytes that may be written to the server without blocking +// Return number of samples that may be written to the server without blocking static int get_space(struct ao *ao) { struct priv *priv = ao->priv; pa_threaded_mainloop_lock(priv->mainloop); size_t space = pa_stream_writable_size(priv->stream); pa_threaded_mainloop_unlock(priv->mainloop); - return space; + return space / ao->sstride; } // Return the current latency in seconds diff --git a/audio/out/ao_rsound.c b/audio/out/ao_rsound.c index 95fadf7188..123ee14ef5 100644 --- a/audio/out/ao_rsound.c +++ b/audio/out/ao_rsound.c @@ -163,13 +163,13 @@ static void audio_resume(struct ao *ao) static int get_space(struct ao *ao) { struct priv *priv = ao->priv; - return rsd_get_avail(priv->rd); + return rsd_get_avail(priv->rd) / ao->sstride; } -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *priv = ao->priv; - return rsd_write(priv->rd, data, len); + return rsd_write(priv->rd, data[0], samples * ao->sstride) / ao->sstride; } static float get_delay(struct ao *ao) diff --git a/audio/out/ao_sdl.c b/audio/out/ao_sdl.c index d95a82615b..c2b1c9d947 100644 --- a/audio/out/ao_sdl.c +++ b/audio/out/ao_sdl.c @@ -261,7 +261,7 @@ static int get_space(struct ao *ao) SDL_LockMutex(priv->buffer_mutex); int space = av_fifo_space(priv->buffer); SDL_UnlockMutex(priv->buffer_mutex); - return space; + return space / ao->sstride; } static void pause(struct ao *ao) @@ -292,20 +292,21 @@ static void resume(struct ao *ao) do_resume(ao); } -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *priv = ao->priv; + int len = samples * ao->sstride; SDL_LockMutex(priv->buffer_mutex); int free = av_fifo_space(priv->buffer); if (len > free) len = free; - av_fifo_generic_write(priv->buffer, data, len, NULL); + av_fifo_generic_write(priv->buffer, data[0], len, NULL); SDL_CondSignal(priv->underrun_cond); SDL_UnlockMutex(priv->buffer_mutex); if (priv->unpause) { priv->unpause = 0; do_resume(ao); } - return len; + return len / ao->sstride; } static float get_delay(struct ao *ao) diff --git a/audio/out/ao_sndio.c b/audio/out/ao_sndio.c index 1786c0158d..cfe0616943 100644 --- a/audio/out/ao_sndio.c +++ b/audio/out/ao_sndio.c @@ -242,16 +242,16 @@ static void reset(struct ao *ao) /* * play given number of bytes until sio_write() blocks */ -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *p = ao->priv; int n; - n = sio_write(p->hdl, data, len); + n = sio_write(p->hdl, data[0], samples * ao->sstride); p->delay += n; if (flags & AOPLAY_FINAL_CHUNK) reset(ao); - return n; + return n / ao->sstride; } /* @@ -271,7 +271,7 @@ static int get_space(struct ao *ao) ; /* nothing */ sio_revents(p->hdl, p->pfd); - return p->par.bufsz * p->par.pchan * p->par.bps - p->delay; + return (p->par.bufsz * p->par.pchan * p->par.bps - p->delay) / ao->sstride; } /* diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c index 5d9f33278c..b43bf04753 100644 --- a/audio/out/ao_wasapi.c +++ b/audio/out/ao_wasapi.c @@ -1197,7 +1197,7 @@ static int get_space(struct ao *ao) if (!ao || !ao->priv) return -1; struct wasapi_state *state = (struct wasapi_state *)ao->priv; - return mp_ring_available(state->ringbuff); + return mp_ring_available(state->ringbuff) / ao->sstride; } static void reset_buffers(struct wasapi_state *state) @@ -1339,25 +1339,24 @@ static void reset(struct ao *ao) reset_buffers(state); } -static int play(struct ao *ao, void *data, int len, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { - int ret = 0; if (!ao || !ao->priv) - return ret; + return 0; struct wasapi_state *state = (struct wasapi_state *)ao->priv; if (WaitForSingleObject(state->fatal_error, 0) == WAIT_OBJECT_0) { /* something bad happened */ - return ret; + return 0; } - ret = mp_ring_write(state->ringbuff, data, len); + int ret = mp_ring_write(state->ringbuff, data[0], samples * ao->sstride); if (!state->is_playing) { /* start playing */ state->is_playing = 1; SetEvent(state->hPlay); } - return ret; + return ret / ao->sstride; } static float get_delay(struct ao *ao) -- cgit v1.2.3 From 347a86198b214b5e79b45d198c5cd2cc3c3a759a Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 10 Nov 2013 23:38:18 +0100 Subject: audio: switch output to mp_audio_buffer Replace the code that used a single buffer with mp_audio_buffer. This also enables non-interleaved output operation, although it's still disabled, and no AO supports it yet. --- audio/out/ao.c | 12 +++++++----- audio/out/ao.h | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'audio/out') diff --git a/audio/out/ao.c b/audio/out/ao.c index fd20270160..50c95830c3 100644 --- a/audio/out/ao.c +++ b/audio/out/ao.c @@ -209,10 +209,9 @@ void ao_uninit(struct ao *ao, bool cut_audio) talloc_free(ao); } -int ao_play(struct ao *ao, void *data, int len, int flags) +int ao_play(struct ao *ao, void **data, int samples, int flags) { - int r = ao->driver->play(ao, &data, len / ao->sstride, flags); - return r < 0 ? r : r * ao->sstride; + return ao->driver->play(ao, data, samples, flags); } int ao_control(struct ao *ao, enum aocontrol cmd, void *arg) @@ -233,7 +232,7 @@ double ao_get_delay(struct ao *ao) int ao_get_space(struct ao *ao) { - return ao->driver->get_space(ao) * ao->sstride; + return ao->driver->get_space(ao); } void ao_reset(struct ao *ao) @@ -260,7 +259,10 @@ int ao_play_silence(struct ao *ao, int samples) return 0; char *p = talloc_size(NULL, samples * ao->sstride); af_fill_silence(p, samples * ao->sstride, ao->format); - int r = ao_play(ao, p, samples * ao->sstride, 0); + void *tmp[MP_NUM_CHANNELS]; + for (int n = 0; n < MP_NUM_CHANNELS; n++) + tmp[n] = p; + int r = ao_play(ao, tmp, samples, 0); talloc_free(p); return r; } diff --git a/audio/out/ao.h b/audio/out/ao.h index eb1af990ff..dff9ad6a8b 100644 --- a/audio/out/ao.h +++ b/audio/out/ao.h @@ -76,8 +76,8 @@ struct ao { int sstride; // size of a sample on each plane // (format_size*num_channels/num_planes) double pts; // some mplayer.c state (why is this here?) - struct bstr buffer; - int buffer_playable_size; // part of the part of the buffer the AO hasn't + struct mp_audio_buffer *buffer; // queued audio; passed to play() later + int buffer_playable_samples;// part of the part of the buffer the AO hasn't // accepted yet with play() bool probing; // if true, don't fail loudly on init bool untimed; @@ -97,7 +97,7 @@ struct ao *ao_init_best(struct mpv_global *global, struct encode_lavc_context *encode_lavc_ctx, int samplerate, int format, struct mp_chmap channels); void ao_uninit(struct ao *ao, bool cut_audio); -int ao_play(struct ao *ao, void *data, int len, int flags); +int ao_play(struct ao *ao, void **data, int samples, int flags); int ao_control(struct ao *ao, enum aocontrol cmd, void *arg); double ao_get_delay(struct ao *ao); int ao_get_space(struct ao *ao); -- cgit v1.2.3 From fedb9229d508df6ee1207f8fe264aa868068a03e Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 10 Nov 2013 23:38:41 +0100 Subject: ao_null: support non-interleaved audio Simply change internals from using byte counts to sample counts. --- audio/out/ao_null.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) (limited to 'audio/out') diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c index 7470d9a824..9fe289e3e4 100644 --- a/audio/out/ao_null.c +++ b/audio/out/ao_null.c @@ -30,7 +30,8 @@ struct priv { double last_time; - float buffered_bytes; + // All values are in samples + float buffered; int buffersize; int outburst; }; @@ -40,9 +41,9 @@ static void drain(struct ao *ao) struct priv *priv = ao->priv; double now = mp_time_sec(); - priv->buffered_bytes -= (now - priv->last_time) * ao->bps; - if (priv->buffered_bytes < 0) - priv->buffered_bytes = 0; + priv->buffered -= (now - priv->last_time) * ao->samplerate; + if (priv->buffered < 0) + priv->buffered = 0; priv->last_time = now; } @@ -51,17 +52,20 @@ static int init(struct ao *ao) struct priv *priv = talloc_zero(ao, struct priv); ao->priv = priv; - ao->format = af_fmt_from_planar(ao->format); - struct mp_chmap_sel sel = {0}; mp_chmap_sel_add_any(&sel); if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) return -1; - int samplesize = af_fmt2bits(ao->format) / 8; - priv->outburst = 256 * ao->channels.num * samplesize; + // Minimal unit of audio samples that can be written at once. If play() is + // called with sizes not aligned to this, a rounded size will be returned. + // (This is not needed by the AO API, but many AOs behave this way.) + priv->outburst = 256; + // A "buffer" for about 0.2 seconds of audio - priv->buffersize = (int)(ao->samplerate * 0.2 / 256 + 1) * priv->outburst; + int bursts = (int)(ao->samplerate * 0.2 + 1) / priv->outburst; + priv->buffersize = priv->outburst * bursts; + priv->last_time = mp_time_sec(); return 0; @@ -76,7 +80,7 @@ static void uninit(struct ao *ao, bool cut_audio) static void reset(struct ao *ao) { struct priv *priv = ao->priv; - priv->buffered_bytes = 0; + priv->buffered = 0; } // stop playing, keep buffers (for pause) @@ -96,19 +100,18 @@ static int get_space(struct ao *ao) struct priv *priv = ao->priv; drain(ao); - return (priv->buffersize - priv->buffered_bytes) / ao->sstride; + return priv->buffersize - priv->buffered; } static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *priv = ao->priv; - int len = samples * ao->sstride; - int maxbursts = (priv->buffersize - priv->buffered_bytes) / priv->outburst; - int playbursts = len / priv->outburst; + int maxbursts = (priv->buffersize - priv->buffered) / priv->outburst; + int playbursts = samples / priv->outburst; int bursts = playbursts > maxbursts ? maxbursts : playbursts; - priv->buffered_bytes += bursts * priv->outburst; - return (bursts * priv->outburst) / ao->sstride; + priv->buffered += bursts * priv->outburst; + return bursts * priv->outburst; } static float get_delay(struct ao *ao) @@ -116,7 +119,7 @@ static float get_delay(struct ao *ao) struct priv *priv = ao->priv; drain(ao); - return priv->buffered_bytes / ao->bps; + return priv->buffered / (double)ao->samplerate; } const struct ao_driver audio_out_null = { -- cgit v1.2.3 From dab6eaaa5e48d7a333564a29b0e04440d24a4ba5 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 10 Nov 2013 23:39:08 +0100 Subject: ao_alsa: support non-interleaved audio ALSA supports non-interleaved audio natively using a separate API function for writing audio. (Though you have to tell it about this on initialization.) ALSA doesn't have separate sample formats for this, so just pretend to negotiate the interleaved format, and assume that all non-interleaved formats have an interleaved companion format. --- audio/out/ao_alsa.c | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) (limited to 'audio/out') diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c index 97c8b5acfd..2ef35db6cf 100644 --- a/audio/out/ao_alsa.c +++ b/audio/out/ao_alsa.c @@ -52,12 +52,11 @@ struct priv { snd_pcm_t *alsa; snd_pcm_format_t alsa_fmt; - size_t bytes_per_sample; int can_pause; snd_pcm_sframes_t prepause_frames; float delay_before_pause; - int buffersize; - int outburst; + int buffersize; // in frames + int outburst; // in frames int cfg_block; char *cfg_device; @@ -251,6 +250,7 @@ static const int mp_to_alsa_format[][2] = { static int find_alsa_format(int af_format) { + af_format = af_fmt_from_planar(af_format); for (int n = 0; mp_to_alsa_format[n][0] != AF_FORMAT_UNKNOWN; n++) { if (mp_to_alsa_format[n][0] == af_format) return mp_to_alsa_format[n][1]; @@ -432,12 +432,6 @@ static int init(struct ao *ao) err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams); CHECK_ALSA_ERROR("Unable to get initial parameters"); - err = snd_pcm_hw_params_set_access - (p->alsa, alsa_hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); - CHECK_ALSA_ERROR("Unable to set access type"); - - ao->format = af_fmt_from_planar(ao->format); - p->alsa_fmt = find_alsa_format(ao->format); if (p->alsa_fmt == SND_PCM_FORMAT_UNKNOWN) { p->alsa_fmt = SND_PCM_FORMAT_S16; @@ -460,6 +454,12 @@ static int init(struct ao *ao) err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt); CHECK_ALSA_ERROR("Unable to set format"); + snd_pcm_access_t access = af_fmt_is_planar(ao->format) + ? SND_PCM_ACCESS_RW_NONINTERLEAVED + : SND_PCM_ACCESS_RW_INTERLEAVED; + err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access); + CHECK_ALSA_ERROR("Unable to set access type"); + int num_channels = ao->channels.num; err = snd_pcm_hw_params_set_channels_near (p->alsa, alsa_hwparams, &num_channels); @@ -480,9 +480,6 @@ static int init(struct ao *ao) (p->alsa, alsa_hwparams, &ao->samplerate, NULL); CHECK_ALSA_ERROR("Unable to set samplerate-2"); - p->bytes_per_sample = af_fmt2bits(ao->format) / 8; - p->bytes_per_sample *= ao->channels.num; - err = snd_pcm_hw_params_set_buffer_time_near (p->alsa, alsa_hwparams, &(unsigned int){BUFFER_TIME}, NULL); CHECK_ALSA_ERROR("Unable to set buffer time near"); @@ -501,14 +498,14 @@ static int init(struct ao *ao) err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize); CHECK_ALSA_ERROR("Unable to get buffersize"); - p->buffersize = bufsize * p->bytes_per_sample; - MP_VERBOSE(ao, "got buffersize=%i\n", p->buffersize); + p->buffersize = bufsize; + MP_VERBOSE(ao, "got buffersize=%i samples\n", p->buffersize); err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL); CHECK_ALSA_ERROR("Unable to get period size"); MP_VERBOSE(ao, "got period size %li\n", chunk_size); - p->outburst = chunk_size * p->bytes_per_sample; + p->outburst = chunk_size; /* setting software parameters */ err = snd_pcm_sw_params_current(p->alsa, alsa_swparams); @@ -539,8 +536,8 @@ static int init(struct ao *ao) p->can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams); - MP_VERBOSE(ao, "opened: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n", - ao->samplerate, ao->channels.num, (int)p->bytes_per_sample, + MP_VERBOSE(ao, "opened: %d Hz/%d channels/%d bps/%d samples buffer/%s\n", + ao->samplerate, ao->channels.num, af_fmt2bits(ao->format), p->buffersize, snd_pcm_format_description(p->alsa_fmt)); return 0; @@ -639,23 +636,24 @@ alsa_error: ; static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *p = ao->priv; - int num_frames; snd_pcm_sframes_t res = 0; - int len = samples * p->bytes_per_sample; if (!(flags & AOPLAY_FINAL_CHUNK)) - len = len / p->outburst * p->outburst; - num_frames = len / p->bytes_per_sample; + samples = samples / p->outburst * p->outburst; if (!p->alsa) { MP_ERR(ao, "Device configuration error."); return -1; } - if (num_frames == 0) + if (samples == 0) return 0; do { - res = snd_pcm_writei(p->alsa, data[0], num_frames); + if (af_fmt_is_planar(ao->format)) { + res = snd_pcm_writen(p->alsa, data, samples); + } else { + res = snd_pcm_writei(p->alsa, data[0], samples); + } if (res == -EINTR) { /* nothing to do */ @@ -692,10 +690,10 @@ static int get_space(struct ao *ao) err = snd_pcm_status(p->alsa, status); CHECK_ALSA_ERROR("cannot get pcm status"); - unsigned space = snd_pcm_status_get_avail(status) * p->bytes_per_sample; + unsigned space = snd_pcm_status_get_avail(status); if (space > p->buffersize) // Buffer underrun? space = p->buffersize; - return space / p->bytes_per_sample; + return space; alsa_error: return 0; -- cgit v1.2.3 From 7510caa0c5ef3db320d1065f869d14c0eddecf79 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 10 Nov 2013 23:39:22 +0100 Subject: ao_openal: support non-interleaved output Since ao_openal simulates multi-channel audio by placing a bunch of mono-sources in 3D space, non-interleaved audio is a perfect match for it. We just have to remove the interleaving code. --- audio/out/ao_openal.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'audio/out') diff --git a/audio/out/ao_openal.c b/audio/out/ao_openal.c index 256e50f281..c126852389 100644 --- a/audio/out/ao_openal.c +++ b/audio/out/ao_openal.c @@ -45,12 +45,12 @@ #define MAX_CHANS MP_NUM_CHANNELS #define NUM_BUF 128 #define CHUNK_SIZE 512 +#define CHUNK_SAMPLES (CHUNK_SIZE / 2) static ALuint buffers[MAX_CHANS][NUM_BUF]; static ALuint sources[MAX_CHANS]; static int cur_buf[MAX_CHANS]; static int unqueue_buf[MAX_CHANS]; -static int16_t *tmpbuf; static struct ao *ao_data; @@ -169,8 +169,7 @@ static int init(struct ao *ao) alcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq); if (alcGetError(dev) == ALC_NO_ERROR && freq) ao->samplerate = freq; - ao->format = AF_FORMAT_S16_NE; - tmpbuf = malloc(CHUNK_SIZE); + ao->format = AF_FORMAT_S16P; return 0; err_out: @@ -182,7 +181,6 @@ static void uninit(struct ao *ao, bool immed) { ALCcontext *ctx = alcGetCurrentContext(); ALCdevice *dev = alcGetContextsDevice(ctx); - free(tmpbuf); if (!immed) { ALint state; alGetSourcei(sources[0], AL_SOURCE_STATE, &state); @@ -251,7 +249,7 @@ static int get_space(struct ao *ao) queued = NUM_BUF - queued - 3; if (queued < 0) return 0; - return (queued * CHUNK_SIZE * ao->channels.num) / ao->sstride; + return queued * CHUNK_SAMPLES; } /** @@ -260,26 +258,21 @@ static int get_space(struct ao *ao) static int play(struct ao *ao, void **data, int samples, int flags) { ALint state; - int i, j, k; - int ch; - int16_t *d = data[0]; - int len = samples * ao->sstride; - len /= ao->channels.num * CHUNK_SIZE; - for (i = 0; i < len; i++) { - for (ch = 0; ch < ao->channels.num; ch++) { - for (j = 0, k = ch; j < CHUNK_SIZE / 2; j++, k += ao->channels.num) - tmpbuf[j] = d[k]; - alBufferData(buffers[ch][cur_buf[ch]], AL_FORMAT_MONO16, tmpbuf, + int num = samples / CHUNK_SAMPLES; + for (int i = 0; i < num; i++) { + for (int ch = 0; ch < ao->channels.num; ch++) { + int16_t *d = data[ch]; + d += i * CHUNK_SAMPLES; + alBufferData(buffers[ch][cur_buf[ch]], AL_FORMAT_MONO16, d, CHUNK_SIZE, ao->samplerate); alSourceQueueBuffers(sources[ch], 1, &buffers[ch][cur_buf[ch]]); cur_buf[ch] = (cur_buf[ch] + 1) % NUM_BUF; } - d += ao->channels.num * CHUNK_SIZE / 2; } alGetSourcei(sources[0], AL_SOURCE_STATE, &state); if (state != AL_PLAYING) // checked here in case of an underrun alSourcePlayv(ao->channels.num, sources); - return len * ao->channels.num * CHUNK_SIZE / ao->sstride; + return num * CHUNK_SAMPLES; } static float get_delay(struct ao *ao) @@ -287,7 +280,7 @@ static float get_delay(struct ao *ao) ALint queued; unqueue_buffers(); alGetSourcei(sources[0], AL_BUFFERS_QUEUED, &queued); - return queued * CHUNK_SIZE / 2 / (float)ao->samplerate; + return queued * CHUNK_SAMPLES / (float)ao->samplerate; } #define OPT_BASE_STRUCT struct priv -- cgit v1.2.3 From 4bd690c9982f07fb3b50ff1a43b9b648ee605d25 Mon Sep 17 00:00:00 2001 From: William Light Date: Mon, 11 Nov 2013 12:59:38 +0100 Subject: ao_jack: refactoring, also fix "no-connect" option --- audio/out/ao_jack.c | 154 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 97 insertions(+), 57 deletions(-) (limited to 'audio/out') diff --git a/audio/out/ao_jack.c b/audio/out/ao_jack.c index f69c6c928d..4815311dc6 100644 --- a/audio/out/ao_jack.c +++ b/audio/out/ao_jack.c @@ -38,9 +38,6 @@ #include -//! maximum number of channels supported, avoids lots of mallocs -#define MAX_CHANS MP_NUM_CHANNELS - //! size of one chunk, if this is too small MPlayer will start to "stutter" //! after a short time of playback #define CHUNK_SIZE (24 * 1024) @@ -48,7 +45,7 @@ #define NUM_CHUNKS 8 struct priv { - jack_port_t * ports[MAX_CHANS]; + jack_port_t * ports[MP_NUM_CHANNELS]; int num_ports; // Number of used ports == number of channels jack_client_t *client; int outburst; @@ -144,7 +141,7 @@ static int outputaudio(jack_nframes_t nframes, void *arg) { struct ao *ao = arg; struct priv *p = ao->priv; - float *bufs[MAX_CHANS]; + float *bufs[MP_NUM_CHANNELS]; int i; for (i = 0; i < p->num_ports; i++) bufs[i] = jack_port_get_buffer(p->ports[i], nframes); @@ -164,79 +161,119 @@ static int outputaudio(jack_nframes_t nframes, void *arg) return 0; } -static int init(struct ao *ao) +static int +connect_to_outports(struct ao *ao) { struct priv *p = ao->priv; + + char *port_name = (p->cfg_port && p->cfg_port[0]) ? p->cfg_port : NULL; const char **matching_ports = NULL; - char *port_name = p->cfg_port && p->cfg_port[0] ? p->cfg_port : NULL; - jack_options_t open_options = JackNullOption; int port_flags = JackPortIsInput; int i; + if (!port_name) + port_flags |= JackPortIsPhysical; + + matching_ports = jack_get_ports(p->client, port_name, NULL, port_flags); + + if (!matching_ports || !matching_ports[0]) { + MP_FATAL(ao, "no ports to connect to\n"); + goto err_get_ports; + } + + for (i = 0; i < p->num_ports && matching_ports[i]; i++) { + if (jack_connect(p->client, jack_port_name(p->ports[i]), + matching_ports[i])) + { + MP_FATAL(ao, "connecting failed\n"); + goto err_connect; + } + } + + free(matching_ports); + return 0; + +err_connect: + free(matching_ports); +err_get_ports: + return -1; +} + +static int +create_ports(struct ao *ao, int nports) +{ + struct priv *p = ao->priv; + int i; + + /* register our output ports */ + for (i = 0; i < nports; i++) { + char pname[30]; + snprintf(pname, sizeof(pname), "out_%d", i); + p->ports[i] = + jack_port_register(p->client, pname, JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + + if (!p->ports[i]) { + MP_FATAL(ao, "not enough ports available\n"); + goto err_port_register; + } + } + + p->num_ports = nports; + return 0; + +err_port_register: + return -1; +} + +static int init(struct ao *ao) +{ + struct priv *p = ao->priv; struct mp_chmap_sel sel = {0}; + jack_options_t open_options; - if (p->stdlayout == 0) { + switch (p->stdlayout) { + case 0: mp_chmap_sel_add_waveext(&sel); - } else if (p->stdlayout == 1) { + break; + + case 1: mp_chmap_sel_add_alsa_def(&sel); - } else { + break; + + default: mp_chmap_sel_add_any(&sel); } if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) - goto err_out; + goto err_chmap; + open_options = JackNullOption; if (!p->autostart) open_options |= JackNoStartServer; + p->client = jack_client_open(p->cfg_client_name, open_options, NULL); if (!p->client) { MP_FATAL(ao, "cannot open server\n"); - goto err_out; + goto err_client_open; } - jack_set_process_callback(p->client, outputaudio, ao); - // list matching ports if connections should be made - if (p->connect) { - if (!port_name) - port_flags |= JackPortIsPhysical; - matching_ports = jack_get_ports(p->client, port_name, NULL, port_flags); - if (!matching_ports || !matching_ports[0]) { - MP_FATAL(ao, "no physical ports available\n"); - goto err_out; - } - i = 1; - p->num_ports = ao->channels.num; - while (matching_ports[i]) - i++; - if (p->num_ports > i) - p->num_ports = i; - } + if (create_ports(ao, ao->channels.num)) + goto err_create_ports; + + jack_set_process_callback(p->client, outputaudio, ao); - // create out output ports - for (i = 0; i < p->num_ports; i++) { - char pname[30]; - snprintf(pname, 30, "out_%d", i); - p->ports[i] = - jack_port_register(p->client, pname, JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput, 0); - if (!p->ports[i]) { - MP_FATAL(ao, "not enough ports available\n"); - goto err_out; - } - } if (jack_activate(p->client)) { MP_FATAL(ao, "activate failed\n"); - goto err_out; - } - for (i = 0; i < p->num_ports; i++) { - if (jack_connect(p->client, jack_port_name(p->ports[i]), - matching_ports[i])) - { - MP_FATAL(ao, "connecting failed\n"); - goto err_out; - } + goto err_activate; } + ao->samplerate = jack_get_sample_rate(p->client); + + if (p->connect) + if (connect_to_outports(ao)) + goto err_connect; + jack_latency_range_t jack_latency_range; jack_port_get_latency_range(p->ports[0], JackPlaybackLatency, &jack_latency_range); @@ -245,19 +282,22 @@ static int init(struct ao *ao) p->callback_interval = 0; if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, p->num_ports)) - goto err_out; + goto err_chmap_sel_get_def; ao->format = AF_FORMAT_FLOAT_NE; int unitsize = ao->channels.num * sizeof(float); p->outburst = (CHUNK_SIZE + unitsize - 1) / unitsize * unitsize; p->ring = mp_ring_new(p, NUM_CHUNKS * p->outburst); - free(matching_ports); return 0; -err_out: - free(matching_ports); - if (p->client) - jack_client_close(p->client); +err_chmap_sel_get_def: +err_connect: + jack_deactivate(p->client); +err_activate: +err_create_ports: + jack_client_close(p->client); +err_client_open: +err_chmap: return -1; } -- cgit v1.2.3 From e1656d369af03d24d8c0008910c805a29a0c27ec Mon Sep 17 00:00:00 2001 From: William Light Date: Mon, 11 Nov 2013 14:33:32 +0100 Subject: ao_jack: switch from interleaved to planar audio --- audio/out/ao_jack.c | 187 ++++++++++++++++++++++++++-------------------------- 1 file changed, 92 insertions(+), 95 deletions(-) (limited to 'audio/out') diff --git a/audio/out/ao_jack.c b/audio/out/ao_jack.c index 4815311dc6..8a55a54239 100644 --- a/audio/out/ao_jack.c +++ b/audio/out/ao_jack.c @@ -4,6 +4,8 @@ * Copyleft 2001 by Felix Bünemann (atmosfear@users.sf.net) * and Reimar Döffinger (Reimar.Doeffinger@stud.uni-karlsruhe.de) * + * Copyleft 2013 by William Light for the mpv project + * * This file is part of MPlayer. * * MPlayer is free software; you can redistribute it and/or modify @@ -40,15 +42,17 @@ //! size of one chunk, if this is too small MPlayer will start to "stutter" //! after a short time of playback -#define CHUNK_SIZE (24 * 1024) +#define CHUNK_SIZE (8 * 1024) //! number of "virtual" chunks the buffer consists of #define NUM_CHUNKS 8 +struct port_ring { + jack_port_t *port; + struct mp_ring *ring; +}; + struct priv { - jack_port_t * ports[MP_NUM_CHANNELS]; - int num_ports; // Number of used ports == number of channels jack_client_t *client; - int outburst; float jack_latency; char *cfg_port; char *cfg_client_name; @@ -57,76 +61,49 @@ struct priv { int autostart; int stdlayout; volatile int paused; - volatile int underrun; // signals if an underrun occured + volatile int underrun; volatile float callback_interval; volatile float callback_time; - struct mp_ring *ring; // buffer for audio data -}; -static void silence(float **bufs, int cnt, int num_bufs); - -struct deinterleave { - float **bufs; - int num_bufs; - int cur_buf; - int pos; + int num_ports; + struct port_ring ports[MP_NUM_CHANNELS]; }; -static void deinterleave(void *info, void *src, int len) -{ - struct deinterleave *di = info; - float *s = src; - int i; - len /= sizeof(float); - for (i = 0; i < len; i++) { - di->bufs[di->cur_buf++][di->pos] = s[i]; - if (di->cur_buf >= di->num_bufs) { - di->cur_buf = 0; - di->pos++; - } - } -} - -/** - * \brief read data from buffer and splitting it into channels - * \param bufs num_bufs float buffers, each will contain the data of one channel - * \param cnt number of samples to read per channel - * \param num_bufs number of channels to split the data into - * \return number of samples read per channel, equals cnt unless there was too - * little data in the buffer - * - * Assumes the data in the buffer is of type float, the number of bytes - * read is res * num_bufs * sizeof(float), where res is the return value. - * If there is not enough data in the buffer remaining parts will be filled - * with silence. - */ -static int read_buffer(struct mp_ring *ring, float **bufs, int cnt, int num_bufs) -{ - struct deinterleave di = { - bufs, num_bufs, 0, 0 - }; - int buffered = mp_ring_buffered(ring); - if (cnt * sizeof(float) * num_bufs > buffered) { - silence(bufs, cnt, num_bufs); - cnt = buffered / sizeof(float) / num_bufs; - } - mp_ring_read_cb(ring, &di, cnt * num_bufs * sizeof(float), deinterleave); - return cnt; -} - -// end ring buffer stuff - /** * \brief fill the buffers with silence * \param bufs num_bufs float buffers, each will contain the data of one channel * \param cnt number of samples in each buffer * \param num_bufs number of buffers */ -static void silence(float **bufs, int cnt, int num_bufs) +static void +silence(float *buf, jack_nframes_t nframes) { - int i; - for (i = 0; i < num_bufs; i++) - memset(bufs[i], 0, cnt * sizeof(float)); + memset(buf, 0, nframes * sizeof(*buf)); +} + +static int +process_port(struct ao *ao, struct port_ring *pr, jack_nframes_t nframes) +{ + struct priv *p = ao->priv; + int buffered; + float *buf; + + buf = jack_port_get_buffer(pr->port, nframes); + + if (p->paused || p->underrun) { + silence(buf, nframes); + return 0; + } + + buffered = mp_ring_buffered(pr->ring) / sizeof(float); + if (buffered < nframes) { + mp_ring_read(pr->ring, (void *) buf, buffered * sizeof(float)); + silence(&buf[buffered], nframes - buffered); + return 1; + } + + mp_ring_read(pr->ring, (void *) buf, nframes * sizeof(float)); + return 0; } /** @@ -137,18 +114,23 @@ static void silence(float **bufs, int cnt, int num_bufs) * * Write silence into buffers if paused or an underrun occured */ -static int outputaudio(jack_nframes_t nframes, void *arg) +static int +process(jack_nframes_t nframes, void *arg) { struct ao *ao = arg; struct priv *p = ao->priv; - float *bufs[MP_NUM_CHANNELS]; - int i; - for (i = 0; i < p->num_ports; i++) - bufs[i] = jack_port_get_buffer(p->ports[i], nframes); - if (p->paused || p->underrun || !p->ring) - silence(bufs, nframes, p->num_ports); - else if (read_buffer(p->ring, bufs, nframes, p->num_ports) < nframes) + int i, underrun; + + underrun = 0; + + for (i = 0; i < p->num_ports; i++) { + if (process_port(ao, &p->ports[i], nframes)) + underrun = 1; + } + + if (underrun) p->underrun = 1; + if (p->estimate) { float now = mp_time_us() / 1000000.0; float diff = p->callback_time + p->callback_interval - now; @@ -158,6 +140,7 @@ static int outputaudio(jack_nframes_t nframes, void *arg) p->callback_time = now; p->callback_interval = (float)nframes / (float)ao->samplerate; } + return 0; } @@ -182,9 +165,8 @@ connect_to_outports(struct ao *ao) } for (i = 0; i < p->num_ports && matching_ports[i]; i++) { - if (jack_connect(p->client, jack_port_name(p->ports[i]), - matching_ports[i])) - { + if (jack_connect(p->client, jack_port_name(p->ports[i].port), + matching_ports[i])) { MP_FATAL(ao, "connecting failed\n"); goto err_connect; } @@ -203,20 +185,23 @@ static int create_ports(struct ao *ao, int nports) { struct priv *p = ao->priv; + struct port_ring *pr; + char pname[30]; int i; - /* register our output ports */ for (i = 0; i < nports; i++) { - char pname[30]; + pr = &p->ports[i]; + snprintf(pname, sizeof(pname), "out_%d", i); - p->ports[i] = - jack_port_register(p->client, pname, JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput, 0); + pr->port = jack_port_register(p->client, pname, JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); - if (!p->ports[i]) { + if (!pr->port) { MP_FATAL(ao, "not enough ports available\n"); goto err_port_register; } + + pr->ring = mp_ring_new(p, NUM_CHUNKS * CHUNK_SIZE); } p->num_ports = nports; @@ -232,6 +217,8 @@ static int init(struct ao *ao) struct mp_chmap_sel sel = {0}; jack_options_t open_options; + ao->format = AF_FORMAT_FLOATP; + switch (p->stdlayout) { case 0: mp_chmap_sel_add_waveext(&sel); @@ -261,7 +248,7 @@ static int init(struct ao *ao) if (create_ports(ao, ao->channels.num)) goto err_create_ports; - jack_set_process_callback(p->client, outputaudio, ao); + jack_set_process_callback(p->client, process, ao); if (jack_activate(p->client)) { MP_FATAL(ao, "activate failed\n"); @@ -275,7 +262,7 @@ static int init(struct ao *ao) goto err_connect; jack_latency_range_t jack_latency_range; - jack_port_get_latency_range(p->ports[0], JackPlaybackLatency, + jack_port_get_latency_range(p->ports[0].port, JackPlaybackLatency, &jack_latency_range); p->jack_latency = (float)(jack_latency_range.max + jack_get_buffer_size(p->client)) / (float)ao->samplerate; @@ -284,10 +271,6 @@ static int init(struct ao *ao) if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, p->num_ports)) goto err_chmap_sel_get_def; - ao->format = AF_FORMAT_FLOAT_NE; - int unitsize = ao->channels.num * sizeof(float); - p->outburst = (CHUNK_SIZE + unitsize - 1) / unitsize * unitsize; - p->ring = mp_ring_new(p, NUM_CHUNKS * p->outburst); return 0; err_chmap_sel_get_def: @@ -304,14 +287,16 @@ err_chmap: static float get_delay(struct ao *ao) { struct priv *p = ao->priv; - int buffered = mp_ring_buffered(p->ring); // could be less + int buffered = mp_ring_buffered(p->ports[0].ring); // could be less float in_jack = p->jack_latency; + if (p->estimate && p->callback_interval > 0) { float elapsed = mp_time_us() / 1000000.0 - p->callback_time; in_jack += p->callback_interval - elapsed; if (in_jack < 0) in_jack = 0; } + return (float)buffered / (float)ao->bps + in_jack; } @@ -321,8 +306,12 @@ static float get_delay(struct ao *ao) static void reset(struct ao *ao) { struct priv *p = ao->priv; + int i; p->paused = 1; - mp_ring_reset(p->ring); + + for (i = 0; i < p->num_ports; i++) + mp_ring_reset(p->ports[i].ring); + p->paused = 0; } @@ -330,10 +319,10 @@ static void reset(struct ao *ao) static void uninit(struct ao *ao, bool immed) { struct priv *p = ao->priv; + if (!immed) mp_sleep_us(get_delay(ao) * 1000 * 1000); - // HACK, make sure jack doesn't loop-output dirty buffers - reset(ao); + mp_sleep_us(100 * 1000); jack_client_close(p->client); } @@ -359,7 +348,7 @@ static void audio_resume(struct ao *ao) static int get_space(struct ao *ao) { struct priv *p = ao->priv; - return mp_ring_available(p->ring) / ao->sstride; + return mp_ring_available(p->ports[0].ring) / ao->sstride; } /** @@ -368,11 +357,19 @@ static int get_space(struct ao *ao) static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *p = ao->priv; - int len = samples * ao->sstride; - if (!(flags & AOPLAY_FINAL_CHUNK)) - len -= len % p->outburst; + struct port_ring *pr; + int i, len, ret; + + len = samples * ao->sstride; + ret = 0; + + for (i = 0; i < p->num_ports; i++) { + pr = &p->ports[i];