mirror of https://github.com/mpv-player/mpv
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.
This commit is contained in:
parent
fedb9229d5
commit
dab6eaaa5e
|
@ -52,12 +52,11 @@
|
||||||
struct priv {
|
struct priv {
|
||||||
snd_pcm_t *alsa;
|
snd_pcm_t *alsa;
|
||||||
snd_pcm_format_t alsa_fmt;
|
snd_pcm_format_t alsa_fmt;
|
||||||
size_t bytes_per_sample;
|
|
||||||
int can_pause;
|
int can_pause;
|
||||||
snd_pcm_sframes_t prepause_frames;
|
snd_pcm_sframes_t prepause_frames;
|
||||||
float delay_before_pause;
|
float delay_before_pause;
|
||||||
int buffersize;
|
int buffersize; // in frames
|
||||||
int outburst;
|
int outburst; // in frames
|
||||||
|
|
||||||
int cfg_block;
|
int cfg_block;
|
||||||
char *cfg_device;
|
char *cfg_device;
|
||||||
|
@ -251,6 +250,7 @@ static const int mp_to_alsa_format[][2] = {
|
||||||
|
|
||||||
static int find_alsa_format(int af_format)
|
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++) {
|
for (int n = 0; mp_to_alsa_format[n][0] != AF_FORMAT_UNKNOWN; n++) {
|
||||||
if (mp_to_alsa_format[n][0] == af_format)
|
if (mp_to_alsa_format[n][0] == af_format)
|
||||||
return mp_to_alsa_format[n][1];
|
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);
|
err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams);
|
||||||
CHECK_ALSA_ERROR("Unable to get initial parameters");
|
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);
|
p->alsa_fmt = find_alsa_format(ao->format);
|
||||||
if (p->alsa_fmt == SND_PCM_FORMAT_UNKNOWN) {
|
if (p->alsa_fmt == SND_PCM_FORMAT_UNKNOWN) {
|
||||||
p->alsa_fmt = SND_PCM_FORMAT_S16;
|
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);
|
err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt);
|
||||||
CHECK_ALSA_ERROR("Unable to set format");
|
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;
|
int num_channels = ao->channels.num;
|
||||||
err = snd_pcm_hw_params_set_channels_near
|
err = snd_pcm_hw_params_set_channels_near
|
||||||
(p->alsa, alsa_hwparams, &num_channels);
|
(p->alsa, alsa_hwparams, &num_channels);
|
||||||
|
@ -480,9 +480,6 @@ static int init(struct ao *ao)
|
||||||
(p->alsa, alsa_hwparams, &ao->samplerate, NULL);
|
(p->alsa, alsa_hwparams, &ao->samplerate, NULL);
|
||||||
CHECK_ALSA_ERROR("Unable to set samplerate-2");
|
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
|
err = snd_pcm_hw_params_set_buffer_time_near
|
||||||
(p->alsa, alsa_hwparams, &(unsigned int){BUFFER_TIME}, NULL);
|
(p->alsa, alsa_hwparams, &(unsigned int){BUFFER_TIME}, NULL);
|
||||||
CHECK_ALSA_ERROR("Unable to set buffer time near");
|
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);
|
err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize);
|
||||||
CHECK_ALSA_ERROR("Unable to get buffersize");
|
CHECK_ALSA_ERROR("Unable to get buffersize");
|
||||||
|
|
||||||
p->buffersize = bufsize * p->bytes_per_sample;
|
p->buffersize = bufsize;
|
||||||
MP_VERBOSE(ao, "got buffersize=%i\n", p->buffersize);
|
MP_VERBOSE(ao, "got buffersize=%i samples\n", p->buffersize);
|
||||||
|
|
||||||
err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL);
|
err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL);
|
||||||
CHECK_ALSA_ERROR("Unable to get period size");
|
CHECK_ALSA_ERROR("Unable to get period size");
|
||||||
|
|
||||||
MP_VERBOSE(ao, "got period size %li\n", chunk_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 */
|
/* setting software parameters */
|
||||||
err = snd_pcm_sw_params_current(p->alsa, alsa_swparams);
|
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);
|
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",
|
MP_VERBOSE(ao, "opened: %d Hz/%d channels/%d bps/%d samples buffer/%s\n",
|
||||||
ao->samplerate, ao->channels.num, (int)p->bytes_per_sample,
|
ao->samplerate, ao->channels.num, af_fmt2bits(ao->format),
|
||||||
p->buffersize, snd_pcm_format_description(p->alsa_fmt));
|
p->buffersize, snd_pcm_format_description(p->alsa_fmt));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -639,23 +636,24 @@ alsa_error: ;
|
||||||
static int play(struct ao *ao, void **data, int samples, int flags)
|
static int play(struct ao *ao, void **data, int samples, int flags)
|
||||||
{
|
{
|
||||||
struct priv *p = ao->priv;
|
struct priv *p = ao->priv;
|
||||||
int num_frames;
|
|
||||||
snd_pcm_sframes_t res = 0;
|
snd_pcm_sframes_t res = 0;
|
||||||
int len = samples * p->bytes_per_sample;
|
|
||||||
if (!(flags & AOPLAY_FINAL_CHUNK))
|
if (!(flags & AOPLAY_FINAL_CHUNK))
|
||||||
len = len / p->outburst * p->outburst;
|
samples = samples / p->outburst * p->outburst;
|
||||||
num_frames = len / p->bytes_per_sample;
|
|
||||||
|
|
||||||
if (!p->alsa) {
|
if (!p->alsa) {
|
||||||
MP_ERR(ao, "Device configuration error.");
|
MP_ERR(ao, "Device configuration error.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_frames == 0)
|
if (samples == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
do {
|
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) {
|
if (res == -EINTR) {
|
||||||
/* nothing to do */
|
/* nothing to do */
|
||||||
|
@ -692,10 +690,10 @@ static int get_space(struct ao *ao)
|
||||||
err = snd_pcm_status(p->alsa, status);
|
err = snd_pcm_status(p->alsa, status);
|
||||||
CHECK_ALSA_ERROR("cannot get pcm 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?
|
if (space > p->buffersize) // Buffer underrun?
|
||||||
space = p->buffersize;
|
space = p->buffersize;
|
||||||
return space / p->bytes_per_sample;
|
return space;
|
||||||
|
|
||||||
alsa_error:
|
alsa_error:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue