vfs: check userland buffers before reading them.
[haiku.git] / src / kits / midi / SoftSynth.cpp
bloba7c003cc4b46536744321a4a6ecc5b7587a811df
1 /*
2 * Copyright 2006-2014, Haiku.
4 * Copyright (c) 2004-2005 Matthijs Hollemans
5 * Copyright (c) 2003 Jerome Leveque
6 * Distributed under the terms of the MIT License.
8 * Authors:
9 * Jérôme Duval
10 * Jérôme Leveque
11 * Matthijs Hollemans
12 * Pete Goodeve
15 #include <MidiRoster.h>
16 #include <MidiConsumer.h>
17 #include <Directory.h>
18 #include <File.h>
19 #include <FindDirectory.h>
20 #include <Node.h>
21 #include <NodeInfo.h>
22 #include <Path.h>
23 #include <PathFinder.h>
25 #include <string.h>
26 #include <stdlib.h>
28 #include <MidiSettings.h>
30 #include "debug.h"
31 #include "MidiGlue.h" // for MAKE_BIGTIME
32 #include "SoftSynth.h"
34 using namespace BPrivate;
36 struct ReverbSettings {
37 double room, damp, width, level;
38 } gReverbSettings[] = {
39 {0.0, 0.0, 0.0, 0.0}, // B_REVERB_NONE
40 {0.2, 0.0, 0.5, 0.9}, // B_REVERB_CLOSET
41 {0.5, 0.0, 0.9, 0.9}, // B_REVERB_GARAGE
42 {0.7, 0.25, 0.9, 0.95}, // B_REVERB_BALLROOM
43 {0.99, 0.3, 1.0, 1.0}, // B_REVERB_CAVERN
44 {1.03, 0.6, 1.0, 1.0} // B_REVERB_DUNGEON
48 BSoftSynth::BSoftSynth()
49 : fInitCheck(false),
50 fSynth(NULL),
51 fSettings(NULL),
52 fSoundPlayer(NULL),
53 fMonitor(NULL),
54 fMonitorSize(0),
55 fMonitorChans(-1)
57 fInstrumentsFile = NULL;
58 SetDefaultInstrumentsFile();
60 fSampleRate = 44100;
61 fInterpMode = B_LINEAR_INTERPOLATION;
62 fMaxVoices = 256;
63 fLimiterThreshold = 7;
64 fReverbEnabled = true;
65 fReverbMode = B_REVERB_BALLROOM;
69 BSoftSynth::~BSoftSynth()
71 // Note: it is possible that we don't get deleted. When BSynth is
72 // created, it is assigned to the global variable be_synth. While
73 // BSynth is alive, it keeps a copy of BSoftSynth around too. Not
74 // a big deal, but the Midi Kit will complain (on stdout) that we
75 // didn't release our endpoints.
77 delete[] fMonitor;
78 Unload();
82 bool
83 BSoftSynth::InitCheck()
85 if (!fSynth)
86 _Init();
87 return fInitCheck;
91 void
92 BSoftSynth::Unload(void)
94 _Done();
95 free(fInstrumentsFile);
96 fInstrumentsFile = NULL;
100 bool
101 BSoftSynth::IsLoaded(void) const
103 return fInstrumentsFile != NULL;
107 status_t
108 BSoftSynth::SetDefaultInstrumentsFile()
110 // TODO: Duplicated code, check MidiSettingsView::_LoadSettings() and
111 // MidiSettingsView::_RetrieveSoftSynthList()
112 // We first search for a setting file (or symlink to it)
113 // in the user settings directory
115 struct BPrivate::midi_settings settings;
116 if (BPrivate::read_midi_settings(&settings) == B_OK) {
117 if (SetInstrumentsFile(settings.soundfont_file) == B_OK)
118 return B_OK;
121 // Try a well-known (and usually present on a default install) soft synth
122 BPath path;
123 if (find_directory(B_SYNTH_DIRECTORY, &path, false, NULL) == B_OK) {
124 path.Append("synth/TimGM6mb.sf2");
125 if (SetInstrumentsFile(path.Path()) == B_OK)
126 return B_OK;
129 // Just use the first soundfont we find
130 BStringList paths;
131 status_t status = BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY,
132 "synth", paths);
134 if (status != B_OK)
135 return B_ERROR;
137 for (int32 i = 0; i < paths.CountStrings(); i++) {
138 BDirectory directory(paths.StringAt(i).String());
139 BEntry entry;
140 if (directory.InitCheck() != B_OK)
141 continue;
142 while (directory.GetNextEntry(&entry) == B_OK) {
143 BNode node(&entry);
144 BNodeInfo nodeInfo(&node);
145 char mimeType[B_MIME_TYPE_LENGTH];
146 // TODO: For some reason the mimetype check fails.
147 // maybe because the file hasn't yet been sniffed and recognized?
148 if (nodeInfo.GetType(mimeType) == B_OK
149 /*&& !strcmp(mimeType, "audio/x-soundfont")*/) {
150 BPath fullPath = paths.StringAt(i).String();
151 fullPath.Append(entry.Name());
152 if (SetInstrumentsFile(fullPath.Path()) == B_OK)
153 return B_OK;
158 // TODO: Write the settings file ?
160 return B_ERROR;
164 status_t
165 BSoftSynth::SetInstrumentsFile(const char* path)
167 if (path == NULL)
168 return B_BAD_VALUE;
170 if (!BEntry(path).Exists())
171 return B_FILE_NOT_FOUND;
173 if (IsLoaded())
174 Unload();
176 fInstrumentsFile = strdup(path);
177 return B_OK;
181 status_t
182 BSoftSynth::LoadAllInstruments()
184 InitCheck();
185 return B_OK;
189 status_t
190 BSoftSynth::LoadInstrument(int16 instrument)
192 UNIMPLEMENTED
193 return B_OK;
197 status_t
198 BSoftSynth::UnloadInstrument(int16 instrument)
200 UNIMPLEMENTED
201 return B_OK;
205 status_t
206 BSoftSynth::RemapInstrument(int16 from, int16 to)
208 UNIMPLEMENTED
209 return B_OK;
213 void
214 BSoftSynth::FlushInstrumentCache(bool startStopCache)
216 // TODO: we may decide not to support this function because it's weird!
218 UNIMPLEMENTED
222 void
223 BSoftSynth::SetVolume(double volume)
225 if (InitCheck())
226 if (volume >= 0.0) {
227 fluid_synth_set_gain(fSynth, volume);
232 double
233 BSoftSynth::Volume(void) const
235 return fluid_synth_get_gain(fSynth);
239 status_t
240 BSoftSynth::SetSamplingRate(int32 rate)
242 if (rate == 22050 || rate == 44100 || rate == 48000) {
243 fSampleRate = rate;
244 return B_OK;
247 return B_BAD_VALUE;
251 int32
252 BSoftSynth::SamplingRate() const
254 return fSampleRate;
258 status_t
259 BSoftSynth::SetInterpolation(interpolation_mode mode)
261 // not used because our synth uses the same format than the soundplayer
262 fInterpMode = mode;
263 return B_OK;
267 interpolation_mode
268 BSoftSynth::Interpolation() const
270 return fInterpMode;
274 status_t
275 BSoftSynth::EnableReverb(bool enabled)
277 fReverbEnabled = enabled;
278 fluid_synth_set_reverb_on(fSynth, enabled);
279 return B_OK;
283 bool
284 BSoftSynth::IsReverbEnabled() const
286 return fReverbEnabled;
290 void
291 BSoftSynth::SetReverb(reverb_mode mode)
293 if (mode < B_REVERB_NONE || mode > B_REVERB_DUNGEON)
294 return;
296 fReverbMode = mode;
297 if (fSynth) {
298 // We access the table using "mode - 1" because B_REVERB_NONE == 1
299 ReverbSettings *rvb = &gReverbSettings[mode - 1];
300 fluid_synth_set_reverb(fSynth, rvb->room, rvb->damp, rvb->width,
301 rvb->level);
306 reverb_mode
307 BSoftSynth::Reverb() const
309 return fReverbMode;
313 status_t
314 BSoftSynth::SetMaxVoices(int32 max)
316 if (max > 0 && max <= 4096) {
317 fMaxVoices = max;
318 return B_OK;
321 return B_BAD_VALUE;
325 int16
326 BSoftSynth::MaxVoices(void) const
328 return fMaxVoices;
332 status_t
333 BSoftSynth::SetLimiterThreshold(int32 threshold)
335 // not used
336 if (threshold > 0 && threshold <= 32) {
337 fLimiterThreshold = threshold;
338 return B_OK;
341 return B_BAD_VALUE;
345 int16
346 BSoftSynth::LimiterThreshold(void) const
348 return fLimiterThreshold;
352 void
353 BSoftSynth::Pause(void)
355 UNIMPLEMENTED
359 void
360 BSoftSynth::Resume(void)
362 UNIMPLEMENTED
366 void
367 BSoftSynth::NoteOff(
368 uchar channel, uchar note, uchar velocity, uint32 time)
370 if (InitCheck()) {
371 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
372 fluid_synth_noteoff(fSynth, channel - 1, note); // velocity isn't used in FS
377 void
378 BSoftSynth::NoteOn(
379 uchar channel, uchar note, uchar velocity, uint32 time)
381 if (InitCheck()) {
382 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
383 fluid_synth_noteon(fSynth, channel - 1, note, velocity);
388 void
389 BSoftSynth::KeyPressure(
390 uchar channel, uchar note, uchar pressure, uint32 time)
392 if (InitCheck()) {
393 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
394 // unavailable
399 void
400 BSoftSynth::ControlChange(
401 uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
403 if (InitCheck()) {
404 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
405 fluid_synth_cc(fSynth, channel - 1, controlNumber, controlValue);
410 void
411 BSoftSynth::ProgramChange(
412 uchar channel, uchar programNumber, uint32 time)
414 if (InitCheck()) {
415 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
416 fluid_synth_program_change(fSynth, channel - 1, programNumber);
421 void
422 BSoftSynth::ChannelPressure(uchar channel, uchar pressure, uint32 time)
424 if (InitCheck()) {
425 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
426 //unavailable
431 void
432 BSoftSynth::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
434 if (InitCheck()) {
435 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
436 // fluid_synth only accepts an int
437 fluid_synth_pitch_bend(fSynth, channel - 1,
438 ((uint32)(msb & 0x7f) << 7) | (lsb & 0x7f));
443 void
444 BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time)
446 if (InitCheck()) {
447 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
448 // unsupported
453 void
454 BSoftSynth::SystemCommon(
455 uchar status, uchar data1, uchar data2, uint32 time)
457 if (InitCheck()) {
458 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
459 // unsupported
464 void
465 BSoftSynth::SystemRealTime(uchar status, uint32 time)
467 if (InitCheck()) {
468 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
469 // unsupported
474 void
475 BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
477 if (InitCheck()) {
478 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
479 // unsupported
484 void
485 BSoftSynth::AllNotesOff(bool justChannel, uint32 time)
487 if (InitCheck()) {
488 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
490 // from BMidi::AllNotesOff
491 for (uchar channel = 1; channel <= 16; ++channel) {
492 fluid_synth_cc(fSynth, channel - 1, B_ALL_NOTES_OFF, 0);
494 if (!justChannel) {
495 for (uchar note = 0; note <= 0x7F; ++note) {
496 fluid_synth_noteoff(fSynth, channel - 1, note);
504 void
505 BSoftSynth::_Init()
507 status_t err;
508 fInitCheck = false;
510 _Done();
512 fSettings = new_fluid_settings();
513 fluid_settings_setnum(fSettings, (char*)"synth.sample-rate", fSampleRate);
514 fluid_settings_setint(fSettings, (char*)"synth.polyphony", fMaxVoices);
516 fSynth = new_fluid_synth(fSettings);
517 if (!fSynth)
518 return;
520 err = fluid_synth_sfload(fSynth, fInstrumentsFile, 1);
521 if (err < B_OK) {
522 fprintf(stderr, "error in fluid_synth_sfload\n");
523 return;
526 SetReverb(fReverbMode);
528 media_raw_audio_format format = media_raw_audio_format::wildcard;
529 format.channel_count = 2;
530 format.frame_rate = fSampleRate;
531 format.format = media_raw_audio_format::B_AUDIO_FLOAT;
533 fSoundPlayer = new BSoundPlayer(&format, "Soft Synth", &PlayBuffer, NULL, this);
534 err = fSoundPlayer->InitCheck();
535 if (err != B_OK) {
536 fprintf(stderr, "error in BSoundPlayer\n");
537 return;
540 fSoundPlayer->SetHasData(true);
541 fSoundPlayer->Start();
543 fInitCheck = true;
547 void
548 BSoftSynth::_Done()
550 if (fSoundPlayer) {
551 fSoundPlayer->SetHasData(false);
552 fSoundPlayer->Stop();
553 delete fSoundPlayer;
554 fSoundPlayer = NULL;
556 if (fSynth) {
557 delete_fluid_synth(fSynth);
558 fSynth = NULL;
560 if (fSettings) {
561 delete_fluid_settings(fSettings);
562 fSettings = NULL;
567 void
568 BSoftSynth::PlayBuffer(void* cookie, void* data, size_t size,
569 const media_raw_audio_format& format)
571 BSoftSynth* synth = (BSoftSynth*)cookie;
573 if (synth->fMonitorSize == 0) {
574 synth->fMonitor = (float*)new void*[size];
575 synth->fMonitorSize = size;
576 synth->fMonitorChans = format.channel_count;
579 // we use float samples
580 if (synth->fSynth) {
581 fluid_synth_write_float(
582 synth->fSynth, size / sizeof(float) / format.channel_count,
583 data, 0, format.channel_count,
584 data, 1, format.channel_count);
586 memcpy(synth->fMonitor, data, size);