xref: /haiku/src/kits/midi/SoftSynth.cpp (revision e3857211d305a595c2d0b58768f25623d5967675)
1 /*
2  * Copyright 2006-2014, Haiku.
3  *
4  * Copyright (c) 2004-2005 Matthijs Hollemans
5  * Copyright (c) 2003 Jerome Leveque
6  * Distributed under the terms of the MIT License.
7  *
8  * Authors:
9  * 		Jérôme Duval
10  *		Jérôme Leveque
11  *		Matthijs Hollemans
12  *		Pete Goodeve
13  */
14 
15 #include <MidiRoster.h>
16 #include <MidiConsumer.h>
17 #include <File.h>
18 #include <FindDirectory.h>
19 #include <Path.h>
20 #include <string.h>
21 #include <stdlib.h>
22 
23 #include "debug.h"
24 #include "MidiGlue.h"   // for MAKE_BIGTIME
25 #include "SoftSynth.h"
26 
27 using namespace BPrivate;
28 
29 struct ReverbSettings {
30 	double room, damp, width, level;
31 } gReverbSettings[] = {
32 		 {0.0, 0.0, 0.0, 0.0},   //  B_REVERB_NONE
33 		 {0.2, 0.0, 0.5, 0.9},   //  B_REVERB_CLOSET
34 		 {0.5, 0.0, 0.9, 0.9},   //  B_REVERB_GARAGE
35 		 {0.7, 0.25, 0.9, 0.95}, //  B_REVERB_BALLROOM
36 		 {0.99, 0.3, 1.0, 1.0},  //  B_REVERB_CAVERN
37 		 {1.03, 0.6, 1.0, 1.0}   //  B_REVERB_DUNGEON
38 };
39 
40 
41 BSoftSynth::BSoftSynth()
42 : 	fInitCheck(false),
43 	fSynth(NULL),
44 	fSettings(NULL),
45 	fSoundPlayer(NULL),
46 	fMonitor(NULL),
47 	fMonitorSize(0),
48 	fMonitorChans(-1)
49 {
50 	fInstrumentsFile = NULL;
51 	SetDefaultInstrumentsFile();
52 
53 	fSampleRate = 44100;
54 	fInterpMode = B_LINEAR_INTERPOLATION;
55 	fMaxVoices = 256;
56 	fLimiterThreshold = 7;
57 	fReverbEnabled = true;
58 	fReverbMode = B_REVERB_BALLROOM;
59 }
60 
61 
62 BSoftSynth::~BSoftSynth()
63 {
64 	// Note: it is possible that we don't get deleted. When BSynth is
65 	// created, it is assigned to the global variable be_synth. While
66 	// BSynth is alive, it keeps a copy of BSoftSynth around too. Not
67 	// a big deal, but the Midi Kit will complain (on stdout) that we
68 	// didn't release our endpoints.
69 
70 	delete[] fMonitor;
71 	Unload();
72 }
73 
74 
75 bool
76 BSoftSynth::InitCheck()
77 {
78 	if (!fSynth)
79 		_Init();
80 	return fInitCheck;
81 }
82 
83 
84 void
85 BSoftSynth::Unload(void)
86 {
87 	_Done();
88 	free(fInstrumentsFile);
89 	fInstrumentsFile = NULL;
90 }
91 
92 
93 bool
94 BSoftSynth::IsLoaded(void) const
95 {
96 	return fInstrumentsFile != NULL;
97 }
98 
99 
100 status_t
101 BSoftSynth::SetDefaultInstrumentsFile()
102 {
103 	// We first search for a setting file (or symlink to it)
104 	// in the user settings directory
105 	char buffer[512];
106 	BPath path;
107 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
108 		path.Append("midi");
109 		BFile file(path.Path(), B_READ_ONLY);
110 		if (file.InitCheck() == B_OK
111 				&& file.Read(buffer, sizeof(buffer)) > 0) {
112 			char soundFont[512];
113 			sscanf(buffer, "# Midi Settings\n soundfont = %s\n",
114 				soundFont);
115 			return SetInstrumentsFile(soundFont);
116 		}
117 	}
118 
119 	// fall back to the old default
120 	// softsynth (big_synth.sy)
121 	// TODO: Remove this
122 	if (find_directory(B_SYNTH_DIRECTORY, &path, false, NULL) == B_OK) {
123 		path.Append(B_BIG_SYNTH_FILE);
124 		return SetInstrumentsFile(path.Path());
125 	}
126 
127 	// TODO: Use the first soundfont found in the synth directory
128 	// instead of hardcoding
129 	if (find_directory(B_SYNTH_DIRECTORY, &path, false, NULL) == B_OK) {
130 		path.Append("TimGM6mb.sf2");
131 		return SetInstrumentsFile(path.Path());
132 	}
133 
134 	// TODO: Write the settings file
135 
136 	return B_ERROR;
137 }
138 
139 
140 status_t
141 BSoftSynth::SetInstrumentsFile(const char* path)
142 {
143 	if (path == NULL)
144 		return B_BAD_VALUE;
145 
146 	if (!BEntry(path).Exists())
147 		return B_FILE_NOT_FOUND;
148 
149 	if (IsLoaded())
150 		Unload();
151 
152 	fInstrumentsFile = strdup(path);
153 	return B_OK;
154 }
155 
156 
157 status_t
158 BSoftSynth::LoadAllInstruments()
159 {
160 	InitCheck();
161 	return B_OK;
162 }
163 
164 
165 status_t
166 BSoftSynth::LoadInstrument(int16 instrument)
167 {
168 	UNIMPLEMENTED
169 	return B_OK;
170 }
171 
172 
173 status_t
174 BSoftSynth::UnloadInstrument(int16 instrument)
175 {
176 	UNIMPLEMENTED
177 	return B_OK;
178 }
179 
180 
181 status_t
182 BSoftSynth::RemapInstrument(int16 from, int16 to)
183 {
184 	UNIMPLEMENTED
185 	return B_OK;
186 }
187 
188 
189 void
190 BSoftSynth::FlushInstrumentCache(bool startStopCache)
191 {
192 	// TODO: we may decide not to support this function because it's weird!
193 
194 	UNIMPLEMENTED
195 }
196 
197 
198 void
199 BSoftSynth::SetVolume(double volume)
200 {
201 	if (InitCheck())
202 		if (volume >= 0.0) {
203 			fluid_synth_set_gain(fSynth, volume);
204 		}
205 }
206 
207 
208 double
209 BSoftSynth::Volume(void) const
210 {
211 	return fluid_synth_get_gain(fSynth);
212 }
213 
214 
215 status_t
216 BSoftSynth::SetSamplingRate(int32 rate)
217 {
218 	if (rate == 22050 || rate == 44100 || rate == 48000) {
219 		fSampleRate = rate;
220 		return B_OK;
221 	}
222 
223 	return B_BAD_VALUE;
224 }
225 
226 
227 int32
228 BSoftSynth::SamplingRate() const
229 {
230 	return fSampleRate;
231 }
232 
233 
234 status_t
235 BSoftSynth::SetInterpolation(interpolation_mode mode)
236 {
237 	// not used because our synth uses the same format than the soundplayer
238 	fInterpMode = mode;
239 	return B_OK;
240 }
241 
242 
243 interpolation_mode
244 BSoftSynth::Interpolation() const
245 {
246 	return fInterpMode;
247 }
248 
249 
250 status_t
251 BSoftSynth::EnableReverb(bool enabled)
252 {
253 	fReverbEnabled = enabled;
254 	fluid_synth_set_reverb_on(fSynth, enabled);
255 	return B_OK;
256 }
257 
258 
259 bool
260 BSoftSynth::IsReverbEnabled() const
261 {
262 	return fReverbEnabled;
263 }
264 
265 
266 void
267 BSoftSynth::SetReverb(reverb_mode mode)
268 {
269 	if (mode < B_REVERB_NONE || mode > B_REVERB_DUNGEON)
270 		return;
271 
272 	fReverbMode = mode;
273 	if (fSynth) {
274 		// We access the table using "mode - 1" because B_REVERB_NONE == 1
275 		ReverbSettings *rvb = &gReverbSettings[mode - 1];
276 		fluid_synth_set_reverb(fSynth, rvb->room, rvb->damp, rvb->width,
277 				rvb->level);
278 	}
279 }
280 
281 
282 reverb_mode
283 BSoftSynth::Reverb() const
284 {
285 	return fReverbMode;
286 }
287 
288 
289 status_t
290 BSoftSynth::SetMaxVoices(int32 max)
291 {
292 	if (max > 0 && max <= 4096) {
293 		fMaxVoices = max;
294 		return B_OK;
295 	}
296 
297 	return B_BAD_VALUE;
298 }
299 
300 
301 int16
302 BSoftSynth::MaxVoices(void) const
303 {
304 	return fMaxVoices;
305 }
306 
307 
308 status_t
309 BSoftSynth::SetLimiterThreshold(int32 threshold)
310 {
311 	// not used
312 	if (threshold > 0 && threshold <= 32) {
313 		fLimiterThreshold = threshold;
314 		return B_OK;
315 	}
316 
317 	return B_BAD_VALUE;
318 }
319 
320 
321 int16
322 BSoftSynth::LimiterThreshold(void) const
323 {
324 	return fLimiterThreshold;
325 }
326 
327 
328 void
329 BSoftSynth::Pause(void)
330 {
331 	UNIMPLEMENTED
332 }
333 
334 
335 void
336 BSoftSynth::Resume(void)
337 {
338 	UNIMPLEMENTED
339 }
340 
341 
342 void
343 BSoftSynth::NoteOff(
344 	uchar channel, uchar note, uchar velocity, uint32 time)
345 {
346 	if (InitCheck()) {
347 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
348 		fluid_synth_noteoff(fSynth, channel - 1, note);	// velocity isn't used in FS
349 	}
350 }
351 
352 
353 void
354 BSoftSynth::NoteOn(
355 	uchar channel, uchar note, uchar velocity, uint32 time)
356 {
357 	if (InitCheck()) {
358 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
359 		fluid_synth_noteon(fSynth, channel - 1, note, velocity);
360 	}
361 }
362 
363 
364 void
365 BSoftSynth::KeyPressure(
366 	uchar channel, uchar note, uchar pressure, uint32 time)
367 {
368 	if (InitCheck()) {
369 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
370 		// unavailable
371 	}
372 }
373 
374 
375 void
376 BSoftSynth::ControlChange(
377 	uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
378 {
379 	if (InitCheck()) {
380 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
381 		fluid_synth_cc(fSynth, channel - 1, controlNumber, controlValue);
382 	}
383 }
384 
385 
386 void
387 BSoftSynth::ProgramChange(
388 	uchar channel, uchar programNumber, uint32 time)
389 {
390 	if (InitCheck()) {
391 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
392 		fluid_synth_program_change(fSynth, channel - 1, programNumber);
393 	}
394 }
395 
396 
397 void
398 BSoftSynth::ChannelPressure(uchar channel, uchar pressure, uint32 time)
399 {
400 	if (InitCheck()) {
401 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
402 		//unavailable
403 	}
404 }
405 
406 
407 void
408 BSoftSynth::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
409 {
410 	if (InitCheck()) {
411 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
412 		// fluid_synth only accepts an int
413 		fluid_synth_pitch_bend(fSynth, channel - 1,
414 			((uint32)(msb & 0x7f) << 7) | (lsb & 0x7f));
415 	}
416 }
417 
418 
419 void
420 BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time)
421 {
422 	if (InitCheck()) {
423 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
424 		// unsupported
425 	}
426 }
427 
428 
429 void
430 BSoftSynth::SystemCommon(
431 	uchar status, uchar data1, uchar data2, uint32 time)
432 {
433 	if (InitCheck()) {
434 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
435 		// unsupported
436 	}
437 }
438 
439 
440 void
441 BSoftSynth::SystemRealTime(uchar status, uint32 time)
442 {
443 	if (InitCheck()) {
444 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
445 		// unsupported
446 	}
447 }
448 
449 
450 void
451 BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
452 {
453 	if (InitCheck()) {
454 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
455 		// unsupported
456 	}
457 }
458 
459 
460 void
461 BSoftSynth::AllNotesOff(bool justChannel, uint32 time)
462 {
463 	if (InitCheck()) {
464 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
465 
466 		// from BMidi::AllNotesOff
467 		for (uchar channel = 1; channel <= 16; ++channel) {
468 			fluid_synth_cc(fSynth, channel - 1, B_ALL_NOTES_OFF, 0);
469 
470 			if (!justChannel) {
471 				for (uchar note = 0; note <= 0x7F; ++note) {
472 					fluid_synth_noteoff(fSynth, channel - 1, note);
473 				}
474 			}
475 		}
476 	}
477 }
478 
479 
480 void
481 BSoftSynth::_Init()
482 {
483 	status_t err;
484 	fInitCheck = false;
485 
486 	_Done();
487 
488 	fSettings = new_fluid_settings();
489 	fluid_settings_setnum(fSettings, (char*)"synth.sample-rate", fSampleRate);
490 	fluid_settings_setint(fSettings, (char*)"synth.polyphony", fMaxVoices);
491 
492 	fSynth = new_fluid_synth(fSettings);
493 	if (!fSynth)
494 		return;
495 
496 	err = fluid_synth_sfload(fSynth, fInstrumentsFile, 1);
497 	if (err < B_OK) {
498 		fprintf(stderr, "error in fluid_synth_sfload\n");
499 		return;
500 	}
501 
502 	SetReverb(fReverbMode);
503 
504 	media_raw_audio_format format = media_raw_audio_format::wildcard;
505 	format.channel_count = 2;
506 	format.frame_rate = fSampleRate;
507 	format.format = media_raw_audio_format::B_AUDIO_FLOAT;
508 
509 	fSoundPlayer = new BSoundPlayer(&format, "Soft Synth", &PlayBuffer, NULL, this);
510 	err = fSoundPlayer->InitCheck();
511 	if (err != B_OK) {
512 		fprintf(stderr, "error in BSoundPlayer\n");
513 		return;
514 	}
515 
516 	fSoundPlayer->SetHasData(true);
517 	fSoundPlayer->Start();
518 
519 	fInitCheck = true;
520 }
521 
522 
523 void
524 BSoftSynth::_Done()
525 {
526 	if (fSoundPlayer) {
527 		fSoundPlayer->SetHasData(false);
528 		fSoundPlayer->Stop();
529 		delete fSoundPlayer;
530 		fSoundPlayer = NULL;
531 	}
532 	if (fSynth) {
533 		delete_fluid_synth(fSynth);
534 		fSynth = NULL;
535 	}
536 	if (fSettings) {
537 		delete_fluid_settings(fSettings);
538 		fSettings = NULL;
539 	}
540 }
541 
542 
543 void
544 BSoftSynth::PlayBuffer(void* cookie, void* data, size_t size,
545 		const media_raw_audio_format& format)
546 {
547 	BSoftSynth* synth = (BSoftSynth*)cookie;
548 
549 	if (synth->fMonitorSize == 0) {
550 		synth->fMonitor = (float*)new void*[size];
551 		synth->fMonitorSize = size;
552 		synth->fMonitorChans = format.channel_count;
553 	}
554 
555 	// we use float samples
556 	if (synth->fSynth) {
557 		fluid_synth_write_float(
558 			synth->fSynth, size / sizeof(float) / format.channel_count,
559 			data, 0, format.channel_count,
560 			data, 1, format.channel_count);
561 
562 		memcpy(synth->fMonitor, data, size);
563 	}
564 }
565 
566