xref: /haiku/src/kits/midi/SoftSynth.cpp (revision c2751c41b7404c4e7b0b6ed723b92505a6cf332b)
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 	// TODO: Use the first soundfont found in the synth directory
120 	// instead of hardcoding
121 	if (find_directory(B_SYNTH_DIRECTORY, &path, false, NULL) == B_OK) {
122 		path.Append("TimGM6mb.sf2");
123 		return SetInstrumentsFile(path.Path());
124 	}
125 
126 	// TODO: Write the settings file
127 
128 	return B_ERROR;
129 }
130 
131 
132 status_t
133 BSoftSynth::SetInstrumentsFile(const char* path)
134 {
135 	if (path == NULL)
136 		return B_BAD_VALUE;
137 
138 	if (!BEntry(path).Exists())
139 		return B_FILE_NOT_FOUND;
140 
141 	if (IsLoaded())
142 		Unload();
143 
144 	fInstrumentsFile = strdup(path);
145 	return B_OK;
146 }
147 
148 
149 status_t
150 BSoftSynth::LoadAllInstruments()
151 {
152 	InitCheck();
153 	return B_OK;
154 }
155 
156 
157 status_t
158 BSoftSynth::LoadInstrument(int16 instrument)
159 {
160 	UNIMPLEMENTED
161 	return B_OK;
162 }
163 
164 
165 status_t
166 BSoftSynth::UnloadInstrument(int16 instrument)
167 {
168 	UNIMPLEMENTED
169 	return B_OK;
170 }
171 
172 
173 status_t
174 BSoftSynth::RemapInstrument(int16 from, int16 to)
175 {
176 	UNIMPLEMENTED
177 	return B_OK;
178 }
179 
180 
181 void
182 BSoftSynth::FlushInstrumentCache(bool startStopCache)
183 {
184 	// TODO: we may decide not to support this function because it's weird!
185 
186 	UNIMPLEMENTED
187 }
188 
189 
190 void
191 BSoftSynth::SetVolume(double volume)
192 {
193 	if (InitCheck())
194 		if (volume >= 0.0) {
195 			fluid_synth_set_gain(fSynth, volume);
196 		}
197 }
198 
199 
200 double
201 BSoftSynth::Volume(void) const
202 {
203 	return fluid_synth_get_gain(fSynth);
204 }
205 
206 
207 status_t
208 BSoftSynth::SetSamplingRate(int32 rate)
209 {
210 	if (rate == 22050 || rate == 44100 || rate == 48000) {
211 		fSampleRate = rate;
212 		return B_OK;
213 	}
214 
215 	return B_BAD_VALUE;
216 }
217 
218 
219 int32
220 BSoftSynth::SamplingRate() const
221 {
222 	return fSampleRate;
223 }
224 
225 
226 status_t
227 BSoftSynth::SetInterpolation(interpolation_mode mode)
228 {
229 	// not used because our synth uses the same format than the soundplayer
230 	fInterpMode = mode;
231 	return B_OK;
232 }
233 
234 
235 interpolation_mode
236 BSoftSynth::Interpolation() const
237 {
238 	return fInterpMode;
239 }
240 
241 
242 status_t
243 BSoftSynth::EnableReverb(bool enabled)
244 {
245 	fReverbEnabled = enabled;
246 	fluid_synth_set_reverb_on(fSynth, enabled);
247 	return B_OK;
248 }
249 
250 
251 bool
252 BSoftSynth::IsReverbEnabled() const
253 {
254 	return fReverbEnabled;
255 }
256 
257 
258 void
259 BSoftSynth::SetReverb(reverb_mode mode)
260 {
261 	if (mode < B_REVERB_NONE || mode > B_REVERB_DUNGEON)
262 		return;
263 
264 	fReverbMode = mode;
265 	if (fSynth) {
266 		// We access the table using "mode - 1" because B_REVERB_NONE == 1
267 		ReverbSettings *rvb = &gReverbSettings[mode - 1];
268 		fluid_synth_set_reverb(fSynth, rvb->room, rvb->damp, rvb->width,
269 				rvb->level);
270 	}
271 }
272 
273 
274 reverb_mode
275 BSoftSynth::Reverb() const
276 {
277 	return fReverbMode;
278 }
279 
280 
281 status_t
282 BSoftSynth::SetMaxVoices(int32 max)
283 {
284 	if (max > 0 && max <= 4096) {
285 		fMaxVoices = max;
286 		return B_OK;
287 	}
288 
289 	return B_BAD_VALUE;
290 }
291 
292 
293 int16
294 BSoftSynth::MaxVoices(void) const
295 {
296 	return fMaxVoices;
297 }
298 
299 
300 status_t
301 BSoftSynth::SetLimiterThreshold(int32 threshold)
302 {
303 	// not used
304 	if (threshold > 0 && threshold <= 32) {
305 		fLimiterThreshold = threshold;
306 		return B_OK;
307 	}
308 
309 	return B_BAD_VALUE;
310 }
311 
312 
313 int16
314 BSoftSynth::LimiterThreshold(void) const
315 {
316 	return fLimiterThreshold;
317 }
318 
319 
320 void
321 BSoftSynth::Pause(void)
322 {
323 	UNIMPLEMENTED
324 }
325 
326 
327 void
328 BSoftSynth::Resume(void)
329 {
330 	UNIMPLEMENTED
331 }
332 
333 
334 void
335 BSoftSynth::NoteOff(
336 	uchar channel, uchar note, uchar velocity, uint32 time)
337 {
338 	if (InitCheck()) {
339 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
340 		fluid_synth_noteoff(fSynth, channel - 1, note);	// velocity isn't used in FS
341 	}
342 }
343 
344 
345 void
346 BSoftSynth::NoteOn(
347 	uchar channel, uchar note, uchar velocity, uint32 time)
348 {
349 	if (InitCheck()) {
350 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
351 		fluid_synth_noteon(fSynth, channel - 1, note, velocity);
352 	}
353 }
354 
355 
356 void
357 BSoftSynth::KeyPressure(
358 	uchar channel, uchar note, uchar pressure, uint32 time)
359 {
360 	if (InitCheck()) {
361 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
362 		// unavailable
363 	}
364 }
365 
366 
367 void
368 BSoftSynth::ControlChange(
369 	uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
370 {
371 	if (InitCheck()) {
372 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
373 		fluid_synth_cc(fSynth, channel - 1, controlNumber, controlValue);
374 	}
375 }
376 
377 
378 void
379 BSoftSynth::ProgramChange(
380 	uchar channel, uchar programNumber, uint32 time)
381 {
382 	if (InitCheck()) {
383 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
384 		fluid_synth_program_change(fSynth, channel - 1, programNumber);
385 	}
386 }
387 
388 
389 void
390 BSoftSynth::ChannelPressure(uchar channel, uchar pressure, uint32 time)
391 {
392 	if (InitCheck()) {
393 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
394 		//unavailable
395 	}
396 }
397 
398 
399 void
400 BSoftSynth::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
401 {
402 	if (InitCheck()) {
403 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
404 		// fluid_synth only accepts an int
405 		fluid_synth_pitch_bend(fSynth, channel - 1,
406 			((uint32)(msb & 0x7f) << 7) | (lsb & 0x7f));
407 	}
408 }
409 
410 
411 void
412 BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time)
413 {
414 	if (InitCheck()) {
415 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
416 		// unsupported
417 	}
418 }
419 
420 
421 void
422 BSoftSynth::SystemCommon(
423 	uchar status, uchar data1, uchar data2, uint32 time)
424 {
425 	if (InitCheck()) {
426 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
427 		// unsupported
428 	}
429 }
430 
431 
432 void
433 BSoftSynth::SystemRealTime(uchar status, uint32 time)
434 {
435 	if (InitCheck()) {
436 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
437 		// unsupported
438 	}
439 }
440 
441 
442 void
443 BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
444 {
445 	if (InitCheck()) {
446 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
447 		// unsupported
448 	}
449 }
450 
451 
452 void
453 BSoftSynth::AllNotesOff(bool justChannel, uint32 time)
454 {
455 	if (InitCheck()) {
456 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
457 
458 		// from BMidi::AllNotesOff
459 		for (uchar channel = 1; channel <= 16; ++channel) {
460 			fluid_synth_cc(fSynth, channel - 1, B_ALL_NOTES_OFF, 0);
461 
462 			if (!justChannel) {
463 				for (uchar note = 0; note <= 0x7F; ++note) {
464 					fluid_synth_noteoff(fSynth, channel - 1, note);
465 				}
466 			}
467 		}
468 	}
469 }
470 
471 
472 void
473 BSoftSynth::_Init()
474 {
475 	status_t err;
476 	fInitCheck = false;
477 
478 	_Done();
479 
480 	fSettings = new_fluid_settings();
481 	fluid_settings_setnum(fSettings, (char*)"synth.sample-rate", fSampleRate);
482 	fluid_settings_setint(fSettings, (char*)"synth.polyphony", fMaxVoices);
483 
484 	fSynth = new_fluid_synth(fSettings);
485 	if (!fSynth)
486 		return;
487 
488 	err = fluid_synth_sfload(fSynth, fInstrumentsFile, 1);
489 	if (err < B_OK) {
490 		fprintf(stderr, "error in fluid_synth_sfload\n");
491 		return;
492 	}
493 
494 	SetReverb(fReverbMode);
495 
496 	media_raw_audio_format format = media_raw_audio_format::wildcard;
497 	format.channel_count = 2;
498 	format.frame_rate = fSampleRate;
499 	format.format = media_raw_audio_format::B_AUDIO_FLOAT;
500 
501 	fSoundPlayer = new BSoundPlayer(&format, "Soft Synth", &PlayBuffer, NULL, this);
502 	err = fSoundPlayer->InitCheck();
503 	if (err != B_OK) {
504 		fprintf(stderr, "error in BSoundPlayer\n");
505 		return;
506 	}
507 
508 	fSoundPlayer->SetHasData(true);
509 	fSoundPlayer->Start();
510 
511 	fInitCheck = true;
512 }
513 
514 
515 void
516 BSoftSynth::_Done()
517 {
518 	if (fSoundPlayer) {
519 		fSoundPlayer->SetHasData(false);
520 		fSoundPlayer->Stop();
521 		delete fSoundPlayer;
522 		fSoundPlayer = NULL;
523 	}
524 	if (fSynth) {
525 		delete_fluid_synth(fSynth);
526 		fSynth = NULL;
527 	}
528 	if (fSettings) {
529 		delete_fluid_settings(fSettings);
530 		fSettings = NULL;
531 	}
532 }
533 
534 
535 void
536 BSoftSynth::PlayBuffer(void* cookie, void* data, size_t size,
537 		const media_raw_audio_format& format)
538 {
539 	BSoftSynth* synth = (BSoftSynth*)cookie;
540 
541 	if (synth->fMonitorSize == 0) {
542 		synth->fMonitor = (float*)new void*[size];
543 		synth->fMonitorSize = size;
544 		synth->fMonitorChans = format.channel_count;
545 	}
546 
547 	// we use float samples
548 	if (synth->fSynth) {
549 		fluid_synth_write_float(
550 			synth->fSynth, size / sizeof(float) / format.channel_count,
551 			data, 0, format.channel_count,
552 			data, 1, format.channel_count);
553 
554 		memcpy(synth->fMonitor, data, size);
555 	}
556 }
557 
558