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