Mercurial > repos > blastem
comparison render_fbdev.c @ 1983:a7b753e260a2 mame_interp
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 09 May 2020 23:39:44 -0700 |
parents | 2fd0a8cb1c80 |
children |
comparison
equal
deleted
inserted
replaced
1937:cafde1255ad3 | 1983:a7b753e260a2 |
---|---|
48 static uint8_t render_gl = 1; | 48 static uint8_t render_gl = 1; |
49 static uint8_t scanlines = 0; | 49 static uint8_t scanlines = 0; |
50 | 50 |
51 static uint32_t last_frame = 0; | 51 static uint32_t last_frame = 0; |
52 static snd_pcm_uframes_t buffer_samples; | 52 static snd_pcm_uframes_t buffer_samples; |
53 static size_t buffer_bytes; | |
53 static unsigned int output_channels, sample_rate; | 54 static unsigned int output_channels, sample_rate; |
54 static uint32_t missing_count; | |
55 | 55 |
56 | 56 |
57 static uint8_t quitting = 0; | 57 static uint8_t quitting = 0; |
58 | 58 |
59 struct audio_source { | |
60 int16_t *front; | |
61 int16_t *back; | |
62 double dt; | |
63 uint64_t buffer_fraction; | |
64 uint64_t buffer_inc; | |
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 uint32_t min_buffered; | |
81 | |
82 typedef int32_t (*mix_func)(audio_source *audio, void *vstream, int len); | |
83 | |
84 static int32_t mix_s16(audio_source *audio, void *vstream, int len) | |
85 { | |
86 int samples = len/(sizeof(int16_t)*output_channels); | |
87 int16_t *stream = vstream; | |
88 int16_t *end = stream + output_channels*samples; | |
89 int16_t *src = audio->front; | |
90 uint32_t i = audio->read_start; | |
91 uint32_t i_end = audio->read_end; | |
92 int16_t *cur = stream; | |
93 size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; | |
94 if (audio->num_channels == 1) { | |
95 while (cur < end && i != i_end) | |
96 { | |
97 *cur += src[i]; | |
98 cur += first_add; | |
99 *cur += src[i++]; | |
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 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); | |
116 } | |
117 if (cur != end) { | |
118 //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); | |
119 return (cur-end)/2; | |
120 } else { | |
121 return ((i_end - i) & audio->mask) / audio->num_channels; | |
122 } | |
123 } | |
124 | |
125 static int32_t mix_f32(audio_source *audio, void *vstream, int len) | |
126 { | |
127 int samples = len/(sizeof(float)*output_channels); | |
128 float *stream = vstream; | |
129 float *end = stream + output_channels*samples; | |
130 int16_t *src = audio->front; | |
131 uint32_t i = audio->read_start; | |
132 uint32_t i_end = audio->read_end; | |
133 float *cur = stream; | |
134 size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; | |
135 if (audio->num_channels == 1) { | |
136 while (cur < end && i != i_end) | |
137 { | |
138 *cur += ((float)src[i]) / 0x7FFF; | |
139 cur += first_add; | |
140 *cur += ((float)src[i++]) / 0x7FFF; | |
141 cur += second_add; | |
142 i &= audio->mask; | |
143 } | |
144 } else { | |
145 while(cur < end && i != i_end) | |
146 { | |
147 *cur += ((float)src[i++]) / 0x7FFF; | |
148 cur += first_add; | |
149 *cur += ((float)src[i++]) / 0x7FFF; | |
150 cur += second_add; | |
151 i &= audio->mask; | |
152 } | |
153 } | |
154 if (cur != end) { | |
155 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); | |
156 return (cur-end)/2; | |
157 } else { | |
158 return ((i_end - i) & audio->mask) / audio->num_channels; | |
159 } | |
160 } | |
161 | |
162 static int32_t mix_null(audio_source *audio, void *vstream, int len) | |
163 { | |
164 return 0; | |
165 } | |
166 | |
167 static mix_func mix; | |
168 | 59 |
169 static void render_close_audio() | 60 static void render_close_audio() |
170 { | 61 { |
171 | 62 |
172 } | 63 } |
173 | 64 |
174 #define BUFFER_INC_RES 0x40000000UL | 65 static snd_pcm_t *audio_handle; |
175 | 66 static void *output_buffer; |
176 void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider) | 67 void render_do_audio_ready(audio_source *src) |
177 { | |
178 src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider; | |
179 } | |
180 | |
181 audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) | |
182 { | |
183 audio_source *ret = NULL; | |
184 uint32_t alloc_size = channels * buffer_samples; | |
185 if (num_audio_sources < 8) { | |
186 ret = malloc(sizeof(audio_source)); | |
187 ret->back = malloc(alloc_size * sizeof(int16_t)); | |
188 ret->front = malloc(alloc_size * sizeof(int16_t)); | |
189 ret->front_populated = 0; | |
190 ret->num_channels = channels; | |
191 audio_sources[num_audio_sources++] = ret; | |
192 } | |
193 if (!ret) { | |
194 fatal_error("Too many audio sources!"); | |
195 } else { | |
196 render_audio_adjust_clock(ret, master_clock, sample_divider); | |
197 double lowpass_cutoff = get_lowpass_cutoff(config); | |
198 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); | |
199 ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider)); | |
200 double alpha = ret->dt / (ret->dt + rc); | |
201 ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); | |
202 ret->buffer_pos = 0; | |
203 ret->buffer_fraction = 0; | |
204 ret->last_left = ret->last_right = 0; | |
205 ret->read_start = 0; | |
206 ret->read_end = buffer_samples * channels; | |
207 ret->mask = 0xFFFFFFFF; | |
208 } | |
209 return ret; | |
210 } | |
211 | |
212 void render_pause_source(audio_source *src) | |
213 { | |
214 for (uint8_t i = 0; i < num_audio_sources; i++) | |
215 { | |
216 if (audio_sources[i] == src) { | |
217 audio_sources[i] = audio_sources[--num_audio_sources]; | |
218 break; | |
219 } | |
220 } | |
221 inactive_audio_sources[num_inactive_audio_sources++] = src; | |
222 } | |
223 | |
224 void render_resume_source(audio_source *src) | |
225 { | |
226 if (num_audio_sources < 8) { | |
227 audio_sources[num_audio_sources++] = src; | |
228 } | |
229 for (uint8_t i = 0; i < num_inactive_audio_sources; i++) | |
230 { | |
231 if (inactive_audio_sources[i] == src) { | |
232 inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources]; | |
233 } | |
234 } | |
235 } | |
236 | |
237 void render_free_source(audio_source *src) | |
238 { | |
239 render_pause_source(src); | |
240 | |
241 free(src->front); | |
242 free(src->back); | |
243 free(src); | |
244 } | |
245 snd_pcm_t *audio_handle; | |
246 static void do_audio_ready(audio_source *src) | |
247 { | 68 { |
248 if (src->front_populated) { | 69 if (src->front_populated) { |
249 fatal_error("Audio source filled up a buffer a second time before other sources finished their first\n"); | 70 fatal_error("Audio source filled up a buffer a second time before other sources finished their first\n"); |
250 } | 71 } |
251 int16_t *tmp = src->front; | 72 int16_t *tmp = src->front; |
252 src->front = src->back; | 73 src->front = src->back; |
253 src->back = tmp; | 74 src->back = tmp; |
254 src->front_populated = 1; | 75 src->front_populated = 1; |
255 src->buffer_pos = 0; | 76 src->buffer_pos = 0; |
256 | 77 |
257 for (uint8_t i = 0; i < num_audio_sources; i++) | 78 if (!all_sources_ready()) { |
258 { | 79 return; |
259 if (!audio_sources[i]->front_populated) { | 80 } |
260 //at least one audio source is not ready yet. | 81 mix_and_convert(output_buffer, buffer_bytes, NULL); |
261 return; | 82 |
262 } | 83 int frames = snd_pcm_writei(audio_handle, output_buffer, buffer_samples); |
263 } | |
264 | |
265 size_t bytes = (mix == mix_s16 ? sizeof(int16_t) : sizeof(float)) * output_channels * buffer_samples; | |
266 void *buffer = malloc(bytes); | |
267 for (uint8_t i = 0; i < num_audio_sources; i++) | |
268 { | |
269 mix(audio_sources[i], buffer, bytes); | |
270 audio_sources[i]->front_populated = 0; | |
271 } | |
272 int frames = snd_pcm_writei(audio_handle, buffer, buffer_samples); | |
273 if (frames < 0) { | 84 if (frames < 0) { |
274 frames = snd_pcm_recover(audio_handle, frames, 0); | 85 frames = snd_pcm_recover(audio_handle, frames, 0); |
275 } | 86 } |
276 if (frames < 0) { | 87 if (frames < 0) { |
277 fprintf(stderr, "Failed to write samples: %s\n", snd_strerror(frames)); | 88 fprintf(stderr, "Failed to write samples: %s\n", snd_strerror(frames)); |
278 } | 89 } |
279 } | |
280 | |
281 static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current) | |
282 { | |
283 int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha); | |
284 current = tmp >> 16; | |
285 return current; | |
286 } | |
287 | |
288 static void interp_sample(audio_source *src, int16_t last, int16_t current) | |
289 { | |
290 int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc); | |
291 tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc)); | |
292 src->back[src->buffer_pos++] = tmp >> 16; | |
293 } | |
294 | |
295 void render_put_mono_sample(audio_source *src, int16_t value) | |
296 { | |
297 value = lowpass_sample(src, src->last_left, value); | |
298 src->buffer_fraction += src->buffer_inc; | |
299 uint32_t base = 0; | |
300 while (src->buffer_fraction > BUFFER_INC_RES) | |
301 { | |
302 src->buffer_fraction -= BUFFER_INC_RES; | |
303 interp_sample(src, src->last_left, value); | |
304 | |
305 if (((src->buffer_pos - base) & src->mask) >= buffer_samples) { | |
306 do_audio_ready(src); | |
307 } | |
308 src->buffer_pos &= src->mask; | |
309 } | |
310 src->last_left = value; | |
311 } | |
312 | |
313 void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right) | |
314 { | |
315 left = lowpass_sample(src, src->last_left, left); | |
316 right = lowpass_sample(src, src->last_right, right); | |
317 src->buffer_fraction += src->buffer_inc; | |
318 uint32_t base = 0; | |
319 while (src->buffer_fraction > BUFFER_INC_RES) | |
320 { | |
321 src->buffer_fraction -= BUFFER_INC_RES; | |
322 | |
323 interp_sample(src, src->last_left, left); | |
324 interp_sample(src, src->last_right, right); | |
325 | |
326 if (((src->buffer_pos - base) & src->mask)/2 >= buffer_samples) { | |
327 do_audio_ready(src); | |
328 } | |
329 src->buffer_pos &= src->mask; | |
330 } | |
331 src->last_left = left; | |
332 src->last_right = right; | |
333 } | 90 } |
334 | 91 |
335 int render_width() | 92 int render_width() |
336 { | 93 { |
337 return main_width; | 94 return main_width; |