]> git.alsa-project.org Git - alsa-plugins.git/blob - rate/rate_samplerate.c
e3e5ba7732387e9f2048b60acdece2bd2139ee77
[alsa-plugins.git] / rate / rate_samplerate.c
1 /*
2  * Rate converter plugin using libsamplerate
3  *
4  * Copyright (c) 2006 by Takashi Iwai <tiwai@suse.de>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  */
16
17 #include <stdio.h>
18 #include <samplerate.h>
19 #include <alsa/asoundlib.h>
20 #include <alsa/pcm_rate.h>
21
22 struct rate_src {
23         unsigned int version;
24         double ratio;
25         int converter;
26         unsigned int channels;
27         int in_int;
28         int out_int;
29         float *src_buf;
30         float *dst_buf;
31         SRC_STATE *state;
32         SRC_DATA data;
33 };
34
35 static snd_pcm_uframes_t input_frames(void *obj, snd_pcm_uframes_t frames)
36 {
37         struct rate_src *rate = obj;
38         if (frames == 0)
39                 return 0;
40         return (snd_pcm_uframes_t)(frames / rate->ratio);
41 }
42
43 static snd_pcm_uframes_t output_frames(void *obj, snd_pcm_uframes_t frames)
44 {
45         struct rate_src *rate = obj;
46         if (frames == 0)
47                 return 0;
48         return (snd_pcm_uframes_t)(frames * rate->ratio);
49 }
50
51 static void pcm_src_free(void *obj)
52 {
53         struct rate_src *rate = obj;
54
55         free(rate->src_buf);
56         free(rate->dst_buf);
57         rate->src_buf = rate->dst_buf = NULL;
58
59         if (rate->state) {
60                 src_delete(rate->state);
61                 rate->state = NULL;
62         }
63 }
64
65 static int pcm_src_init(void *obj, snd_pcm_rate_info_t *info)
66 {
67         struct rate_src *rate = obj;
68         int err;
69
70         if (! rate->state || rate->channels != info->channels) {
71                 if (rate->state)
72                         src_delete(rate->state);
73                 rate->channels = info->channels;
74                 rate->state = src_new(rate->converter, rate->channels, &err);
75                 if (! rate->state)
76                         return -EINVAL;
77         }
78
79         rate->ratio = (double)info->out.rate / (double)info->in.rate;
80
81         free(rate->src_buf);
82         rate->src_buf = malloc(sizeof(float) * rate->channels * info->in.period_size);
83         free(rate->dst_buf);
84         rate->dst_buf = malloc(sizeof(float) * rate->channels * info->out.period_size);
85         if (! rate->src_buf || ! rate->dst_buf) {
86                 pcm_src_free(rate);
87                 return -ENOMEM;
88         }
89
90         rate->data.data_in = rate->src_buf;
91         rate->data.data_out = rate->dst_buf;
92         rate->data.src_ratio = rate->ratio;
93         rate->data.end_of_input = 0;
94
95 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
96         if (rate->version >= 0x010003) {
97                 rate->in_int = info->in.format == SND_PCM_FORMAT_S32;
98                 rate->out_int = info->out.format == SND_PCM_FORMAT_S32;
99         }
100 #endif
101
102         return 0;
103 }
104
105 static int pcm_src_adjust_pitch(void *obj, snd_pcm_rate_info_t *info)
106 {
107         struct rate_src *rate = obj;
108
109         rate->ratio = ((double)info->out.period_size / (double)info->in.period_size);
110         rate->data.src_ratio = rate->ratio;
111         return 0;
112 }
113
114 static void pcm_src_reset(void *obj)
115 {
116         struct rate_src *rate = obj;
117
118         src_reset(rate->state);
119 }
120
121 static void do_convert(struct rate_src *rate,
122                        void *dst, unsigned int dst_frames,
123                        const void *src, unsigned int src_frames)
124 {
125         unsigned int ofs;
126
127         rate->data.input_frames = src_frames;
128         rate->data.output_frames = dst_frames;
129         rate->data.end_of_input = 0;
130         
131         if (rate->in_int)
132                 src_int_to_float_array(src, rate->src_buf, src_frames * rate->channels);
133         else
134                 src_short_to_float_array(src, rate->src_buf, src_frames * rate->channels);
135         src_process(rate->state, &rate->data);
136         if (rate->data.output_frames_gen < dst_frames)
137                 ofs = dst_frames - rate->data.output_frames_gen;
138         else
139                 ofs = 0;
140         if (rate->out_int)
141                 src_float_to_int_array(rate->dst_buf, dst + ofs * rate->channels * 4,
142                                        rate->data.output_frames_gen * rate->channels);
143         else
144                 src_float_to_short_array(rate->dst_buf, dst + ofs * rate->channels * 2,
145                                          rate->data.output_frames_gen * rate->channels);
146 }
147
148 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
149 static void pcm_src_convert(void *obj,
150                             const snd_pcm_channel_area_t *dst_areas,
151                             snd_pcm_uframes_t dst_offset,
152                             unsigned int dst_frames,
153                             const snd_pcm_channel_area_t *src_areas,
154                             snd_pcm_uframes_t src_offset,
155                             unsigned int src_frames)
156 {
157         struct rate_src *rate = obj;
158         const void *src = snd_pcm_channel_area_addr(src_areas, src_offset);
159         void *dst = snd_pcm_channel_area_addr(dst_areas, dst_offset);
160
161         do_convert(rate, dst, dst_frames, src, src_frames);
162 }
163 #endif
164
165 static void pcm_src_convert_s16(void *obj, int16_t *dst, unsigned int dst_frames,
166                                 const int16_t *src, unsigned int src_frames)
167 {
168         struct rate_src *rate = obj;
169
170         do_convert(rate, dst, dst_frames, src, src_frames);
171 }
172
173 static void pcm_src_close(void *obj)
174 {
175         free(obj);
176 }
177
178 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
179 static int get_supported_rates(void *obj ATTRIBUTE_UNUSED, unsigned int *rate_min,
180                                unsigned int *rate_max)
181 {
182         *rate_min = *rate_max = 0; /* both unlimited */
183         return 0;
184 }
185
186 static void dump(void *obj ATTRIBUTE_UNUSED, snd_output_t *out)
187 {
188         snd_output_printf(out, "Converter: libsamplerate\n");
189 }
190 #endif
191
192 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
193 static int get_supported_formats(void *obj, uint64_t *in_formats,
194                                  uint64_t *out_formats,
195                                  unsigned int *flags)
196 {
197         *in_formats = *out_formats =
198                 (1ULL << SND_PCM_FORMAT_S16) |
199                 (1ULL << SND_PCM_FORMAT_S32);
200         *flags = SND_PCM_RATE_FLAG_INTERLEAVED;
201         return 0;
202 }
203 #endif
204
205 static snd_pcm_rate_ops_t pcm_src_ops = {
206         .close = pcm_src_close,
207         .init = pcm_src_init,
208         .free = pcm_src_free,
209         .reset = pcm_src_reset,
210         .adjust_pitch = pcm_src_adjust_pitch,
211 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
212         .convert = pcm_src_convert,
213 #endif
214         .convert_s16 = pcm_src_convert_s16,
215         .input_frames = input_frames,
216         .output_frames = output_frames,
217 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
218         .version = SND_PCM_RATE_PLUGIN_VERSION,
219         .get_supported_rates = get_supported_rates,
220         .dump = dump,
221 #endif
222 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
223         .get_supported_formats = get_supported_formats,
224 #endif
225 };
226
227 static int pcm_src_open(unsigned int version, void **objp,
228                         snd_pcm_rate_ops_t *ops, int type)
229 {
230         struct rate_src *rate;
231
232         rate = calloc(1, sizeof(*rate));
233         if (! rate)
234                 return -ENOMEM;
235
236         rate->version = version;
237         rate->converter = type;
238
239         *objp = rate;
240 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
241         if (version == 0x010001) {
242                 memcpy(ops, &pcm_src_ops, sizeof(snd_pcm_rate_old_ops_t));
243                 return 0;
244         }
245 #endif
246 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
247         if (version == 0x010002) {
248                 memcpy(ops, &pcm_src_ops, sizeof(snd_pcm_rate_v2_ops_t));
249                 return 0;
250         }
251 #endif
252         *ops = pcm_src_ops;
253         return 0;
254 }
255
256 int SND_PCM_RATE_PLUGIN_ENTRY(samplerate) (unsigned int version, void **objp,
257                                            snd_pcm_rate_ops_t *ops)
258 {
259         return pcm_src_open(version, objp, ops, SRC_SINC_FASTEST);
260 }
261
262 int SND_PCM_RATE_PLUGIN_ENTRY(samplerate_best) (unsigned int version, void **objp,
263                                                 snd_pcm_rate_ops_t *ops)
264 {
265         return pcm_src_open(version, objp, ops, SRC_SINC_BEST_QUALITY);
266 }
267
268 int SND_PCM_RATE_PLUGIN_ENTRY(samplerate_medium) (unsigned int version, void **objp,
269                                                   snd_pcm_rate_ops_t *ops)
270 {
271         return pcm_src_open(version, objp, ops, SRC_SINC_MEDIUM_QUALITY);
272 }
273
274 int SND_PCM_RATE_PLUGIN_ENTRY(samplerate_order) (unsigned int version, void **objp,
275                                                  snd_pcm_rate_ops_t *ops)
276 {
277         return pcm_src_open(version, objp, ops, SRC_ZERO_ORDER_HOLD);
278 }
279
280 int SND_PCM_RATE_PLUGIN_ENTRY(samplerate_linear) (unsigned int version, void **objp,
281                                                   snd_pcm_rate_ops_t *ops)
282 {
283         return pcm_src_open(version, objp, ops, SRC_LINEAR);
284 }