/* * Copyright © 2014 Nick Bowler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright (c) 2009 by Takashi Iwai * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include struct dplii_state { snd_pcm_extplug_t ext; AVAudioResampleContext *avr; }; static inline void *area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset) { unsigned int bitofs = area->first + area->step * offset; return (char *) area->addr + bitofs / 8; } static snd_pcm_sframes_t dplii_transfer(snd_pcm_extplug_t *ext, const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset, snd_pcm_uframes_t size) { struct dplii_state *dplii = (void *)ext; void *src = area_addr(src_areas, src_offset); void *dst = area_addr(dst_areas, dst_offset); return avresample_convert(dplii->avr, &dst, 2*size, size, &src, 2*size, size); } static int dplii_init(snd_pcm_extplug_t *ext) { struct dplii_state *dplii = (void *)ext; unsigned rate; int rc; dplii->avr = avresample_alloc_context(); if (!dplii->avr) return -ENOMEM; av_opt_set_int(dplii->avr, "in_channel_layout", AV_CH_LAYOUT_5POINT0, 0); av_opt_set_int(dplii->avr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); av_opt_set_int(dplii->avr, "in_sample_rate", ext->rate, 0); av_opt_set_int(dplii->avr, "out_sample_rate", ext->rate, 0); av_opt_set_int(dplii->avr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0); av_opt_set_int(dplii->avr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); av_opt_set_int(dplii->avr, "matrix_encoding", AV_MATRIX_ENCODING_DPLII, 0); rc = avresample_open(dplii->avr); if (rc < 0) { avresample_free(&dplii->avr); return -ENODEV; } return 0; } static int dplii_close(snd_pcm_extplug_t *ext) { struct dplii_state *dplii = (void *)ext; avresample_close(dplii->avr); avresample_free(&dplii->avr); return 0; } static const unsigned chmap5[] = { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_FC, SND_CHMAP_RL, SND_CHMAP_RR, }; static snd_pcm_chmap_query_t **dplii_query_chmaps(snd_pcm_extplug_t *ext) { snd_pcm_chmap_query_t **maps; snd_pcm_chmap_query_t *p; maps = malloc(2 * sizeof *maps); if (!maps) return NULL; p = malloc(sizeof *p + 5 * sizeof *p->map.pos); if (!p) { free(maps); return NULL; } p->type = SND_CHMAP_TYPE_FIXED; p->map.channels = 5; memcpy(p->map.pos, chmap5, 5 * sizeof p->map.pos[0]); maps[0] = p; maps[1] = NULL; return maps; } static snd_pcm_chmap_t *dplii_get_chmap(snd_pcm_extplug_t *ext) { snd_pcm_chmap_t *map; map = malloc(sizeof *map + 5 * sizeof map->pos[0]); if (map) { map->channels = 5; memcpy(map->pos, chmap5, 5 * sizeof map->pos[0]); } return map; } static const snd_pcm_extplug_callback_t dplii_callback = { .init = dplii_init, .transfer = dplii_transfer, .close = dplii_close, .query_chmaps = dplii_query_chmaps, .get_chmap = dplii_get_chmap, }; SND_PCM_PLUGIN_DEFINE_FUNC(dplii) { snd_config_iterator_t i, next; snd_config_t *slave = NULL; struct dplii_state *dplii; int rc; if (stream != SND_PCM_STREAM_PLAYBACK) { SNDERR("dplii is only for playback"); return -EINVAL; } snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); const char *id; if (snd_config_get_id(n, &id) < 0) continue; if (!strcmp(id, "comment") || !strcmp(id, "type") || !strcmp(id, "hint")) continue; if (!strcmp(id, "slave")) { slave = n; continue; } SNDERR("Unknown field %s", id); return -EINVAL; } if (!slave) { SNDERR("no slave configuration for %s", name); return -EINVAL; } dplii = malloc(sizeof *dplii); if (!dplii) return -ENOMEM; *dplii = (struct dplii_state) { .ext = { .version = SND_PCM_EXTPLUG_VERSION, .name = "Dolby Pro-Logic II Encoder Plugin", .callback = &dplii_callback, .private_data = dplii, }, }; rc = snd_pcm_extplug_create(&dplii->ext, name, root, slave, stream, mode); if (rc < 0) { free(dplii); return rc; } snd_pcm_extplug_set_param(&dplii->ext, SND_PCM_EXTPLUG_HW_CHANNELS, 5); snd_pcm_extplug_set_slave_param(&dplii->ext, SND_PCM_EXTPLUG_HW_CHANNELS, 2); snd_pcm_extplug_set_param(&dplii->ext, SND_PCM_EXTPLUG_HW_FORMAT, SND_PCM_FORMAT_S16); snd_pcm_extplug_set_slave_param(&dplii->ext, SND_PCM_EXTPLUG_HW_FORMAT, SND_PCM_FORMAT_S16); *pcmp = dplii->ext.pcm; return 0; } SND_PCM_PLUGIN_SYMBOL(dplii);