From afc36544b7e3ffdfe378343b42e2c6773a3514a5 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Mon, 28 Dec 2015 22:07:51 -0600 Subject: [PATCH] Add a WildMidi softsynth device - This removes the preceding psuedo MIDI device for WildMidi. --- src/CMakeLists.txt | 2 +- src/sound/i_musicinterns.h | 39 ++--- src/sound/music_midi_wildmidi.cpp | 163 ------------------- src/sound/music_midistream.cpp | 2 +- src/sound/music_wildmidi_mididevice.cpp | 206 ++++++++++++++++++++++++ src/wildmidi/wildmidi_lib.cpp | 150 ++++++++++++++++- src/wildmidi/wildmidi_lib.h | 16 ++ zdoom.vcproj | 8 +- 8 files changed, 390 insertions(+), 196 deletions(-) delete mode 100644 src/sound/music_midi_wildmidi.cpp create mode 100644 src/sound/music_wildmidi_mididevice.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a86e96f01..18fdb992c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1086,12 +1086,12 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE sound/music_midistream.cpp sound/music_midi_base.cpp sound/music_midi_timidity.cpp - sound/music_midi_wildmidi.cpp sound/music_mus_opl.cpp sound/music_stream.cpp sound/music_fluidsynth_mididevice.cpp sound/music_softsynth_mididevice.cpp sound/music_timidity_mididevice.cpp + sound/music_wildmidi_mididevice.cpp sound/music_win_mididevice.cpp sound/oalsound.cpp sound/sndfile_decoder.cpp diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 4aa5f8254..03282ef37 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -219,25 +219,6 @@ protected: #endif }; -class WildMidiMIDIDevice : public PseudoMIDIDevice -{ -public: - WildMidiMIDIDevice(); - ~WildMidiMIDIDevice(); - - int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); - bool Preprocess(MIDIStreamer *song, bool looping); - bool IsOpen() const; - -protected: - - midi *mMidi; - bool mLoop; - - static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); -}; - - // Base class for software synthesizer MIDI output devices ------------------ class SoftSynthMIDIDevice : public MIDIDevice @@ -350,6 +331,26 @@ protected: FILE *File; }; +// WildMidi implementation of a MIDI device --------------------------------- + +class WildMIDIDevice : public SoftSynthMIDIDevice +{ +public: + WildMIDIDevice(); + ~WildMIDIDevice(); + + int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + void PrecacheInstruments(const WORD *instruments, int count); + FString GetStats(); + +protected: + WildMidi_Renderer *Renderer; + + void HandleEvent(int status, int parm1, int parm2); + void HandleLongEvent(const BYTE *data, int len); + void ComputeOutput(float *buffer, int len); +}; + // FluidSynth implementation of a MIDI device ------------------------------- #ifdef HAVE_FLUIDSYNTH diff --git a/src/sound/music_midi_wildmidi.cpp b/src/sound/music_midi_wildmidi.cpp deleted file mode 100644 index f6df48459..000000000 --- a/src/sound/music_midi_wildmidi.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "i_musicinterns.h" -#include "c_cvars.h" -#include "cmdlib.h" -#include "templates.h" -#include "version.h" - - -CVAR(String, wildmidi_config, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) - -static FString currentConfig; - -// added because Timidity's output is rather loud. -CUSTOM_CVAR (Float, wildmidi_mastervolume, 1.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (self < 0.f) - self = 0.f; - else if (self > 1.f) - self = 1.f; -} - -CUSTOM_CVAR (Int, wildmidi_frequency, 44100, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ // Clamp frequency to Timidity's limits - if (self < 11000) - self = 11000; - else if (self > 65000) - self = 65000; -} - -//========================================================================== -// -// WildMidiMIDIDevice Constructor -// -//========================================================================== - -WildMidiMIDIDevice::WildMidiMIDIDevice() -{ - mMidi = NULL; - mLoop = false; -} - -//========================================================================== -// -// WildMidiMIDIDevice Destructor -// -//========================================================================== - -WildMidiMIDIDevice::~WildMidiMIDIDevice () -{ - if (mMidi != NULL) WildMidi_Close(mMidi); - // do not shut down the device so that it can be reused for the next song being played. -} - -//========================================================================== -// -// WildMidiMIDIDevice :: Preprocess -// -//========================================================================== - -bool WildMidiMIDIDevice::Preprocess(MIDIStreamer *song, bool looping) -{ - TArray midi; - - // Write MIDI song to temporary file - song->CreateSMF(midi, looping ? 0 : 1); - - mMidi = WildMidi_OpenBuffer(&midi[0], midi.Size()); - if (mMidi == NULL) - { - Printf(PRINT_BOLD, "Could not open temp music file\n"); - } - mLoop = looping; - return false; -} - -//========================================================================== -// -// WildMidiMIDIDevice :: Open -// -//========================================================================== - -int WildMidiMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) -{ - if (currentConfig.CompareNoCase(wildmidi_config) != 0) - { - if (currentConfig.IsNotEmpty()) WildMidi_Shutdown(); - currentConfig = ""; - if (!WildMidi_Init(wildmidi_config, wildmidi_frequency, WM_MO_ENHANCED_RESAMPLING)) - { - currentConfig = wildmidi_config; - } - else - { - return 1; - } - } - - Stream = GSnd->CreateStream(FillStream, 32 * 1024, 0, wildmidi_frequency, this); - if (Stream == NULL) - { - Printf(PRINT_BOLD, "Could not create music stream.\n"); - return 1; - } - - return 0; -} - - -//========================================================================== -// -// WildMidiMIDIDevice :: FillStream -// -//========================================================================== - -bool WildMidiMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata) -{ - char *buffer = (char*)buff; - WildMidiMIDIDevice *song = (WildMidiMIDIDevice *)userdata; - if (song->mMidi != NULL) - { - while (len > 0) - { - int written = WildMidi_GetOutput(song->mMidi, buffer, len); - if (written < 0) - { - // error - memset(buffer, 0, len); - return false; - } - buffer += written; - len -= written; - - if (len > 0) - { - if (!song->mLoop) - { - memset(buffer, 0, len); - return written > 0; - } - else - { - // loop the sound (i.e. go back to start.) - unsigned long spos = 0; - WildMidi_FastSeek(song->mMidi, &spos); - } - - } - } - } - - return true; -} - -//========================================================================== -// -// WildMidiMIDIDevice :: IsOpen -// -//========================================================================== - -bool WildMidiMIDIDevice::IsOpen() const -{ - return mMidi != NULL; -} - diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 42e8b5926..37616b9a6 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -294,7 +294,7 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const return new TimidityPPMIDIDevice; case MDEV_WILDMIDI: - return new WildMidiMIDIDevice; + return new WildMIDIDevice; default: return NULL; diff --git a/src/sound/music_wildmidi_mididevice.cpp b/src/sound/music_wildmidi_mididevice.cpp new file mode 100644 index 000000000..44c060f26 --- /dev/null +++ b/src/sound/music_wildmidi_mididevice.cpp @@ -0,0 +1,206 @@ +/* +** music_wildmidi_mididevice.cpp +** Provides access to WildMidi as a generic MIDI device. +** +**--------------------------------------------------------------------------- +** Copyright 2015 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "i_musicinterns.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "w_wad.h" +#include "v_text.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static FString CurrentConfig; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +CVAR(String, wildmidi_config, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, wildmidi_frequency, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// WildMIDIDevice Constructor +// +//========================================================================== + +WildMIDIDevice::WildMIDIDevice() +{ + Renderer = NULL; + + if (wildmidi_frequency >= 11025 && wildmidi_frequency < 65536) + { // Use our own sample rate instead of the global one + SampleRate = wildmidi_frequency; + } + else + { // Else make sure we're not outside of WildMidi's range + SampleRate = clamp(SampleRate, 11025, 65535); + } + + if (CurrentConfig.CompareNoCase(wildmidi_config) != 0 || SampleRate != WildMidi_GetSampleRate()) + { + if (CurrentConfig.IsNotEmpty()) + { + WildMidi_Shutdown(); + CurrentConfig = ""; + } + if (!WildMidi_Init(wildmidi_config, SampleRate, WM_MO_ENHANCED_RESAMPLING)) + { + CurrentConfig = wildmidi_config; + } + } + if (CurrentConfig.IsNotEmpty()) + { + Renderer = new WildMidi_Renderer(); + } +} + +//========================================================================== +// +// WildMIDIDevice Destructor +// +//========================================================================== + +WildMIDIDevice::~WildMIDIDevice() +{ + Close(); + if (Renderer != NULL) + { + delete Renderer; + } + // Do not shut down the device so that it can be reused for the next song being played. +} + +//========================================================================== +// +// WildMIDIDevice :: Open +// +// Returns 0 on success. +// +//========================================================================== + +int WildMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +{ + if (Renderer == NULL) + { + return 1; + } + int ret = OpenStream(2, 0, callback, userdata); + if (ret == 0) + { +// Renderer->Reset(); + } + return ret; +} + +//========================================================================== +// +// WildMIDIDevice :: PrecacheInstruments +// +// Each entry is packed as follows: +// Bits 0- 6: Instrument number +// Bits 7-13: Bank number +// Bit 14: Select drum set if 1, tone bank if 0 +// +//========================================================================== + +void WildMIDIDevice::PrecacheInstruments(const WORD *instruments, int count) +{ + for (int i = 0; i < count; ++i) + { + Renderer->LoadInstrument((instruments[i] >> 7) & 127, instruments[i] >> 14, instruments[i] & 127); + } +} + + +//========================================================================== +// +// WildMIDIDevice :: HandleEvent +// +//========================================================================== + +void WildMIDIDevice::HandleEvent(int status, int parm1, int parm2) +{ + Renderer->ShortEvent(status, parm1, parm2); +} + +//========================================================================== +// +// WildMIDIDevice :: HandleLongEvent +// +//========================================================================== + +void WildMIDIDevice::HandleLongEvent(const BYTE *data, int len) +{ + Renderer->LongEvent((const char *)data, len); +} + +//========================================================================== +// +// WildMIDIDevice :: ComputeOutput +// +//========================================================================== + +void WildMIDIDevice::ComputeOutput(float *buffer, int len) +{ + Renderer->ComputeOutput(buffer, len); +} + +//========================================================================== +// +// WildMIDIDevice :: GetStats +// +//========================================================================== + +FString WildMIDIDevice::GetStats() +{ + FString out; + out.Format("%3d voices", Renderer->GetVoiceCount()); + return out; +} diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 385ac6c1d..f20420e75 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -3643,6 +3643,18 @@ static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count) return buffer; } +int *WM_Mix(midi *handle, int *buffer, unsigned long count) +{ + if (((struct _mdi *)handle)->info.mixer_options & WM_MO_ENHANCED_RESAMPLING) + { + return WM_Mix_Gauss(handle, buffer, count); + } + else + { + return WM_Mix_Linear(handle, buffer, count); + } +} + static int WM_DoGetOutput(midi * handle, char * buffer, unsigned long int size) { unsigned long int buffer_used = 0; @@ -3702,14 +3714,7 @@ static int WM_DoGetOutput(midi * handle, char * buffer, } /* do mixing here */ - if (mdi->info.mixer_options & WM_MO_ENHANCED_RESAMPLING) - { - tmp_buffer = WM_Mix_Gauss(handle, tmp_buffer, real_samples_to_mix); - } - else - { - tmp_buffer = WM_Mix_Linear(handle, tmp_buffer, real_samples_to_mix); - } + tmp_buffer = WM_Mix(handle, tmp_buffer, real_samples_to_mix); buffer_used += real_samples_to_mix * 4; size -= (real_samples_to_mix << 2); @@ -3807,6 +3812,11 @@ WM_SYMBOL int WildMidi_Init(const char * config_file, unsigned short int rate, return 0; } +WM_SYMBOL int WildMidi_GetSampleRate(void) +{ + return _WM_SampleRate; +} + WM_SYMBOL int WildMidi_MasterVolume(unsigned char master_volume) { struct _mdi *mdi = NULL; struct _hndl * tmp_handle = first_handle; @@ -3947,6 +3957,23 @@ WildMidi_OpenBuffer(unsigned char *midibuffer, unsigned long int size) { return ret; } +midi *WildMidi_NewMidi() { + midi * ret = NULL; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return NULL; + } + ret = Init_MDI(); + if (ret) { + if (add_handle(ret) != 0) { + WildMidi_Close(ret); + ret = NULL; + } + } + return ret; +} + WM_SYMBOL int WildMidi_FastSeek(midi * handle, unsigned long int *sample_pos) { struct _mdi *mdi; struct _event *event; @@ -4197,3 +4224,110 @@ WM_SYMBOL int WildMidi_Shutdown(void) { return 0; } + +WildMidi_Renderer::WildMidi_Renderer() +{ + handle = WildMidi_NewMidi(); +} + +WildMidi_Renderer::~WildMidi_Renderer() +{ + WildMidi_Close((midi *)handle); +} + +void WildMidi_Renderer::ShortEvent(int status, int parm1, int parm2) +{ + _mdi *mdi = (_mdi *)handle; + _event_data ev; + + ev.channel = status & 0x0F; + switch ((status & 0xF0) >> 4) // command + { + case 0x8: + ev.data = (parm1 << 8) | parm2; + do_note_off(mdi, &ev); + break; + + case 0x9: + ev.data = (parm1 << 8) | parm2; + do_note_on(mdi, &ev); + break; + + case 0xA: + ev.data = (parm1 << 8) | parm2; + do_aftertouch(mdi, &ev); + break; + + case 0xC: + ev.data = parm1; + do_patch(mdi, &ev); + break; + + case 0xD: + ev.data = parm1; + do_channel_pressure(mdi, &ev); + break; + + case 0xE: + ev.data = parm1 | (parm2 << 7); + do_pitch(mdi, &ev); + break; + + case 0xB: // Controllers + ev.data = parm2; + switch (parm1) + { + case 0: do_control_bank_select(mdi, &ev); break; + case 6: do_control_data_entry_course(mdi, &ev); break; // [sic] + case 7: do_control_channel_volume(mdi, &ev); break; + case 8: do_control_channel_balance(mdi, &ev); break; + case 10: do_control_channel_pan(mdi, &ev); break; + case 11: do_control_channel_expression(mdi, &ev); break; + case 38: do_control_data_entry_fine(mdi, &ev); break; + case 64: do_control_channel_hold(mdi, &ev); break; + case 96: do_control_data_increment(mdi, &ev); break; + case 97: do_control_data_decrement(mdi, &ev); break; + case 98: + case 99: do_control_non_registered_param(mdi, &ev); break; + case 100: do_control_registered_param_fine(mdi, &ev); break; + case 101: do_control_registered_param_course(mdi, &ev); break; // [sic] + case 120: do_control_channel_sound_off(mdi, &ev); break; + case 121: do_control_channel_controllers_off(mdi, &ev); break; + case 123: do_control_channel_notes_off(mdi, &ev); break; + } + } +} + +void WildMidi_Renderer::LongEvent(const char *data, int len) +{ +} + +void WildMidi_Renderer::ComputeOutput(float *fbuffer, int len) +{ + _mdi *mdi = (_mdi *)handle; + int *buffer = (int *)fbuffer; + int *newbuf = WM_Mix(handle, buffer, len); +// assert(newbuf - buffer == len); + if (mdi->info.mixer_options & WM_MO_REVERB) { + _WM_do_reverb(mdi->reverb, buffer, len * 2); + } + for (; buffer < newbuf; ++buffer) + { + *(float *)buffer = (float)*buffer / 32768.f; + } +} + +void WildMidi_Renderer::LoadInstrument(int bank, int percussion, int instr) +{ + load_patch((_mdi *)handle, (bank << 8) | instr | (percussion ? 0x80 : 0)); +} + +int WildMidi_Renderer::GetVoiceCount() +{ + int count = 0; + for (_note *note_data = ((_mdi *)handle)->note; note_data != NULL; note_data = note_data->next) + { + count++; + } + return count; +} diff --git a/src/wildmidi/wildmidi_lib.h b/src/wildmidi/wildmidi_lib.h index e6317a271..46a517d90 100644 --- a/src/wildmidi/wildmidi_lib.h +++ b/src/wildmidi/wildmidi_lib.h @@ -64,6 +64,7 @@ WM_SYMBOL struct _WM_Info * WildMidi_GetInfo (midi * handle); WM_SYMBOL int WildMidi_FastSeek (midi * handle, unsigned long int *sample_pos); WM_SYMBOL int WildMidi_Close (midi * handle); WM_SYMBOL int WildMidi_Shutdown (void); +WM_SYMBOL int WildMidi_GetSampleRate (void); /* #if defined(__cplusplus) @@ -71,5 +72,20 @@ WM_SYMBOL int WildMidi_Shutdown (void); #endif */ +class WildMidi_Renderer +{ +public: + WildMidi_Renderer(); + ~WildMidi_Renderer(); + + void ShortEvent(int status, int parm1, int parm2); + void LongEvent(const char *data, int len); + void ComputeOutput(float *buffer, int len); + void LoadInstrument(int bank, int percussion, int instr); + int GetVoiceCount(); +private: + void *handle; +}; + #endif /* WILDMIDI_LIB_H */ diff --git a/zdoom.vcproj b/zdoom.vcproj index 126efb7f5..a5d7bfb8e 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -2553,10 +2553,6 @@ RelativePath="src\sound\music_midi_timidity.cpp" > - - @@ -2597,6 +2593,10 @@ RelativePath=".\src\sound\music_timidity_mididevice.cpp" > + +