add async resampling to max rate for device using -u X
Adrian Smith
10 years ago
46 | 46 | #endif |
47 | 47 | " -r <rate>\t\tMax sample rate for output device, enables output device to be off when squeezelite is started\n" |
48 | 48 | #if RESAMPLE |
49 | " -u [params]\t\tUpsample to max rate for device, params = <quality>:<flags>:<attenuation>:<precision>:<passband_end>:<stopband_start>:<phase_response>,\n" | |
50 | " \t\t\t quality = (v|h|m|l|q)(|L|I|M)(|s),\n" | |
49 | " -u [params]\t\tUpsample, params = <recipe>:<flags>:<attenuation>:<precision>:<passband_end>:<stopband_start>:<phase_response>,\n" | |
50 | " \t\t\t recipe = (v|h|m|l|q)(|L|I|M)(|s), (|X) = async - resample to max rate for device, otherwise resample to max sync rate\n" | |
51 | 51 | " \t\t\t flags = num in hex,\n" |
52 | 52 | " \t\t\t attenuation = attenuation in dB to apply (default is -1db if not explicitly set),\n" |
53 | 53 | " \t\t\t precision = number of bits precision (NB. HQ = 20. VHQ = 28),\n" |
377 | 377 | |
378 | 378 | printf("Output devices:\n"); |
379 | 379 | for (i = 0; i < Pa_GetDeviceCount(); ++i) { |
380 | printf(" %i - %s\n", i, Pa_GetDeviceInfo(i)->name); | |
380 | printf(" %i - %s [%s]\n", i, Pa_GetDeviceInfo(i)->name, Pa_GetHostApiInfo(Pa_GetDeviceInfo(i)->hostApi)->name); | |
381 | 381 | } |
382 | 382 | printf("\n"); |
383 | 383 |
111 | 111 | |
112 | 112 | } while (!done); |
113 | 113 | |
114 | LOG_DEBUG("processing track complete - frames in: %lu out: %lu diff: %i", process.total_in, process.total_out, | |
115 | process.total_in * process.sample_factor - process.total_out); | |
114 | LOG_DEBUG("processing track complete - frames in: %lu out: %lu", process.total_in, process.total_out); | |
116 | 115 | } |
117 | 116 | |
118 | 117 | // new stream - called with decode mutex set |
126 | 125 | |
127 | 126 | if (active) { |
128 | 127 | |
129 | if (process.inbuf) free(process.inbuf); | |
130 | if (process.outbuf) free(process.outbuf); | |
128 | unsigned max_in_frames, max_out_frames; | |
129 | ||
130 | process.in_frames = process.out_frames = 0; | |
131 | process.total_in = process.total_out = 0; | |
132 | ||
133 | max_in_frames = codec->min_space / BYTES_PER_FRAME ; | |
134 | ||
135 | // increase size of output buffer by 10% as output rate is not an exact multiple of input rate | |
136 | if (process.out_sample_rate % process.in_sample_rate == 0) { | |
137 | max_out_frames = max_in_frames * process.out_sample_rate / process.in_sample_rate; | |
138 | } else { | |
139 | max_out_frames = (int)(1.1 * (float)max_in_frames * (float)process.out_sample_rate / (float)process.in_sample_rate); | |
140 | } | |
141 | ||
142 | if (process.max_in_frames != max_in_frames) { | |
143 | LOG_DEBUG("creating process buf in frames: %u", max_in_frames); | |
144 | if (process.inbuf) free(process.inbuf); | |
145 | process.inbuf = malloc(max_in_frames * BYTES_PER_FRAME); | |
146 | process.max_in_frames = max_in_frames; | |
147 | } | |
131 | 148 | |
132 | process.inbuf = malloc(codec->min_space); | |
133 | process.outbuf = malloc(codec->min_space * process.sample_factor); | |
149 | if (process.max_out_frames != max_out_frames) { | |
150 | LOG_DEBUG("creating process buf out frames: %u", max_out_frames); | |
151 | if (process.outbuf) free(process.outbuf); | |
152 | process.outbuf = malloc(max_out_frames * BYTES_PER_FRAME); | |
153 | process.max_out_frames = max_out_frames; | |
154 | } | |
134 | 155 | |
135 | 156 | if (!process.inbuf || !process.outbuf) { |
136 | 157 | LOG_ERROR("malloc fail creating process buffers"); |
137 | 158 | *direct = true; |
138 | 159 | return raw_sample_rate; |
139 | 160 | } |
140 | ||
141 | process.sample_rate = raw_sample_rate; | |
142 | process.max_in_frames = codec->min_space / BYTES_PER_FRAME ; | |
143 | process.max_out_frames = codec->min_space / BYTES_PER_FRAME * process.sample_factor; | |
144 | ||
145 | process.in_frames = process.out_frames = 0; | |
146 | process.total_in = process.total_out = 0; | |
147 | 161 | |
148 | return raw_sample_rate * process.sample_factor; | |
162 | return process.out_sample_rate; | |
149 | 163 | } |
150 | 164 | |
151 | 165 | return raw_sample_rate; |
166 | 180 | |
167 | 181 | bool enabled = INIT_FUNC(opt); |
168 | 182 | |
183 | memset(&process, 0, sizeof(process)); | |
184 | ||
169 | 185 | if (enabled) { |
170 | 186 | LOCK_D; |
171 | 187 | decode.process = true; |
56 | 56 | void (* soxr_delete)(soxr_t); |
57 | 57 | soxr_error_t (* soxr_process)(soxr_t, soxr_in_t, size_t, size_t *, soxr_out_t, size_t olen, size_t *); |
58 | 58 | size_t *(* soxr_num_clips)(soxr_t); |
59 | bool max_rate; | |
59 | 60 | }; |
60 | 61 | |
61 | 62 | static struct soxr *r; |
73 | 74 | |
74 | 75 | if (idone != process->in_frames) { |
75 | 76 | // should not get here if buffers are big enough... |
76 | LOG_ERROR("should not get here - partial sox process: %u of %u", (unsigned)idone, process->in_frames); | |
77 | LOG_ERROR("should not get here - partial sox process: %u of %u processed %u of %u out", | |
78 | (unsigned)idone, process->in_frames, (unsigned)odone, process->max_out_frames); | |
77 | 79 | } |
78 | 80 | |
79 | 81 | process->out_frames = odone; |
121 | 123 | } |
122 | 124 | |
123 | 125 | bool resample_newstream(struct processstate *process, unsigned raw_sample_rate, unsigned max_sample_rate) { |
124 | unsigned outrate = raw_sample_rate; | |
125 | while (outrate <= max_sample_rate) outrate <<= 1; | |
126 | outrate >>= 1; | |
127 | ||
128 | process->sample_factor = outrate / raw_sample_rate; | |
126 | unsigned outrate; | |
127 | ||
128 | if (r->max_rate) { | |
129 | outrate = max_sample_rate; | |
130 | } else { | |
131 | outrate = raw_sample_rate; | |
132 | while (outrate <= max_sample_rate) outrate <<= 1; | |
133 | outrate >>= 1; | |
134 | } | |
135 | ||
136 | process->in_sample_rate = raw_sample_rate; | |
137 | process->out_sample_rate = outrate; | |
129 | 138 | |
130 | 139 | if (r->resampler) { |
131 | 140 | r->soxr_delete(r->resampler); |
197 | 206 | |
198 | 207 | r->resampler = NULL; |
199 | 208 | r->old_clips = 0; |
209 | r->max_rate = false; | |
200 | 210 | r->soxr_io_spec = dlsym(handle, "soxr_io_spec"); |
201 | 211 | r->soxr_quality_spec = dlsym(handle, "soxr_quality_spec"); |
202 | 212 | r->soxr_create = dlsym(handle, "soxr_create"); |
214 | 224 | } |
215 | 225 | |
216 | 226 | bool resample_init(char *opt) { |
217 | char *qual = NULL, *flags = NULL; | |
227 | char *recipe = NULL, *flags = NULL; | |
218 | 228 | char *atten = NULL; |
219 | 229 | char *precision = NULL, *passband_end = NULL, *stopband_begin = NULL, *phase_response = NULL; |
220 | 230 | |
224 | 234 | } |
225 | 235 | |
226 | 236 | if (opt) { |
227 | qual = next_param(opt, ':'); | |
237 | recipe = next_param(opt, ':'); | |
228 | 238 | flags = next_param(NULL, ':'); |
229 | 239 | atten = next_param(NULL, ':'); |
230 | 240 | precision = next_param(NULL, ':'); |
244 | 254 | r->q_stopband_begin = 0; |
245 | 255 | r->q_phase_response = -1; |
246 | 256 | |
247 | if (qual && qual[0] != '\0') { | |
248 | if (strchr(qual, 'v')) r->q_recipe = SOXR_VHQ; | |
249 | if (strchr(qual, 'h')) r->q_recipe = SOXR_HQ; | |
250 | if (strchr(qual, 'm')) r->q_recipe = SOXR_MQ; | |
251 | if (strchr(qual, 'l')) r->q_recipe = SOXR_LQ; | |
252 | if (strchr(qual, 'q')) r->q_recipe = SOXR_QQ; | |
253 | if (strchr(qual, 'L')) r->q_recipe |= SOXR_LINEAR_PHASE; | |
254 | if (strchr(qual, 'I')) r->q_recipe |= SOXR_INTERMEDIATE_PHASE; | |
255 | if (strchr(qual, 'M')) r->q_recipe |= SOXR_MINIMUM_PHASE; | |
256 | if (strchr(qual, 's')) r->q_recipe |= SOXR_STEEP_FILTER; | |
257 | if (recipe && recipe[0] != '\0') { | |
258 | if (strchr(recipe, 'v')) r->q_recipe = SOXR_VHQ; | |
259 | if (strchr(recipe, 'h')) r->q_recipe = SOXR_HQ; | |
260 | if (strchr(recipe, 'm')) r->q_recipe = SOXR_MQ; | |
261 | if (strchr(recipe, 'l')) r->q_recipe = SOXR_LQ; | |
262 | if (strchr(recipe, 'q')) r->q_recipe = SOXR_QQ; | |
263 | if (strchr(recipe, 'L')) r->q_recipe |= SOXR_LINEAR_PHASE; | |
264 | if (strchr(recipe, 'I')) r->q_recipe |= SOXR_INTERMEDIATE_PHASE; | |
265 | if (strchr(recipe, 'M')) r->q_recipe |= SOXR_MINIMUM_PHASE; | |
266 | if (strchr(recipe, 's')) r->q_recipe |= SOXR_STEEP_FILTER; | |
267 | // X = async resampling to max_rate | |
268 | if (strchr(recipe, 'X')) r->max_rate = true; | |
257 | 269 | } |
258 | 270 | |
259 | 271 | if (flags) { |
283 | 295 | r->q_phase_response = atof(phase_response); |
284 | 296 | } |
285 | 297 | |
286 | LOG_INFO("resampling enabled recipe: 0x%02x, flags: 0x%02x, scale: %03.2f, precision: %03.1f, passband_end: %03.5f, stopband_begin: %03.5f, phase_response: %03.1f", | |
298 | LOG_INFO("resampling %s recipe: 0x%02x, flags: 0x%02x, scale: %03.2f, precision: %03.1f, passband_end: %03.5f, stopband_begin: %03.5f, phase_response: %03.1f", | |
299 | r->max_rate ? "async" : "sync", | |
287 | 300 | r->q_recipe, r->q_flags, r->scale, r->q_precision, r->q_passband_end, r->q_stopband_begin, r->q_phase_response); |
288 | 301 | |
289 | 302 | return true; |