Mercurial > repos > blastem
comparison render_sdl.c @ 1796:51417bb557b6
Configurable gain for overall output and individual components
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 23 Mar 2019 17:18:10 -0700 |
parents | 52a47611a273 |
children | 5ff8f0d28188 |
comparison
equal
deleted
inserted
replaced
1795:a02b4ed940b6 | 1796:51417bb557b6 |
---|---|
59 int16_t *front; | 59 int16_t *front; |
60 int16_t *back; | 60 int16_t *back; |
61 double dt; | 61 double dt; |
62 uint64_t buffer_fraction; | 62 uint64_t buffer_fraction; |
63 uint64_t buffer_inc; | 63 uint64_t buffer_inc; |
64 float gain_mult; | |
64 uint32_t buffer_pos; | 65 uint32_t buffer_pos; |
65 uint32_t read_start; | 66 uint32_t read_start; |
66 uint32_t read_end; | 67 uint32_t read_end; |
67 uint32_t lowpass_alpha; | 68 uint32_t lowpass_alpha; |
68 uint32_t mask; | 69 uint32_t mask; |
76 static audio_source *inactive_audio_sources[8]; | 77 static audio_source *inactive_audio_sources[8]; |
77 static uint8_t num_audio_sources; | 78 static uint8_t num_audio_sources; |
78 static uint8_t num_inactive_audio_sources; | 79 static uint8_t num_inactive_audio_sources; |
79 static uint8_t sync_to_audio; | 80 static uint8_t sync_to_audio; |
80 static uint32_t min_buffered; | 81 static uint32_t min_buffered; |
81 | 82 static float overall_gain_mult, *mix_buf; |
82 typedef int32_t (*mix_func)(audio_source *audio, void *vstream, int len); | 83 static int sample_size; |
83 | 84 |
84 static int32_t mix_s16(audio_source *audio, void *vstream, int len) | 85 typedef void (*conv_func)(float *samples, void *vstream, int sample_count); |
85 { | 86 |
86 int samples = len/(sizeof(int16_t)*2); | 87 static void convert_null(float *samples, void *vstream, int sample_count) |
88 { | |
89 memset(vstream, 0, sample_count * sample_size); | |
90 } | |
91 | |
92 static void convert_s16(float *samples, void *vstream, int sample_count) | |
93 { | |
87 int16_t *stream = vstream; | 94 int16_t *stream = vstream; |
88 int16_t *end = stream + output_channels*samples; | 95 for (int16_t *end = stream + sample_count; stream < end; stream++, samples++) |
96 { | |
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; | |
89 int16_t *src = audio->front; | 127 int16_t *src = audio->front; |
90 uint32_t i = audio->read_start; | 128 uint32_t i = audio->read_start; |
91 uint32_t i_end = audio->read_end; | 129 uint32_t i_end = audio->read_end; |
92 int16_t *cur = stream; | 130 float *cur = stream; |
131 float gain_mult = audio->gain_mult * overall_gain_mult; | |
93 size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; | 132 size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; |
94 if (audio->num_channels == 1) { | 133 if (audio->num_channels == 1) { |
95 while (cur < end && i != i_end) | 134 while (cur < end && i != i_end) |
96 { | 135 { |
97 *cur += src[i]; | 136 *cur += gain_mult * ((float)src[i]) / 0x7FFF; |
98 cur += first_add; | 137 cur += first_add; |
99 *cur += src[i++]; | 138 *cur += gain_mult * ((float)src[i++]) / 0x7FFF; |
100 cur += second_add; | |
101 i &= audio->mask; | |
102 } | |
103 } else { | |
104 while (cur < end && i != i_end) | |
105 { | |
106 *cur += src[i++]; | |
107 cur += first_add; | |
108 *cur += src[i++]; | |
109 cur += second_add; | |
110 i &= audio->mask; | |
111 } | |
112 } | |
113 | |
114 if (cur != end) { | |
115 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); | |
116 } | |
117 if (!sync_to_audio) { | |
118 audio->read_start = i; | |
119 } | |
120 if (cur != end) { | |
121 return (cur-end)/2; | |
122 } else { | |
123 return ((i_end - i) & audio->mask) / audio->num_channels; | |
124 } | |
125 } | |
126 | |
127 static int32_t mix_f32(audio_source *audio, void *vstream, int len) | |
128 { | |
129 int samples = len/(sizeof(float)*2); | |
130 float *stream = vstream; | |
131 float *end = stream + 2*samples; | |
132 int16_t *src = audio->front; | |
133 uint32_t i = audio->read_start; | |
134 uint32_t i_end = audio->read_end; | |
135 float *cur = stream; | |
136 size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; | |
137 if (audio->num_channels == 1) { | |
138 while (cur < end && i != i_end) | |
139 { | |
140 *cur += ((float)src[i]) / 0x7FFF; | |
141 cur += first_add; | |
142 *cur += ((float)src[i++]) / 0x7FFF; | |
143 cur += second_add; | 139 cur += second_add; |
144 i &= audio->mask; | 140 i &= audio->mask; |
145 } | 141 } |
146 } else { | 142 } else { |
147 while(cur < end && i != i_end) | 143 while(cur < end && i != i_end) |
148 { | 144 { |
149 *cur += ((float)src[i++]) / 0x7FFF; | 145 *cur += gain_mult * ((float)src[i++]) / 0x7FFF; |
150 cur += first_add; | 146 cur += first_add; |
151 *cur += ((float)src[i++]) / 0x7FFF; | 147 *cur += gain_mult * ((float)src[i++]) / 0x7FFF; |
152 cur += second_add; | 148 cur += second_add; |
153 i &= audio->mask; | 149 i &= audio->mask; |
154 } | 150 } |
155 } | 151 } |
156 if (!sync_to_audio) { | 152 if (!sync_to_audio) { |
162 } else { | 158 } else { |
163 return ((i_end - i) & audio->mask) / audio->num_channels; | 159 return ((i_end - i) & audio->mask) / audio->num_channels; |
164 } | 160 } |
165 } | 161 } |
166 | 162 |
167 static int32_t mix_null(audio_source *audio, void *vstream, int len) | 163 static conv_func convert; |
168 { | |
169 return 0; | |
170 } | |
171 | |
172 static mix_func mix; | |
173 | 164 |
174 static void audio_callback(void * userdata, uint8_t *byte_stream, int len) | 165 static void audio_callback(void * userdata, uint8_t *byte_stream, int len) |
175 { | 166 { |
176 uint8_t num_populated; | 167 uint8_t num_populated; |
177 memset(byte_stream, 0, len); | 168 float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream; |
169 | |
170 int samples = len / sample_size; | |
171 memset(mix_dest, 0, samples * sizeof(float)); | |
178 SDL_LockMutex(audio_mutex); | 172 SDL_LockMutex(audio_mutex); |
179 do { | 173 do { |
180 num_populated = 0; | 174 num_populated = 0; |
181 for (uint8_t i = 0; i < num_audio_sources; i++) | 175 for (uint8_t i = 0; i < num_audio_sources; i++) |
182 { | 176 { |
190 } | 184 } |
191 } while(!quitting && num_populated < num_audio_sources); | 185 } while(!quitting && num_populated < num_audio_sources); |
192 if (!quitting) { | 186 if (!quitting) { |
193 for (uint8_t i = 0; i < num_audio_sources; i++) | 187 for (uint8_t i = 0; i < num_audio_sources; i++) |
194 { | 188 { |
195 mix(audio_sources[i], byte_stream, len); | 189 mix_f32(audio_sources[i], mix_dest, samples); |
196 audio_sources[i]->front_populated = 0; | 190 audio_sources[i]->front_populated = 0; |
197 SDL_CondSignal(audio_sources[i]->cond); | 191 SDL_CondSignal(audio_sources[i]->cond); |
198 } | 192 } |
199 } | 193 } |
200 SDL_UnlockMutex(audio_mutex); | 194 SDL_UnlockMutex(audio_mutex); |
195 convert(mix_dest, byte_stream, samples); | |
201 } | 196 } |
202 | 197 |
203 #define NO_LAST_BUFFERED -2000000000 | 198 #define NO_LAST_BUFFERED -2000000000 |
204 static int32_t last_buffered = NO_LAST_BUFFERED; | 199 static int32_t last_buffered = NO_LAST_BUFFERED; |
205 static float average_change; | 200 static float average_change; |
208 static float max_adjust; | 203 static float max_adjust; |
209 static int32_t cur_min_buffered; | 204 static int32_t cur_min_buffered; |
210 static uint32_t min_remaining_buffer; | 205 static uint32_t min_remaining_buffer; |
211 static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len) | 206 static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len) |
212 { | 207 { |
213 memset(byte_stream, 0, len); | |
214 if (cur_min_buffered < 0) { | 208 if (cur_min_buffered < 0) { |
215 //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet | 209 //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet |
216 return; | 210 return; |
217 } | 211 } |
218 cur_min_buffered = 0x7FFFFFFF; | 212 cur_min_buffered = 0x7FFFFFFF; |
219 min_remaining_buffer = 0xFFFFFFFF; | 213 min_remaining_buffer = 0xFFFFFFFF; |
214 float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream; | |
215 int samples = len / sample_size; | |
216 memset(mix_dest, 0, samples * sizeof(float)); | |
220 for (uint8_t i = 0; i < num_audio_sources; i++) | 217 for (uint8_t i = 0; i < num_audio_sources; i++) |
221 { | 218 { |
222 | 219 |
223 int32_t buffered = mix(audio_sources[i], byte_stream, len); | 220 int32_t buffered = mix_f32(audio_sources[i], mix_dest, samples); |
224 cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered; | 221 cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered; |
225 uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered; | 222 uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered; |
226 min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; | 223 min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; |
227 } | 224 } |
225 convert(mix_dest, byte_stream, samples); | |
228 } | 226 } |
229 | 227 |
230 static void lock_audio() | 228 static void lock_audio() |
231 { | 229 { |
232 if (sync_to_audio) { | 230 if (sync_to_audio) { |
248 static void render_close_audio() | 246 static void render_close_audio() |
249 { | 247 { |
250 SDL_LockMutex(audio_mutex); | 248 SDL_LockMutex(audio_mutex); |
251 quitting = 1; | 249 quitting = 1; |
252 SDL_CondSignal(audio_ready); | 250 SDL_CondSignal(audio_ready); |
251 if (mix_buf) { | |
252 free(mix_buf); | |
253 mix_buf = NULL; | |
254 } | |
253 SDL_UnlockMutex(audio_mutex); | 255 SDL_UnlockMutex(audio_mutex); |
254 SDL_CloseAudio(); | 256 SDL_CloseAudio(); |
255 } | 257 } |
256 | 258 |
257 #define BUFFER_INC_RES 0x40000000UL | 259 #define BUFFER_INC_RES 0x40000000UL |
294 } | 296 } |
295 if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { | 297 if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { |
296 SDL_PauseAudio(0); | 298 SDL_PauseAudio(0); |
297 } | 299 } |
298 return ret; | 300 return ret; |
301 } | |
302 | |
303 static float db_to_mult(float gain) | |
304 { | |
305 return powf(10.0f, gain/20.0f); | |
306 } | |
307 | |
308 void render_audio_source_gaindb(audio_source *src, float gain) | |
309 { | |
310 src->gain_mult = db_to_mult(gain); | |
299 } | 311 } |
300 | 312 |
301 void render_pause_source(audio_source *src) | 313 void render_pause_source(audio_source *src) |
302 { | 314 { |
303 uint8_t need_pause = 0; | 315 uint8_t need_pause = 0; |
1042 } | 1054 } |
1043 buffer_samples = actual.samples; | 1055 buffer_samples = actual.samples; |
1044 sample_rate = actual.freq; | 1056 sample_rate = actual.freq; |
1045 output_channels = actual.channels; | 1057 output_channels = actual.channels; |
1046 debug_message("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); | 1058 debug_message("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); |
1059 sample_size = SDL_AUDIO_BITSIZE(actual.format) / 8; | |
1047 if (actual.format == AUDIO_S16SYS) { | 1060 if (actual.format == AUDIO_S16SYS) { |
1048 debug_message("signed 16-bit int format"); | 1061 debug_message("signed 16-bit int format"); |
1049 mix = mix_s16; | 1062 convert = convert_s16; |
1063 mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); | |
1050 } else if (actual.format == AUDIO_F32SYS) { | 1064 } else if (actual.format == AUDIO_F32SYS) { |
1051 debug_message("32-bit float format"); | 1065 debug_message("32-bit float format"); |
1052 mix = mix_f32; | 1066 convert = clamp_f32; |
1067 mix_buf = NULL; | |
1053 } else { | 1068 } else { |
1054 debug_message("unsupported format %X\n", actual.format); | 1069 debug_message("unsupported format %X\n", actual.format); |
1055 warning("Unsupported audio sample format: %X\n", actual.format); | 1070 warning("Unsupported audio sample format: %X\n", actual.format); |
1056 mix = mix_null; | 1071 convert = convert_null; |
1057 } | 1072 mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); |
1073 } | |
1074 char * gain_str = tern_find_path(config, "audio\0gain\0", TVAL_PTR).ptrval; | |
1075 overall_gain_mult = db_to_mult(gain_str ? atof(gain_str) : 0.0f); | |
1058 } | 1076 } |
1059 | 1077 |
1060 void window_setup(void) | 1078 void window_setup(void) |
1061 { | 1079 { |
1062 uint32_t flags = SDL_WINDOW_RESIZABLE; | 1080 uint32_t flags = SDL_WINDOW_RESIZABLE; |