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
34using namespace BPrivate;
35
36struct 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
48BSoftSynth::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
69BSoftSynth::~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
82bool
83BSoftSynth::InitCheck()
84{
85	if (!fSynth)
86		_Init();
87	return fInitCheck;
88}
89
90
91void
92BSoftSynth::Unload(void)
93{
94	_Done();
95	free(fInstrumentsFile);
96	fInstrumentsFile = NULL;
97}
98
99
100bool
101BSoftSynth::IsLoaded(void) const
102{
103	return fInstrumentsFile != NULL;
104}
105
106
107status_t
108BSoftSynth::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
164status_t
165BSoftSynth::SetInstrumentsFile(const char* path)
166{
167	if (path == NULL)
168		return B_BAD_VALUE;
169
170	if (!BEntry(path).Exists())
171		return B_ENTRY_NOT_FOUND;
172
173	if (IsLoaded())
174		Unload();
175
176	fInstrumentsFile = strdup(path);
177	return B_OK;
178}
179
180
181status_t
182BSoftSynth::LoadAllInstruments()
183{
184	InitCheck();
185	return B_OK;
186}
187
188
189status_t
190BSoftSynth::LoadInstrument(int16 instrument)
191{
192	UNIMPLEMENTED
193	return B_OK;
194}
195
196
197status_t
198BSoftSynth::UnloadInstrument(int16 instrument)
199{
200	UNIMPLEMENTED
201	return B_OK;
202}
203
204
205status_t
206BSoftSynth::RemapInstrument(int16 from, int16 to)
207{
208	UNIMPLEMENTED
209	return B_OK;
210}
211
212
213void
214BSoftSynth::FlushInstrumentCache(bool startStopCache)
215{
216	// TODO: we may decide not to support this function because it's weird!
217
218	UNIMPLEMENTED
219}
220
221
222void
223BSoftSynth::SetVolume(double volume)
224{
225	if (InitCheck())
226		if (volume >= 0.0) {
227			fluid_synth_set_gain(fSynth, volume);
228		}
229}
230
231
232double
233BSoftSynth::Volume(void) const
234{
235	return fluid_synth_get_gain(fSynth);
236}
237
238
239status_t
240BSoftSynth::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
251int32
252BSoftSynth::SamplingRate() const
253{
254	return fSampleRate;
255}
256
257
258status_t
259BSoftSynth::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
267interpolation_mode
268BSoftSynth::Interpolation() const
269{
270	return fInterpMode;
271}
272
273
274status_t
275BSoftSynth::EnableReverb(bool enabled)
276{
277	fReverbEnabled = enabled;
278	fluid_synth_set_reverb_on(fSynth, enabled);
279	return B_OK;
280}
281
282
283bool
284BSoftSynth::IsReverbEnabled() const
285{
286	return fReverbEnabled;
287}
288
289
290void
291BSoftSynth::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
306reverb_mode
307BSoftSynth::Reverb() const
308{
309	return fReverbMode;
310}
311
312
313status_t
314BSoftSynth::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
325int16
326BSoftSynth::MaxVoices(void) const
327{
328	return fMaxVoices;
329}
330
331
332status_t
333BSoftSynth::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
345int16
346BSoftSynth::LimiterThreshold(void) const
347{
348	return fLimiterThreshold;
349}
350
351
352void
353BSoftSynth::Pause(void)
354{
355	UNIMPLEMENTED
356}
357
358
359void
360BSoftSynth::Resume(void)
361{
362	UNIMPLEMENTED
363}
364
365
366void
367BSoftSynth::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
377void
378BSoftSynth::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
388void
389BSoftSynth::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
399void
400BSoftSynth::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
410void
411BSoftSynth::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
421void
422BSoftSynth::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
431void
432BSoftSynth::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
443void
444BSoftSynth::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
453void
454BSoftSynth::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
464void
465BSoftSynth::SystemRealTime(uchar status, uint32 time)
466{
467	if (InitCheck()) {
468		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
469		// unsupported
470	}
471}
472
473
474void
475BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
476{
477	if (InitCheck()) {
478		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
479		// unsupported
480	}
481}
482
483
484void
485BSoftSynth::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
504void
505BSoftSynth::_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
547void
548BSoftSynth::_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
567void
568BSoftSynth::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