xref: /haiku/src/kits/midi/SoftSynth.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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,
360 			((uint32)(msb & 0x7f) << 7) | (lsb & 0x7f));
361 	}
362 }
363 
364 
365 void
366 BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time)
367 {
368 	if (InitCheck()) {
369 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
370 		// unsupported
371 	}
372 }
373 
374 
375 void
376 BSoftSynth::SystemCommon(
377 	uchar status, uchar data1, uchar data2, uint32 time)
378 {
379 	if (InitCheck()) {
380 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
381 		// unsupported
382 	}
383 }
384 
385 
386 void
387 BSoftSynth::SystemRealTime(uchar status, uint32 time)
388 {
389 	if (InitCheck()) {
390 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
391 		// unsupported
392 	}
393 }
394 
395 
396 void
397 BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
398 {
399 	if (InitCheck()) {
400 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
401 		// unsupported
402 	}
403 }
404 
405 
406 void
407 BSoftSynth::AllNotesOff(bool justChannel, uint32 time)
408 {
409 	if (InitCheck()) {
410 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
411 
412 		// from BMidi::AllNotesOff
413 		for (uchar channel = 1; channel <= 16; ++channel) {
414 			fluid_synth_cc(fSynth, channel - 1, B_ALL_NOTES_OFF, 0);
415 
416 			if (!justChannel) {
417 				for (uchar note = 0; note <= 0x7F; ++note) {
418 					fluid_synth_noteoff(fSynth, channel - 1, note);
419 				}
420 			}
421 		}
422 	}
423 }
424 
425 
426 void
427 BSoftSynth::_Init()
428 {
429 	status_t err;
430 	fInitCheck = false;
431 
432 	_Done();
433 
434 	fSettings = new_fluid_settings();
435 	fluid_settings_setnum(fSettings, (char*)"synth.sample-rate", fSampleRate);
436 	fluid_settings_setint(fSettings, (char*)"synth.polyphony", fMaxVoices);
437 
438 	fSynth = new_fluid_synth(fSettings);
439 	if (!fSynth)
440 		return;
441 
442 	err = fluid_synth_sfload(fSynth, fInstrumentsFile, 1);
443 	if (err < B_OK) {
444 		fprintf(stderr, "error in fluid_synth_sfload\n");
445 		return;
446 	}
447 
448 	media_raw_audio_format format = media_raw_audio_format::wildcard;
449 	format.channel_count = 2;
450 	format.frame_rate = fSampleRate;
451 	format.format = media_raw_audio_format::B_AUDIO_FLOAT;
452 
453 	fSoundPlayer = new BSoundPlayer(&format, "Soft Synth", &PlayBuffer, NULL, this);
454 	err = fSoundPlayer->InitCheck();
455 	if (err != B_OK) {
456 		fprintf(stderr, "error in BSoundPlayer\n");
457 		return;
458 	}
459 
460 	fSoundPlayer->SetHasData(true);
461 	fSoundPlayer->Start();
462 
463 	fInitCheck = true;
464 }
465 
466 
467 void
468 BSoftSynth::_Done()
469 {
470 	if (fSoundPlayer) {
471 		fSoundPlayer->SetHasData(false);
472 		fSoundPlayer->Stop();
473 		delete fSoundPlayer;
474 		fSoundPlayer = NULL;
475 	}
476 	if (fSynth) {
477 		delete_fluid_synth(fSynth);
478 		fSynth = NULL;
479 	}
480 	if (fSettings) {
481 		delete_fluid_settings(fSettings);
482 		fSettings = NULL;
483 	}
484 }
485 
486 
487 void
488 BSoftSynth::PlayBuffer(void * cookie, void * data, size_t size, const media_raw_audio_format & format)
489 {
490 	BSoftSynth *synth = (BSoftSynth *)cookie;
491 
492 	// we use float samples
493 
494 	if (synth->fSynth)
495 		fluid_synth_write_float(synth->fSynth, size / sizeof(float) / format.channel_count,
496 			data, 0, format.channel_count,
497 			data, 1, format.channel_count);
498 }
499 
500