1 /*
2 ** configuration.cpp
3 ** Handle zmusic's configuration.
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2019 Christoph Oelckers
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 #ifdef _WIN32
36 #include <Windows.h>
37 #include <mmsystem.h>
38 #endif
39 #include <algorithm>
40 #include "critsec.h"
41 #include "dumb.h"
42 
43 #include "zmusic_internal.h"
44 #include "musinfo.h"
45 #include "midiconfig.h"
46 #include "mididevices/music_alsa_state.h"
47 
48 #ifdef HAVE_TIMIDITY
49 #include "timidity/timidity.h"
50 #include "timiditypp/timidity.h"
51 #endif
52 #ifdef HAVE_OPL
53 #include "oplsynth/oplio.h"
54 #endif
55 
56 struct Dummy
57 {
ChangeSettingIntDummy58 	void ChangeSettingInt(const char*, int) {}
ChangeSettingNumDummy59 	void ChangeSettingNum(const char*, double) {}
ChangeSettingStringDummy60 	void ChangeSettingString(const char*, const char*) {}
61 };
62 
63 #define devType() ((currSong)? (currSong)->GetDeviceType() : MDEV_DEFAULT)
64 
65 
66 MiscConfig miscConfig;
67 ZMusicCallbacks musicCallbacks;
68 
69 class SoundFontWrapperInterface : public MusicIO::SoundFontReaderInterface
70 {
71 	void* handle;
72 
73 public:
SoundFontWrapperInterface(void * h)74 	SoundFontWrapperInterface(void* h)
75 	{
76 		handle = h;
77 	}
78 
open_file(const char * fn)79 	struct MusicIO::FileInterface* open_file(const char* fn) override
80 	{
81 		auto rd = musicCallbacks.SF_OpenFile(handle, fn);
82 		if (rd)
83 		{
84 			auto fr = new CustomFileReader(rd);
85 			if (fr) fr->filename = fn? fn : "timidity.cfg";
86 			return fr;
87 		}
88 		else return nullptr;
89 	}
add_search_path(const char * path)90 	void add_search_path(const char* path) override
91 	{
92 		musicCallbacks.SF_AddToSearchPath(handle, path);
93 	}
close()94 	void close() override
95 	{
96 		musicCallbacks.SF_Close(handle);
97 		delete this;
98 	}
99 };
100 
101 namespace MusicIO {
ClientOpenSoundFont(const char * name,int type)102 	SoundFontReaderInterface* ClientOpenSoundFont(const char* name, int type)
103 	{
104 		if (!musicCallbacks.OpenSoundFont) return nullptr;
105 		auto iface = musicCallbacks.OpenSoundFont(name, type);
106 		if (!iface) return nullptr;
107 		return new SoundFontWrapperInterface(iface);
108 	}
109 }
110 
111 
ZMusic_Print(int type,const char * msg,va_list args)112 void ZMusic_Print(int type, const char* msg, va_list args)
113 {
114 	static char printbuf[4096];
115 	vsnprintf(printbuf, 4096, msg, args);
116 	if (musicCallbacks.MessageFunc)
117 	{
118 		musicCallbacks.MessageFunc(type, printbuf);
119 	}
120 	else fputs(printbuf, type >= ZMUSIC_MSG_WARNING ? stderr : stdout);
121 }
122 
ZMusic_Printf(int type,const char * msg,...)123 void ZMusic_Printf(int type, const char* msg, ...)
124 {
125 	va_list ap;
126 	va_start(ap, msg);
127 	ZMusic_Print(type, msg, ap);
128 	va_end(ap);
129 }
130 
ZMusic_SetCallbacks(const ZMusicCallbacks * cb)131 DLL_EXPORT void ZMusic_SetCallbacks(const ZMusicCallbacks* cb)
132 {
133 	musicCallbacks = *cb;
134 	// If not all these are set the sound font interface is not usable.
135 	if (!cb->SF_AddToSearchPath || !cb->SF_OpenFile || !cb->SF_Close)
136 		musicCallbacks.OpenSoundFont = nullptr;
137 }
138 
ZMusic_SetGenMidi(const uint8_t * data)139 DLL_EXPORT void ZMusic_SetGenMidi(const uint8_t* data)
140 {
141 #ifdef HAVE_OPL
142 	memcpy(oplConfig.OPLinstruments, data, 175 * 36);
143 	oplConfig.genmidiset = true;
144 #endif
145 }
146 
ZMusic_SetWgOpn(const void * data,unsigned len)147 DLL_EXPORT void ZMusic_SetWgOpn(const void* data, unsigned len)
148 {
149 #ifdef HAVE_OPN
150 	opnConfig.default_bank.resize(len);
151 	memcpy(opnConfig.default_bank.data(), data, len);
152 #endif
153 }
154 
ZMusic_SetDmxGus(const void * data,unsigned len)155 DLL_EXPORT void ZMusic_SetDmxGus(const void* data, unsigned len)
156 {
157 #ifdef HAVE_GUS
158 	gusConfig.dmxgus.resize(len);
159 	memcpy(gusConfig.dmxgus.data(), data, len);
160 #endif
161 }
162 
ZMusic_EnumerateMidiDevices()163 int ZMusic_EnumerateMidiDevices()
164 {
165 #ifdef HAVE_SYSTEM_MIDI
166 	#ifdef __linux__
167 		auto & sequencer = AlsaSequencer::Get();
168 		return sequencer.EnumerateDevices();
169 	#elif _WIN32
170 		// TODO: move the weird stuff from music_midi_base.cpp here, or at least to this lib and call it here
171 		return {};
172 	#endif
173 #else
174 	return {};
175 #endif
176 }
177 
178 
179 struct MidiDeviceList
180 {
181 	std::vector<ZMusicMidiOutDevice> devices;
~MidiDeviceListMidiDeviceList182 	~MidiDeviceList()
183 	{
184 		for (auto& device : devices)
185 		{
186 			free(device.Name);
187 		}
188 	}
BuildMidiDeviceList189 	void Build()
190 	{
191 #ifdef HAVE_OPN
192 		devices.push_back({ strdup("libOPN"), -8, MIDIDEV_FMSYNTH });
193 #endif
194 #ifdef HAVE_ADL
195 		devices.push_back({ strdup("libADL"), -7, MIDIDEV_FMSYNTH });
196 #endif
197 #ifdef HAVE_WILDMIDI
198 		devices.push_back({ strdup("WildMidi"), -6, MIDIDEV_SWSYNTH });
199 #endif
200 #ifdef HAVE_FLUIDSYNTH
201 		devices.push_back({ strdup("FluidSynth"), -5, MIDIDEV_SWSYNTH });
202 #endif
203 #ifdef HAVE_GUS
204 		devices.push_back({ strdup("GUS Emulation"), -4, MIDIDEV_SWSYNTH });
205 #endif
206 #ifdef HAVE_OPL
207 		devices.push_back({ strdup("OPL Synth Emulation"), -3, MIDIDEV_FMSYNTH });
208 #endif
209 #ifdef HAVE_TIMIDITY
210 		devices.push_back({ strdup("TiMidity++"), -2, MIDIDEV_SWSYNTH });
211 #endif
212 
213 #ifdef HAVE_SYSTEM_MIDI
214 #ifdef __linux__
215 		auto& sequencer = AlsaSequencer::Get();
216 		sequencer.EnumerateDevices();
217 		auto& dev = sequencer.GetInternalDevices();
218 		for (auto& d : dev)
219 		{
220 			ZMusicMidiOutDevice mdev = { strdup(d.Name.c_str()), d.ID, MIDIDEV_MAPPER };	// fixme: Correctly determine the type of the device.
221 			devices.push_back(mdev);
222 		}
223 #elif _WIN32
224 		UINT nummididevices = midiOutGetNumDevs();
225 		for (uint32_t id = 0; id < nummididevices; ++id)
226 		{
227 			MIDIOUTCAPSW caps;
228 			MMRESULT res;
229 
230 			res = midiOutGetDevCapsW(id, &caps, sizeof(caps));
231 			if (res == MMSYSERR_NOERROR)
232 			{
233 				auto len = wcslen(caps.szPname);
234 				int size_needed = WideCharToMultiByte(CP_UTF8, 0, caps.szPname, (int)len, nullptr, 0, nullptr, nullptr);
235 				char* outbuf = (char*)malloc(size_needed + 1);
236 				WideCharToMultiByte(CP_UTF8, 0, caps.szPname, (int)len, outbuf, size_needed, nullptr, nullptr);
237 				outbuf[size_needed] = 0;
238 
239 				ZMusicMidiOutDevice mdev = { outbuf, int(id), caps.wTechnology };
240 				devices.push_back(mdev);
241 			}
242 		}
243 #endif
244 #endif
245 	}
246 
247 };
248 
249 static MidiDeviceList devlist;
250 
ZMusic_GetMidiDevices(int * pAmount)251 DLL_EXPORT const ZMusicMidiOutDevice* ZMusic_GetMidiDevices(int* pAmount)
252 {
253 	if (devlist.devices.size() == 0) devlist.Build();
254 	if (pAmount) *pAmount = (int)devlist.devices.size();
255 	return devlist.devices.data();
256 }
257 
258 
259 
260 template<class valtype>
ChangeAndReturn(valtype & variable,valtype value,valtype * realv)261 void ChangeAndReturn(valtype &variable, valtype value, valtype *realv)
262 {
263 	variable = value;
264 	if (realv) *realv = value;
265 }
266 
267 #define FLUID_CHORUS_MOD_SINE		0
268 #define FLUID_CHORUS_MOD_TRIANGLE	1
269 #define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE
270 
271 #ifdef HAVE_TIMIDITY
272 
273 //==========================================================================
274 //
275 // Timidity++ uses a static global set of configuration variables.
276 // THese can be changed live while the synth is playing but need synchronization.
277 //
278 // Currently the synth is not reentrant due to this and a handful
279 // of other global variables.
280 //
281 //==========================================================================
282 
ChangeVarSync(T & var,T value)283 template<class T> void ChangeVarSync(T& var, T value)
284 {
285 	std::lock_guard<FCriticalSection> lock(TimidityPlus::ConfigMutex);
286 	var = value;
287 }
288 
289 //==========================================================================
290 //
291 // Timidity++ reverb is a bit more complicated because it is two properties in one value.
292 //
293 //==========================================================================
294 
295 /*
296 * reverb=0     no reverb                 0
297 * reverb=1     old reverb                1
298 * reverb=1,n   set reverb level to n   (-1 to -127)
299 * reverb=2     "global" old reverb       2
300 * reverb=2,n   set reverb level to n   (-1 to -127) - 128
301 * reverb=3     new reverb                3
302 * reverb=3,n   set reverb level to n   (-1 to -127) - 256
303 * reverb=4     "global" new reverb       4
304 * reverb=4,n   set reverb level to n   (-1 to -127) - 384
305 */
306 static int local_timidity_reverb_level;
307 static int local_timidity_reverb;
308 
TimidityPlus_SetReverb()309 static void TimidityPlus_SetReverb()
310 {
311 	int value = 0;
312 	int mode = local_timidity_reverb;
313 	int level = local_timidity_reverb_level;
314 
315 	if (mode == 0 || level == 0) value = mode;
316 	else value = (mode - 1) * -128 - level;
317 	ChangeVarSync(TimidityPlus::timidity_reverb, value);
318 }
319 #endif
320 
321 
322 //==========================================================================
323 //
324 // change an integer value
325 //
326 //==========================================================================
327 
ChangeMusicSettingInt(EIntConfigKey key,MusInfo * currSong,int value,int * pRealValue)328 DLL_EXPORT zmusic_bool ChangeMusicSettingInt(EIntConfigKey key, MusInfo *currSong, int value, int *pRealValue)
329 {
330 	switch (key)
331 	{
332 		default:
333 			return false;
334 
335 #ifdef HAVE_ADL
336 		case zmusic_adl_chips_count:
337 			ChangeAndReturn(adlConfig.adl_chips_count, value, pRealValue);
338 			return devType() == MDEV_ADL;
339 
340 		case zmusic_adl_emulator_id:
341 			ChangeAndReturn(adlConfig.adl_emulator_id, value, pRealValue);
342 			return devType() == MDEV_ADL;
343 
344 		case zmusic_adl_run_at_pcm_rate:
345 			ChangeAndReturn(adlConfig.adl_run_at_pcm_rate, value, pRealValue);
346 			return devType() == MDEV_ADL;
347 
348 		case zmusic_adl_fullpan:
349 			ChangeAndReturn(adlConfig.adl_fullpan, value, pRealValue);
350 			return devType() == MDEV_ADL;
351 
352 		case zmusic_adl_bank:
353 			ChangeAndReturn(adlConfig.adl_bank, value, pRealValue);
354 			return devType() == MDEV_ADL;
355 
356 		case zmusic_adl_use_custom_bank:
357 			ChangeAndReturn(adlConfig.adl_use_custom_bank, value, pRealValue);
358 			return devType() == MDEV_ADL;
359 
360 		case zmusic_adl_volume_model:
361 			ChangeAndReturn(adlConfig.adl_volume_model, value, pRealValue);
362 			return devType() == MDEV_ADL;
363 #endif
364 
365 		case zmusic_fluid_reverb:
366 			if (currSong != NULL)
367 				currSong->ChangeSettingInt("fluidsynth.synth.reverb.active", value);
368 
369 			ChangeAndReturn(fluidConfig.fluid_reverb, value, pRealValue);
370 			return false;
371 
372 		case zmusic_fluid_chorus:
373 			if (currSong != NULL)
374 				currSong->ChangeSettingInt("fluidsynth.synth.chorus.active", value);
375 
376 			ChangeAndReturn(fluidConfig.fluid_chorus, value, pRealValue);
377 			return false;
378 
379 		case zmusic_fluid_voices:
380 			if (value < 16)
381 				value = 16;
382 			else if (value > 4096)
383 				value = 4096;
384 
385 			if (currSong != NULL)
386 				currSong->ChangeSettingInt("fluidsynth.synth.polyphony", value);
387 
388 			ChangeAndReturn(fluidConfig.fluid_voices, value, pRealValue);
389 			return false;
390 
391 		case zmusic_fluid_interp:
392 			// Values are: 0 = FLUID_INTERP_NONE
393 			//             1 = FLUID_INTERP_LINEAR
394 			//             4 = FLUID_INTERP_4THORDER (the FluidSynth default)
395 			//             7 = FLUID_INTERP_7THORDER
396 			// (And here I thought it was just a linear list.)
397 			// Round undefined values to the nearest valid one.
398 			if (value < 0)
399 				value = 0;
400 			else if (value == 2)
401 				value = 1;
402 			else if (value == 3 || value == 5)
403 				value = 4;
404 			else if (value == 6 || value > 7)
405 				value = 7;
406 
407 			if (currSong != NULL)
408 				currSong->ChangeSettingInt("fluidsynth.synth.interpolation", value);
409 
410 			ChangeAndReturn(fluidConfig.fluid_interp, value, pRealValue);
411 			return false;
412 
413 		case zmusic_fluid_samplerate:
414 			// This will only take effect for the next song. (Q: Is this even needed?)
415 			ChangeAndReturn(fluidConfig.fluid_samplerate, std::max<int>(value, 0), pRealValue);
416 			return false;
417 
418 		// I don't know if this setting even matters for us, since we aren't letting
419 		// FluidSynth drives its own output.
420 		case zmusic_fluid_threads:
421 			if (value < 1)
422 				value = 1;
423 			else if (value > 256)
424 				value = 256;
425 
426 			ChangeAndReturn(fluidConfig.fluid_threads, value, pRealValue);
427 			return false;
428 
429 		case zmusic_fluid_chorus_voices:
430 			if (value < 0)
431 				value = 0;
432 			else if (value > 99)
433 				value = 99;
434 
435 			if (currSong != NULL)
436 				currSong->ChangeSettingNum("fluidsynth.z.chorus", value);
437 
438 			ChangeAndReturn(fluidConfig.fluid_chorus_voices, value, pRealValue);
439 			return false;
440 
441 		case zmusic_fluid_chorus_type:
442 			if (value != FLUID_CHORUS_MOD_SINE && value != FLUID_CHORUS_MOD_TRIANGLE)
443 				value = FLUID_CHORUS_DEFAULT_TYPE;
444 
445 			if (currSong != NULL)
446 				currSong->ChangeSettingNum("fluidsynth.z.chorus", value); // Uses float to simplify the checking code in the renderer.
447 
448 			ChangeAndReturn(fluidConfig.fluid_chorus_type, value, pRealValue);
449 			return false;
450 
451 #ifdef HAVE_OPL
452 		case zmusic_opl_numchips:
453 			if (value <= 0)
454 				value = 1;
455 			else if (value > MAXOPL2CHIPS)
456 				value = MAXOPL2CHIPS;
457 
458 			if (currSong != NULL)
459 				currSong->ChangeSettingInt("opl.numchips", value);
460 
461 			ChangeAndReturn(oplConfig.numchips, value, pRealValue);
462 			return false;
463 
464 		case zmusic_opl_core:
465 			if (value < 0) value = 0;
466 			else if (value > 3) value = 3;
467 			ChangeAndReturn(oplConfig.core, value, pRealValue);
468 			return devType() == MDEV_OPL;
469 
470 		case zmusic_opl_fullpan:
471 			ChangeAndReturn(oplConfig.fullpan, value, pRealValue);
472 			return false;
473 #endif
474 #ifdef HAVE_OPN
475 		case zmusic_opn_chips_count:
476 			ChangeAndReturn(opnConfig.opn_chips_count, value, pRealValue);
477 			return devType() == MDEV_OPN;
478 
479 		case zmusic_opn_emulator_id:
480 			ChangeAndReturn(opnConfig.opn_emulator_id, value, pRealValue);
481 			return devType() == MDEV_OPN;
482 
483 		case zmusic_opn_run_at_pcm_rate:
484 			ChangeAndReturn(opnConfig.opn_run_at_pcm_rate, value, pRealValue);
485 			return devType() == MDEV_OPN;
486 
487 		case zmusic_opn_fullpan:
488 			ChangeAndReturn(opnConfig.opn_fullpan, value, pRealValue);
489 			return devType() == MDEV_OPN;
490 
491 		case zmusic_opn_use_custom_bank:
492 			ChangeAndReturn(opnConfig.opn_use_custom_bank, value, pRealValue);
493 			return devType() == MDEV_OPN;
494 #endif
495 #ifdef HAVE_GUS
496 		case zmusic_gus_dmxgus:
497 			ChangeAndReturn(gusConfig.gus_dmxgus, value, pRealValue);
498 			return devType() == MDEV_GUS;
499 
500 		case zmusic_gus_midi_voices:
501 			ChangeAndReturn(gusConfig.midi_voices, value, pRealValue);
502 			return devType() == MDEV_GUS;
503 
504 		case zmusic_gus_memsize:
505 			ChangeAndReturn(gusConfig.gus_memsize, value, pRealValue);
506 			return devType() == MDEV_GUS && gusConfig.gus_dmxgus;
507 #endif
508 #ifdef HAVE_TIMIDITY
509 		case zmusic_timidity_modulation_wheel:
510 			ChangeVarSync(TimidityPlus::timidity_modulation_wheel, value);
511 			if (pRealValue) *pRealValue = value;
512 			return false;
513 
514 		case zmusic_timidity_portamento:
515 			ChangeVarSync(TimidityPlus::timidity_portamento, value);
516 			if (pRealValue) *pRealValue = value;
517 			return false;
518 
519 		case zmusic_timidity_reverb:
520 			if (value < 0 || value > 4) value = 0;
521 			else TimidityPlus_SetReverb();
522 			local_timidity_reverb = value;
523 			if (pRealValue) *pRealValue = value;
524 			return false;
525 
526 		case zmusic_timidity_reverb_level:
527 			if (value < 0 || value > 127) value = 0;
528 			else TimidityPlus_SetReverb();
529 			local_timidity_reverb_level = value;
530 			if (pRealValue) *pRealValue = value;
531 			return false;
532 
533 		case zmusic_timidity_chorus:
534 			ChangeVarSync(TimidityPlus::timidity_chorus, value);
535 			if (pRealValue) *pRealValue = value;
536 			return false;
537 
538 		case zmusic_timidity_surround_chorus:
539 			ChangeVarSync(TimidityPlus::timidity_surround_chorus, value);
540 			if (pRealValue) *pRealValue = value;
541 			return devType() == MDEV_TIMIDITY;
542 
543 		case zmusic_timidity_channel_pressure:
544 			ChangeVarSync(TimidityPlus::timidity_channel_pressure, value);
545 			if (pRealValue) *pRealValue = value;
546 			return false;
547 
548 		case zmusic_timidity_lpf_def:
549 			ChangeVarSync(TimidityPlus::timidity_lpf_def, value);
550 			if (pRealValue) *pRealValue = value;
551 			return false;
552 
553 		case zmusic_timidity_temper_control:
554 			ChangeVarSync(TimidityPlus::timidity_temper_control, value);
555 			if (pRealValue) *pRealValue = value;
556 			return false;
557 
558 		case zmusic_timidity_modulation_envelope:
559 			ChangeVarSync(TimidityPlus::timidity_modulation_envelope, value);
560 			if (pRealValue) *pRealValue = value;
561 			return devType() == MDEV_TIMIDITY;
562 
563 		case zmusic_timidity_overlap_voice_allow:
564 			ChangeVarSync(TimidityPlus::timidity_overlap_voice_allow, value);
565 			if (pRealValue) *pRealValue = value;
566 			return false;
567 
568 		case zmusic_timidity_drum_effect:
569 			ChangeVarSync(TimidityPlus::timidity_drum_effect, value);
570 			if (pRealValue) *pRealValue = value;
571 			return false;
572 
573 		case zmusic_timidity_pan_delay:
574 			ChangeVarSync(TimidityPlus::timidity_pan_delay, value);
575 			if (pRealValue) *pRealValue = value;
576 			return false;
577 
578 		case zmusic_timidity_key_adjust:
579 			if (value < -24) value = -24;
580 			else if (value > 24) value = 24;
581 			ChangeVarSync(TimidityPlus::timidity_key_adjust, value);
582 			if (pRealValue) *pRealValue = value;
583 			return false;
584 #endif
585 #ifdef HAVE_WILDMIDI
586 		case zmusic_wildmidi_reverb:
587 			if (currSong != NULL)
588 				currSong->ChangeSettingInt("wildmidi.reverb", value);
589 			wildMidiConfig.reverb = value;
590 			if (pRealValue) *pRealValue = value;
591 			return false;
592 
593 		case zmusic_wildmidi_enhanced_resampling:
594 			if (currSong != NULL)
595 				currSong->ChangeSettingInt("wildmidi.resampling", value);
596 			wildMidiConfig.enhanced_resampling = value;
597 			if (pRealValue) *pRealValue = value;
598 			return false;
599 #endif
600 		case zmusic_snd_midiprecache:
601 			ChangeAndReturn(miscConfig.snd_midiprecache, value, pRealValue);
602 			return false;
603 
604 		case zmusic_snd_streambuffersize:
605 			if (value < 16)
606 			{
607 				value = 16;
608 			}
609 			else if (value > 1024)
610 			{
611 				value = 1024;
612 			}
613 			ChangeAndReturn(miscConfig.snd_streambuffersize, value, pRealValue);
614 			return false;
615 
616 		case zmusic_mod_samplerate:
617 			ChangeAndReturn(dumbConfig.mod_samplerate, value, pRealValue);
618 			return false;
619 
620 		case zmusic_mod_volramp:
621 			ChangeAndReturn(dumbConfig.mod_volramp, value, pRealValue);
622 			return false;
623 
624 		case zmusic_mod_interp:
625 			ChangeAndReturn(dumbConfig.mod_interp, value, pRealValue);
626 			return false;
627 
628 		case zmusic_mod_autochip:
629 			ChangeAndReturn(dumbConfig.mod_autochip, value, pRealValue);
630 			return false;
631 
632 		case zmusic_mod_autochip_size_force:
633 			ChangeAndReturn(dumbConfig.mod_autochip_size_force, value, pRealValue);
634 			return false;
635 
636 		case zmusic_mod_autochip_size_scan:
637 			ChangeAndReturn(dumbConfig.mod_autochip_size_scan, value, pRealValue);
638 			return false;
639 
640 		case zmusic_mod_autochip_scan_threshold:
641 			ChangeAndReturn(dumbConfig.mod_autochip_scan_threshold, value, pRealValue);
642 			return false;
643 
644 		case zmusic_snd_mididevice:
645 		{
646 			bool change = miscConfig.snd_mididevice != value;
647 			miscConfig.snd_mididevice = value;
648 			return change;
649 		}
650 
651 		case zmusic_snd_outputrate:
652 			miscConfig.snd_outputrate = value;
653 			return false;
654 
655 	}
656 	return false;
657 }
658 
ChangeMusicSettingFloat(EFloatConfigKey key,MusInfo * currSong,float value,float * pRealValue)659 DLL_EXPORT zmusic_bool ChangeMusicSettingFloat(EFloatConfigKey key, MusInfo* currSong, float value, float *pRealValue)
660 {
661 	switch (key)
662 	{
663 		default:
664 			return false;
665 
666 		case zmusic_fluid_gain:
667 			if (value < 0)
668 				value = 0;
669 			else if (value > 10)
670 				value = 10;
671 
672 			if (currSong != NULL)
673 				currSong->ChangeSettingNum("fluidsynth.synth.gain", value);
674 
675 			ChangeAndReturn(fluidConfig.fluid_gain, value, pRealValue);
676 			return false;
677 
678 		case zmusic_fluid_reverb_roomsize:
679 			if (value < 0)
680 				value = 0;
681 			else if (value > 1.2f)
682 				value = 1.2f;
683 
684 			if (currSong != NULL)
685 				currSong->ChangeSettingNum("fluidsynth.z.reverb", value);
686 
687 			ChangeAndReturn(fluidConfig.fluid_reverb_roomsize, value, pRealValue);
688 			return false;
689 
690 		case zmusic_fluid_reverb_damping:
691 			if (value < 0)
692 				value = 0;
693 			else if (value > 1)
694 				value = 1;
695 
696 			if (currSong != NULL)
697 				currSong->ChangeSettingNum("fluidsynth.z.reverb", value);
698 
699 			ChangeAndReturn(fluidConfig.fluid_reverb_damping, value, pRealValue);
700 			return false;
701 
702 		case zmusic_fluid_reverb_width:
703 			if (value < 0)
704 				value = 0;
705 			else if (value > 100)
706 				value = 100;
707 
708 			if (currSong != NULL)
709 				currSong->ChangeSettingNum("fluidsynth.z.reverb", value);
710 
711 			ChangeAndReturn(fluidConfig.fluid_reverb_width, value, pRealValue);
712 			return false;
713 
714 		case zmusic_fluid_reverb_level:
715 			if (value < 0)
716 				value = 0;
717 			else if (value > 1)
718 				value = 1;
719 
720 			if (currSong != NULL)
721 				currSong->ChangeSettingNum("fluidsynth.z.reverb", value);
722 
723 			ChangeAndReturn(fluidConfig.fluid_reverb_level, value, pRealValue);
724 			return false;
725 
726 		case zmusic_fluid_chorus_level:
727 			if (value < 0)
728 				value = 0;
729 			else if (value > 1)
730 				value = 1;
731 
732 			if (currSong != NULL)
733 				currSong->ChangeSettingNum("fluidsynth.z.chorus", value);
734 
735 			ChangeAndReturn(fluidConfig.fluid_chorus_level, value, pRealValue);
736 			return false;
737 
738 		case zmusic_fluid_chorus_speed:
739 			if (value < 0.29f)
740 				value = 0.29f;
741 			else if (value > 5)
742 				value = 5;
743 
744 			if (currSong != NULL)
745 				currSong->ChangeSettingNum("fluidsynth.z.chorus", value);
746 
747 			ChangeAndReturn(fluidConfig.fluid_chorus_speed, value, pRealValue);
748 			return false;
749 
750 		// depth is in ms and actual maximum depends on the sample rate
751 		case zmusic_fluid_chorus_depth:
752 			if (value < 0)
753 				value = 0;
754 			else if (value > 21)
755 				value = 21;
756 
757 			if (currSong != NULL)
758 				currSong->ChangeSettingNum("fluidsynth.z.chorus", value);
759 
760 			ChangeAndReturn(fluidConfig.fluid_chorus_depth, value, pRealValue);
761 			return false;
762 
763 #ifdef HAVE_TIMIDITY
764 		case zmusic_timidity_drum_power:
765 			if (value < 0) value = 0;
766 			else if (value > MAX_AMPLIFICATION / 100.f) value = MAX_AMPLIFICATION / 100.f;
767 			ChangeVarSync(TimidityPlus::timidity_drum_power, value);
768 			if (pRealValue) *pRealValue = value;
769 			return false;
770 
771 		// For testing mainly.
772 		case zmusic_timidity_tempo_adjust:
773 			if (value < 0.25) value = 0.25;
774 			else if (value > 10) value = 10;
775 			ChangeVarSync(TimidityPlus::timidity_tempo_adjust, value);
776 			if (pRealValue) *pRealValue = value;
777 			return false;
778 
779 		case zmusic_timidity_min_sustain_time:
780 			if (value < 0) value = 0;
781 			ChangeVarSync(TimidityPlus::min_sustain_time, value);
782 			if (pRealValue) *pRealValue = value;
783 			return false;
784 #endif
785 
786 		case zmusic_gme_stereodepth:
787 			if (currSong != nullptr)
788 				currSong->ChangeSettingNum("GME.stereodepth", value);
789 			ChangeAndReturn(miscConfig.gme_stereodepth, value, pRealValue);
790 			return false;
791 
792 		case zmusic_mod_dumb_mastervolume:
793 			if (value < 0) value = 0;
794 			ChangeAndReturn(dumbConfig.mod_dumb_mastervolume, value, pRealValue);
795 			return false;
796 
797 		case zmusic_snd_musicvolume:
798 			miscConfig.snd_musicvolume = value;
799 			return false;
800 
801 		case zmusic_relative_volume:
802 			miscConfig.relative_volume = value;
803 			return false;
804 
805 		case zmusic_snd_mastervolume:
806 			miscConfig.snd_mastervolume = value;
807 			return false;
808 
809 	}
810 	return false;
811 }
812 
ChangeMusicSettingString(EStringConfigKey key,MusInfo * currSong,const char * value)813 DLL_EXPORT zmusic_bool ChangeMusicSettingString(EStringConfigKey key, MusInfo* currSong, const char *value)
814 {
815 	switch (key)
816 	{
817 		default:
818 			return false;
819 
820 #ifdef HAVE_ADL
821 		case zmusic_adl_custom_bank:
822 			adlConfig.adl_custom_bank = value;
823 			return devType() == MDEV_ADL;
824 #endif
825 		case zmusic_fluid_lib:
826 			fluidConfig.fluid_lib = value;
827 			return false; // only takes effect for next song.
828 
829 		case zmusic_fluid_patchset:
830 			fluidConfig.fluid_patchset = value;
831 #ifdef HAVE_TIMIDITY
832 			if (timidityConfig.timidity_config.empty()) timidityConfig.timidity_config = value; // Also use for Timidity++ if nothing has been set.
833 #endif
834 			return devType() == MDEV_FLUIDSYNTH;
835 
836 #ifdef HAVE_OPN
837 		case zmusic_opn_custom_bank:
838 			opnConfig.opn_custom_bank = value;
839 			return devType() == MDEV_OPN && opnConfig.opn_use_custom_bank;
840 #endif
841 #ifdef HAVE_GUS
842 		case zmusic_gus_config:
843 			gusConfig.gus_config = value;
844 			return devType() == MDEV_GUS;
845 #endif
846 #ifdef HAVE_GUS
847 		case zmusic_gus_patchdir:
848 			gusConfig.gus_patchdir = value;
849 			return devType() == MDEV_GUS && gusConfig.gus_dmxgus;
850 #endif
851 #ifdef HAVE_TIMIDITY
852 		case zmusic_timidity_config:
853 			timidityConfig.timidity_config = value;
854 			return devType() == MDEV_TIMIDITY;
855 #endif
856 #ifdef HAVE_WILDMIDI
857 		case zmusic_wildmidi_config:
858 			wildMidiConfig.config = value;
859 			return devType() == MDEV_WILDMIDI;
860 #endif
861 	}
862 	return false;
863 }
864 
865 static ZMusicConfigurationSetting config[] = {
866 #ifdef HAVE_ADL
867 	{"zmusic_adl_chips_count", zmusic_adl_chips_count, ZMUSIC_VAR_INT, 5},
868 	{"zmusic_adl_emulator_id", zmusic_adl_emulator_id, ZMUSIC_VAR_INT, 0},
869 	{"zmusic_adl_run_at_pcm_rate", zmusic_adl_run_at_pcm_rate, ZMUSIC_VAR_BOOL, 0},
870 	{"zmusic_adl_fullpan", zmusic_adl_fullpan, ZMUSIC_VAR_BOOL, 1},
871 	{"zmusic_adl_bank", zmusic_adl_bank, ZMUSIC_VAR_INT, 14},
872 	{"zmusic_adl_use_custom_bank", zmusic_adl_use_custom_bank, ZMUSIC_VAR_BOOL, 0},
873 	{"zmusic_adl_volume_model", zmusic_adl_volume_model, ZMUSIC_VAR_INT, 3},
874 	{"zmusic_adl_custom_bank", zmusic_adl_custom_bank, ZMUSIC_VAR_STRING, 0},
875 #endif
876 	{"zmusic_fluid_reverb", zmusic_fluid_reverb, ZMUSIC_VAR_BOOL, 0},
877 	{"zmusic_fluid_chorus", zmusic_fluid_chorus, ZMUSIC_VAR_BOOL, 0},
878 	{"zmusic_fluid_voices", zmusic_fluid_voices, ZMUSIC_VAR_INT, 128},
879 	{"zmusic_fluid_interp", zmusic_fluid_interp, ZMUSIC_VAR_INT, 1},
880 	{"zmusic_fluid_samplerate", zmusic_fluid_samplerate, ZMUSIC_VAR_INT, 0},
881 	{"zmusic_fluid_threads", zmusic_fluid_threads, ZMUSIC_VAR_INT, 1},
882 	{"zmusic_fluid_chorus_voices", zmusic_fluid_chorus_voices, ZMUSIC_VAR_INT, 3},
883 	{"zmusic_fluid_chorus_type", zmusic_fluid_chorus_type, ZMUSIC_VAR_INT, 0},
884 	{"zmusic_fluid_gain", zmusic_fluid_gain, ZMUSIC_VAR_FLOAT, 0},
885 	{"zmusic_fluid_reverb_roomsize", zmusic_fluid_reverb_roomsize, ZMUSIC_VAR_FLOAT, 0.75f},
886 	{"zmusic_fluid_reverb_damping", zmusic_fluid_reverb_damping, ZMUSIC_VAR_FLOAT, 0.23f},
887 	{"zmusic_fluid_reverb_width", zmusic_fluid_reverb_width, ZMUSIC_VAR_FLOAT, 0.75f},
888 	{"zmusic_fluid_reverb_level", zmusic_fluid_reverb_level, ZMUSIC_VAR_FLOAT, 0.57f},
889 	{"zmusic_fluid_chorus_level", zmusic_fluid_chorus_level, ZMUSIC_VAR_FLOAT, 1.2f},
890 	{"zmusic_fluid_chorus_speed", zmusic_fluid_chorus_speed, ZMUSIC_VAR_FLOAT, 0.3f},
891 	{"zmusic_fluid_chorus_depth", zmusic_fluid_chorus_depth, ZMUSIC_VAR_FLOAT, 8},
892 	{"zmusic_fluid_lib", zmusic_fluid_lib, ZMUSIC_VAR_STRING, 0},
893 #ifdef HAVE_OPL
894 	{"zmusic_opl_numchips", zmusic_opl_numchips, ZMUSIC_VAR_INT, 2},
895 	{"zmusic_opl_core", zmusic_opl_core, ZMUSIC_VAR_INT, 0},
896 	{"zmusic_opl_fullpan", zmusic_opl_fullpan, ZMUSIC_VAR_BOOL, 1},
897 #endif
898 #ifdef HAVE_OPN
899 	{"zmusic_opn_chips_count", zmusic_opn_chips_count, ZMUSIC_VAR_INT, 8},
900 	{"zmusic_opn_emulator_id", zmusic_opn_emulator_id, ZMUSIC_VAR_INT, 0},
901 	{"zmusic_opn_run_at_pcm_rate", zmusic_opn_run_at_pcm_rate, ZMUSIC_VAR_BOOL, 0},
902 	{"zmusic_opn_fullpan", zmusic_opn_fullpan, ZMUSIC_VAR_BOOL, 2},
903 	{"zmusic_opn_use_custom_bank", zmusic_opn_use_custom_bank, ZMUSIC_VAR_BOOL, 0},
904 	{"zmusic_opn_custom_bank", zmusic_opn_custom_bank, ZMUSIC_VAR_STRING, 0},
905 #endif
906 #ifdef HAVE_GUS
907 	{"zmusic_gus_dmxgus", zmusic_gus_dmxgus, ZMUSIC_VAR_BOOL, 0},
908 	{"zmusic_gus_midi_voices", zmusic_gus_midi_voices, ZMUSIC_VAR_INT, 32},
909 	{"zmusic_gus_memsize", zmusic_gus_memsize, ZMUSIC_VAR_INT, 0},
910 	{"zmusic_gus_config", zmusic_gus_config, ZMUSIC_VAR_STRING, 0},
911 	{"zmusic_gus_patchdir", zmusic_gus_patchdir, ZMUSIC_VAR_STRING, 0},
912 #endif
913 #ifdef HAVE_TIMIDITY
914 	{"zmusic_timidity_modulation_wheel", zmusic_timidity_modulation_wheel, ZMUSIC_VAR_BOOL, 1},
915 	{"zmusic_timidity_portamento", zmusic_timidity_portamento, ZMUSIC_VAR_BOOL, 0},
916 	{"zmusic_timidity_reverb", zmusic_timidity_reverb, ZMUSIC_VAR_INT, 0},
917 	{"zmusic_timidity_reverb_level", zmusic_timidity_reverb_level, ZMUSIC_VAR_INT, 0},
918 	{"zmusic_timidity_chorus", zmusic_timidity_chorus, ZMUSIC_VAR_BOOL, 0},
919 	{"zmusic_timidity_surround_chorus", zmusic_timidity_surround_chorus, ZMUSIC_VAR_BOOL, 0},
920 	{"zmusic_timidity_channel_pressure", zmusic_timidity_channel_pressure, ZMUSIC_VAR_BOOL, 0},
921 	{"zmusic_timidity_lpf_def", zmusic_timidity_lpf_def, ZMUSIC_VAR_BOOL, 1},
922 	{"zmusic_timidity_temper_control", zmusic_timidity_temper_control, ZMUSIC_VAR_BOOL, 1},
923 	{"zmusic_timidity_modulation_envelope", zmusic_timidity_modulation_envelope, ZMUSIC_VAR_BOOL, 1},
924 	{"zmusic_timidity_overlap_voice_allow", zmusic_timidity_overlap_voice_allow, ZMUSIC_VAR_BOOL, 1},
925 	{"zmusic_timidity_drum_effect", zmusic_timidity_drum_effect, ZMUSIC_VAR_BOOL, 0},
926 	{"zmusic_timidity_pan_delay", zmusic_timidity_pan_delay, ZMUSIC_VAR_BOOL, 0},
927 	{"zmusic_timidity_key_adjust", zmusic_timidity_key_adjust, ZMUSIC_VAR_INT, 0},
928 	{"zmusic_timidity_drum_power", zmusic_timidity_drum_power, ZMUSIC_VAR_FLOAT, 1},
929 	{"zmusic_timidity_tempo_adjust", zmusic_timidity_tempo_adjust, ZMUSIC_VAR_FLOAT, 1},
930 	{"zmusic_timidity_min_sustain_time", zmusic_timidity_min_sustain_time, ZMUSIC_VAR_FLOAT, 5000},
931 	{"zmusic_timidity_config", zmusic_timidity_config, ZMUSIC_VAR_STRING, 0},
932 #endif
933 #ifdef HAVE_WILDMIDI
934 	{"zmusic_wildmidi_reverb", zmusic_wildmidi_reverb, ZMUSIC_VAR_BOOL, 0},
935 	{"zmusic_wildmidi_enhanced_resampling", zmusic_wildmidi_enhanced_resampling, ZMUSIC_VAR_BOOL, 1},
936 	{"zmusic_wildmidi_config", zmusic_wildmidi_config, ZMUSIC_VAR_STRING, 0},
937 #endif
938 	{"zmusic_mod_samplerate", zmusic_mod_samplerate, ZMUSIC_VAR_INT, 0},
939 	{"zmusic_mod_volramp", zmusic_mod_volramp, ZMUSIC_VAR_INT, 2},
940 	{"zmusic_mod_interp", zmusic_mod_interp, ZMUSIC_VAR_BOOL, 2},
941 	{"zmusic_mod_autochip", zmusic_mod_autochip, ZMUSIC_VAR_BOOL, 0},
942 	{"zmusic_mod_autochip_size_force", zmusic_mod_autochip_size_force, ZMUSIC_VAR_INT, 100},
943 	{"zmusic_mod_autochip_size_scan", zmusic_mod_autochip_size_scan, ZMUSIC_VAR_INT, 500},
944 	{"zmusic_mod_autochip_scan_threshold", zmusic_mod_autochip_scan_threshold, ZMUSIC_VAR_INT, 12},
945 	{"zmusic_mod_dumb_mastervolume", zmusic_mod_dumb_mastervolume, ZMUSIC_VAR_FLOAT, 1},
946 
947 	{"zmusic_gme_stereodepth", zmusic_gme_stereodepth, ZMUSIC_VAR_FLOAT, 0},
948 
949 	{"zmusic_snd_midiprecache", zmusic_snd_midiprecache, ZMUSIC_VAR_BOOL, 1},
950 	{"zmusic_snd_streambuffersize", zmusic_snd_streambuffersize, ZMUSIC_VAR_INT, 64},
951 	{"zmusic_snd_mididevice", zmusic_snd_mididevice, ZMUSIC_VAR_INT, 0},
952 	{"zmusic_snd_outputrate", zmusic_snd_outputrate, ZMUSIC_VAR_INT, 44100},
953 	{"zmusic_snd_musicvolume", zmusic_snd_musicvolume, ZMUSIC_VAR_FLOAT, 1},
954 	{"zmusic_relative_volume", zmusic_relative_volume, ZMUSIC_VAR_FLOAT, 1},
955 	{"zmusic_snd_mastervolume", zmusic_snd_mastervolume, ZMUSIC_VAR_FLOAT, 1},
956 	{}
957 };
958 
ZMusic_GetConfiguration()959 DLL_EXPORT const ZMusicConfigurationSetting* ZMusic_GetConfiguration()
960 {
961 	return config;
962 }
963