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