From d1c4420ee5ee34d79f6858503941467a8a12c3e5 Mon Sep 17 00:00:00 2001 From: mahlzeit Date: Fri, 11 Mar 2005 16:48:12 +0000 Subject: [PATCH] Added preliminary support for using TiMidity++ as softsynth. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11678 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/kits/midi/SoftSynth.cpp | 175 ++++++++++++++++++++++++++++++------ src/kits/midi/SoftSynth.h | 56 +++++++++++- 2 files changed, 201 insertions(+), 30 deletions(-) diff --git a/src/kits/midi/SoftSynth.cpp b/src/kits/midi/SoftSynth.cpp index 45d273e3a3..8e27848044 100644 --- a/src/kits/midi/SoftSynth.cpp +++ b/src/kits/midi/SoftSynth.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 Matthijs Hollemans + * Copyright (c) 2004-2005 Matthijs Hollemans * Copyright (c) 2003 Jerome Leveque * * Permission is hereby granted, free of charge, to any person obtaining a @@ -21,12 +21,16 @@ * DEALINGS IN THE SOFTWARE. */ +#include +#include +#include #include #include #include #include #include "debug.h" +#include "MidiGlue.h" // for MAKE_BIGTIME #include "SoftSynth.h" using namespace BPrivate; @@ -45,13 +49,29 @@ BSoftSynth::BSoftSynth() reverbEnabled = true; reverbMode = B_REVERB_BALLROOM; volumeScale = 1.0; + + Init(); } //------------------------------------------------------------------------------ BSoftSynth::~BSoftSynth() { + // Note: it is possible that we don't get deleted. When BSynth is + // created, it is assigned to the global variable be_synth. While + // BSynth is alive, it keeps a copy of BSoftSynth around too. Not + // a big deal, but the Midi Kit will complain (on stdout) that we + // didn't release our endpoints. + Unload(); + Done(); +} + +//------------------------------------------------------------------------------ + +bool BSoftSynth::InitCheck(void) const +{ + return initCheck; } //------------------------------------------------------------------------------ @@ -90,14 +110,10 @@ status_t BSoftSynth::SetDefaultInstrumentsFile() status_t BSoftSynth::SetInstrumentsFile(const char* path) { if (path == NULL) - { return B_BAD_VALUE; - } if (IsLoaded()) - { Unload(); - } instrumentsFile = strdup(path); return B_OK; @@ -298,8 +314,12 @@ void BSoftSynth::Resume(void) void BSoftSynth::NoteOff( uchar channel, uchar note, uchar velocity, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + producer->SprayNoteOff( + channel - 1, note, velocity, MAKE_BIGTIME(time)); + } } //------------------------------------------------------------------------------ @@ -307,8 +327,11 @@ void BSoftSynth::NoteOff( void BSoftSynth::NoteOn( uchar channel, uchar note, uchar velocity, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + producer->SprayNoteOn(channel - 1, note, velocity, MAKE_BIGTIME(time)); + } } //------------------------------------------------------------------------------ @@ -316,8 +339,12 @@ void BSoftSynth::NoteOn( void BSoftSynth::KeyPressure( uchar channel, uchar note, uchar pressure, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + producer->SprayKeyPressure( + channel - 1, note, pressure, MAKE_BIGTIME(time)); + } } //------------------------------------------------------------------------------ @@ -325,8 +352,12 @@ void BSoftSynth::KeyPressure( void BSoftSynth::ControlChange( uchar channel, uchar controlNumber, uchar controlValue, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + producer->SprayControlChange( + channel - 1, controlNumber, controlValue, MAKE_BIGTIME(time)); + } } //------------------------------------------------------------------------------ @@ -334,32 +365,46 @@ void BSoftSynth::ControlChange( void BSoftSynth::ProgramChange( uchar channel, uchar programNumber, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + producer->SprayProgramChange( + channel - 1, programNumber, MAKE_BIGTIME(time)); + } } //------------------------------------------------------------------------------ void BSoftSynth::ChannelPressure(uchar channel, uchar pressure, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + producer->SprayChannelPressure( + channel - 1, pressure, MAKE_BIGTIME(time)); + } } //------------------------------------------------------------------------------ void BSoftSynth::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + producer->SprayPitchBend(channel - 1, lsb, msb, MAKE_BIGTIME(time)); + } } //------------------------------------------------------------------------------ void BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + producer->SpraySystemExclusive(data, length, MAKE_BIGTIME(time)); + } } //------------------------------------------------------------------------------ @@ -367,32 +412,104 @@ void BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time) void BSoftSynth::SystemCommon( uchar status, uchar data1, uchar data2, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + producer->SpraySystemCommon(status, data1, data2, MAKE_BIGTIME(time)); + } } //------------------------------------------------------------------------------ void BSoftSynth::SystemRealTime(uchar status, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + producer->SpraySystemRealTime(status, MAKE_BIGTIME(time)); + } } //------------------------------------------------------------------------------ void BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + producer->SprayTempoChange(beatsPerMinute, MAKE_BIGTIME(time)); + } } //------------------------------------------------------------------------------ void BSoftSynth::AllNotesOff(bool justChannel, uint32 time) { - snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE); - UNIMPLEMENTED + if (InitCheck()) + { + snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); + + // from BMidi::AllNotesOff + for (uchar channel = 1; channel <= 16; ++channel) + { + producer->SprayControlChange(channel, B_ALL_NOTES_OFF, 0, time); + + if (!justChannel) + { + for (uchar note = 0; note <= 0x7F; ++note) + { + producer->SprayNoteOff(channel, note, 0, time); + } + } + } + } +} + +//------------------------------------------------------------------------------ + +void BSoftSynth::Init() +{ + initCheck = false; + + producer = new BMidiLocalProducer("TiMidity feeder"); + + int32 id = 0; + while ((consumer = BMidiRoster::NextConsumer(&id)) != NULL) + { + if (strcmp(consumer->Name(), "TiMidity input") == 0) + break; + + consumer->Release(); + } + + if (consumer == NULL) + { + fprintf(stderr, "[midi] TiMidity consumer not found\n"); + return; + } + + if (producer->Connect(consumer) != B_OK) + { + fprintf(stderr, "[midi] Could not connect to TiMidity\n"); + return; + } + + initCheck = true; +} + +//------------------------------------------------------------------------------ + +void BSoftSynth::Done() +{ + if (consumer != NULL) + { + producer->Disconnect(consumer); + consumer->Release(); + consumer = NULL; + } + + producer->Release(); + producer = NULL; } //------------------------------------------------------------------------------ diff --git a/src/kits/midi/SoftSynth.h b/src/kits/midi/SoftSynth.h index 256cec621e..78482acd5c 100644 --- a/src/kits/midi/SoftSynth.h +++ b/src/kits/midi/SoftSynth.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 Matthijs Hollemans + * Copyright (c) 2004-2005 Matthijs Hollemans * Copyright (c) 2003 Jerome Leveque * * Permission is hereby granted, free of charge, to any person obtaining a @@ -24,15 +24,50 @@ #ifndef _SOFT_SYNTH_H #define _SOFT_SYNTH_H +/* + WORK IN PROGRESS! + + This version of SoftSynth is a wrapper for Michael Pfeiffer's port + of TiMidity++ 2.11.3/3 (http://www.bebits.com/app/2736). + + It works, but not good enough yet. Playback from MidiPlayer sounds + a lot worse than using TiMidity in standalone mode. Either TiMidity's + MidiConsumer doesn't work properly or the Midi Kit messes things up; + I haven't investigated yet. + + To try it out, download TiMidity and the sound files (30MB): + http://bepdf.sourceforge.net/download/midi/TiMidity++-2.11.3_3.x86.zip + http://bepdf.sourceforge.net/download/midi/eawpats11_full_beos.zip + + Follow the instructions in the archive to install. Then double-click + "Start TiMidity Server" to launch TiMidity. The server will publish a + consumer endpoint. You can verify this with PatchBay. + + Build the Haiku Midi Kit. Put libmidi.so and libmidi2.so in ~/config/lib. + Quit the BeOS midi_server. Launch the Haiku midi_server. + + Build the Haiku MidiPlayer (or use the BeOS MidiPlayer). Start it and + choose a MIDI file. If all went fine, you will hear TiMidity play back + the song. Just not very well. :-) + + Note: You can still use the Midi Kit if you don't install TiMidity, + but the software synth will simply make no sound. + */ + #include #include +class BMidiConsumer; +class BMidiLocalProducer; + namespace BPrivate { class BSoftSynth { public: + bool InitCheck(void) const; + void Unload(void); bool IsLoaded(void) const; @@ -89,6 +124,10 @@ private: BSoftSynth(); ~BSoftSynth(); + void Init(); + void Done(); + + bool initCheck; char* instrumentsFile; int32 sampleRate; interpolation_mode interpMode; @@ -97,6 +136,21 @@ private: reverb_mode reverbMode; bool reverbEnabled; double volumeScale; + + /* + Note: Maybe this isn't the most efficient way to do things. + Now a producer connects to BMidiSynth, which is a consumer. + That consumer directly calls our NoteOff() etc, methods. + We create a new producer and connect it to TiMidity's consumer. + It would save some indirection if BMidiSynth's consumer would + be a proxy for TiMidity's. (I don't think that is possible, + because BMidiSynth is a BMidi object which always creates a + new consumer regardless. In any case, notes have to travel + a long way before they reach TiMidity. + */ + + BMidiConsumer* consumer; + BMidiLocalProducer* producer; }; } // namespace BPrivate