comparison render_audio.c @ 1865:4c322abd9fa5

Split generic part of audio code into a separate file so it can be used in other targets besides SDL
author Michael Pavone <pavone@retrodev.com>
date Fri, 17 May 2019 08:43:30 -0700
parents
children e4671a39d155
comparison
equal deleted inserted replaced
1863:d60f2d7c02a5 1865:4c322abd9fa5
1 #include <limits.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <math.h>
5 #include "render_audio.h"
6 #include "util.h"
7 #include "config.h"
8 #include "blastem.h"
9
10 static uint8_t output_channels;
11 static uint32_t buffer_samples, sample_rate;
12 static uint32_t missing_count;
13
14 static audio_source *audio_sources[8];
15 static audio_source *inactive_audio_sources[8];
16 static uint8_t num_audio_sources;
17 static uint8_t num_inactive_audio_sources;
18
19 static float overall_gain_mult, *mix_buf;
20 static int sample_size;
21
22 typedef void (*conv_func)(float *samples, void *vstream, int sample_count);
23
24 static void convert_null(float *samples, void *vstream, int sample_count)
25 {
26 memset(vstream, 0, sample_count * sample_size);
27 }
28
29 static void convert_s16(float *samples, void *vstream, int sample_count)
30 {
31 int16_t *stream = vstream;
32 for (int16_t *end = stream + sample_count; stream < end; stream++, samples++)
33 {
34 float sample = *samples;
35 int16_t out_sample;
36 if (sample >= 1.0f) {
37 out_sample = 0x7FFF;
38 } else if (sample <= -1.0f) {
39 out_sample = -0x8000;
40 } else {
41 out_sample = sample * 0x7FFF;
42 }
43 *stream = out_sample;
44 }
45 }
46
47 static void clamp_f32(float *samples, void *vstream, int sample_count)
48 {
49 for (; sample_count > 0; sample_count--, samples++)
50 {
51 float sample = *samples;
52 if (sample > 1.0f) {
53 sample = 1.0f;
54 } else if (sample < -1.0f) {
55 sample = -1.0f;
56 }
57 *samples = sample;
58 }
59 }
60
61 static int32_t mix_f32(audio_source *audio, float *stream, int samples)
62 {
63 float *end = stream + samples;
64 int16_t *src = audio->front;
65 uint32_t i = audio->read_start;
66 uint32_t i_end = audio->read_end;
67 float *cur = stream;
68 float gain_mult = audio->gain_mult * overall_gain_mult;
69 size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1;
70 if (audio->num_channels == 1) {
71 while (cur < end && i != i_end)
72 {
73 *cur += gain_mult * ((float)src[i]) / 0x7FFF;
74 cur += first_add;
75 *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
76 cur += second_add;
77 i &= audio->mask;
78 }
79 } else {
80 while(cur < end && i != i_end)
81 {
82 *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
83 cur += first_add;
84 *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
85 cur += second_add;
86 i &= audio->mask;
87 }
88 }
89 if (!render_is_audio_sync()) {
90 audio->read_start = i;
91 }
92 if (cur != end) {
93 debug_message("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask);
94 return (cur-end)/2;
95 } else {
96 return ((i_end - i) & audio->mask) / audio->num_channels;
97 }
98 }
99
100 static conv_func convert;
101
102
103 int mix_and_convert(unsigned char *byte_stream, int len, int *min_remaining_out)
104 {
105 int samples = len / sample_size;
106 float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream;
107 memset(mix_dest, 0, samples * sizeof(float));
108 int min_buffered = INT_MAX;
109 int min_remaining_buffer = INT_MAX;
110 for (uint8_t i = 0; i < num_audio_sources; i++)
111 {
112 int buffered = mix_f32(audio_sources[i], mix_dest, samples);
113 int remaining = (audio_sources[i]->mask + 1) / audio_sources[i]->num_channels - buffered;
114 min_buffered = buffered < min_buffered ? buffered : min_buffered;
115 min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer;
116 audio_sources[i]->front_populated = 0;
117 render_buffer_consumed(audio_sources[i]);
118 }
119 convert(mix_dest, byte_stream, samples);
120 if (min_remaining_out) {
121 *min_remaining_out = min_remaining_buffer;
122 }
123 return min_buffered;
124 }
125
126 uint8_t all_sources_ready(void)
127 {
128 uint8_t num_populated = 0;
129 num_populated = 0;
130 for (uint8_t i = 0; i < num_audio_sources; i++)
131 {
132 if (audio_sources[i]->front_populated) {
133 num_populated++;
134 }
135 }
136 return num_populated == num_audio_sources;
137 }
138
139 #define BUFFER_INC_RES 0x40000000UL
140
141 void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider)
142 {
143 src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider;
144 }
145
146 void render_audio_adjust_speed(float adjust_ratio)
147 {
148 for (uint8_t i = 0; i < num_audio_sources; i++)
149 {
150 audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5;
151 }
152 }
153
154 audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels)
155 {
156 audio_source *ret = NULL;
157 uint32_t alloc_size = render_is_audio_sync() ? channels * buffer_samples : nearest_pow2(render_min_buffered() * 4 * channels);
158 render_lock_audio();
159 if (num_audio_sources < 8) {
160 ret = calloc(1, sizeof(audio_source));
161 ret->back = malloc(alloc_size * sizeof(int16_t));
162 ret->front = render_is_audio_sync() ? malloc(alloc_size * sizeof(int16_t)) : ret->back;
163 ret->front_populated = 0;
164 ret->opaque = render_new_audio_opaque();
165 ret->num_channels = channels;
166 audio_sources[num_audio_sources++] = ret;
167 }
168 render_unlock_audio();
169 if (!ret) {
170 fatal_error("Too many audio sources!");
171 } else {
172 render_audio_adjust_clock(ret, master_clock, sample_divider);
173 double lowpass_cutoff = get_lowpass_cutoff(config);
174 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI);
175 ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider));
176 double alpha = ret->dt / (ret->dt + rc);
177 ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
178 ret->buffer_pos = 0;
179 ret->buffer_fraction = 0;
180 ret->last_left = ret->last_right = 0;
181 ret->read_start = 0;
182 ret->read_end = render_is_audio_sync() ? buffer_samples * channels : 0;
183 ret->mask = render_is_audio_sync() ? 0xFFFFFFFF : alloc_size-1;
184 ret->gain_mult = 1.0f;
185 }
186 render_audio_created(ret);
187
188 return ret;
189 }
190
191
192 static float db_to_mult(float gain)
193 {
194 return powf(10.0f, gain/20.0f);
195 }
196
197 void render_audio_source_gaindb(audio_source *src, float gain)
198 {
199 src->gain_mult = db_to_mult(gain);
200 }
201
202 void render_pause_source(audio_source *src)
203 {
204 uint8_t found = 0, remaining_sources;
205 render_lock_audio();
206 for (uint8_t i = 0; i < num_audio_sources; i++)
207 {
208 if (audio_sources[i] == src) {
209 audio_sources[i] = audio_sources[--num_audio_sources];
210 found = 1;
211 remaining_sources = num_audio_sources;
212 break;
213 }
214 }
215
216 render_unlock_audio();
217 if (found) {
218 render_source_paused(src, remaining_sources);
219 }
220 inactive_audio_sources[num_inactive_audio_sources++] = src;
221 }
222
223 void render_resume_source(audio_source *src)
224 {
225 render_lock_audio();
226 if (num_audio_sources < 8) {
227 audio_sources[num_audio_sources++] = src;
228 }
229 render_unlock_audio();
230 for (uint8_t i = 0; i < num_inactive_audio_sources; i++)
231 {
232 if (inactive_audio_sources[i] == src) {
233 inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources];
234 }
235 }
236 render_source_resumed(src);
237 }
238
239 void render_free_source(audio_source *src)
240 {
241 render_pause_source(src);
242
243 free(src->front);
244 if (render_is_audio_sync()) {
245 free(src->back);
246 render_free_audio_opaque(src->opaque);
247 }
248 free(src);
249 num_inactive_audio_sources--;
250 }
251
252 static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current)
253 {
254 int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha);
255 current = tmp >> 16;
256 return current;
257 }
258
259 static void interp_sample(audio_source *src, int16_t last, int16_t current)
260 {
261 int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc);
262 tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc));
263 src->back[src->buffer_pos++] = tmp >> 16;
264 }
265
266 static uint32_t sync_samples;
267 void render_put_mono_sample(audio_source *src, int16_t value)
268 {
269 value = lowpass_sample(src, src->last_left, value);
270 src->buffer_fraction += src->buffer_inc;
271 uint32_t base = render_is_audio_sync() ? 0 : src->read_end;
272 while (src->buffer_fraction > BUFFER_INC_RES)
273 {
274 src->buffer_fraction -= BUFFER_INC_RES;
275 interp_sample(src, src->last_left, value);
276
277 if (((src->buffer_pos - base) & src->mask) >= sync_samples) {
278 render_do_audio_ready(src);
279 }
280 src->buffer_pos &= src->mask;
281 }
282 src->last_left = value;
283 }
284
285 void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right)
286 {
287 left = lowpass_sample(src, src->last_left, left);
288 right = lowpass_sample(src, src->last_right, right);
289 src->buffer_fraction += src->buffer_inc;
290 uint32_t base = render_is_audio_sync() ? 0 : src->read_end;
291 while (src->buffer_fraction > BUFFER_INC_RES)
292 {
293 src->buffer_fraction -= BUFFER_INC_RES;
294
295 interp_sample(src, src->last_left, left);
296 interp_sample(src, src->last_right, right);
297
298 if (((src->buffer_pos - base) & src->mask)/2 >= sync_samples) {
299 render_do_audio_ready(src);
300 }
301 src->buffer_pos &= src->mask;
302 }
303 src->last_left = left;
304 src->last_right = right;
305 }
306
307 static void update_source(audio_source *src, double rc, uint8_t sync_changed)
308 {
309 double alpha = src->dt / (src->dt + rc);
310 int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
311 src->lowpass_alpha = lowpass_alpha;
312 if (sync_changed) {
313 uint32_t alloc_size = render_is_audio_sync() ? src->num_channels * buffer_samples : nearest_pow2(render_min_buffered() * 4 * src->num_channels);
314 src->back = realloc(src->back, alloc_size * sizeof(int16_t));
315 if (render_is_audio_sync()) {
316 src->front = malloc(alloc_size * sizeof(int16_t));
317 } else {
318 free(src->front);
319 src->front = src->back;
320 }
321 src->mask = render_is_audio_sync() ? 0xFFFFFFFF : alloc_size-1;
322 src->read_start = 0;
323 src->read_end = render_is_audio_sync() ? buffer_samples * src->num_channels : 0;
324 src->buffer_pos = 0;
325 }
326 }
327
328 uint8_t old_audio_sync;
329 void render_audio_initialized(render_audio_format format, uint32_t rate, uint8_t channels, uint32_t buffer_size, int sample_size_in)
330 {
331 sample_rate = rate;
332 output_channels = channels;
333 buffer_samples = buffer_size;
334 sample_size = sample_size_in;
335 if (mix_buf) {
336 free(mix_buf);
337 mix_buf = NULL;
338 }
339 switch(format)
340 {
341 case RENDER_AUDIO_S16:
342 convert = convert_s16;
343 mix_buf = calloc(output_channels * buffer_samples, sizeof(float));
344 break;
345 case RENDER_AUDIO_FLOAT:
346 convert = clamp_f32;
347 break;
348 case RENDER_AUDIO_UNKNOWN:
349 convert = convert_null;
350 mix_buf = calloc(output_channels * buffer_samples, sizeof(float));
351 break;
352 }
353 uint32_t syncs = render_audio_syncs_per_sec();
354 if (syncs) {
355 sync_samples = rate / syncs;
356 } else {
357 sync_samples = buffer_samples;
358 }
359 char * gain_str = tern_find_path(config, "audio\0gain\0", TVAL_PTR).ptrval;
360 overall_gain_mult = db_to_mult(gain_str ? atof(gain_str) : 0.0f);
361 uint8_t sync_changed = old_audio_sync != render_is_audio_sync();
362 old_audio_sync = render_is_audio_sync();
363 double lowpass_cutoff = get_lowpass_cutoff(config);
364 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI);
365 render_lock_audio();
366 for (uint8_t i = 0; i < num_audio_sources; i++)
367 {
368 update_source(audio_sources[i], rc, sync_changed);
369 }
370 render_unlock_audio();
371 for (uint8_t i = 0; i < num_inactive_audio_sources; i++)
372 {
373 update_source(inactive_audio_sources[i], rc, sync_changed);
374 }
375 }