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