xref: /haiku/src/kits/midi/SoftSynth.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
1 /*
2  * Copyright (c) 2004-2005 Matthijs Hollemans
3  * Copyright (c) 2003 Jerome Leveque
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <MidiRoster.h>
25 #include <MidiConsumer.h>
26 #include <MidiProducer.h>
27 #include <FindDirectory.h>
28 #include <Path.h>
29 #include <string.h>
30 #include <stdlib.h>
31 
32 #include "debug.h"
33 #include "MidiGlue.h"   // for MAKE_BIGTIME
34 #include "SoftSynth.h"
35 
36 using namespace BPrivate;
37 
38 //------------------------------------------------------------------------------
39 
40 BSoftSynth::BSoftSynth()
41 {
42 	instrumentsFile = NULL;
43 	SetDefaultInstrumentsFile();
44 
45 	sampleRate = 22050;
46 	interpMode = B_LINEAR_INTERPOLATION;
47 	maxVoices = 28;
48 	limiterThreshold = 7;
49 	reverbEnabled = true;
50 	reverbMode = B_REVERB_BALLROOM;
51 	volumeScale = 1.0;
52 
53 	Init();
54 }
55 
56 //------------------------------------------------------------------------------
57 
58 BSoftSynth::~BSoftSynth()
59 {
60 	// Note: it is possible that we don't get deleted. When BSynth is
61 	// created, it is assigned to the global variable be_synth. While
62 	// BSynth is alive, it keeps a copy of BSoftSynth around too. Not
63 	// a big deal, but the Midi Kit will complain (on stdout) that we
64 	// didn't release our endpoints.
65 
66 	Unload();
67 	Done();
68 }
69 
70 //------------------------------------------------------------------------------
71 
72 bool BSoftSynth::InitCheck(void) const
73 {
74 	return initCheck;
75 }
76 
77 //------------------------------------------------------------------------------
78 
79 void BSoftSynth::Unload(void)
80 {
81 	/* TODO: purge samples from memory */
82 
83 	free(instrumentsFile);
84 	instrumentsFile = NULL;
85 }
86 
87 //------------------------------------------------------------------------------
88 
89 bool BSoftSynth::IsLoaded(void) const
90 {
91 	return instrumentsFile != NULL;
92 }
93 
94 //------------------------------------------------------------------------------
95 
96 status_t BSoftSynth::SetDefaultInstrumentsFile()
97 {
98 	BPath path;
99 	if (B_OK == find_directory(B_SYNTH_DIRECTORY, &path, false, NULL))
100 	{
101 		path.Append(B_BIG_SYNTH_FILE);
102 		return SetInstrumentsFile(path.Path());
103 	}
104 
105 	return B_ERROR;
106 }
107 
108 //------------------------------------------------------------------------------
109 
110 status_t BSoftSynth::SetInstrumentsFile(const char* path)
111 {
112 	if (path == NULL)
113 		return B_BAD_VALUE;
114 
115 	if (IsLoaded())
116 		Unload();
117 
118 	instrumentsFile = strdup(path);
119 	return B_OK;
120 }
121 
122 //------------------------------------------------------------------------------
123 
124 status_t BSoftSynth::LoadAllInstruments()
125 {
126 	/* TODO: Should load all of the instruments from the sample bank. */
127 
128 	UNIMPLEMENTED
129 	return B_OK;
130 }
131 
132 //------------------------------------------------------------------------------
133 
134 status_t BSoftSynth::LoadInstrument(int16 instrument)
135 {
136 	UNIMPLEMENTED
137 	return B_OK;
138 }
139 
140 //------------------------------------------------------------------------------
141 
142 status_t BSoftSynth::UnloadInstrument(int16 instrument)
143 {
144 	UNIMPLEMENTED
145 	return B_OK;
146 }
147 
148 //------------------------------------------------------------------------------
149 
150 status_t BSoftSynth::RemapInstrument(int16 from, int16 to)
151 {
152 	UNIMPLEMENTED
153 	return B_OK;
154 }
155 
156 //------------------------------------------------------------------------------
157 
158 void BSoftSynth::FlushInstrumentCache(bool startStopCache)
159 {
160 	// TODO: we may decide not to support this function because it's weird!
161 
162 	UNIMPLEMENTED
163 }
164 
165 //------------------------------------------------------------------------------
166 
167 void BSoftSynth::SetVolume(double volume)
168 {
169 	if (volume >= 0.0)
170 	{
171 		volumeScale = volume;
172 	}
173 }
174 
175 //------------------------------------------------------------------------------
176 
177 double BSoftSynth::Volume(void) const
178 {
179 	return volumeScale;
180 }
181 
182 //------------------------------------------------------------------------------
183 
184 status_t BSoftSynth::SetSamplingRate(int32 rate)
185 {
186 	// TODO: According to the BeBook, we should round rate to the nearest
187 	// acceptable value. However, this function may change depending on the
188 	// softsynth back-end we'll use.
189 
190 	if (rate == 11025 || rate == 22050 || rate == 44100)
191 	{
192 		sampleRate = rate;
193 		return B_OK;
194 	}
195 
196 	return B_BAD_VALUE;
197 }
198 
199 //------------------------------------------------------------------------------
200 
201 int32 BSoftSynth::SamplingRate() const
202 {
203 	return sampleRate;
204 }
205 
206 //------------------------------------------------------------------------------
207 
208 status_t BSoftSynth::SetInterpolation(interpolation_mode mode)
209 {
210 	// TODO: this function could change depending on the synth back-end.
211 
212 	interpMode = mode;
213 	return B_OK;
214 }
215 
216 //------------------------------------------------------------------------------
217 
218 interpolation_mode BSoftSynth::Interpolation() const
219 {
220 	return interpMode;
221 }
222 
223 //------------------------------------------------------------------------------
224 
225 status_t BSoftSynth::EnableReverb(bool enabled)
226 {
227 	reverbEnabled = enabled;
228 	return B_OK;
229 }
230 
231 //------------------------------------------------------------------------------
232 
233 bool BSoftSynth::IsReverbEnabled() const
234 {
235 	return reverbEnabled;
236 }
237 
238 //------------------------------------------------------------------------------
239 
240 void BSoftSynth::SetReverb(reverb_mode mode)
241 {
242 	// TODO: this function could change depending on the synth back-end.
243 
244 	reverbMode = mode;
245 }
246 
247 //------------------------------------------------------------------------------
248 
249 reverb_mode BSoftSynth::Reverb() const
250 {
251 	return reverbMode;
252 }
253 
254 //------------------------------------------------------------------------------
255 
256 status_t BSoftSynth::SetMaxVoices(int32 max)
257 {
258 	// TODO: this function could change depending on the synth back-end.
259 
260 	if (max > 0 && max <= 32)
261 	{
262 		maxVoices = max;
263 		return B_OK;
264 	}
265 
266 	return B_BAD_VALUE;
267 }
268 
269 //------------------------------------------------------------------------------
270 
271 int16 BSoftSynth::MaxVoices(void) const
272 {
273 	return maxVoices;
274 }
275 
276 //------------------------------------------------------------------------------
277 
278 status_t BSoftSynth::SetLimiterThreshold(int32 threshold)
279 {
280 	// TODO: this function could change depending on the synth back-end.
281 
282 	if (threshold > 0 && threshold <= 32)
283 	{
284 		limiterThreshold = threshold;
285 		return B_OK;
286 	}
287 
288 	return B_BAD_VALUE;
289 }
290 
291 //------------------------------------------------------------------------------
292 
293 int16 BSoftSynth::LimiterThreshold(void) const
294 {
295 	return limiterThreshold;
296 }
297 
298 //------------------------------------------------------------------------------
299 
300 void BSoftSynth::Pause(void)
301 {
302 	UNIMPLEMENTED
303 }
304 
305 //------------------------------------------------------------------------------
306 
307 void BSoftSynth::Resume(void)
308 {
309 	UNIMPLEMENTED
310 }
311 
312 //------------------------------------------------------------------------------
313 
314 void BSoftSynth::NoteOff(
315 	uchar channel, uchar note, uchar velocity, uint32 time)
316 {
317 	if (InitCheck())
318 	{
319 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
320 		producer->SprayNoteOff(
321 			channel - 1, note, velocity, MAKE_BIGTIME(time));
322 	}
323 }
324 
325 //------------------------------------------------------------------------------
326 
327 void BSoftSynth::NoteOn(
328 	uchar channel, uchar note, uchar velocity, uint32 time)
329 {
330 	if (InitCheck())
331 	{
332 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
333 		producer->SprayNoteOn(channel - 1, note, velocity, MAKE_BIGTIME(time));
334 	}
335 }
336 
337 //------------------------------------------------------------------------------
338 
339 void BSoftSynth::KeyPressure(
340 	uchar channel, uchar note, uchar pressure, uint32 time)
341 {
342 	if (InitCheck())
343 	{
344 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
345 		producer->SprayKeyPressure(
346 			channel - 1, note, pressure, MAKE_BIGTIME(time));
347 	}
348 }
349 
350 //------------------------------------------------------------------------------
351 
352 void BSoftSynth::ControlChange(
353 	uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
354 {
355 	if (InitCheck())
356 	{
357 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
358 		producer->SprayControlChange(
359 			channel - 1, controlNumber, controlValue, MAKE_BIGTIME(time));
360 	}
361 }
362 
363 //------------------------------------------------------------------------------
364 
365 void BSoftSynth::ProgramChange(
366 	uchar channel, uchar programNumber, uint32 time)
367 {
368 	if (InitCheck())
369 	{
370 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
371 		producer->SprayProgramChange(
372 			channel - 1, programNumber, MAKE_BIGTIME(time));
373 	}
374 }
375 
376 //------------------------------------------------------------------------------
377 
378 void BSoftSynth::ChannelPressure(uchar channel, uchar pressure, uint32 time)
379 {
380 	if (InitCheck())
381 	{
382 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
383 		producer->SprayChannelPressure(
384 			channel - 1, pressure, MAKE_BIGTIME(time));
385 	}
386 }
387 
388 //------------------------------------------------------------------------------
389 
390 void BSoftSynth::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
391 {
392 	if (InitCheck())
393 	{
394 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
395 		producer->SprayPitchBend(channel - 1, lsb, msb, MAKE_BIGTIME(time));
396 	}
397 }
398 
399 //------------------------------------------------------------------------------
400 
401 void BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time)
402 {
403 	if (InitCheck())
404 	{
405 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
406 		producer->SpraySystemExclusive(data, length, MAKE_BIGTIME(time));
407 	}
408 }
409 
410 //------------------------------------------------------------------------------
411 
412 void BSoftSynth::SystemCommon(
413 	uchar status, uchar data1, uchar data2, uint32 time)
414 {
415 	if (InitCheck())
416 	{
417 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
418 		producer->SpraySystemCommon(status, data1, data2, MAKE_BIGTIME(time));
419 	}
420 }
421 
422 //------------------------------------------------------------------------------
423 
424 void BSoftSynth::SystemRealTime(uchar status, uint32 time)
425 {
426 	if (InitCheck())
427 	{
428 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
429 		producer->SpraySystemRealTime(status, MAKE_BIGTIME(time));
430 	}
431 }
432 
433 //------------------------------------------------------------------------------
434 
435 void BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
436 {
437 	if (InitCheck())
438 	{
439 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
440 		producer->SprayTempoChange(beatsPerMinute, MAKE_BIGTIME(time));
441 	}
442 }
443 
444 //------------------------------------------------------------------------------
445 
446 void BSoftSynth::AllNotesOff(bool justChannel, uint32 time)
447 {
448 	if (InitCheck())
449 	{
450 		snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
451 
452 		// from BMidi::AllNotesOff
453 		for (uchar channel = 1; channel <= 16; ++channel)
454 		{
455 			producer->SprayControlChange(channel, B_ALL_NOTES_OFF, 0, time);
456 
457 			if (!justChannel)
458 			{
459 				for (uchar note = 0; note <= 0x7F; ++note)
460 				{
461 					producer->SprayNoteOff(channel, note, 0, time);
462 				}
463 			}
464 		}
465 	}
466 }
467 
468 //------------------------------------------------------------------------------
469 
470 void BSoftSynth::Init()
471 {
472 	initCheck = false;
473 
474 	producer = new BMidiLocalProducer("TiMidity feeder");
475 
476 	int32 id = 0;
477 	while ((consumer = BMidiRoster::NextConsumer(&id)) != NULL)
478 	{
479 		if (strcmp(consumer->Name(), "TiMidity input") == 0)
480 			break;
481 
482 		consumer->Release();
483 	}
484 
485 	if (consumer == NULL)
486 	{
487 		fprintf(stderr, "[midi] TiMidity consumer not found\n");
488 		return;
489 	}
490 
491 	if (producer->Connect(consumer) != B_OK)
492 	{
493 		fprintf(stderr, "[midi] Could not connect to TiMidity\n");
494 		return;
495 	}
496 
497 	initCheck = true;
498 }
499 
500 //------------------------------------------------------------------------------
501 
502 void BSoftSynth::Done()
503 {
504 	if (consumer != NULL)
505 	{
506 		producer->Disconnect(consumer);
507 		consumer->Release();
508 		consumer = NULL;
509 	}
510 
511 	producer->Release();
512 	producer = NULL;
513 }
514 
515 //------------------------------------------------------------------------------
516