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