Mercurial > repos > blastem
comparison render_sdl.c @ 1566:cbb40af77a94
Moved resample rate adjustment to after frame flip rather than in audio callback as it makes more sense there. Needs adjustment to avoid audible pitch changes
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 17 Apr 2018 00:20:41 -0700 |
parents | 61fafcbc2c38 |
children | 66387b1645e4 |
comparison
equal
deleted
inserted
replaced
1565:61fafcbc2c38 | 1566:cbb40af77a94 |
---|---|
57 uint32_t mask; | 57 uint32_t mask; |
58 int16_t last_left; | 58 int16_t last_left; |
59 int16_t last_right; | 59 int16_t last_right; |
60 uint8_t num_channels; | 60 uint8_t num_channels; |
61 uint8_t front_populated; | 61 uint8_t front_populated; |
62 uint8_t adjusted; | |
63 }; | 62 }; |
64 | 63 |
65 static audio_source *audio_sources[8]; | 64 static audio_source *audio_sources[8]; |
66 static uint8_t num_audio_sources; | 65 static uint8_t num_audio_sources; |
67 static uint8_t sync_to_audio; | 66 static uint8_t sync_to_audio; |
92 *(cur++) += src[i++]; | 91 *(cur++) += src[i++]; |
93 i &= audio->mask; | 92 i &= audio->mask; |
94 } | 93 } |
95 } | 94 } |
96 | 95 |
96 if (cur != end) { | |
97 printf("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); | |
98 } | |
97 if (!sync_to_audio) { | 99 if (!sync_to_audio) { |
98 audio->read_start = i; | 100 audio->read_start = i; |
99 } | 101 } |
100 if (cur != end) { | 102 if (cur != end) { |
101 printf("Underflow of %d samples\n", (int)(end-cur)/2); | 103 //printf("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); |
102 return (cur-end)/2; | 104 return (cur-end)/2; |
103 } else { | 105 } else { |
104 return ((i_end - i) & audio->mask) / audio->num_channels; | 106 return ((i_end - i) & audio->mask) / audio->num_channels; |
105 } | 107 } |
106 } | 108 } |
131 } | 133 } |
132 if (!sync_to_audio) { | 134 if (!sync_to_audio) { |
133 audio->read_start = i; | 135 audio->read_start = i; |
134 } | 136 } |
135 if (cur != end) { | 137 if (cur != end) { |
136 printf("Underflow of %d samples\n", (int)(end-cur)/2); | 138 printf("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); |
137 return (cur-end)/2; | 139 return (cur-end)/2; |
138 } else { | 140 } else { |
139 return ((i_end - i) & audio->mask) / audio->num_channels; | 141 return ((i_end - i) & audio->mask) / audio->num_channels; |
140 } | 142 } |
141 } | 143 } |
177 SDL_UnlockMutex(audio_mutex); | 179 SDL_UnlockMutex(audio_mutex); |
178 } | 180 } |
179 | 181 |
180 #define NO_LAST_BUFFERED -2000000000 | 182 #define NO_LAST_BUFFERED -2000000000 |
181 static int32_t last_buffered = NO_LAST_BUFFERED; | 183 static int32_t last_buffered = NO_LAST_BUFFERED; |
182 static uint8_t need_adjust, need_pause; | 184 static float average_change; |
183 static float adjust_ratio, average_change; | |
184 #define BUFFER_FRAMES_THRESHOLD 6 | 185 #define BUFFER_FRAMES_THRESHOLD 6 |
185 #define BASE_MAX_ADJUST 0.05 | 186 #define BASE_MAX_ADJUST 0.025 |
186 static float max_adjust; | 187 static float max_adjust; |
188 static int32_t cur_min_buffered; | |
189 static uint32_t min_remaining_buffer; | |
187 static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len) | 190 static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len) |
188 { | 191 { |
189 //TODO: update progress tracking so we can adjust resample rate | 192 //TODO: update progress tracking so we can adjust resample rate |
190 memset(byte_stream, 0, len); | 193 memset(byte_stream, 0, len); |
191 int32_t cur_min_buffered = 0x7FFFFFFF; | 194 cur_min_buffered = 0x7FFFFFFF; |
192 uint32_t min_remaining_buffer = 0xFFFFFFFF; | 195 min_remaining_buffer = 0xFFFFFFFF; |
193 for (uint8_t i = 0; i < num_audio_sources; i++) | 196 for (uint8_t i = 0; i < num_audio_sources; i++) |
194 { | 197 { |
195 | 198 |
196 printf("buffered at start: %d, ", ((audio_sources[i]->read_end - audio_sources[i]->read_start) & audio_sources[i]->mask) / audio_sources[i]->num_channels); | |
197 int32_t buffered = mix(audio_sources[i], byte_stream, len); | 199 int32_t buffered = mix(audio_sources[i], byte_stream, len); |
198 printf("buffered after mix: %d\n", buffered); | |
199 cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered; | 200 cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered; |
200 uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered; | 201 uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered; |
201 min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; | 202 min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; |
202 } | 203 } |
203 if (last_buffered > NO_LAST_BUFFERED) { | 204 printf("cur_min_buffered: %d, source 0 %d:%d\n", cur_min_buffered, audio_sources[0]->read_start, audio_sources[0]->read_end); |
204 average_change *= 0.8f; | |
205 average_change += ((int32_t)cur_min_buffered - last_buffered) * 0.2f; | |
206 } | |
207 last_buffered = cur_min_buffered; | |
208 float frames_to_problem; | |
209 if (average_change < 0) { | |
210 frames_to_problem = (float)cur_min_buffered / -average_change; | |
211 } else { | |
212 frames_to_problem = (float)min_remaining_buffer / average_change; | |
213 } | |
214 if ( | |
215 frames_to_problem < BUFFER_FRAMES_THRESHOLD || cur_min_buffered < 0 | |
216 || (cur_min_buffered < min_buffered/2 && average_change < 0) | |
217 || (cur_min_buffered > (2*min_buffered) && average_change > 0) | |
218 ) { | |
219 need_adjust = num_audio_sources; | |
220 if (cur_min_buffered < 0) { | |
221 adjust_ratio = max_adjust; | |
222 need_pause = 1; | |
223 } else { | |
224 adjust_ratio = -1.5 * average_change / buffer_samples; | |
225 if (fabsf(adjust_ratio) > max_adjust) { | |
226 adjust_ratio = adjust_ratio > 0 ? max_adjust : -max_adjust; | |
227 } | |
228 } | |
229 printf("frames_to_problem: %f, avg_change: %f, adjust_ratio: %f\n", frames_to_problem, average_change, adjust_ratio); | |
230 average_change = 0; | |
231 last_buffered = NO_LAST_BUFFERED; | |
232 for (uint8_t i = 0; i < num_audio_sources; i++) | |
233 { | |
234 audio_sources[i]->adjusted = 0; | |
235 } | |
236 } else { | |
237 printf("no adjust - frames_to_problem: %f, avg_change: %f, cur_min_buffered: %d, min_remaining_buffer: %d\n", frames_to_problem, average_change, cur_min_buffered, min_remaining_buffer); | |
238 } | |
239 } | 205 } |
240 | 206 |
241 static void lock_audio() | 207 static void lock_audio() |
242 { | 208 { |
243 if (sync_to_audio) { | 209 if (sync_to_audio) { |
273 } | 239 } |
274 | 240 |
275 audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) | 241 audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) |
276 { | 242 { |
277 audio_source *ret = NULL; | 243 audio_source *ret = NULL; |
278 uint32_t alloc_size = sync_to_audio ? channels * buffer_samples : nearest_pow2(min_buffered * 2 * channels); | 244 uint32_t alloc_size = sync_to_audio ? channels * buffer_samples : nearest_pow2(min_buffered * 4 * channels); |
279 lock_audio(); | 245 lock_audio(); |
280 if (num_audio_sources < 8) { | 246 if (num_audio_sources < 8) { |
281 ret = malloc(sizeof(audio_source)); | 247 ret = malloc(sizeof(audio_source)); |
282 ret->back = malloc(alloc_size * sizeof(int16_t)); | 248 ret->back = malloc(alloc_size * sizeof(int16_t)); |
283 ret->front = sync_to_audio ? malloc(alloc_size * sizeof(int16_t)) : ret->back; | 249 ret->front = sync_to_audio ? malloc(alloc_size * sizeof(int16_t)) : ret->back; |
284 ret->front_populated = 0; | 250 ret->front_populated = 0; |
285 ret->cond = SDL_CreateCond(); | 251 ret->cond = SDL_CreateCond(); |
286 ret->num_channels = channels; | 252 ret->num_channels = channels; |
287 ret->adjusted = 0; | |
288 audio_sources[num_audio_sources++] = ret; | 253 audio_sources[num_audio_sources++] = ret; |
289 } | 254 } |
290 unlock_audio(); | 255 unlock_audio(); |
291 if (!ret) { | 256 if (!ret) { |
292 fatal_error("Too many audio sources!"); | 257 fatal_error("Too many audio sources!"); |
367 src->buffer_pos = 0; | 332 src->buffer_pos = 0; |
368 SDL_CondSignal(audio_ready); | 333 SDL_CondSignal(audio_ready); |
369 SDL_UnlockMutex(audio_mutex); | 334 SDL_UnlockMutex(audio_mutex); |
370 } else { | 335 } else { |
371 uint32_t num_buffered; | 336 uint32_t num_buffered; |
372 uint8_t local_need_pause; | |
373 SDL_LockAudio(); | 337 SDL_LockAudio(); |
374 src->read_end = src->buffer_pos; | 338 src->read_end = src->buffer_pos; |
375 num_buffered = ((src->read_end - src->read_start) & src->mask) / src->num_channels; | 339 num_buffered = ((src->read_end - src->read_start) & src->mask) / src->num_channels; |
376 if (need_adjust && !src->adjusted) { | |
377 src->adjusted = 1; | |
378 need_adjust--; | |
379 src->buffer_inc = ((double)src->buffer_inc) + ((double)src->buffer_inc) * adjust_ratio + 0.5; | |
380 } | |
381 local_need_pause = need_pause; | |
382 need_pause = 0; | |
383 SDL_UnlockAudio(); | 340 SDL_UnlockAudio(); |
384 if (local_need_pause) { | 341 if (num_buffered >= min_buffered && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { |
385 SDL_PauseAudio(1); | |
386 } else if (num_buffered >= min_buffered && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { | |
387 SDL_PauseAudio(0); | 342 SDL_PauseAudio(0); |
388 } | 343 } |
389 } | 344 } |
390 } | 345 } |
391 | 346 |
848 if (SDL_OpenAudio(&desired, &actual) < 0) { | 803 if (SDL_OpenAudio(&desired, &actual) < 0) { |
849 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); | 804 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); |
850 } | 805 } |
851 buffer_samples = actual.samples; | 806 buffer_samples = actual.samples; |
852 sample_rate = actual.freq; | 807 sample_rate = actual.freq; |
853 max_adjust = BASE_MAX_ADJUST * (float)buffer_samples / (float)sample_rate; | |
854 printf("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); | 808 printf("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); |
855 if (actual.format == AUDIO_S16SYS) { | 809 if (actual.format == AUDIO_S16SYS) { |
856 puts("signed 16-bit int format"); | 810 puts("signed 16-bit int format"); |
857 mix = mix_s16; | 811 mix = mix_s16; |
858 } else if (actual.format == AUDIO_F32SYS) { | 812 } else if (actual.format == AUDIO_F32SYS) { |
914 source_frame = 0; | 868 source_frame = 0; |
915 source_frame_count = frame_repeat[0]; | 869 source_frame_count = frame_repeat[0]; |
916 //sync samples with audio thread approximately every 8 lines | 870 //sync samples with audio thread approximately every 8 lines |
917 sync_samples = 8 * sample_rate / (source_hz * (VID_PAL ? 313 : 262)); | 871 sync_samples = 8 * sample_rate / (source_hz * (VID_PAL ? 313 : 262)); |
918 max_repeat++; | 872 max_repeat++; |
919 float mult = max_repeat > 1 ? 2.5 : 1.5; | 873 float mult = max_repeat > 1 ? 1 : 1.35; |
920 min_buffered = (((float)max_repeat * mult * (float)sample_rate/(float)source_hz) / (float)buffer_samples) + 0.9999; | 874 min_buffered = (((float)max_repeat * mult * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999; |
921 min_buffered *= buffer_samples; | 875 //min_buffered *= buffer_samples; |
922 printf("Min samples buffered before audio start: %d\n", min_buffered); | 876 printf("Min samples buffered before audio start: %d\n", min_buffered); |
877 max_adjust = BASE_MAX_ADJUST / source_hz; | |
923 } | 878 } |
924 | 879 |
925 void render_update_caption(char *title) | 880 void render_update_caption(char *title) |
926 { | 881 { |
927 caption = title; | 882 caption = title; |
1105 } | 1060 } |
1106 start = last_frame; | 1061 start = last_frame; |
1107 frame_counter = 0; | 1062 frame_counter = 0; |
1108 } | 1063 } |
1109 } | 1064 } |
1110 while (source_frame_count > 0) | 1065 if (!sync_to_audio) { |
1111 { | 1066 int32_t local_cur_min, local_min_remaining; |
1112 render_update_display(); | 1067 SDL_LockAudio(); |
1113 source_frame_count--; | 1068 if (last_buffered > NO_LAST_BUFFERED) { |
1114 } | 1069 average_change *= 0.9f; |
1115 source_frame++; | 1070 average_change += (cur_min_buffered - last_buffered) * 0.1f; |
1116 if (source_frame >= source_hz) { | 1071 } |
1117 source_frame = 0; | 1072 local_cur_min = cur_min_buffered; |
1118 } | 1073 local_min_remaining = min_remaining_buffer; |
1119 source_frame_count = frame_repeat[source_frame]; | 1074 last_buffered = cur_min_buffered; |
1075 SDL_UnlockAudio(); | |
1076 float frames_to_problem; | |
1077 if (average_change < 0) { | |
1078 frames_to_problem = (float)local_cur_min / -average_change; | |
1079 } else { | |
1080 frames_to_problem = (float)local_min_remaining / average_change; | |
1081 } | |
1082 if ( | |
1083 frames_to_problem < BUFFER_FRAMES_THRESHOLD | |
1084 || (average_change < 0 && local_cur_min < min_buffered / 2) | |
1085 || (average_change >0 && local_cur_min > min_buffered * 2) | |
1086 ) { | |
1087 float adjust_ratio; | |
1088 if (cur_min_buffered < 0) { | |
1089 adjust_ratio = max_adjust; | |
1090 SDL_PauseAudio(1); | |
1091 last_buffered = NO_LAST_BUFFERED; | |
1092 } else { | |
1093 adjust_ratio = -1.5 * average_change / ((float)sample_rate / (float)source_hz); | |
1094 if (fabsf(adjust_ratio) > max_adjust) { | |
1095 adjust_ratio = adjust_ratio > 0 ? max_adjust : -max_adjust; | |
1096 } | |
1097 } | |
1098 printf("frames_to_problem: %f, avg_change: %f, adjust_ratio: %f\n", frames_to_problem, average_change, adjust_ratio); | |
1099 average_change = 0; | |
1100 for (uint8_t i = 0; i < num_audio_sources; i++) | |
1101 { | |
1102 audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5; | |
1103 } | |
1104 } else { | |
1105 printf("no adjust - frames_to_problem: %f, avg_change: %f, cur_min_buffered: %d, min_remaining_buffer: %d\n", frames_to_problem, average_change, local_cur_min, local_min_remaining); | |
1106 } | |
1107 while (source_frame_count > 0) | |
1108 { | |
1109 render_update_display(); | |
1110 source_frame_count--; | |
1111 } | |
1112 source_frame++; | |
1113 if (source_frame >= source_hz) { | |
1114 source_frame = 0; | |
1115 } | |
1116 source_frame_count = frame_repeat[source_frame]; | |
1117 } | |
1120 } | 1118 } |
1121 | 1119 |
1122 static ui_render_fun render_ui; | 1120 static ui_render_fun render_ui; |
1123 void render_set_ui_render_fun(ui_render_fun fun) | 1121 void render_set_ui_render_fun(ui_render_fun fun) |
1124 { | 1122 { |