Chris@0
|
1 /*
|
Chris@0
|
2 ** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
|
Chris@0
|
3 **
|
Chris@0
|
4 ** This program is free software; you can redistribute it and/or modify
|
Chris@0
|
5 ** it under the terms of the GNU General Public License as published by
|
Chris@0
|
6 ** the Free Software Foundation; either version 2 of the License, or
|
Chris@0
|
7 ** (at your option) any later version.
|
Chris@0
|
8 **
|
Chris@0
|
9 ** This program is distributed in the hope that it will be useful,
|
Chris@0
|
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
Chris@0
|
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Chris@0
|
12 ** GNU General Public License for more details.
|
Chris@0
|
13 **
|
Chris@0
|
14 ** You should have received a copy of the GNU General Public License
|
Chris@0
|
15 ** along with this program; if not, write to the Free Software
|
Chris@0
|
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
Chris@0
|
17 */
|
Chris@0
|
18
|
Chris@0
|
19 /*
|
Chris@0
|
20 ** This code is part of Secret Rabbit Code aka libsamplerate. A commercial
|
Chris@0
|
21 ** use license for this code is available, please see:
|
Chris@0
|
22 ** http://www.mega-nerd.com/SRC/procedure.html
|
Chris@0
|
23 */
|
Chris@0
|
24
|
Chris@0
|
25 #include <stdio.h>
|
Chris@0
|
26 #include <stdlib.h>
|
Chris@0
|
27 #include <string.h>
|
Chris@0
|
28
|
Chris@0
|
29 #include "config.h"
|
Chris@0
|
30 #include "float_cast.h"
|
Chris@0
|
31 #include "common.h"
|
Chris@0
|
32
|
Chris@0
|
33 static int zoh_vari_process (SRC_PRIVATE *psrc, SRC_DATA *data) ;
|
Chris@0
|
34 static void zoh_reset (SRC_PRIVATE *psrc) ;
|
Chris@0
|
35
|
Chris@0
|
36 /*========================================================================================
|
Chris@0
|
37 */
|
Chris@0
|
38
|
Chris@0
|
39 #define ZOH_MAGIC_MARKER MAKE_MAGIC ('s', 'r', 'c', 'z', 'o', 'h')
|
Chris@0
|
40
|
Chris@0
|
41 typedef struct
|
Chris@0
|
42 { int zoh_magic_marker ;
|
Chris@0
|
43 int channels ;
|
Chris@0
|
44 int reset ;
|
Chris@0
|
45 long in_count, in_used ;
|
Chris@0
|
46 long out_count, out_gen ;
|
Chris@0
|
47 float last_value [1] ;
|
Chris@0
|
48 } ZOH_DATA ;
|
Chris@0
|
49
|
Chris@0
|
50 /*----------------------------------------------------------------------------------------
|
Chris@0
|
51 */
|
Chris@0
|
52
|
Chris@0
|
53 static int
|
Chris@0
|
54 zoh_vari_process (SRC_PRIVATE *psrc, SRC_DATA *data)
|
Chris@0
|
55 { ZOH_DATA *priv ;
|
Chris@0
|
56 double src_ratio, input_index, rem ;
|
Chris@0
|
57 int ch ;
|
Chris@0
|
58
|
Chris@0
|
59 if (data->input_frames <= 0)
|
Chris@0
|
60 return SRC_ERR_NO_ERROR ;
|
Chris@0
|
61
|
Chris@0
|
62 if (psrc->private_data == NULL)
|
Chris@0
|
63 return SRC_ERR_NO_PRIVATE ;
|
Chris@0
|
64
|
Chris@0
|
65 priv = (ZOH_DATA*) psrc->private_data ;
|
Chris@0
|
66
|
Chris@0
|
67 if (priv->reset)
|
Chris@0
|
68 { /* If we have just been reset, set the last_value data. */
|
Chris@0
|
69 for (ch = 0 ; ch < priv->channels ; ch++)
|
Chris@0
|
70 priv->last_value [ch] = data->data_in [ch] ;
|
Chris@0
|
71 priv->reset = 0 ;
|
Chris@0
|
72 } ;
|
Chris@0
|
73
|
Chris@0
|
74 priv->in_count = data->input_frames * priv->channels ;
|
Chris@0
|
75 priv->out_count = data->output_frames * priv->channels ;
|
Chris@0
|
76 priv->in_used = priv->out_gen = 0 ;
|
Chris@0
|
77
|
Chris@0
|
78 src_ratio = psrc->last_ratio ;
|
Chris@0
|
79 input_index = psrc->last_position ;
|
Chris@0
|
80
|
Chris@0
|
81 /* Calculate samples before first sample in input array. */
|
Chris@0
|
82 while (input_index < 1.0 && priv->out_gen < priv->out_count)
|
Chris@0
|
83 {
|
Chris@0
|
84 if (priv->in_used + priv->channels * input_index >= priv->in_count)
|
Chris@0
|
85 break ;
|
Chris@0
|
86
|
Chris@0
|
87 if (priv->out_count > 0 && fabs (psrc->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
|
Chris@0
|
88 src_ratio = psrc->last_ratio + priv->out_gen * (data->src_ratio - psrc->last_ratio) / priv->out_count ;
|
Chris@0
|
89
|
Chris@0
|
90 for (ch = 0 ; ch < priv->channels ; ch++)
|
Chris@0
|
91 { data->data_out [priv->out_gen] = priv->last_value [ch] ;
|
Chris@0
|
92 priv->out_gen ++ ;
|
Chris@0
|
93 } ;
|
Chris@0
|
94
|
Chris@0
|
95 /* Figure out the next index. */
|
Chris@0
|
96 input_index += 1.0 / src_ratio ;
|
Chris@0
|
97 } ;
|
Chris@0
|
98
|
Chris@0
|
99 rem = fmod_one (input_index) ;
|
Chris@0
|
100 priv->in_used += priv->channels * lrint (input_index - rem) ;
|
Chris@0
|
101 input_index = rem ;
|
Chris@0
|
102
|
Chris@0
|
103 /* Main processing loop. */
|
Chris@0
|
104 while (priv->out_gen < priv->out_count && priv->in_used + priv->channels * input_index <= priv->in_count)
|
Chris@0
|
105 {
|
Chris@0
|
106 if (priv->out_count > 0 && fabs (psrc->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
|
Chris@0
|
107 src_ratio = psrc->last_ratio + priv->out_gen * (data->src_ratio - psrc->last_ratio) / priv->out_count ;
|
Chris@0
|
108
|
Chris@0
|
109 for (ch = 0 ; ch < priv->channels ; ch++)
|
Chris@0
|
110 { data->data_out [priv->out_gen] = data->data_in [priv->in_used - priv->channels + ch] ;
|
Chris@0
|
111 priv->out_gen ++ ;
|
Chris@0
|
112 } ;
|
Chris@0
|
113
|
Chris@0
|
114 /* Figure out the next index. */
|
Chris@0
|
115 input_index += 1.0 / src_ratio ;
|
Chris@0
|
116 rem = fmod_one (input_index) ;
|
Chris@0
|
117
|
Chris@0
|
118 priv->in_used += priv->channels * lrint (input_index - rem) ;
|
Chris@0
|
119 input_index = rem ;
|
Chris@0
|
120 } ;
|
Chris@0
|
121
|
Chris@0
|
122 if (priv->in_used > priv->in_count)
|
Chris@0
|
123 { input_index += (priv->in_used - priv->in_count) / priv->channels ;
|
Chris@0
|
124 priv->in_used = priv->in_count ;
|
Chris@0
|
125 } ;
|
Chris@0
|
126
|
Chris@0
|
127 psrc->last_position = input_index ;
|
Chris@0
|
128
|
Chris@0
|
129 if (priv->in_used > 0)
|
Chris@0
|
130 for (ch = 0 ; ch < priv->channels ; ch++)
|
Chris@0
|
131 priv->last_value [ch] = data->data_in [priv->in_used - priv->channels + ch] ;
|
Chris@0
|
132
|
Chris@0
|
133 /* Save current ratio rather then target ratio. */
|
Chris@0
|
134 psrc->last_ratio = src_ratio ;
|
Chris@0
|
135
|
Chris@0
|
136 data->input_frames_used = priv->in_used / priv->channels ;
|
Chris@0
|
137 data->output_frames_gen = priv->out_gen / priv->channels ;
|
Chris@0
|
138
|
Chris@0
|
139 return SRC_ERR_NO_ERROR ;
|
Chris@0
|
140 } /* zoh_vari_process */
|
Chris@0
|
141
|
Chris@0
|
142 /*------------------------------------------------------------------------------
|
Chris@0
|
143 */
|
Chris@0
|
144
|
Chris@0
|
145 const char*
|
Chris@0
|
146 zoh_get_name (int src_enum)
|
Chris@0
|
147 {
|
Chris@0
|
148 if (src_enum == SRC_ZERO_ORDER_HOLD)
|
Chris@0
|
149 return "ZOH Interpolator" ;
|
Chris@0
|
150
|
Chris@0
|
151 return NULL ;
|
Chris@0
|
152 } /* zoh_get_name */
|
Chris@0
|
153
|
Chris@0
|
154 const char*
|
Chris@0
|
155 zoh_get_description (int src_enum)
|
Chris@0
|
156 {
|
Chris@0
|
157 if (src_enum == SRC_ZERO_ORDER_HOLD)
|
Chris@0
|
158 return "Zero order hold interpolator, very fast, poor quality." ;
|
Chris@0
|
159
|
Chris@0
|
160 return NULL ;
|
Chris@0
|
161 } /* zoh_get_descrition */
|
Chris@0
|
162
|
Chris@0
|
163 int
|
Chris@0
|
164 zoh_set_converter (SRC_PRIVATE *psrc, int src_enum)
|
Chris@0
|
165 { ZOH_DATA *priv = NULL ;
|
Chris@0
|
166
|
Chris@0
|
167 if (src_enum != SRC_ZERO_ORDER_HOLD)
|
Chris@0
|
168 return SRC_ERR_BAD_CONVERTER ;
|
Chris@0
|
169
|
Chris@0
|
170 if (psrc->private_data != NULL)
|
Chris@0
|
171 { free (psrc->private_data) ;
|
Chris@0
|
172 psrc->private_data = NULL ;
|
Chris@0
|
173 } ;
|
Chris@0
|
174
|
Chris@0
|
175 if (psrc->private_data == NULL)
|
Chris@0
|
176 { priv = calloc (1, sizeof (*priv) + psrc->channels * sizeof (float)) ;
|
Chris@0
|
177 if (priv == NULL)
|
Chris@0
|
178 return SRC_ERR_MALLOC_FAILED ;
|
Chris@0
|
179 psrc->private_data = priv ;
|
Chris@0
|
180 } ;
|
Chris@0
|
181
|
Chris@0
|
182 priv->zoh_magic_marker = ZOH_MAGIC_MARKER ;
|
Chris@0
|
183 priv->channels = psrc->channels ;
|
Chris@0
|
184
|
Chris@0
|
185 psrc->const_process = zoh_vari_process ;
|
Chris@0
|
186 psrc->vari_process = zoh_vari_process ;
|
Chris@0
|
187 psrc->reset = zoh_reset ;
|
Chris@0
|
188
|
Chris@0
|
189 zoh_reset (psrc) ;
|
Chris@0
|
190
|
Chris@0
|
191 return SRC_ERR_NO_ERROR ;
|
Chris@0
|
192 } /* zoh_set_converter */
|
Chris@0
|
193
|
Chris@0
|
194 /*===================================================================================
|
Chris@0
|
195 */
|
Chris@0
|
196
|
Chris@0
|
197 static void
|
Chris@0
|
198 zoh_reset (SRC_PRIVATE *psrc)
|
Chris@0
|
199 { ZOH_DATA *priv ;
|
Chris@0
|
200
|
Chris@0
|
201 priv = (ZOH_DATA*) psrc->private_data ;
|
Chris@0
|
202 if (priv == NULL)
|
Chris@0
|
203 return ;
|
Chris@0
|
204
|
Chris@0
|
205 priv->channels = psrc->channels ;
|
Chris@0
|
206 priv->reset = 1 ;
|
Chris@0
|
207 memset (priv->last_value, 0, sizeof (priv->last_value [0]) * priv->channels) ;
|
Chris@0
|
208
|
Chris@0
|
209 return ;
|
Chris@0
|
210 } /* zoh_reset */
|
Chris@0
|
211
|