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
25using namespace BPrivate;
26
27
28BSoftSynth::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
46BSoftSynth::~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
58bool
59BSoftSynth::InitCheck()
60{
61	if (!fSynth)
62		_Init();
63	return fInitCheck;
64}
65
66
67void
68BSoftSynth::Unload(void)
69{
70	_Done();
71	free(fInstrumentsFile);
72	fInstrumentsFile = NULL;
73}
74
75
76bool
77BSoftSynth::IsLoaded(void) const
78{
79	return fInstrumentsFile != NULL;
80}
81
82
83status_t
84BSoftSynth::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
96status_t
97BSoftSynth::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
110status_t
111BSoftSynth::LoadAllInstruments()
112{
113	InitCheck();
114	return B_OK;
115}
116
117
118status_t
119BSoftSynth::LoadInstrument(int16 instrument)
120{
121	UNIMPLEMENTED
122	return B_OK;
123}
124
125
126status_t
127BSoftSynth::UnloadInstrument(int16 instrument)
128{
129	UNIMPLEMENTED
130	return B_OK;
131}
132
133
134status_t
135BSoftSynth::RemapInstrument(int16 from, int16 to)
136{
137	UNIMPLEMENTED
138	return B_OK;
139}
140
141
142void
143BSoftSynth::FlushInstrumentCache(bool startStopCache)
144{
145	// TODO: we may decide not to support this function because it's weird!
146
147	UNIMPLEMENTED
148}
149
150
151void
152BSoftSynth::SetVolume(double volume)
153{
154	if (InitCheck())
155		if (volume >= 0.0) {
156			fluid_synth_set_gain(fSynth, volume);
157		}
158}
159
160
161double
162BSoftSynth::Volume(void) const
163{
164	return fluid_synth_get_gain(fSynth);
165}
166
167
168status_t
169BSoftSynth::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
180int32
181BSoftSynth::SamplingRate() const
182{
183	return fSampleRate;
184}
185
186
187status_t
188BSoftSynth::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
196interpolation_mode
197BSoftSynth::Interpolation() const
198{
199	return fInterpMode;
200}
201
202
203status_t
204BSoftSynth::EnableReverb(bool enabled)
205{
206	fReverbEnabled = enabled;
207	fluid_synth_set_reverb_on(fSynth, enabled);
208	return B_OK;
209}
210
211
212bool
213BSoftSynth::IsReverbEnabled() const
214{
215	return fReverbEnabled;
216}
217
218
219void
220BSoftSynth::SetReverb(reverb_mode mode)
221{
222	// TODO: this function could change depending on the synth back-end.
223
224	fReverbMode = mode;
225}
226
227
228reverb_mode
229BSoftSynth::Reverb() const
230{
231	return fReverbMode;
232}
233
234
235status_t
236BSoftSynth::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
247int16
248BSoftSynth::MaxVoices(void) const
249{
250	return fMaxVoices;
251}
252
253
254status_t
255BSoftSynth::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
267int16
268BSoftSynth::LimiterThreshold(void) const
269{
270	return fLimiterThreshold;
271}
272
273
274void
275BSoftSynth::Pause(void)
276{
277	UNIMPLEMENTED
278}
279
280
281void
282BSoftSynth::Resume(void)
283{
284	UNIMPLEMENTED
285}
286
287
288void
289BSoftSynth::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
299void
300BSoftSynth::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
310void
311BSoftSynth::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
321void
322BSoftSynth::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
332void
333BSoftSynth::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
343void
344BSoftSynth::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
353void
354BSoftSynth::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
365void
366BSoftSynth::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
375void
376BSoftSynth::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
386void
387BSoftSynth::SystemRealTime(uchar status, uint32 time)
388{
389	if (InitCheck()) {
390		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
391		// unsupported
392	}
393}
394
395
396void
397BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
398{
399	if (InitCheck()) {
400		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
401		// unsupported
402	}
403}
404
405
406void
407BSoftSynth::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
426void
427BSoftSynth::_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
467void
468BSoftSynth::_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
487void
488BSoftSynth::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