2 * Rate converter plugin using libavcodec's resampler
3 * Copyright (c) 2007 by Nicholas Kain <njkain@gmail.com>
5 * based on rate converter that uses libsamplerate
6 * Copyright (c) 2006 by Takashi Iwai <tiwai@suse.de>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
20 #include <alsa/asoundlib.h>
21 #include <alsa/pcm_rate.h>
22 #include AVCODEC_HEADER
25 static int filter_size = 16;
26 static int phase_shift = 10; /* auto-adjusts */
27 static double cutoff = 0; /* auto-adjusts */
30 struct AVResampleContext *context;
37 unsigned int channels;
40 static snd_pcm_uframes_t input_frames(void *obj, snd_pcm_uframes_t frames)
45 static snd_pcm_uframes_t output_frames(void *obj, snd_pcm_uframes_t frames)
50 static void pcm_src_free(void *obj)
52 struct rate_src *rate = obj;
56 for (i=0; i<rate->channels; i++) {
62 for (i=0; i<rate->channels; i++) {
67 rate->out = rate->in = NULL;
70 av_resample_close(rate->context);
75 static int pcm_src_init(void *obj, snd_pcm_rate_info_t *info)
77 struct rate_src *rate = obj;
80 if (! rate->context || rate->channels != info->channels) {
82 rate->channels = info->channels;
83 ir = rate->in_rate = info->in.rate;
84 or = rate->out_rate = info->out.rate;
92 cutoff = 1.0 - 1.0/filter_size;
96 rate->context = av_resample_init(info->out.rate, info->in.rate,
97 filter_size, phase_shift,
98 (info->out.rate >= info->in.rate ? 0 : 1), cutoff);
103 rate->out = malloc(rate->channels * sizeof(int16_t *));
104 rate->in = malloc(rate->channels * sizeof(int16_t *));
105 for (i=0; i<rate->channels; i++) {
106 rate->out[i] = calloc(info->out.period_size * 2,
108 rate->in[i] = calloc(info->in.period_size * 2,
111 rate->point = info->in.period_size / 2;
112 if (!rate->out || !rate->in) {
120 static int pcm_src_adjust_pitch(void *obj, snd_pcm_rate_info_t *info)
122 struct rate_src *rate = obj;
124 if (info->out.rate != rate->out_rate || info->in.rate != rate->in_rate)
125 pcm_src_init(obj, info);
129 static void pcm_src_reset(void *obj)
131 struct rate_src *rate = obj;
135 static void deinterleave(const int16_t *src, int16_t **dst, unsigned int frames,
136 unsigned int chans, int overflow)
141 memcpy(dst + overflow, src, frames*sizeof(int16_t));
142 } else if (chans == 2) {
143 for (j=overflow; j<(frames + overflow); j++) {
144 dst[0][j] = *(src++);
145 dst[1][j] = *(src++);
148 for (j=overflow; j<(frames + overflow); j++) {
149 for (i=0; i<chans; i++) {
150 dst[i][j] = *(src++);
156 static void reinterleave(int16_t **src, int16_t *dst, unsigned int frames,
162 memcpy(dst, src, frames*sizeof(int16_t));
163 } else if (chans == 2) {
164 for (j=0; j<frames; j++) {
165 *(dst++) = src[0][j];
166 *(dst++) = src[1][j];
169 for (j=0; j<frames; j++) {
170 for (i=0; i<chans; i++) {
171 *(dst++) = src[i][j];
177 static void pcm_src_convert_s16(void *obj, int16_t *dst, unsigned int
178 dst_frames, const int16_t *src, unsigned int src_frames)
180 struct rate_src *rate = obj;
181 int consumed = 0, chans=rate->channels, ret=0, i;
182 int total_in = rate->stored + src_frames, new_stored;
184 deinterleave(src, rate->in, src_frames, chans, rate->point);
185 for (i=0; i<chans; ++i) {
186 ret = av_resample(rate->context, rate->out[i],
187 rate->in[i]+rate->point-rate->stored, &consumed,
188 total_in, dst_frames, i == (chans - 1));
189 new_stored = total_in-consumed;
190 memmove(rate->in[i]+rate->point-new_stored,
191 rate->in[i]+rate->point-rate->stored+consumed,
192 new_stored*sizeof(int16_t));
194 av_resample_compensate(rate->context,
195 total_in-src_frames>filter_size?0:1, src_frames);
196 reinterleave(rate->out, dst, ret, chans);
197 rate->stored = total_in-consumed;
200 static void pcm_src_close(void *obj)
205 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
206 static int get_supported_rates(void *obj, unsigned int *rate_min,
207 unsigned int *rate_max)
209 *rate_min = *rate_max = 0; /* both unlimited */
213 static void dump(void *obj, snd_output_t *out)
215 snd_output_printf(out, "Converter: liblavc\n");
219 static snd_pcm_rate_ops_t pcm_src_ops = {
220 .close = pcm_src_close,
221 .init = pcm_src_init,
222 .free = pcm_src_free,
223 .reset = pcm_src_reset,
224 .adjust_pitch = pcm_src_adjust_pitch,
225 .convert_s16 = pcm_src_convert_s16,
226 .input_frames = input_frames,
227 .output_frames = output_frames,
228 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
229 .version = SND_PCM_RATE_PLUGIN_VERSION,
230 .get_supported_rates = get_supported_rates,
235 int pcm_src_open(unsigned int version, void **objp, snd_pcm_rate_ops_t *ops)
238 struct rate_src *rate;
240 #if SND_PCM_RATE_PLUGIN_VERSION < 0x010002
241 if (version != SND_PCM_RATE_PLUGIN_VERSION) {
242 fprintf(stderr, "Invalid rate plugin version %x\n", version);
246 rate = calloc(1, sizeof(*rate));
251 rate->context = NULL;
252 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
253 if (version == 0x010001)
254 memcpy(ops, &pcm_src_ops, sizeof(snd_pcm_rate_old_ops_t));
261 int SND_PCM_RATE_PLUGIN_ENTRY(lavcrate)(unsigned int version, void **objp,
262 snd_pcm_rate_ops_t *ops)
264 return pcm_src_open(version, objp, ops);
266 int SND_PCM_RATE_PLUGIN_ENTRY(lavcrate_higher)(unsigned int version,
267 void **objp, snd_pcm_rate_ops_t *ops)
270 return pcm_src_open(version, objp, ops);
272 int SND_PCM_RATE_PLUGIN_ENTRY(lavcrate_high)(unsigned int version,
273 void **objp, snd_pcm_rate_ops_t *ops)
276 return pcm_src_open(version, objp, ops);
278 int SND_PCM_RATE_PLUGIN_ENTRY(lavcrate_fast)(unsigned int version,
279 void **objp, snd_pcm_rate_ops_t *ops)
282 return pcm_src_open(version, objp, ops);
284 int SND_PCM_RATE_PLUGIN_ENTRY(lavcrate_faster)(unsigned int version,
285 void **objp, snd_pcm_rate_ops_t *ops)
288 return pcm_src_open(version, objp, ops);