1 // for finding memory leaks in debug mode with Visual Studio
2 #if defined _DEBUG && defined _MSC_VER
3 #include <crtdbg.h>
4 #endif
5
6 #include <stdio.h>
7 #include <stdint.h>
8 #include "ft2_header.h"
9 #include "ft2_config.h"
10 #include "ft2_audio.h"
11 #include "ft2_gui.h"
12 #include "ft2_mouse.h"
13 #include "ft2_audioselector.h"
14 #include "ft2_structs.h"
15
16 enum
17 {
18 INPUT_DEVICE = 0,
19 OUTPUT_DEVICE = 1
20 };
21
22 #define MAX_DEV_STR_LEN 256
23
24 // hide POSIX warnings
25 #ifdef _MSC_VER
26 #pragma warning(disable: 4996)
27 #endif
28
getReasonableAudioDevice(int32_t iscapture)29 static char *getReasonableAudioDevice(int32_t iscapture) // can and will return NULL
30 {
31 int32_t numAudioDevs = SDL_GetNumAudioDevices(iscapture);
32 if (numAudioDevs == 0 || numAudioDevs > 1)
33 return NULL; // we don't know which audio output device is the default device
34
35 const char *devName = SDL_GetAudioDeviceName(0, iscapture);
36 if (devName == NULL)
37 return NULL;
38
39 return strdup(devName);
40 }
41
getAudioOutputDeviceFromConfig(void)42 char *getAudioOutputDeviceFromConfig(void)
43 {
44 bool audioDeviceRead = false;
45 char *devString = NULL;
46
47 FILE *f = UNICHAR_FOPEN(editor.audioDevConfigFileLocationU, "r");
48 if (f != NULL)
49 {
50 devString = (char *)malloc(MAX_DEV_STR_LEN+1);
51 if (devString == NULL)
52 {
53 fclose(f);
54 return NULL; // out of memory
55 }
56
57 devString[0] = '\0';
58 fgets(devString, MAX_DEV_STR_LEN, f);
59 fclose(f);
60
61 const int32_t devStringLen = (int32_t)strlen(devString);
62 if (devStringLen > 0)
63 {
64 if (devString[devStringLen-1] == '\n')
65 devString[devStringLen-1] = '\0';
66
67 if (!(devStringLen == 1 && devString[0] == ' ')) // space only = no device
68 audioDeviceRead = true;
69 }
70 }
71
72 if (!audioDeviceRead)
73 {
74 if (devString != NULL)
75 free(devString);
76
77 devString = getReasonableAudioDevice(OUTPUT_DEVICE);
78 }
79
80 // SDL_OpenAudioDevice() doesn't seem to like an empty audio device string
81 if (devString != NULL && devString[0] == '\0')
82 {
83 free(devString);
84 return NULL;
85 }
86
87 return devString;
88 }
89
getAudioInputDeviceFromConfig(void)90 char *getAudioInputDeviceFromConfig(void)
91 {
92 bool audioDeviceRead = false;
93 char *devString = NULL;
94
95 FILE *f = UNICHAR_FOPEN(editor.audioDevConfigFileLocationU, "r");
96 if (f != NULL)
97 {
98 devString = (char *)malloc(MAX_DEV_STR_LEN+1);
99 if (devString == NULL)
100 {
101 fclose(f);
102 return NULL; // out of memory
103 }
104
105 devString[0] = '\0';
106 fgets(devString, MAX_DEV_STR_LEN, f); // skip first line (we want the input device)
107 fgets(devString, MAX_DEV_STR_LEN, f);
108 fclose(f);
109
110 const int32_t devStringLen = (int32_t)strlen(devString);
111 if (devStringLen > 0)
112 {
113 if (devString[devStringLen-1] == '\n')
114 devString[devStringLen-1] = '\0';
115
116 if (!(devStringLen == 1 && devString[0] == ' ')) // space only = no device
117 audioDeviceRead = true;
118 }
119 }
120
121 if (!audioDeviceRead)
122 {
123 if (devString != NULL)
124 free(devString);
125
126 devString = getReasonableAudioDevice(INPUT_DEVICE);
127 }
128
129 // SDL_OpenAudioDevice() doesn't seem to like an empty audio device string
130 if (devString != NULL && devString[0] == '\0')
131 {
132 free(devString);
133 return NULL;
134 }
135
136 return devString;
137 }
138
saveAudioDevicesToConfig(const char * outputDevice,const char * inputDevice)139 bool saveAudioDevicesToConfig(const char *outputDevice, const char *inputDevice)
140 {
141 FILE *f = UNICHAR_FOPEN(editor.audioDevConfigFileLocationU, "w");
142 if (f == NULL)
143 return false;
144
145 if (outputDevice != NULL)
146 fputs(outputDevice, f);
147 else
148 fputc(' ', f);
149
150 fputc('\n', f);
151
152 if (inputDevice != NULL)
153 fputs(inputDevice, f);
154 else
155 fputc(' ', f);
156
157 fclose(f);
158 return true;
159 }
160
drawAudioOutputList(void)161 void drawAudioOutputList(void)
162 {
163 clearRect(114, 18, AUDIO_SELECTORS_BOX_WIDTH, 66);
164
165 if (audio.outputDeviceNum == 0)
166 {
167 textOut(114, 18, PAL_FORGRND, "No audio output devices found!");
168 return;
169 }
170
171 for (int32_t i = 0; i < 6; i++)
172 {
173 const int32_t deviceEntry = getScrollBarPos(SB_AUDIO_OUTPUT_SCROLL) + i;
174 if (deviceEntry < audio.outputDeviceNum)
175 {
176 if (audio.outputDeviceNames[deviceEntry] == NULL)
177 continue;
178
179 const uint16_t y = 18 + (uint16_t)(i * 11);
180
181 if (audio.currOutputDevice != NULL)
182 {
183 if (strcmp(audio.currOutputDevice, audio.outputDeviceNames[deviceEntry]) == 0)
184 fillRect(114, y, AUDIO_SELECTORS_BOX_WIDTH, 10, PAL_BOXSLCT); // selection background color
185 }
186
187 char *tmpString = utf8ToCp437(audio.outputDeviceNames[deviceEntry], true);
188 if (tmpString != NULL)
189 {
190 textOutClipX(114, y, PAL_FORGRND, tmpString, 114 + AUDIO_SELECTORS_BOX_WIDTH);
191 free(tmpString);
192 }
193 }
194 }
195 }
196
drawAudioInputList(void)197 void drawAudioInputList(void)
198 {
199 clearRect(114, 105, AUDIO_SELECTORS_BOX_WIDTH, 44);
200
201 if (audio.inputDeviceNum == 0)
202 {
203 textOut(114, 105, PAL_FORGRND, "No audio input devices found!");
204 return;
205 }
206
207 for (int32_t i = 0; i < 4; i++)
208 {
209 const int32_t deviceEntry = getScrollBarPos(SB_AUDIO_INPUT_SCROLL) + i;
210 if (deviceEntry < audio.inputDeviceNum)
211 {
212 if (audio.inputDeviceNames[deviceEntry] == NULL)
213 continue;
214
215 const uint16_t y = 105 + (uint16_t)(i * 11);
216
217 if (audio.currInputDevice != NULL)
218 {
219 if (strcmp(audio.currInputDevice, audio.inputDeviceNames[deviceEntry]) == 0)
220 fillRect(114, y, AUDIO_SELECTORS_BOX_WIDTH, 10, PAL_BOXSLCT); // selection background color
221 }
222
223 char *tmpString = utf8ToCp437(audio.inputDeviceNames[deviceEntry], true);
224 if (tmpString != NULL)
225 {
226 textOutClipX(114, y, PAL_FORGRND, tmpString, 114 + AUDIO_SELECTORS_BOX_WIDTH);
227 free(tmpString);
228 }
229 }
230 }
231 }
232
testAudioDeviceListsMouseDown(void)233 bool testAudioDeviceListsMouseDown(void)
234 {
235 if (!ui.configScreenShown || editor.currConfigScreen != CONFIG_SCREEN_IO_DEVICES)
236 return false;
237
238 const int32_t mx = mouse.x;
239 const int32_t my = mouse.y;
240
241 if (my < 18 || my > 149 || mx < 114 || mx >= 114+AUDIO_SELECTORS_BOX_WIDTH)
242 return false;
243
244 if (my < 84)
245 {
246 // output device list
247
248 const int32_t deviceNum = (int32_t)scrollBars[SB_AUDIO_OUTPUT_SCROLL].pos + ((my - 18) / 11);
249 if (audio.outputDeviceNum <= 0 || deviceNum >= audio.outputDeviceNum)
250 return true;
251
252 char *devString = audio.outputDeviceNames[deviceNum];
253 if (devString != NULL && (audio.currOutputDevice == NULL || strcmp(audio.currOutputDevice, devString) != 0))
254 {
255 if (audio.currOutputDevice != NULL)
256 free(audio.currOutputDevice);
257
258 const uint32_t devStringLen = (uint32_t)strlen(devString);
259
260 audio.currOutputDevice = (char *)malloc(devStringLen+1);
261 if (audio.currOutputDevice == NULL)
262 return true;
263
264 audio.currOutputDevice[0] = '\0';
265
266 if (devStringLen > 0)
267 strcpy(audio.currOutputDevice, devString);
268
269 if (!setNewAudioSettings())
270 okBox(0, "System message", "Couldn't open audio input device!");
271 else
272 drawAudioOutputList();
273 }
274
275 return true;
276 }
277 else if (my >= 105)
278 {
279 // input device list
280
281 const int32_t deviceNum = (int32_t)scrollBars[SB_AUDIO_INPUT_SCROLL].pos + ((my - 105) / 11);
282 if (audio.inputDeviceNum <= 0 || deviceNum >= audio.inputDeviceNum)
283 return true;
284
285 char *devString = audio.inputDeviceNames[deviceNum];
286 if (devString != NULL && (audio.currInputDevice == NULL || strcmp(audio.currInputDevice, devString) != 0))
287 {
288 if (audio.currInputDevice != NULL)
289 free(audio.currInputDevice);
290
291 const uint32_t devStringLen = (uint32_t)strlen(devString);
292
293 audio.currInputDevice = (char *)malloc(devStringLen+1);
294 if (audio.currInputDevice == NULL)
295 return true;
296
297 audio.currInputDevice[0] = '\0';
298
299 if (devStringLen > 0)
300 strcpy(audio.currInputDevice, devString);
301
302 drawAudioInputList();
303 }
304
305 return true;
306 }
307
308 return false;
309 }
310
freeAudioDeviceLists(void)311 void freeAudioDeviceLists(void)
312 {
313 for (int32_t i = 0; i < MAX_AUDIO_DEVICES; i++)
314 {
315 if (audio.outputDeviceNames[i] != NULL)
316 {
317 free(audio.outputDeviceNames[i]);
318 audio.outputDeviceNames[i] = NULL;
319 }
320
321 if (audio.inputDeviceNames[i] != NULL)
322 {
323 free(audio.inputDeviceNames[i]);
324 audio.inputDeviceNames[i] = NULL;
325 }
326
327 audio.outputDeviceNum = 0;
328 audio.inputDeviceNum = 0;
329 }
330 }
331
freeAudioDeviceSelectorBuffers(void)332 void freeAudioDeviceSelectorBuffers(void)
333 {
334 if (editor.audioDevConfigFileLocationU != NULL)
335 {
336 free(editor.audioDevConfigFileLocationU);
337 editor.audioDevConfigFileLocationU = NULL;
338 }
339
340 if (audio.currOutputDevice != NULL)
341 {
342 free(audio.currOutputDevice);
343 audio.currOutputDevice = NULL;
344 }
345
346 if (audio.currInputDevice != NULL)
347 {
348 free(audio.currInputDevice);
349 audio.currInputDevice = NULL;
350 }
351
352 if (audio.lastWorkingAudioDeviceName != NULL)
353 {
354 free(audio.lastWorkingAudioDeviceName);
355 audio.lastWorkingAudioDeviceName = NULL;
356 }
357
358 freeAudioDeviceLists();
359 }
360
setToDefaultAudioOutputDevice(void)361 void setToDefaultAudioOutputDevice(void)
362 {
363 if (audio.currOutputDevice != NULL)
364 {
365 free(audio.currOutputDevice);
366 audio.currOutputDevice = NULL;
367 }
368
369 /* If we have more than one device, we can't know which one
370 ** is the default (and thus not get its device name).
371 ** SDL2 ought to have a function for this!
372 */
373 if (SDL_GetNumAudioDevices(false) == 1)
374 {
375 const char *devName = SDL_GetAudioDeviceName(0, false);
376 if (devName != NULL)
377 audio.currOutputDevice = strdup(devName);
378 }
379 }
380
setToDefaultAudioInputDevice(void)381 void setToDefaultAudioInputDevice(void)
382 {
383 if (audio.currInputDevice != NULL)
384 {
385 free(audio.currInputDevice);
386 audio.currInputDevice = NULL;
387 }
388
389 /* If we have more than one device, we can't know which one
390 ** is the default (and thus not get its device name).
391 ** SDL2 ought to have a function for this!
392 */
393 if (SDL_GetNumAudioDevices(true) == 1)
394 {
395 const char *devName = SDL_GetAudioDeviceName(0, true);
396 if (devName != NULL)
397 audio.currInputDevice = strdup(devName);
398 }
399 }
400
rescanAudioDevices(void)401 void rescanAudioDevices(void)
402 {
403 const bool listShown = (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES);
404
405 freeAudioDeviceLists();
406
407 // GET AUDIO OUTPUT DEVICES
408
409 audio.outputDeviceNum = SDL_GetNumAudioDevices(false);
410 if (audio.outputDeviceNum > MAX_AUDIO_DEVICES)
411 audio.outputDeviceNum = MAX_AUDIO_DEVICES;
412
413 for (int32_t i = 0; i < audio.outputDeviceNum; i++)
414 {
415 const char *deviceName = SDL_GetAudioDeviceName(i, false);
416 if (deviceName == NULL)
417 {
418 audio.outputDeviceNum--; // hide device
419 continue;
420 }
421
422 const uint32_t stringLen = (uint32_t)strlen(deviceName);
423
424 audio.outputDeviceNames[i] = (char *)malloc(stringLen + 1);
425 if (audio.outputDeviceNames[i] == NULL)
426 break;
427
428 if (stringLen > 0)
429 strcpy(audio.outputDeviceNames[i], deviceName);
430 }
431
432 // GET AUDIO INPUT DEVICES
433
434 audio.inputDeviceNum = SDL_GetNumAudioDevices(true);
435 if (audio.inputDeviceNum > MAX_AUDIO_DEVICES)
436 audio.inputDeviceNum = MAX_AUDIO_DEVICES;
437
438 for (int32_t i = 0; i < audio.inputDeviceNum; i++)
439 {
440 const char *deviceName = SDL_GetAudioDeviceName(i, true);
441 if (deviceName == NULL)
442 {
443 audio.inputDeviceNum--; // hide device
444 continue;
445 }
446
447 const uint32_t stringLen = (uint32_t)strlen(deviceName);
448
449 audio.inputDeviceNames[i] = (char *)malloc(stringLen + 1);
450 if (audio.inputDeviceNames[i] == NULL)
451 break;
452
453 if (stringLen > 0)
454 strcpy(audio.inputDeviceNames[i], deviceName);
455 }
456
457 setScrollBarEnd(SB_AUDIO_OUTPUT_SCROLL, audio.outputDeviceNum);
458 setScrollBarPos(SB_AUDIO_OUTPUT_SCROLL, 0, false);
459 setScrollBarEnd(SB_AUDIO_INPUT_SCROLL, audio.inputDeviceNum);
460 setScrollBarPos(SB_AUDIO_INPUT_SCROLL, 0, false);
461
462 // DRAW LISTS
463
464 if (listShown)
465 {
466 drawAudioOutputList();
467 drawAudioInputList();
468 drawScrollBar(SB_AUDIO_OUTPUT_SCROLL);
469 drawScrollBar(SB_AUDIO_INPUT_SCROLL);
470 }
471 }
472
scrollAudInputDevListUp(void)473 void scrollAudInputDevListUp(void)
474 {
475 scrollBarScrollUp(SB_AUDIO_INPUT_SCROLL, 1);
476 }
477
scrollAudInputDevListDown(void)478 void scrollAudInputDevListDown(void)
479 {
480 scrollBarScrollDown(SB_AUDIO_INPUT_SCROLL, 1);
481 }
482
scrollAudOutputDevListUp(void)483 void scrollAudOutputDevListUp(void)
484 {
485 scrollBarScrollUp(SB_AUDIO_OUTPUT_SCROLL, 1);
486 }
487
scrollAudOutputDevListDown(void)488 void scrollAudOutputDevListDown(void)
489 {
490 scrollBarScrollDown(SB_AUDIO_OUTPUT_SCROLL, 1);
491 }
492
sbAudOutputSetPos(uint32_t pos)493 void sbAudOutputSetPos(uint32_t pos)
494 {
495 if (ui.configScreenShown && (editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES))
496 drawAudioOutputList();
497
498 (void)pos;
499 }
500
sbAudInputSetPos(uint32_t pos)501 void sbAudInputSetPos(uint32_t pos)
502 {
503 if (ui.configScreenShown && (editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES))
504 drawAudioInputList();
505
506 (void)pos;
507 }
508