Mercurial > repos > blastem
comparison render_sdl.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 | e07fc3d473b2 |
children | 55198fc9cc1f |
comparison
equal
deleted
inserted
replaced
1863:d60f2d7c02a5 | 1865:4c322abd9fa5 |
---|---|
44 static uint8_t render_gl = 1; | 44 static uint8_t render_gl = 1; |
45 static uint8_t scanlines = 0; | 45 static uint8_t scanlines = 0; |
46 | 46 |
47 static uint32_t last_frame = 0; | 47 static uint32_t last_frame = 0; |
48 | 48 |
49 static uint8_t output_channels; | |
50 static uint32_t buffer_samples, sample_rate; | |
51 static uint32_t missing_count; | |
52 | |
53 static SDL_mutex * audio_mutex; | 49 static SDL_mutex * audio_mutex; |
54 static SDL_cond * audio_ready; | 50 static SDL_cond * audio_ready; |
55 static uint8_t quitting = 0; | 51 static uint8_t quitting = 0; |
56 | 52 |
57 struct audio_source { | |
58 SDL_cond *cond; | |
59 int16_t *front; | |
60 int16_t *back; | |
61 double dt; | |
62 uint64_t buffer_fraction; | |
63 uint64_t buffer_inc; | |
64 float gain_mult; | |
65 uint32_t buffer_pos; | |
66 uint32_t read_start; | |
67 uint32_t read_end; | |
68 uint32_t lowpass_alpha; | |
69 uint32_t mask; | |
70 int16_t last_left; | |
71 int16_t last_right; | |
72 uint8_t num_channels; | |
73 uint8_t front_populated; | |
74 }; | |
75 | |
76 static audio_source *audio_sources[8]; | |
77 static audio_source *inactive_audio_sources[8]; | |
78 static uint8_t num_audio_sources; | |
79 static uint8_t num_inactive_audio_sources; | |
80 static uint8_t sync_to_audio; | 53 static uint8_t sync_to_audio; |
81 static uint32_t min_buffered; | 54 static uint32_t min_buffered; |
82 static float overall_gain_mult, *mix_buf; | 55 |
83 static int sample_size; | 56 uint32_t render_min_buffered(void) |
84 | 57 { |
85 typedef void (*conv_func)(float *samples, void *vstream, int sample_count); | 58 return min_buffered; |
86 | 59 } |
87 static void convert_null(float *samples, void *vstream, int sample_count) | 60 |
88 { | 61 uint8_t render_is_audio_sync(void) |
89 memset(vstream, 0, sample_count * sample_size); | 62 { |
90 } | 63 return sync_to_audio; |
91 | 64 } |
92 static void convert_s16(float *samples, void *vstream, int sample_count) | 65 |
93 { | 66 void render_buffer_consumed(audio_source *src) |
94 int16_t *stream = vstream; | 67 { |
95 for (int16_t *end = stream + sample_count; stream < end; stream++, samples++) | 68 SDL_CondSignal(src->opaque); |
96 { | 69 } |
97 float sample = *samples; | |
98 int16_t out_sample; | |
99 if (sample >= 1.0f) { | |
100 out_sample = 0x7FFF; | |
101 } else if (sample <= -1.0f) { | |
102 out_sample = -0x8000; | |
103 } else { | |
104 out_sample = sample * 0x7FFF; | |
105 } | |
106 *stream = out_sample; | |
107 } | |
108 } | |
109 | |
110 static void clamp_f32(float *samples, void *vstream, int sample_count) | |
111 { | |
112 for (; sample_count > 0; sample_count--, samples++) | |
113 { | |
114 float sample = *samples; | |
115 if (sample > 1.0f) { | |
116 sample = 1.0f; | |
117 } else if (sample < -1.0f) { | |
118 sample = -1.0f; | |
119 } | |
120 *samples = sample; | |
121 } | |
122 } | |
123 | |
124 static int32_t mix_f32(audio_source *audio, float *stream, int samples) | |
125 { | |
126 float *end = stream + samples; | |
127 int16_t *src = audio->front; | |
128 uint32_t i = audio->read_start; | |
129 uint32_t i_end = audio->read_end; | |
130 float *cur = stream; | |
131 float gain_mult = audio->gain_mult * overall_gain_mult; | |
132 size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; | |
133 if (audio->num_channels == 1) { | |
134 while (cur < end && i != i_end) | |
135 { | |
136 *cur += gain_mult * ((float)src[i]) / 0x7FFF; | |
137 cur += first_add; | |
138 *cur += gain_mult * ((float)src[i++]) / 0x7FFF; | |
139 cur += second_add; | |
140 i &= audio->mask; | |
141 } | |
142 } else { | |
143 while(cur < end && i != i_end) | |
144 { | |
145 *cur += gain_mult * ((float)src[i++]) / 0x7FFF; | |
146 cur += first_add; | |
147 *cur += gain_mult * ((float)src[i++]) / 0x7FFF; | |
148 cur += second_add; | |
149 i &= audio->mask; | |
150 } | |
151 } | |
152 if (!sync_to_audio) { | |
153 audio->read_start = i; | |
154 } | |
155 if (cur != end) { | |
156 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); | |
157 return (cur-end)/2; | |
158 } else { | |
159 return ((i_end - i) & audio->mask) / audio->num_channels; | |
160 } | |
161 } | |
162 | |
163 static conv_func convert; | |
164 | 70 |
165 static void audio_callback(void * userdata, uint8_t *byte_stream, int len) | 71 static void audio_callback(void * userdata, uint8_t *byte_stream, int len) |
166 { | 72 { |
167 uint8_t num_populated; | |
168 SDL_LockMutex(audio_mutex); | 73 SDL_LockMutex(audio_mutex); |
74 uint8_t all_ready; | |
169 do { | 75 do { |
170 num_populated = 0; | 76 all_ready = all_sources_ready(); |
171 for (uint8_t i = 0; i < num_audio_sources; i++) | 77 if (!quitting && !all_ready) { |
172 { | |
173 if (audio_sources[i]->front_populated) { | |
174 num_populated++; | |
175 } | |
176 } | |
177 if (!quitting && num_populated < num_audio_sources) { | |
178 fflush(stdout); | |
179 SDL_CondWait(audio_ready, audio_mutex); | 78 SDL_CondWait(audio_ready, audio_mutex); |
180 } | 79 } |
181 } while(!quitting && num_populated < num_audio_sources); | 80 } while(!quitting && !all_ready); |
182 int samples = len / sample_size; | |
183 float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream; | |
184 memset(mix_dest, 0, samples * sizeof(float)); | |
185 if (!quitting) { | 81 if (!quitting) { |
186 for (uint8_t i = 0; i < num_audio_sources; i++) | 82 mix_and_convert(byte_stream, len, NULL); |
187 { | 83 } |
188 mix_f32(audio_sources[i], mix_dest, samples); | |
189 audio_sources[i]->front_populated = 0; | |
190 SDL_CondSignal(audio_sources[i]->cond); | |
191 } | |
192 } | |
193 convert(mix_dest, byte_stream, samples); | |
194 SDL_UnlockMutex(audio_mutex); | 84 SDL_UnlockMutex(audio_mutex); |
195 } | 85 } |
196 | 86 |
197 #define NO_LAST_BUFFERED -2000000000 | 87 #define NO_LAST_BUFFERED -2000000000 |
198 static int32_t last_buffered = NO_LAST_BUFFERED; | 88 static int32_t last_buffered = NO_LAST_BUFFERED; |
206 { | 96 { |
207 if (cur_min_buffered < 0) { | 97 if (cur_min_buffered < 0) { |
208 //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet | 98 //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet |
209 return; | 99 return; |
210 } | 100 } |
211 cur_min_buffered = 0x7FFFFFFF; | 101 cur_min_buffered = mix_and_convert(byte_stream, len, &min_remaining_buffer); |
212 min_remaining_buffer = 0xFFFFFFFF; | 102 } |
213 float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream; | 103 |
214 int samples = len / sample_size; | 104 void render_lock_audio() |
215 memset(mix_dest, 0, samples * sizeof(float)); | |
216 for (uint8_t i = 0; i < num_audio_sources; i++) | |
217 { | |
218 | |
219 int32_t buffered = mix_f32(audio_sources[i], mix_dest, samples); | |
220 cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered; | |
221 uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered; | |
222 min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; | |
223 } | |
224 convert(mix_dest, byte_stream, samples); | |
225 } | |
226 | |
227 static void lock_audio() | |
228 { | 105 { |
229 if (sync_to_audio) { | 106 if (sync_to_audio) { |
230 SDL_LockMutex(audio_mutex); | 107 SDL_LockMutex(audio_mutex); |
231 } else { | 108 } else { |
232 SDL_LockAudio(); | 109 SDL_LockAudio(); |
233 } | 110 } |
234 } | 111 } |
235 | 112 |
236 static void unlock_audio() | 113 void render_unlock_audio() |
237 { | 114 { |
238 if (sync_to_audio) { | 115 if (sync_to_audio) { |
239 SDL_UnlockMutex(audio_mutex); | 116 SDL_UnlockMutex(audio_mutex); |
240 } else { | 117 } else { |
241 SDL_UnlockAudio(); | 118 SDL_UnlockAudio(); |
247 SDL_LockMutex(audio_mutex); | 124 SDL_LockMutex(audio_mutex); |
248 quitting = 1; | 125 quitting = 1; |
249 SDL_CondSignal(audio_ready); | 126 SDL_CondSignal(audio_ready); |
250 SDL_UnlockMutex(audio_mutex); | 127 SDL_UnlockMutex(audio_mutex); |
251 SDL_CloseAudio(); | 128 SDL_CloseAudio(); |
129 /* | |
130 FIXME: move this to render_audio.c | |
252 if (mix_buf) { | 131 if (mix_buf) { |
253 free(mix_buf); | 132 free(mix_buf); |
254 mix_buf = NULL; | 133 mix_buf = NULL; |
255 } | 134 } |
256 } | 135 */ |
257 | 136 } |
258 #define BUFFER_INC_RES 0x40000000UL | 137 |
259 | 138 void *render_new_audio_opaque(void) |
260 void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider) | 139 { |
261 { | 140 return SDL_CreateCond(); |
262 src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider; | 141 } |
263 } | 142 |
264 | 143 void render_free_audio_opaque(void *opaque) |
265 audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) | 144 { |
266 { | 145 SDL_DestroyCond(opaque); |
267 audio_source *ret = NULL; | 146 } |
268 uint32_t alloc_size = sync_to_audio ? channels * buffer_samples : nearest_pow2(min_buffered * 4 * channels); | 147 |
269 lock_audio(); | 148 void render_audio_created(audio_source *source) |
270 if (num_audio_sources < 8) { | 149 { |
271 ret = malloc(sizeof(audio_source)); | |
272 ret->back = malloc(alloc_size * sizeof(int16_t)); | |
273 ret->front = sync_to_audio ? malloc(alloc_size * sizeof(int16_t)) : ret->back; | |
274 ret->front_populated = 0; | |
275 ret->cond = SDL_CreateCond(); | |
276 ret->num_channels = channels; | |
277 audio_sources[num_audio_sources++] = ret; | |
278 } | |
279 unlock_audio(); | |
280 if (!ret) { | |
281 fatal_error("Too many audio sources!"); | |
282 } else { | |
283 render_audio_adjust_clock(ret, master_clock, sample_divider); | |
284 double lowpass_cutoff = get_lowpass_cutoff(config); | |
285 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); | |
286 ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider)); | |
287 double alpha = ret->dt / (ret->dt + rc); | |
288 ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); | |
289 ret->buffer_pos = 0; | |
290 ret->buffer_fraction = 0; | |
291 ret->last_left = ret->last_right = 0; | |
292 ret->read_start = 0; | |
293 ret->read_end = sync_to_audio ? buffer_samples * channels : 0; | |
294 ret->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1; | |
295 ret->gain_mult = 1.0f; | |
296 } | |
297 if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { | 150 if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { |
298 SDL_PauseAudio(0); | 151 SDL_PauseAudio(0); |
299 } | 152 } |
300 return ret; | 153 } |
301 } | 154 |
302 | 155 void render_source_paused(audio_source *src, uint8_t remaining_sources) |
303 static float db_to_mult(float gain) | 156 { |
304 { | 157 if (sync_to_audio) { |
305 return powf(10.0f, gain/20.0f); | 158 SDL_CondSignal(audio_ready); |
306 } | 159 } |
307 | 160 if (!remaining_sources) { |
308 void render_audio_source_gaindb(audio_source *src, float gain) | 161 SDL_PauseAudio(0); |
309 { | 162 } |
310 src->gain_mult = db_to_mult(gain); | 163 } |
311 } | 164 |
312 | 165 void render_source_resumed(audio_source *src) |
313 void render_pause_source(audio_source *src) | 166 { |
314 { | |
315 uint8_t need_pause = 0; | |
316 lock_audio(); | |
317 for (uint8_t i = 0; i < num_audio_sources; i++) | |
318 { | |
319 if (audio_sources[i] == src) { | |
320 audio_sources[i] = audio_sources[--num_audio_sources]; | |
321 if (sync_to_audio) { | |
322 SDL_CondSignal(audio_ready); | |
323 } | |
324 break; | |
325 } | |
326 } | |
327 if (!num_audio_sources) { | |
328 need_pause = 1; | |
329 } | |
330 unlock_audio(); | |
331 if (need_pause) { | |
332 SDL_PauseAudio(1); | |
333 } | |
334 inactive_audio_sources[num_inactive_audio_sources++] = src; | |
335 } | |
336 | |
337 void render_resume_source(audio_source *src) | |
338 { | |
339 lock_audio(); | |
340 if (num_audio_sources < 8) { | |
341 audio_sources[num_audio_sources++] = src; | |
342 } | |
343 unlock_audio(); | |
344 for (uint8_t i = 0; i < num_inactive_audio_sources; i++) | |
345 { | |
346 if (inactive_audio_sources[i] == src) { | |
347 inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources]; | |
348 } | |
349 } | |
350 if (sync_to_audio) { | 167 if (sync_to_audio) { |
351 SDL_PauseAudio(0); | 168 SDL_PauseAudio(0); |
352 } | 169 } |
353 } | 170 } |
354 | 171 |
355 void render_free_source(audio_source *src) | 172 void render_do_audio_ready(audio_source *src) |
356 { | |
357 render_pause_source(src); | |
358 | |
359 free(src->front); | |
360 if (sync_to_audio) { | |
361 free(src->back); | |
362 SDL_DestroyCond(src->cond); | |
363 } | |
364 free(src); | |
365 } | |
366 static uint32_t sync_samples; | |
367 static void do_audio_ready(audio_source *src) | |
368 { | 173 { |
369 if (sync_to_audio) { | 174 if (sync_to_audio) { |
370 SDL_LockMutex(audio_mutex); | 175 SDL_LockMutex(audio_mutex); |
371 while (src->front_populated) { | 176 while (src->front_populated) { |
372 SDL_CondWait(src->cond, audio_mutex); | 177 SDL_CondWait(src->opaque, audio_mutex); |
373 } | 178 } |
374 int16_t *tmp = src->front; | 179 int16_t *tmp = src->front; |
375 src->front = src->back; | 180 src->front = src->back; |
376 src->back = tmp; | 181 src->back = tmp; |
377 src->front_populated = 1; | 182 src->front_populated = 1; |
386 SDL_UnlockAudio(); | 191 SDL_UnlockAudio(); |
387 if (num_buffered >= min_buffered && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { | 192 if (num_buffered >= min_buffered && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { |
388 SDL_PauseAudio(0); | 193 SDL_PauseAudio(0); |
389 } | 194 } |
390 } | 195 } |
391 } | |
392 | |
393 static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current) | |
394 { | |
395 int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha); | |
396 current = tmp >> 16; | |
397 return current; | |
398 } | |
399 | |
400 static void interp_sample(audio_source *src, int16_t last, int16_t current) | |
401 { | |
402 int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc); | |
403 tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc)); | |
404 src->back[src->buffer_pos++] = tmp >> 16; | |
405 } | |
406 | |
407 void render_put_mono_sample(audio_source *src, int16_t value) | |
408 { | |
409 value = lowpass_sample(src, src->last_left, value); | |
410 src->buffer_fraction += src->buffer_inc; | |
411 uint32_t base = sync_to_audio ? 0 : src->read_end; | |
412 while (src->buffer_fraction > BUFFER_INC_RES) | |
413 { | |
414 src->buffer_fraction -= BUFFER_INC_RES; | |
415 interp_sample(src, src->last_left, value); | |
416 | |
417 if (((src->buffer_pos - base) & src->mask) >= sync_samples) { | |
418 do_audio_ready(src); | |
419 } | |
420 src->buffer_pos &= src->mask; | |
421 } | |
422 src->last_left = value; | |
423 } | |
424 | |
425 void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right) | |
426 { | |
427 left = lowpass_sample(src, src->last_left, left); | |
428 right = lowpass_sample(src, src->last_right, right); | |
429 src->buffer_fraction += src->buffer_inc; | |
430 uint32_t base = sync_to_audio ? 0 : src->read_end; | |
431 while (src->buffer_fraction > BUFFER_INC_RES) | |
432 { | |
433 src->buffer_fraction -= BUFFER_INC_RES; | |
434 | |
435 interp_sample(src, src->last_left, left); | |
436 interp_sample(src, src->last_right, right); | |
437 | |
438 if (((src->buffer_pos - base) & src->mask)/2 >= sync_samples) { | |
439 do_audio_ready(src); | |
440 } | |
441 src->buffer_pos &= src->mask; | |
442 } | |
443 src->last_left = left; | |
444 src->last_right = right; | |
445 } | 196 } |
446 | 197 |
447 static SDL_Joystick * joysticks[MAX_JOYSTICKS]; | 198 static SDL_Joystick * joysticks[MAX_JOYSTICKS]; |
448 static int joystick_sdl_index[MAX_JOYSTICKS]; | 199 static int joystick_sdl_index[MAX_JOYSTICKS]; |
449 static uint8_t joystick_index_locked[MAX_JOYSTICKS]; | 200 static uint8_t joystick_index_locked[MAX_JOYSTICKS]; |
1111 static int source_hz; | 862 static int source_hz; |
1112 static int source_frame; | 863 static int source_frame; |
1113 static int source_frame_count; | 864 static int source_frame_count; |
1114 static int frame_repeat[60]; | 865 static int frame_repeat[60]; |
1115 | 866 |
867 static uint32_t sample_rate; | |
1116 static void init_audio() | 868 static void init_audio() |
1117 { | 869 { |
1118 SDL_AudioSpec desired, actual; | 870 SDL_AudioSpec desired, actual; |
1119 char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval; | 871 char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval; |
1120 int rate = rate_str ? atoi(rate_str) : 0; | 872 int rate = rate_str ? atoi(rate_str) : 0; |
1135 desired.userdata = NULL; | 887 desired.userdata = NULL; |
1136 | 888 |
1137 if (SDL_OpenAudio(&desired, &actual) < 0) { | 889 if (SDL_OpenAudio(&desired, &actual) < 0) { |
1138 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); | 890 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); |
1139 } | 891 } |
1140 buffer_samples = actual.samples; | |
1141 sample_rate = actual.freq; | 892 sample_rate = actual.freq; |
1142 output_channels = actual.channels; | |
1143 debug_message("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); | 893 debug_message("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); |
1144 sample_size = SDL_AUDIO_BITSIZE(actual.format) / 8; | 894 render_audio_format format = RENDER_AUDIO_UNKNOWN; |
1145 if (actual.format == AUDIO_S16SYS) { | 895 if (actual.format == AUDIO_S16SYS) { |
1146 debug_message("signed 16-bit int format\n"); | 896 debug_message("signed 16-bit int format\n"); |
1147 convert = convert_s16; | 897 format = RENDER_AUDIO_S16; |
1148 mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); | |
1149 } else if (actual.format == AUDIO_F32SYS) { | 898 } else if (actual.format == AUDIO_F32SYS) { |
1150 debug_message("32-bit float format\n"); | 899 debug_message("32-bit float format\n"); |
1151 convert = clamp_f32; | 900 format = RENDER_AUDIO_FLOAT; |
1152 mix_buf = NULL; | |
1153 } else { | 901 } else { |
1154 debug_message("unsupported format %X\n", actual.format); | 902 debug_message("unsupported format %X\n", actual.format); |
1155 warning("Unsupported audio sample format: %X\n", actual.format); | 903 warning("Unsupported audio sample format: %X\n", actual.format); |
1156 convert = convert_null; | 904 } |
1157 mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); | 905 render_audio_initialized(format, actual.freq, actual.channels, actual.samples, SDL_AUDIO_BITSIZE(actual.format) / 8); |
1158 } | |
1159 char * gain_str = tern_find_path(config, "audio\0gain\0", TVAL_PTR).ptrval; | |
1160 overall_gain_mult = db_to_mult(gain_str ? atof(gain_str) : 0.0f); | |
1161 } | 906 } |
1162 | 907 |
1163 void window_setup(void) | 908 void window_setup(void) |
1164 { | 909 { |
1165 uint32_t flags = SDL_WINDOW_RESIZABLE; | 910 uint32_t flags = SDL_WINDOW_RESIZABLE; |
1350 | 1095 |
1351 atexit(render_quit); | 1096 atexit(render_quit); |
1352 } | 1097 } |
1353 #include<unistd.h> | 1098 #include<unistd.h> |
1354 static int in_toggle; | 1099 static int in_toggle; |
1355 static void update_source(audio_source *src, double rc, uint8_t sync_changed) | |
1356 { | |
1357 double alpha = src->dt / (src->dt + rc); | |
1358 int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha); | |
1359 src->lowpass_alpha = lowpass_alpha; | |
1360 if (sync_changed) { | |
1361 uint32_t alloc_size = sync_to_audio ? src->num_channels * buffer_samples : nearest_pow2(min_buffered * 4 * src->num_channels); | |
1362 src->back = realloc(src->back, alloc_size * sizeof(int16_t)); | |
1363 if (sync_to_audio) { | |
1364 src->front = malloc(alloc_size * sizeof(int16_t)); | |
1365 } else { | |
1366 free(src->front); | |
1367 src->front = src->back; | |
1368 } | |
1369 src->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1; | |
1370 src->read_start = 0; | |
1371 src->read_end = sync_to_audio ? buffer_samples * src->num_channels : 0; | |
1372 src->buffer_pos = 0; | |
1373 } | |
1374 } | |
1375 | 1100 |
1376 void render_config_updated(void) | 1101 void render_config_updated(void) |
1377 { | 1102 { |
1378 uint8_t old_sync_to_audio = sync_to_audio; | 1103 uint8_t old_sync_to_audio = sync_to_audio; |
1379 | 1104 |
1436 render_close_audio(); | 1161 render_close_audio(); |
1437 quitting = 0; | 1162 quitting = 0; |
1438 init_audio(); | 1163 init_audio(); |
1439 render_set_video_standard(video_standard); | 1164 render_set_video_standard(video_standard); |
1440 | 1165 |
1441 double lowpass_cutoff = get_lowpass_cutoff(config); | |
1442 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); | |
1443 lock_audio(); | |
1444 for (uint8_t i = 0; i < num_audio_sources; i++) | |
1445 { | |
1446 update_source(audio_sources[i], rc, old_sync_to_audio != sync_to_audio); | |
1447 } | |
1448 unlock_audio(); | |
1449 for (uint8_t i = 0; i < num_inactive_audio_sources; i++) | |
1450 { | |
1451 update_source(inactive_audio_sources[i], rc, old_sync_to_audio != sync_to_audio); | |
1452 } | |
1453 drain_events(); | 1166 drain_events(); |
1454 in_toggle = 0; | 1167 in_toggle = 0; |
1455 if (!was_paused) { | 1168 if (!was_paused) { |
1456 SDL_PauseAudio(0); | 1169 SDL_PauseAudio(0); |
1457 } | 1170 } |
1458 } | 1171 } |
1459 | 1172 |
1460 SDL_Window *render_get_window(void) | 1173 SDL_Window *render_get_window(void) |
1461 { | 1174 { |
1462 return main_window; | 1175 return main_window; |
1176 } | |
1177 | |
1178 uint32_t render_audio_syncs_per_sec(void) | |
1179 { | |
1180 //sync samples with audio thread approximately every 8 lines when doing sync to video | |
1181 return sync_to_audio ? 0 : source_hz * (video_standard == VID_PAL ? 313 : 262) / 8; | |
1463 } | 1182 } |
1464 | 1183 |
1465 void render_set_video_standard(vid_std std) | 1184 void render_set_video_standard(vid_std std) |
1466 { | 1185 { |
1467 video_standard = std; | 1186 video_standard = std; |
1489 frame_repeat[source_hz-1] += display_hz - dst_frames; | 1208 frame_repeat[source_hz-1] += display_hz - dst_frames; |
1490 } | 1209 } |
1491 } | 1210 } |
1492 source_frame = 0; | 1211 source_frame = 0; |
1493 source_frame_count = frame_repeat[0]; | 1212 source_frame_count = frame_repeat[0]; |
1494 //sync samples with audio thread approximately every 8 lines | |
1495 sync_samples = sync_to_audio ? buffer_samples : 8 * sample_rate / (source_hz * (std == VID_PAL ? 313 : 262)); | |
1496 max_repeat++; | 1213 max_repeat++; |
1497 min_buffered = (((float)max_repeat * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999; | 1214 min_buffered = (((float)max_repeat * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999; |
1498 //min_buffered *= buffer_samples; | 1215 //min_buffered *= buffer_samples; |
1499 debug_message("Min samples buffered before audio start: %d\n", min_buffered); | 1216 debug_message("Min samples buffered before audio start: %d\n", min_buffered); |
1500 max_adjust = BASE_MAX_ADJUST / source_hz; | 1217 max_adjust = BASE_MAX_ADJUST / source_hz; |
1806 } else if (local_cur_min < min_buffered / 2) { | 1523 } else if (local_cur_min < min_buffered / 2) { |
1807 adjust_ratio = max_adjust; | 1524 adjust_ratio = max_adjust; |
1808 } | 1525 } |
1809 if (adjust_ratio != 0.0f) { | 1526 if (adjust_ratio != 0.0f) { |
1810 average_change = 0; | 1527 average_change = 0; |
1811 for (uint8_t i = 0; i < num_audio_sources; i++) | 1528 render_audio_adjust_speed(adjust_ratio); |
1812 { | 1529 |
1813 audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5; | |
1814 } | |
1815 } | 1530 } |
1816 while (source_frame_count > 0) | 1531 while (source_frame_count > 0) |
1817 { | 1532 { |
1818 render_update_display(); | 1533 render_update_display(); |
1819 source_frame_count--; | 1534 source_frame_count--; |
1907 uint32_t render_overscan_top() | 1622 uint32_t render_overscan_top() |
1908 { | 1623 { |
1909 return overscan_top[video_standard]; | 1624 return overscan_top[video_standard]; |
1910 } | 1625 } |
1911 | 1626 |
1912 void render_wait_quit(vdp_context * context) | 1627 void render_wait_quit(void) |
1913 { | 1628 { |
1914 SDL_Event event; | 1629 SDL_Event event; |
1915 while(SDL_WaitEvent(&event)) { | 1630 while(SDL_WaitEvent(&event)) { |
1916 switch (event.type) { | 1631 switch (event.type) { |
1917 case SDL_QUIT: | 1632 case SDL_QUIT: |
2075 drain_events(); | 1790 drain_events(); |
2076 in_toggle = 0; | 1791 in_toggle = 0; |
2077 need_ui_fb_resize = 1; | 1792 need_ui_fb_resize = 1; |
2078 } | 1793 } |
2079 | 1794 |
2080 uint32_t render_audio_buffer() | |
2081 { | |
2082 return buffer_samples; | |
2083 } | |
2084 | |
2085 uint32_t render_sample_rate() | |
2086 { | |
2087 return sample_rate; | |
2088 } | |
2089 | |
2090 void render_errorbox(char *title, char *message) | 1795 void render_errorbox(char *title, char *message) |
2091 { | 1796 { |
2092 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, NULL); | 1797 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, NULL); |
2093 } | 1798 } |
2094 | 1799 |