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;