diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9332e4919..a46c822f0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -188,6 +188,7 @@ else( WIN32 ) if( GTK2_FOUND ) set( ZDOOM_LIBS ${ZDOOM_LIBS} ${GTK2_LIBRARIES} ) include_directories( ${GTK2_INCLUDE_DIRS} ) + link_directories( ${GTK2_LIBRARY_DIRS} ) else( GTK2_FOUND ) set( NO_GTK ON ) endif( GTK2_FOUND ) @@ -1174,6 +1175,7 @@ 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 @@ -1222,6 +1224,12 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE timidity/playmidi.cpp timidity/resample.cpp timidity/timidity.cpp + wildmidi/file_io.cpp + wildmidi/gus_pat.cpp + wildmidi/lock.cpp + wildmidi/reverb.cpp + wildmidi/wildmidi_lib.cpp + wildmidi/wm_error.cpp xlat/parse_xlat.cpp fragglescript/t_fspic.cpp fragglescript/t_func.cpp @@ -1359,6 +1367,8 @@ source_group("Audio Files\\OPL Synth" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURC source_group("Audio Files\\OPL Synth\\DOSBox" FILES oplsynth/dosbox/opl.cpp oplsynth/dosbox/opl.h) source_group("Audio Files\\Timidity\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.h$") source_group("Audio Files\\Timidity\\Source" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.cpp$") +source_group("Audio Files\\WildMidi\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/wildmidi/.+\\.h$") +source_group("Audio Files\\WildMidi\\Source" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/wildmidi/.+\\.cpp$") source_group("Decorate++" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/thingdef/.+") source_group("FraggleScript" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/fragglescript/.+") source_group("Games\\Doom Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_doom/.+") diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 069fca6e1..85edc53ab 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -1347,9 +1347,10 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply int rootnode = npc->ConversationRoot; if (reply->NextNode < 0) { - npc->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1]; - if (gameaction != ga_slideshow) + unsigned next = (unsigned)(rootnode - reply->NextNode - 1); + if (gameaction != ga_slideshow && next < StrifeDialogues.Size()) { + npc->Conversation = StrifeDialogues[next]; P_StartConversation (npc, player->mo, player->ConversationFaceTalker, false); return; } diff --git a/src/p_user.cpp b/src/p_user.cpp index 0d5cdce20..331a978b7 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -566,6 +566,10 @@ void APlayerPawn::Serialize (FArchive &arc) { arc << AirCapacity; } + if (SaveVersion >= 4526) + { + arc << ViewHeight; + } } //=========================================================================== diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index c72005813..cb0f26ae6 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -1372,6 +1372,7 @@ static void S_AddSNDINFO (int lump) else if (sc.Compare("default")) MidiDevices[nm] = MDEV_DEFAULT; else if (sc.Compare("fluidsynth")) MidiDevices[nm] = MDEV_FLUIDSYNTH; else if (sc.Compare("gus")) MidiDevices[nm] = MDEV_GUS; + else if (sc.Compare("wildmidi")) MidiDevices[nm] = MDEV_WILDMIDI; else sc.ScriptError("Unknown MIDI device %s\n", sc.String); } break; diff --git a/src/s_sound.h b/src/s_sound.h index 49fb81fb3..16aa8b333 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -394,6 +394,7 @@ enum EMidiDevice MDEV_TIMIDITY = 3, MDEV_FLUIDSYNTH = 4, MDEV_GUS = 5, + MDEV_WILDMIDI = 6, }; typedef TMap MusicAliasMap; diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 3f8fac0da..721be5192 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -189,6 +189,7 @@ void I_ShutdownMusic(void) assert (currSong == NULL); } Timidity::FreeAll(); + WildMidi_Shutdown(); #ifdef _WIN32 I_ShutdownMusicWin32(); #endif // _WIN32 diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 77aa31312..4aa5f8254 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -24,6 +24,7 @@ #include "i_music.h" #include "s_sound.h" #include "files.h" +#include "wildmidi/wildmidi_lib.h" void I_InitMusicWin32 (); void I_ShutdownMusicWin32 (); @@ -218,6 +219,24 @@ 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 ------------------ diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index 649f9002e..e1afa09a8 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -11,9 +11,9 @@ static DWORD nummididevices; static bool nummididevicesset; #ifdef HAVE_FLUIDSYNTH -#define NUM_DEF_DEVICES 5 +#define NUM_DEF_DEVICES 6 #else -#define NUM_DEF_DEVICES 4 +#define NUM_DEF_DEVICES 5 #endif static void AddDefaultMidiDevices(FOptionValues *opt) @@ -33,8 +33,10 @@ static void AddDefaultMidiDevices(FOptionValues *opt) pair[p+1].Value = -3.0; pair[p+2].Text = "TiMidity++"; pair[p+2].Value = -2.0; - pair[p+3].Text = "Sound System"; - pair[p+3].Value = -1.0; + pair[p+3].Text = "WildMidi"; + pair[p+3].Value = -6.0; + pair[p+4].Text = "Sound System"; + pair[p+4].Value = -1.0; } @@ -70,7 +72,7 @@ CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) if (!nummididevicesset) return; - if ((self >= (signed)nummididevices) || (self < -5)) + if ((self >= (signed)nummididevices) || (self < -6)) { Printf ("ID out of range. Using default device.\n"); self = 0; @@ -211,6 +213,7 @@ void I_BuildMIDIMenuList (FOptionValues *opt) CCMD (snd_listmididevices) { + Printf("%s-6. WildMidi\n", -6 == snd_mididevice ? TEXTCOLOR_BOLD : ""); #ifdef HAVE_FLUIDSYNTH Printf("%s-5. FluidSynth\n", -5 == snd_mididevice ? TEXTCOLOR_BOLD : ""); #endif diff --git a/src/sound/music_midi_wildmidi.cpp b/src/sound/music_midi_wildmidi.cpp new file mode 100644 index 000000000..f6df48459 --- /dev/null +++ b/src/sound/music_midi_wildmidi.cpp @@ -0,0 +1,163 @@ +#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 6c85c06af..42e8b5926 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -240,6 +240,7 @@ EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device) #ifdef HAVE_FLUIDSYNTH case -5: return MDEV_FLUIDSYNTH; #endif + case -6: return MDEV_WILDMIDI; default: #ifdef _WIN32 return MDEV_MMAPI; @@ -292,6 +293,9 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const case MDEV_TIMIDITY: return new TimidityPPMIDIDevice; + case MDEV_WILDMIDI: + return new WildMidiMIDIDevice; + default: return NULL; } diff --git a/src/version.h b/src/version.h index 0cb89765d..3034e2c3e 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4525 +#define SAVEVER 4526 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) diff --git a/src/wildmidi/common.h b/src/wildmidi/common.h new file mode 100644 index 000000000..44e90efdd --- /dev/null +++ b/src/wildmidi/common.h @@ -0,0 +1,94 @@ +/* + common.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __COMMON_H +#define __COMMON_H + +#define SAMPLE_16BIT 0x01 +#define SAMPLE_UNSIGNED 0x02 +#define SAMPLE_LOOP 0x04 +#define SAMPLE_PINGPONG 0x08 +#define SAMPLE_REVERSE 0x10 +#define SAMPLE_SUSTAIN 0x20 +#define SAMPLE_ENVELOPE 0x40 +#define SAMPLE_CLAMPED 0x80 + +#ifdef DEBUG_SAMPLES +#define SAMPLE_CONVERT_DEBUG(dx) printf("\r%s\n",dx) +#else +#define SAMPLE_CONVERT_DEBUG(dx) +#endif + +extern unsigned short int _WM_SampleRate; + +struct _sample { + unsigned long int data_length; + unsigned long int loop_start; + unsigned long int loop_end; + unsigned long int loop_size; + unsigned char loop_fraction; + unsigned short int rate; + unsigned long int freq_low; + unsigned long int freq_high; + unsigned long int freq_root; + unsigned char modes; + signed long int env_rate[7]; + signed long int env_target[7]; + unsigned long int inc_div; + signed short *data; + struct _sample *next; +}; + +struct _env { + float time; + float level; + unsigned char set; +}; + +struct _patch { + unsigned short patchid; + unsigned char loaded; + char *filename; + signed short int amp; + unsigned char keep; + unsigned char remove; + struct _env env[6]; + unsigned char note; + unsigned long int inuse_count; + struct _sample *first_sample; + struct _patch *next; +}; + +/* Set our global defines here */ +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + +#endif /* __COMMON_H */ diff --git a/src/wildmidi/file_io.cpp b/src/wildmidi/file_io.cpp new file mode 100644 index 000000000..f14e22bbf --- /dev/null +++ b/src/wildmidi/file_io.cpp @@ -0,0 +1,71 @@ +/* +** file_io.cpp +** ZDoom compatible file IO interface for WildMIDI +** (This file was completely redone to remove the low level IO code references) +** +**--------------------------------------------------------------------------- +** Copyright 2010 Christoph Oelckers +** 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. +**--------------------------------------------------------------------------- +** +*/ + +#include "../files.h" +#include "wm_error.h" +#include "file_io.h" + +unsigned char *_WM_BufferFile(const char *filename, unsigned long int *size) +{ + FileReader file; + + if (!file.Open(filename)) + { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); + return NULL; + } + + long fsize = file.GetLength(); + + if (fsize > WM_MAXFILESIZE) + { + /* don't bother loading suspiciously long files */ + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LONGFIL, filename, 0); + return NULL; + } + + unsigned char *data = (unsigned char*)malloc(fsize+1); + if (data == NULL) + { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); + return NULL; + } + + file.Read(data, fsize); + data[fsize] = 0; + *size = fsize; + return data; +} diff --git a/src/wildmidi/file_io.h b/src/wildmidi/file_io.h new file mode 100644 index 000000000..d38caffbb --- /dev/null +++ b/src/wildmidi/file_io.h @@ -0,0 +1,33 @@ +/* + file_io.c + + file handling + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __FILE_IO_H +#define __FILE_IO_H + +#define WM_MAXFILESIZE 0x1fffffff +extern unsigned char *_WM_BufferFile (const char *filename, unsigned long int *size); + +#endif /* __FILE_IO_H */ diff --git a/src/wildmidi/gus_pat.cpp b/src/wildmidi/gus_pat.cpp new file mode 100644 index 000000000..be3422db5 --- /dev/null +++ b/src/wildmidi/gus_pat.cpp @@ -0,0 +1,900 @@ +/* + gus_pat.c + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +//#include "config.h" + +#include +#include +#include +#include + +#include "gus_pat.h" +#include "common.h" +#include "wm_error.h" +#include "file_io.h" + +#ifdef DEBUG_GUSPAT +#define GUSPAT_FILENAME_DEBUG(dx) fprintf(stderr,"\r%s\n",dx) + +#define GUSPAT_INT_DEBUG(dx,dy) fprintf(stderr,"\r%s: %i\n",dx,dy) +#define GUSPAT_FLOAT_DEBUG(dx,dy) fprintf(stderr,"\r%s: %f\n",dx,dy) +#define GUSPAT_START_DEBUG() fprintf(stderr,"\r") +#define GUSPAT_MODE_DEBUG(dx,dy,dz) if (dx & dy) fprintf(stderr,"%s",dz) +#define GUSPAT_END_DEBUG() fprintf(stderr,"\n") +#else +#define GUSPAT_FILENAME_DEBUG(dx) +#define GUSPAT_INT_DEBUG(dx,dy) +#define GUSPAT_FLOAT_DEBUG(dx,dy) +#define GUSPAT_START_DEBUG() +#define GUSPAT_MODE_DEBUG(dx,dy,dz) +#define GUSPAT_END_DEBUG() +#endif + +/* + * sample data conversion functions + * convert data to signed shorts + */ + +/* 8bit signed */ +static int convert_8s(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = (*read_data++) << 8; + } while (read_data != read_end); + return 0; + } + + _WM_ERROR_NEW("(%s:%i) ERROR: calloc failed (%s)", __FUNCTION__, __LINE__, + strerror(errno)); + return -1; +} + +/* 8bit signed ping pong */ +static int convert_8sp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = (*read_data++) << 8; + } while (read_data != read_end); + + *write_data = (*read_data++ << 8); + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = (*read_data++ << 8); + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b++ = (*read_data++) << 8; + } while (read_data != read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit signed reverse */ +static int convert_8sr(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + gus_sample->data_length - 1; + do { + *write_data-- = (*read_data++) << 8; + } while (read_data != read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->modes ^= SAMPLE_REVERSE; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit signed reverse ping pong */ +static int convert_8srp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = (*read_data--) << 8; + } while (read_data != read_end); + + *write_data = (*read_data-- << 8); + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_start; + do { + *write_data = (*read_data--) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = (*read_data-- << 8); + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b++ = (*read_data--) << 8; + write_data_b++; + } while (read_data != read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned */ +static int convert_8u(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + gus_sample->modes ^= SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned ping pong */ +static int convert_8up(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + + *write_data = ((*read_data++) ^ 0x80) << 8; + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_end; + do { + *write_data = ((*read_data++) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = ((*read_data++) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b++ = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_UNSIGNED; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned reverse */ +static int convert_8ur(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + gus_sample->data_length - 1; + do { + *write_data-- = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned reverse ping pong */ +static int convert_8urp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = ((*read_data--) ^ 0x80) << 8; + } while (read_data != read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_start; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b++ = ((*read_data--) ^ 0x80) << 8; + } while (read_data != read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed */ +static int convert_16s(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = *read_data++; + *write_data++ |= (*read_data++) << 8; + } while (read_data < read_end); + + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed ping pong */ +static int convert_16sp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = (*read_data++); + *write_data++ |= (*read_data++) << 8; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= (*read_data++) << 8; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++); + *write_data |= (*read_data++) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = *(read_data++); + *write_data |= (*read_data++) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b = *(read_data++); + *write_data_b++ |= (*read_data++) << 8; + } while (read_data < read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed reverse */ +static int convert_16sr(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1; + do { + *write_data = *read_data++; + *write_data-- |= (*read_data++) << 8; + } while (read_data < read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_REVERSE; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed reverse ping pong */ +static int convert_16srp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = (*read_data--) << 8; + *write_data++ |= *read_data--; + } while (read_data < read_end); + + *write_data = (*read_data-- << 8); + *write_data |= *read_data--; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_start; + do { + *write_data = (*read_data--) << 8; + *write_data |= *read_data--; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = ((*read_data--) << 8); + *write_data |= *read_data--; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b = (*read_data--) << 8; + *write_data_b++ |= *read_data--; + } while (read_data < read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned */ +static int convert_16u(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = *read_data++; + *write_data++ |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned ping pong */ +static int convert_16up(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = (*read_data++); + *write_data++ |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b = (*read_data++); + *write_data_b++ |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned reverse */ +static int convert_16ur(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1; + do { + *write_data = *read_data++; + *write_data-- |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned reverse ping pong */ +static int convert_16urp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data++ |= *read_data--; + } while (read_data < read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_start; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b = ((*read_data--) ^ 0x80) << 8; + *write_data_b++ |= *read_data--; + } while (read_data < read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* sample loading */ + +struct _sample * _WM_load_gus_pat(const char *filename, int fix_release) { + unsigned char *gus_patch; + unsigned long int gus_size; + unsigned long int gus_ptr; + unsigned char no_of_samples; + struct _sample *gus_sample = NULL; + struct _sample *first_gus_sample = NULL; + unsigned long int i = 0; + + int (*do_convert[])(unsigned char *data, struct _sample *gus_sample) = { + convert_8s, + convert_16s, + convert_8u, + convert_16u, + convert_8sp, + convert_16sp, + convert_8up, + convert_16up, + convert_8sr, + convert_16sr, + convert_8ur, + convert_16ur, + convert_8srp, + convert_16srp, + convert_8urp, + convert_16urp + }; + unsigned long int tmp_loop; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); SAMPLE_CONVERT_DEBUG(filename); + + if ((gus_patch = _WM_BufferFile(filename, &gus_size)) == NULL) { + return NULL; + } + if (gus_size < 239) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + if (memcmp(gus_patch, "GF1PATCH110\0ID#000002", 22) + && memcmp(gus_patch, "GF1PATCH100\0ID#000002", 22)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(unsupported format)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + if (gus_patch[82] > 1) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(unsupported format)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + if (gus_patch[151] > 1) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(unsupported format)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + + GUSPAT_FILENAME_DEBUG(filename); GUSPAT_INT_DEBUG("voices",gus_patch[83]); + + no_of_samples = gus_patch[198]; + gus_ptr = 239; + while (no_of_samples) { + unsigned long int tmp_cnt; + if (first_gus_sample == NULL) { + first_gus_sample = (struct _sample*)malloc(sizeof(struct _sample)); + gus_sample = first_gus_sample; + } else { + gus_sample->next = (struct _sample*)malloc(sizeof(struct _sample)); + gus_sample = gus_sample->next; + } + if (gus_sample == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + + gus_sample->next = NULL; + gus_sample->loop_fraction = gus_patch[gus_ptr + 7]; + gus_sample->data_length = (gus_patch[gus_ptr + 11] << 24) + | (gus_patch[gus_ptr + 10] << 16) + | (gus_patch[gus_ptr + 9] << 8) | gus_patch[gus_ptr + 8]; + gus_sample->loop_start = (gus_patch[gus_ptr + 15] << 24) + | (gus_patch[gus_ptr + 14] << 16) + | (gus_patch[gus_ptr + 13] << 8) | gus_patch[gus_ptr + 12]; + gus_sample->loop_end = (gus_patch[gus_ptr + 19] << 24) + | (gus_patch[gus_ptr + 18] << 16) + | (gus_patch[gus_ptr + 17] << 8) | gus_patch[gus_ptr + 16]; + gus_sample->rate = (gus_patch[gus_ptr + 21] << 8) + | gus_patch[gus_ptr + 20]; + gus_sample->freq_low = ((gus_patch[gus_ptr + 25] << 24) + | (gus_patch[gus_ptr + 24] << 16) + | (gus_patch[gus_ptr + 23] << 8) | gus_patch[gus_ptr + 22]); + gus_sample->freq_high = ((gus_patch[gus_ptr + 29] << 24) + | (gus_patch[gus_ptr + 28] << 16) + | (gus_patch[gus_ptr + 27] << 8) | gus_patch[gus_ptr + 26]); + gus_sample->freq_root = ((gus_patch[gus_ptr + 33] << 24) + | (gus_patch[gus_ptr + 32] << 16) + | (gus_patch[gus_ptr + 31] << 8) | gus_patch[gus_ptr + 30]); + + /* This is done this way instead of ((freq * 1024) / rate) to avoid 32bit overflow. */ + /* Result is 0.001% inacurate */ + gus_sample->inc_div = ((gus_sample->freq_root * 512) / gus_sample->rate) * 2; + +#if 0 + /* We dont use this info at this time, kept in here for info */ + printf("\rTremolo Sweep: %i, Rate: %i, Depth %i\n", + gus_patch[gus_ptr+49], gus_patch[gus_ptr+50], gus_patch[gus_ptr+51]); + printf("\rVibrato Sweep: %i, Rate: %i, Depth %i\n", + gus_patch[gus_ptr+52], gus_patch[gus_ptr+53], gus_patch[gus_ptr+54]); +#endif + gus_sample->modes = gus_patch[gus_ptr + 55]; + GUSPAT_START_DEBUG(); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_16BIT, "16bit "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_UNSIGNED, "Unsigned "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_LOOP, "Loop "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_PINGPONG, "PingPong "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_REVERSE, "Reverse "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_SUSTAIN, "Sustain "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_ENVELOPE, "Envelope "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_CLAMPED, "Clamped "); GUSPAT_END_DEBUG(); + + if (gus_sample->loop_start > gus_sample->loop_end) { + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->loop_start; + gus_sample->loop_start = tmp_loop; + gus_sample->loop_fraction = + ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + } + + /* + FIXME: Experimental Hacky Fix + */ + if (fix_release) { + if (env_time_table[gus_patch[gus_ptr + 40]] + < env_time_table[gus_patch[gus_ptr + 41]]) { + unsigned char tmp_hack_rate = gus_patch[gus_ptr + 41]; + gus_patch[gus_ptr + 41] = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = tmp_hack_rate; + } + } + + for (i = 0; i < 6; i++) { + if (gus_sample->modes & SAMPLE_ENVELOPE) { + unsigned char env_rate = gus_patch[gus_ptr + 37 + i]; + gus_sample->env_target[i] = 16448 * gus_patch[gus_ptr + 43 + i]; + GUSPAT_INT_DEBUG("Envelope Level",gus_patch[gus_ptr+43+i]); GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[env_rate]); + gus_sample->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[env_rate])); + GUSPAT_INT_DEBUG("Envelope Rate",gus_sample->env_rate[i]); GUSPAT_INT_DEBUG("GUSPAT Rate",env_rate); + if (gus_sample->env_rate[i] == 0) { + _WM_ERROR_NEW("%s: Warning: found invalid envelope(%lu) rate setting in %s. Using %f instead.", + __FUNCTION__, i, filename, env_time_table[63]); + gus_sample->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[63]); + } + } else { + gus_sample->env_target[i] = 4194303; + gus_sample->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[63]); + } + } + + gus_sample->env_target[6] = 0; + gus_sample->env_rate[6] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + + gus_ptr += 96; + tmp_cnt = gus_sample->data_length; + + if (do_convert[(((gus_sample->modes & 0x18) >> 1) + | (gus_sample->modes & 0x03))](&gus_patch[gus_ptr], gus_sample) + == -1) { + free(gus_patch); + return NULL; + } + + gus_ptr += tmp_cnt; + gus_sample->loop_start = (gus_sample->loop_start << 10) + | (((gus_sample->loop_fraction & 0x0f) << 10) / 16); + gus_sample->loop_end = (gus_sample->loop_end << 10) + | (((gus_sample->loop_fraction & 0xf0) << 6) / 16); + gus_sample->loop_size = gus_sample->loop_end - gus_sample->loop_start; + gus_sample->data_length = gus_sample->data_length << 10; + no_of_samples--; + } + free(gus_patch); + return first_gus_sample; +} diff --git a/src/wildmidi/gus_pat.h b/src/wildmidi/gus_pat.h new file mode 100644 index 000000000..084b8473a --- /dev/null +++ b/src/wildmidi/gus_pat.h @@ -0,0 +1,77 @@ +/* + gus_pat.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __GUS_PAT_H +#define __GUS_PAT_H + +/* Guspat Envelope Rate Timings */ + +static float env_time_table[] = { +/* Row 1 = (4095.0 / (x * ( 1.0 / (1.6 * 14.0) ))) / 1000000.0 */ + 0.0f, 0.091728000f, 0.045864000f, 0.030576000f, 0.022932000f, 0.018345600f, 0.015288000f, 0.013104000f, + 0.011466000f, 0.010192000f, 0.009172800f, 0.008338909f, 0.007644000f, 0.007056000f, 0.006552000f, 0.006115200f, + 0.005733000f, 0.005395765f, 0.005096000f, 0.004827789f, 0.004586400f, 0.004368000f, 0.004169455f, 0.003988174f, + 0.003822000f, 0.003669120f, 0.003528000f, 0.003397333f, 0.003276000f, 0.003163034f, 0.003057600f, 0.002958968f, + 0.002866500f, 0.002779636f, 0.002697882f, 0.002620800f, 0.002548000f, 0.002479135f, 0.002413895f, 0.002352000f, + 0.002293200f, 0.002237268f, 0.002184000f, 0.002133209f, 0.002084727f, 0.002038400f, 0.001994087f, 0.001951660f, + 0.001911000f, 0.001872000f, 0.001834560f, 0.001798588f, 0.001764000f, 0.001730717f, 0.001698667f, 0.001667782f, + 0.001638000f, 0.001609263f, 0.001581517f, 0.001554712f, 0.001528800f, 0.001503738f, 0.001479484f, 0.001456000f, + +/* Row 2 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 8.0 ))) / 1000000.0 */ + 0.0f, 0.733824000f, 0.366912000f, 0.244608000f, 0.183456000f, 0.146764800f, 0.122304000f, 0.104832000f, + 0.091728000f, 0.081536000f, 0.073382400f, 0.066711273f, 0.061152000f, 0.056448000f, 0.052416000f, 0.048921600f, + 0.045864000f, 0.043166118f, 0.040768000f, 0.038622316f, 0.036691200f, 0.034944000f, 0.033355636f, 0.031905391f, + 0.030576000f, 0.029352960f, 0.028224000f, 0.027178667f, 0.026208000f, 0.025304276f, 0.024460800f, 0.023671742f, + 0.022932000f, 0.022237091f, 0.021583059f, 0.020966400f, 0.020384000f, 0.019833081f, 0.019311158f, 0.018816000f, + 0.018345600f, 0.017898146f, 0.017472000f, 0.017065674f, 0.016677818f, 0.016307200f, 0.015952696f, 0.015613277f, + 0.015288000f, 0.014976000f, 0.014676480f, 0.014388706f, 0.014112000f, 0.013845736f, 0.013589333f, 0.013342255f, + 0.013104000f, 0.012874105f, 0.012652138f, 0.012437695f, 0.012230400f, 0.012029902f, 0.011835871f, 0.011648000f, + +/* Row 3 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 64.0 ))) / 1000000.0 */ + 0.0f, 5.870592000f, 2.935296000f, 1.956864000f, 1.467648000f, 1.174118400f, 0.978432000f, 0.838656000f, + 0.733824000f, 0.652288000f, 0.587059200f, 0.533690182f, 0.489216000f, 0.451584000f, 0.419328000f, 0.391372800f, + 0.366912000f, 0.345328941f, 0.326144000f, 0.308978526f, 0.293529600f, 0.279552000f, 0.266845091f, 0.255243130f, + 0.244608000f, 0.234823680f, 0.225792000f, 0.217429333f, 0.209664000f, 0.202434207f, 0.195686400f, 0.189373935f, + 0.183456000f, 0.177896727f, 0.172664471f, 0.167731200f, 0.163072000f, 0.158664649f, 0.154489263f, 0.150528000f, + 0.146764800f, 0.143185171f, 0.139776000f, 0.136525395f, 0.133422545f, 0.130457600f, 0.127621565f, 0.124906213f, + 0.122304000f, 0.119808000f, 0.117411840f, 0.115109647f, 0.112896000f, 0.110765887f, 0.108714667f, 0.106738036f, + 0.104832000f, 0.102992842f, 0.101217103f, 0.099501559f, 0.097843200f, 0.096239213f, 0.094686968f, 0.093184000f, + +/* Row 4 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 512.0))) / 1000000.0 */ + 0.0f, 46.964736000f,23.482368000f,15.654912000f,11.741184000f, 9.392947200f, 7.827456000f, 6.709248000f, + 5.870592000f, 5.218304000f, 4.696473600f, 4.269521455f, 3.913728000f, 3.612672000f, 3.354624000f, 3.130982400f, + 2.935296000f, 2.762631529f, 2.609152000f, 2.471828211f, 2.348236800f, 2.236416000f, 2.134760727f, 2.041945043f, + 1.956864000f, 1.878589440f, 1.806336000f, 1.739434667f, 1.677312000f, 1.619473655f, 1.565491200f, 1.514991484f, + 1.467648000f, 1.423173818f, 1.381315765f, 1.341849600f, 1.304576000f, 1.269317189f, 1.235914105f, 1.204224000f, + 1.174118400f, 1.145481366f, 1.118208000f, 1.092203163f, 1.067380364f, 1.043660800f, 1.020972522f, 0.999249702f, + 0.978432000f, 0.958464000f, 0.939294720f, 0.920877176f, 0.903168000f, 0.886127094f, 0.869717333f, 0.853904291f, + 0.838656000f, 0.823942737f, 0.809736828f, 0.796012475f, 0.782745600f, 0.769913705f, 0.757495742f, 0.745472000f +}; + +extern struct _sample * _WM_load_gus_pat (const char *filename, int _fix_release); + +#endif /* __GUS_PAT_H */ + diff --git a/src/wildmidi/lock.cpp b/src/wildmidi/lock.cpp new file mode 100644 index 000000000..f8abfe926 --- /dev/null +++ b/src/wildmidi/lock.cpp @@ -0,0 +1,87 @@ +/* + lock.c - data locking code for lib + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +//#include "config.h" + +#ifndef __DJGPP__ + +#ifdef _WIN32 +#include +#else +#define _GNU_SOURCE +#include +#endif + +#include "lock.h" +#include "common.h" + +/* + _WM_Lock(wmlock) + + wmlock = a pointer to a value + + returns nothing + + Attempts to set a lock on the MDI tree so that + only 1 library command may access it at any time. + If lock fails the process retries until successful. + */ +void _WM_Lock(int * wmlock) { + LOCK_START: + /* Check if lock is clear, if so set it */ + if (*wmlock == 0) { + (*wmlock)++; + /* Now that the lock is set, make sure we + * don't have a race condition. If so, + * decrement the lock by one and retry. */ + if (*wmlock == 1) { + return; /* Lock cleanly set */ + } + (*wmlock)--; + } +#ifdef _WIN32 + Sleep(10); +#else + usleep(500); +#endif + goto LOCK_START; +} + +/* + _WM_Unlock(wmlock) + + wmlock = a pointer to a value + + returns nothing + + Removes a lock previously placed on the MDI tree. + */ +void _WM_Unlock(int *wmlock) { + /* We don't want a -1 lock, so just to make sure */ + if ((*wmlock) != 0) { + (*wmlock)--; + } +} + +#endif /* __DJGPP__ */ diff --git a/src/wildmidi/lock.h b/src/wildmidi/lock.h new file mode 100644 index 000000000..6504bbecf --- /dev/null +++ b/src/wildmidi/lock.h @@ -0,0 +1,36 @@ +/* + lock.h - data locking code for lib + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __LOCK_H +#define __LOCK_H + +extern void _WM_Lock (int * wmlock); +extern void _WM_Unlock (int *wmlock); + +#ifdef __DJGPP__ +#define _WM_Lock(p) do {} while (0) +#define _WM_Unlock(p) do {} while (0) +#endif + +#endif /* __LOCK_H */ diff --git a/src/wildmidi/reverb.cpp b/src/wildmidi/reverb.cpp new file mode 100644 index 000000000..d183a1c71 --- /dev/null +++ b/src/wildmidi/reverb.cpp @@ -0,0 +1,398 @@ +/* + reverb.c + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +//#include "config.h" + +#include +#include + +#include "common.h" +#include "reverb.h" + +/* + reverb function + */ +void _WM_reset_reverb(struct _rvb *rvb) { + int i, j, k; + for (i = 0; i < rvb->l_buf_size; i++) { + rvb->l_buf[i] = 0; + } + for (i = 0; i < rvb->r_buf_size; i++) { + rvb->r_buf[i] = 0; + } + for (k = 0; k < 8; k++) { + for (i = 0; i < 6; i++) { + for (j = 0; j < 2; j++) { + rvb->l_buf_flt_in[k][i][j] = 0; + rvb->l_buf_flt_out[k][i][j] = 0; + rvb->r_buf_flt_in[k][i][j] = 0; + rvb->r_buf_flt_out[k][i][j] = 0; + } + } + } +} + +/* + _WM_init_reverb + + ========================= + Engine Description + + 8 reflective points around the room + 2 speaker positions + 1 listener position + + Sounds come from the speakers to all points and to the listener. + Sound comes from the reflective points to the listener. + These sounds are combined, put through a filter that mimics surface absorbtion. + The combined sounds are also sent to the reflective points on the opposite side. + + */ +struct _rvb * +_WM_init_reverb(int rate, float room_x, float room_y, float listen_x, + float listen_y) { + + /* filters set at 125Hz, 250Hz, 500Hz, 1000Hz, 2000Hz, 4000Hz */ + double Freq[] = {125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0}; + + /* numbers calculated from + * 101.325 kPa, 20 deg C, 50% relative humidity */ + double dbAirAbs[] = {-0.00044, -0.00131, -0.002728, -0.004665, -0.009887, -0.029665}; + + /* modify these to adjust the absorption qualities of the surface. + * Remember that lower frequencies are less effected by surfaces + * Note: I am currently playing with the values and finding the ideal surfaces + * for nice default reverb. + */ + double dbAttn[8][6] = { + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942}, + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942} + }; + /* + double dbAttn[6] = { + // concrete covered in carpet + // -0.175, -0.537, -1.412, -4.437, -7.959, -7.959 + // pleated drapes + -0.630, -3.223, -5.849, -12.041, -10.458, -7.959 + }; + */ + + /* distance */ + double SPL_DST[8] = {0.0}; + double SPR_DST[8] = {0.0}; + double RFN_DST[8] = {0.0}; + + double MAXL_DST = 0.0; + double MAXR_DST = 0.0; + + double SPL_LSN_XOFS = 0.0; + double SPL_LSN_YOFS = 0.0; + double SPL_LSN_DST = 0.0; + + double SPR_LSN_XOFS = 0.0; + double SPR_LSN_YOFS = 0.0; + double SPR_LSN_DST = 0.0; + + + struct _rvb *rtn_rvb = (struct _rvb*)malloc(sizeof(struct _rvb)); + int j = 0; + int i = 0; + + struct _coord { + double x; + double y; + }; + +#if 0 + struct _coord SPL = {2.5, 5.0}; /* Left Speaker Position */ + struct _coord SPR = {7.5, 5.0}; /* Right Speaker Position */ + /* position of the reflective points */ + struct _coord RFN[] = { + { 5.0, 0.0}, + { 0.0, 6.66666}, + { 0.0, 13.3333}, + { 5.0, 20.0}, + { 10.0, 20.0}, + { 15.0, 13.3333}, + { 15.0, 6.66666}, + { 10.0, 0.0} + }; +#else + struct _coord SPL; /* Left Speaker Position */ + struct _coord SPR; /* Right Speaker Position */ + /* position of the reflective points */ + struct _coord RFN[8]; + + SPL.x = room_x / 4.0; + SPR.x = room_x / 4.0 * 3.0; + SPL.y = room_y / 10.0; + SPR.y = room_y / 10.0; + + RFN[0].x = room_x / 3.0; + RFN[0].y = 0.0; + RFN[1].x = 0.0; + RFN[1].y = room_y / 3.0; + RFN[2].x = 0.0; + RFN[2].y = room_y / 3.0 * 2.0; + RFN[3].x = room_x / 3.0; + RFN[3].y = room_y; + RFN[4].x = room_x / 3.0 * 2.0; + RFN[4].y = room_y; + RFN[5].x = room_x; + RFN[5].y = room_y / 3.0 * 2.0; + RFN[6].x = room_x; + RFN[6].y = room_y / 3.0; + RFN[7].x = room_x / 3.0 * 2.0; + RFN[7].y = 0.0; +#endif + + SPL_LSN_XOFS = SPL.x - listen_x; + SPL_LSN_YOFS = SPL.y - listen_y; + SPL_LSN_DST = sqrt((SPL_LSN_XOFS * SPL_LSN_XOFS) + (SPL_LSN_YOFS * SPL_LSN_YOFS)); + + if (SPL_LSN_DST > MAXL_DST) + MAXL_DST = SPL_LSN_DST; + + SPR_LSN_XOFS = SPR.x - listen_x; + SPR_LSN_YOFS = SPR.y - listen_y; + SPR_LSN_DST = sqrt((SPR_LSN_XOFS * SPR_LSN_XOFS) + (SPR_LSN_YOFS * SPR_LSN_YOFS)); + + if (SPR_LSN_DST > MAXR_DST) + MAXR_DST = SPR_LSN_DST; + + if (rtn_rvb == NULL) { + return NULL; + } + + for (j = 0; j < 8; j++) { + double SPL_RFL_XOFS = 0; + double SPL_RFL_YOFS = 0; + double SPR_RFL_XOFS = 0; + double SPR_RFL_YOFS = 0; + double RFN_XOFS = listen_x - RFN[j].x; + double RFN_YOFS = listen_y - RFN[j].y; + RFN_DST[j] = sqrt((RFN_XOFS * RFN_XOFS) + (RFN_YOFS * RFN_YOFS)); + + SPL_RFL_XOFS = SPL.x - RFN[i].x; + SPL_RFL_YOFS = SPL.y - RFN[i].y; + SPR_RFL_XOFS = SPR.x - RFN[i].x; + SPR_RFL_YOFS = SPR.y - RFN[i].y; + SPL_DST[i] = sqrt( + (SPL_RFL_XOFS * SPL_RFL_XOFS) + (SPL_RFL_YOFS * SPL_RFL_YOFS)); + SPR_DST[i] = sqrt( + (SPR_RFL_XOFS * SPR_RFL_XOFS) + (SPR_RFL_YOFS * SPR_RFL_YOFS)); + /* + add the 2 distances together and remove the speaker to listener distance + so we dont have to delay the initial output + */ + SPL_DST[i] += RFN_DST[i]; + + /* so i dont have to delay speaker output */ + SPL_DST[i] -= SPL_LSN_DST; + + if (i < 4) { + if (SPL_DST[i] > MAXL_DST) + MAXL_DST = SPL_DST[i]; + } else { + if (SPL_DST[i] > MAXR_DST) + MAXR_DST = SPL_DST[i]; + } + + SPR_DST[i] += RFN_DST[i]; + + /* so i dont have to delay speaker output */ + SPR_DST[i] -= SPR_LSN_DST; + + if (i < 4) { + if (SPR_DST[i] > MAXL_DST) + MAXL_DST = SPR_DST[i]; + } else { + if (SPR_DST[i] > MAXR_DST) + MAXR_DST = SPR_DST[i]; + } + + RFN_DST[j] *= 2.0; + + if (j < 4) { + if (RFN_DST[j] > MAXL_DST) + MAXL_DST = RFN_DST[j]; + } else { + if (RFN_DST[j] > MAXR_DST) + MAXR_DST = RFN_DST[j]; + } + + for (i = 0; i < 6; i++) { + double srate = (double) rate; + double bandwidth = 2.0; + double omega = 2.0 * M_PI * Freq[i] / srate; + double sn = sin(omega); + double cs = cos(omega); + double alpha = sn * sinh(M_LN2 / 2 * bandwidth * omega / sn); + double A = pow(10.0, ((/*dbAttn[i]*/dbAttn[j][i] + + (dbAirAbs[i] * RFN_DST[j])) / 40.0) ); + /* + Peaking band EQ filter + */ + double b0 = 1 + (alpha * A); + double b1 = -2 * cs; + double b2 = 1 - (alpha * A); + double a0 = 1 + (alpha / A); + double a1 = -2 * cs; + double a2 = 1 - (alpha / A); + + rtn_rvb->coeff[j][i][0] = (signed long int) ((b0 / a0) * 1024.0); + rtn_rvb->coeff[j][i][1] = (signed long int) ((b1 / a0) * 1024.0); + rtn_rvb->coeff[j][i][2] = (signed long int) ((b2 / a0) * 1024.0); + rtn_rvb->coeff[j][i][3] = (signed long int) ((a1 / a0) * 1024.0); + rtn_rvb->coeff[j][i][4] = (signed long int) ((a2 / a0) * 1024.0); + } + } + + /* init the reverb buffers */ + rtn_rvb->l_buf_size = (int) ((float) rate * (MAXL_DST / 340.29)); + rtn_rvb->l_buf = (long*)malloc( + sizeof(signed long int) * (rtn_rvb->l_buf_size + 1)); + rtn_rvb->l_out = 0; + + rtn_rvb->r_buf_size = (int) ((float) rate * (MAXR_DST / 340.29)); + rtn_rvb->r_buf = (long*)malloc( + sizeof(signed long int) * (rtn_rvb->r_buf_size + 1)); + rtn_rvb->r_out = 0; + + for (i = 0; i < 4; i++) { + rtn_rvb->l_sp_in[i] = (int) ((float) rate * (SPL_DST[i] / 340.29)); + rtn_rvb->l_sp_in[i + 4] = (int) ((float) rate + * (SPL_DST[i + 4] / 340.29)); + rtn_rvb->r_sp_in[i] = (int) ((float) rate * (SPR_DST[i] / 340.29)); + rtn_rvb->r_sp_in[i + 4] = (int) ((float) rate + * (SPR_DST[i + 4] / 340.29)); + rtn_rvb->l_in[i] = (int) ((float) rate * (RFN_DST[i] / 340.29)); + rtn_rvb->r_in[i] = (int) ((float) rate * (RFN_DST[i + 4] / 340.29)); + } + + rtn_rvb->gain = 4; + + _WM_reset_reverb(rtn_rvb); + return rtn_rvb; +} + +/* _WM_free_reverb - free up memory used for reverb */ +void _WM_free_reverb(struct _rvb *rvb) { + if (!rvb) return; + free(rvb->l_buf); + free(rvb->r_buf); + free(rvb); +} + +void _WM_do_reverb(struct _rvb *rvb, signed long int *buffer, int size) { + int i, j, k; + signed long int l_buf_flt = 0; + signed long int r_buf_flt = 0; + signed long int l_rfl = 0; + signed long int r_rfl = 0; + int vol_div = 64; + + for (i = 0; i < size; i += 2) { + signed long int tmp_l_val = 0; + signed long int tmp_r_val = 0; + /* + add the initial reflections + from each speaker, 4 to go the left, 4 go to the right buffers + */ + tmp_l_val = buffer[i] / vol_div; + tmp_r_val = buffer[i + 1] / vol_div; + for (j = 0; j < 4; j++) { + rvb->l_buf[rvb->l_sp_in[j]] += tmp_l_val; + rvb->l_sp_in[j] = (rvb->l_sp_in[j] + 1) % rvb->l_buf_size; + rvb->l_buf[rvb->r_sp_in[j]] += tmp_r_val; + rvb->r_sp_in[j] = (rvb->r_sp_in[j] + 1) % rvb->l_buf_size; + + rvb->r_buf[rvb->l_sp_in[j + 4]] += tmp_l_val; + rvb->l_sp_in[j + 4] = (rvb->l_sp_in[j + 4] + 1) % rvb->r_buf_size; + rvb->r_buf[rvb->r_sp_in[j + 4]] += tmp_r_val; + rvb->r_sp_in[j + 4] = (rvb->r_sp_in[j + 4] + 1) % rvb->r_buf_size; + } + + /* + filter the reverb output and add to buffer + */ + l_rfl = rvb->l_buf[rvb->l_out]; + rvb->l_buf[rvb->l_out] = 0; + rvb->l_out = (rvb->l_out + 1) % rvb->l_buf_size; + + r_rfl = rvb->r_buf[rvb->r_out]; + rvb->r_buf[rvb->r_out] = 0; + rvb->r_out = (rvb->r_out + 1) % rvb->r_buf_size; + + for (k = 0; k < 8; k++) { + for (j = 0; j < 6; j++) { + l_buf_flt = ((l_rfl * rvb->coeff[k][j][0]) + + (rvb->l_buf_flt_in[k][j][0] * rvb->coeff[k][j][1]) + + (rvb->l_buf_flt_in[k][j][1] * rvb->coeff[k][j][2]) + - (rvb->l_buf_flt_out[k][j][0] * rvb->coeff[k][j][3]) + - (rvb->l_buf_flt_out[k][j][1] * rvb->coeff[k][j][4])) + / 1024; + rvb->l_buf_flt_in[k][j][1] = rvb->l_buf_flt_in[k][j][0]; + rvb->l_buf_flt_in[k][j][0] = l_rfl; + rvb->l_buf_flt_out[k][j][1] = rvb->l_buf_flt_out[k][j][0]; + rvb->l_buf_flt_out[k][j][0] = l_buf_flt; + buffer[i] += l_buf_flt / 8; + + r_buf_flt = ((r_rfl * rvb->coeff[k][j][0]) + + (rvb->r_buf_flt_in[k][j][0] * rvb->coeff[k][j][1]) + + (rvb->r_buf_flt_in[k][j][1] * rvb->coeff[k][j][2]) + - (rvb->r_buf_flt_out[k][j][0] * rvb->coeff[k][j][3]) + - (rvb->r_buf_flt_out[k][j][1] * rvb->coeff[k][j][4])) + / 1024; + rvb->r_buf_flt_in[k][j][1] = rvb->r_buf_flt_in[k][j][0]; + rvb->r_buf_flt_in[k][j][0] = r_rfl; + rvb->r_buf_flt_out[k][j][1] = rvb->r_buf_flt_out[k][j][0]; + rvb->r_buf_flt_out[k][j][0] = r_buf_flt; + buffer[i + 1] += r_buf_flt / 8; + } + } + + /* + add filtered result back into the buffers but on the opposite side + */ + tmp_l_val = buffer[i + 1] / vol_div; + tmp_r_val = buffer[i] / vol_div; + for (j = 0; j < 4; j++) { + rvb->l_buf[rvb->l_in[j]] += tmp_l_val; + rvb->l_in[j] = (rvb->l_in[j] + 1) % rvb->l_buf_size; + + rvb->r_buf[rvb->r_in[j]] += tmp_r_val; + rvb->r_in[j] = (rvb->r_in[j] + 1) % rvb->r_buf_size; + } + } +} + diff --git a/src/wildmidi/reverb.h b/src/wildmidi/reverb.h new file mode 100644 index 000000000..b8f5a6333 --- /dev/null +++ b/src/wildmidi/reverb.h @@ -0,0 +1,57 @@ +/* + reverb.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __REVERB_H +#define __REVERB_H + +struct _rvb { + /* filter data */ + signed long int l_buf_flt_in[8][6][2]; + signed long int l_buf_flt_out[8][6][2]; + signed long int r_buf_flt_in[8][6][2]; + signed long int r_buf_flt_out[8][6][2]; + signed long int coeff[8][6][5]; + /* buffer data */ + signed long int *l_buf; + signed long int *r_buf; + int l_buf_size; + int r_buf_size; + int l_out; + int r_out; + int l_sp_in[8]; + int r_sp_in[8]; + int l_in[4]; + int r_in[4]; + int gain; + unsigned long int max_reverb_time; +}; + + extern void _WM_reset_reverb (struct _rvb *rvb); + extern struct _rvb *_WM_init_reverb(int rate, float room_x, float room_y, float listen_x, float listen_y); + extern void _WM_free_reverb (struct _rvb *rvb); + extern void _WM_do_reverb (struct _rvb *rvb, signed long int *buffer, int size); + +#endif /* __REVERB_H */ diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp new file mode 100644 index 000000000..b5ea6b887 --- /dev/null +++ b/src/wildmidi/wildmidi_lib.cpp @@ -0,0 +1,4288 @@ +/* + wildmidi_lib.c + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2014 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +//#include "config.h" + +#define UNUSED(x) (void)(x) + +#include +#include +#include +#ifndef _WIN32 +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include + +/* +#ifdef _WIN32 +#include +#include +*/ +#undef strcasecmp +#define strcasecmp _stricmp +#undef strncasecmp +#define strncasecmp _strnicmp + + + +#include "common.h" +#include "wm_error.h" +#include "file_io.h" +#include "lock.h" +#include "reverb.h" +#include "gus_pat.h" +#include "wildmidi_lib.h" + +#define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\') +#ifdef _WIN32 +#define HAS_DRIVE_SPEC(f) ((f)[0] && ((f)[1] == ':')) +#else +#define HAS_DRIVE_SPEC(f) (0) +#endif +#define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC((f))) + + +// Why is this shit even being used...? :( +#define __builtin_expect(a, b) a + +/* + * ========================= + * Global Data and Data Structs + * ========================= + */ + +#define MEM_CHUNK 8192 + +static int WM_Initialized = 0; +static signed short int WM_MasterVolume = 948; +static unsigned short int WM_MixerOptions = 0; + +static char WM_Version[] = "WildMidi Processing Library"; + +unsigned short int _WM_SampleRate; + +static struct _patch *patch[128]; + +static float reverb_room_width = 16.875f; +static float reverb_room_length = 22.5f; + +static float reverb_listen_posx = 8.4375f; +static float reverb_listen_posy = 16.875f; + +static int fix_release = 0; +static int auto_amp = 0; +static int auto_amp_with_amp = 0; + +static int patch_lock; + +struct _channel { + unsigned char bank; + struct _patch *patch; + unsigned char hold; + unsigned char volume; + unsigned char pressure; + unsigned char expression; + signed char balance; + signed char pan; + signed short int left_adjust; + signed short int right_adjust; + signed short int pitch; + signed short int pitch_range; + signed long int pitch_adjust; + unsigned short reg_data; + unsigned char reg_non; + unsigned char isdrum; +}; + +#define HOLD_OFF 0x02 + +struct _note { + unsigned short noteid; + unsigned char velocity; + struct _patch *patch; + struct _sample *sample; + unsigned long int sample_pos; + unsigned long int sample_inc; + signed long int env_inc; + unsigned char env; + signed long int env_level; + unsigned char modes; + unsigned char hold; + unsigned char active; + struct _note *replay; + struct _note *next; + unsigned long int vol_lvl; + unsigned char is_off; +}; + +struct _miditrack { + unsigned long int length; + unsigned long int ptr; + unsigned long int delta; + unsigned char running_event; + unsigned char EOT; +}; + +struct _mdi_patches { + struct _patch *patch; + struct _mdi_patch *next; +}; + +struct _event_data { + unsigned char channel; + unsigned long int data; +}; + +struct _mdi { + int lock; + unsigned long int samples_to_mix; + struct _event *events; + struct _event *current_event; + unsigned long int event_count; + unsigned long int events_size; /* try to stay optimally ahead to prevent reallocs */ + + unsigned short midi_master_vol; + struct _WM_Info info; + struct _WM_Info *tmp_info; + struct _channel channel[16]; + struct _note *note; + struct _note note_table[2][16][128]; + + struct _patch **patches; + unsigned long int patch_count; + signed short int amp; + + signed long int *mix_buffer; + unsigned long int mix_buffer_size; + + struct _rvb *reverb; +}; + +struct _event { + void (*do_event)(struct _mdi *mdi, struct _event_data *data); + struct _event_data event_data; + unsigned long int samples_to_next; + unsigned long int samples_to_next_fixed; +}; + +#define FPBITS 10 +#define FPMASK ((1L<> 1); + int j; + int sign; + double ck; + double x, x_inc, xz; + double z[35]; + double *gptr, *t; + + _WM_Lock(&gauss_lock); + if (gauss_table) { + _WM_Unlock(&gauss_lock); + return; + } + + newt_coeffs[0][0] = 1; + for (i = 0; i <= n; i++) { + newt_coeffs[i][0] = 1; + newt_coeffs[i][i] = 1; + + if (i > 1) { + newt_coeffs[i][0] = newt_coeffs[i - 1][0] / i; + newt_coeffs[i][i] = newt_coeffs[i - 1][0] / i; + } + + for (j = 1; j < i; j++) { + newt_coeffs[i][j] = newt_coeffs[i - 1][j - 1] + + newt_coeffs[i - 1][j]; + if (i > 1) + newt_coeffs[i][j] /= i; + } + z[i] = i / (4 * M_PI); + } + + for (i = 0; i <= n; i++) + for (j = 0, sign = (int)pow(-1., i); j <= i; j++, sign *= -1) + newt_coeffs[i][j] *= sign; + + t = (double*)malloc((1<event_count >= mdi->events_size) { + mdi->events_size += MEM_CHUNK; + mdi->events = (struct _event*)realloc(mdi->events, + (mdi->events_size * sizeof(struct _event))); + } +} + +static void WM_InitPatches(void) { + int i; + for (i = 0; i < 128; i++) { + patch[i] = NULL; + } +} + +static void WM_FreePatches(void) { + int i; + struct _patch * tmp_patch; + struct _sample * tmp_sample; + + _WM_Lock(&patch_lock); + for (i = 0; i < 128; i++) { + while (patch[i]) { + while (patch[i]->first_sample) { + tmp_sample = patch[i]->first_sample->next; + free(patch[i]->first_sample->data); + free(patch[i]->first_sample); + patch[i]->first_sample = tmp_sample; + } + free(patch[i]->filename); + tmp_patch = patch[i]->next; + free(patch[i]); + patch[i] = tmp_patch; + } + } + _WM_Unlock(&patch_lock); +} + +/* wm_strdup -- adds extra space for appending up to 4 chars */ +static char *wm_strdup (const char *str) { + size_t l = strlen(str) + 5; + char *d = (char *) malloc(l * sizeof(char)); + if (d) { + strcpy(d, str); + return d; + } + return NULL; +} + +static inline int wm_isdigit(int c) { + return (c >= '0' && c <= '9'); +} + +#define TOKEN_CNT_INC 8 +static char** WM_LC_Tokenize_Line(char *line_data) { + int line_length = strlen(line_data); + int token_data_length = 0; + int line_ofs = 0; + int token_start = 0; + char **token_data = NULL; + int token_count = 0; + + if (line_length == 0) + return NULL; + + do { + /* ignore everything after # */ + if (line_data[line_ofs] == '#') { + break; + } + + if ((line_data[line_ofs] == ' ') || (line_data[line_ofs] == '\t')) { + /* whitespace means we aren't in a token */ + if (token_start) { + token_start = 0; + line_data[line_ofs] = '\0'; + } + } else { + if (!token_start) { + /* the start of a token in the line */ + token_start = 1; + if (token_count >= token_data_length) { + token_data_length += TOKEN_CNT_INC; + token_data = (char**)realloc(token_data, token_data_length * sizeof(char *)); + if (token_data == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM,"to parse config", errno); + return NULL; + } + } + + token_data[token_count] = &line_data[line_ofs]; + token_count++; + } + } + line_ofs++; + } while (line_ofs != line_length); + + /* if we have found some tokens then add a null token to the end */ + if (token_count) { + if (token_count >= token_data_length) { + token_data = (char**)realloc(token_data, + ((token_count + 1) * sizeof(char *))); + } + token_data[token_count] = NULL; + } + + return token_data; +} + +static int WM_LoadConfig(const char *config_file) { + unsigned long int config_size = 0; + char *config_buffer = NULL; + const char *dir_end = NULL; + char *config_dir = NULL; + unsigned long int config_ptr = 0; + unsigned long int line_start_ptr = 0; + unsigned short int patchid = 0; + struct _patch * tmp_patch; + char **line_tokens = NULL; + int token_count = 0; + + config_buffer = (char *) _WM_BufferFile(config_file, &config_size); + if (!config_buffer) { + WM_FreePatches(); + return -1; + } + + + // This part was rewritten because the original depended on a header that was GPL'd. + dir_end = strrchr(config_file, '/'); +#ifdef _WIN32 + const char *dir_end2 = strrchr(config_file, '\\'); + if (dir_end2 > dir_end) dir_end = dir_end2; +#endif + + if (dir_end) { + config_dir = (char*)malloc((dir_end - config_file + 2)); + if (config_dir == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", + errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + free(config_buffer); + return -1; + } + strncpy(config_dir, config_file, (dir_end - config_file + 1)); + config_dir[dir_end - config_file + 1] = '\0'; + } + + config_ptr = 0; + line_start_ptr = 0; + + /* handle files without a newline at the end: this relies on + * _WM_BufferFile() allocating the buffer with one extra byte */ + config_buffer[config_size] = '\n'; + + while (config_ptr <= config_size) { + if (config_buffer[config_ptr] == '\r' || + config_buffer[config_ptr] == '\n') + { + config_buffer[config_ptr] = '\0'; + + if (config_ptr != line_start_ptr) { + line_tokens = WM_LC_Tokenize_Line(&config_buffer[line_start_ptr]); + if (line_tokens) { + if (strcasecmp(line_tokens[0], "dir") == 0) { + free(config_dir); + if (!line_tokens[1]) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in dir line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } else if (!(config_dir = wm_strdup(line_tokens[1]))) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + "to parse config", errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } + if (!IS_DIR_SEPARATOR(config_dir[strlen(config_dir) - 1])) { + config_dir[strlen(config_dir) + 1] = '\0'; + config_dir[strlen(config_dir)] = '/'; + } + } else if (strcasecmp(line_tokens[0], "source") == 0) { + char *new_config = NULL; + if (!line_tokens[1]) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in source line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) { + new_config = (char*)malloc( + strlen(config_dir) + strlen(line_tokens[1]) + + 1); + if (new_config == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + "to parse config", errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + strcpy(new_config, config_dir); + strcpy(&new_config[strlen(config_dir)], line_tokens[1]); + } else { + if (!(new_config = wm_strdup(line_tokens[1]))) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + "to parse config", errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } + } + if (WM_LoadConfig(new_config) == -1) { + free(new_config); + free(line_tokens); + free(config_buffer); + free(config_dir); + return -1; + } + free(new_config); + } else if (strcasecmp(line_tokens[0], "bank") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in bank line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + patchid = (atoi(line_tokens[1]) & 0xFF) << 8; + } else if (strcasecmp(line_tokens[0], "drumset") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in drumset line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + patchid = ((atoi(line_tokens[1]) & 0xFF) << 8) | 0x80; + } else if (strcasecmp(line_tokens[0], "reverb_room_width") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_room_width line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_room_width = (float) atof(line_tokens[1]); + if (reverb_room_width < 1.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_width < 1 meter, setting to minimum of 1 meter)", + 0); + reverb_room_width = 1.0f; + } else if (reverb_room_width > 100.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_width > 100 meters, setting to maximum of 100 meters)", + 0); + reverb_room_width = 100.0f; + } + } else if (strcasecmp(line_tokens[0], "reverb_room_length") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_room_length line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_room_length = (float) atof(line_tokens[1]); + if (reverb_room_length < 1.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_length < 1 meter, setting to minimum of 1 meter)", + 0); + reverb_room_length = 1.0f; + } else if (reverb_room_length > 100.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_length > 100 meters, setting to maximum of 100 meters)", + 0); + reverb_room_length = 100.0f; + } + } else if (strcasecmp(line_tokens[0], "reverb_listener_posx") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_listen_posx line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_listen_posx = (float) atof(line_tokens[1]); + if ((reverb_listen_posx > reverb_room_width) + || (reverb_listen_posx < 0.0f)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_listen_posx set outside of room)", + 0); + reverb_listen_posx = reverb_room_width / 2.0f; + } + } else if (strcasecmp(line_tokens[0], + "reverb_listener_posy") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_listen_posy line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_listen_posy = (float) atof(line_tokens[1]); + if ((reverb_listen_posy > reverb_room_width) + || (reverb_listen_posy < 0.0f)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_listen_posy set outside of room)", + 0); + reverb_listen_posy = reverb_room_length * 0.75f; + } + } else if (strcasecmp(line_tokens[0], + "guspat_editor_author_cant_read_so_fix_release_time_for_me") + == 0) { + fix_release = 1; + } else if (strcasecmp(line_tokens[0], "auto_amp") == 0) { + auto_amp = 1; + } else if (strcasecmp(line_tokens[0], "auto_amp_with_amp") + == 0) { + auto_amp = 1; + auto_amp_with_amp = 1; + } else if (wm_isdigit(line_tokens[0][0])) { + patchid = (patchid & 0xFF80) + | (atoi(line_tokens[0]) & 0x7F); + if (patch[(patchid & 0x7F)] == NULL) { + patch[(patchid & 0x7F)] = (struct _patch*)malloc( + sizeof(struct _patch)); + if (patch[(patchid & 0x7F)] == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + NULL, errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + tmp_patch = patch[(patchid & 0x7F)]; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } else { + tmp_patch = patch[(patchid & 0x7F)]; + if (tmp_patch->patchid == patchid) { + free(tmp_patch->filename); + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + } else { + if (tmp_patch->next) { + while (tmp_patch->next) { + if (tmp_patch->next->patchid == patchid) + break; + tmp_patch = tmp_patch->next; + } + if (tmp_patch->next == NULL) { + if ((tmp_patch->next = (struct _patch*)malloc( + sizeof(struct _patch))) + == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_MEM, NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_LOAD, config_file, + 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + tmp_patch = tmp_patch->next; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } else { + tmp_patch = tmp_patch->next; + free(tmp_patch->filename); + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + } + } else { + tmp_patch->next = (struct _patch*)malloc( + sizeof(struct _patch)); + if (tmp_patch->next == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_MEM, NULL, errno); + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + tmp_patch = tmp_patch->next; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } + } + } + if (!line_tokens[1]) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in patch line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) { + tmp_patch->filename = (char*)malloc( + strlen(config_dir) + strlen(line_tokens[1]) + + 5); + if (tmp_patch->filename == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + strcpy(tmp_patch->filename, config_dir); + strcat(tmp_patch->filename, line_tokens[1]); + } else { + if (!(tmp_patch->filename = wm_strdup(line_tokens[1]))) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + } + if (strncasecmp( + &tmp_patch->filename[strlen(tmp_patch->filename) + - 4], ".pat", 4) != 0) { + strcat(tmp_patch->filename, ".pat"); + } + tmp_patch->env[0].set = 0x00; + tmp_patch->env[1].set = 0x00; + tmp_patch->env[2].set = 0x00; + tmp_patch->env[3].set = 0x00; + tmp_patch->env[4].set = 0x00; + tmp_patch->env[5].set = 0x00; + tmp_patch->keep = 0; + tmp_patch->remove = 0; + + token_count = 0; + while (line_tokens[token_count]) { + if (strncasecmp(line_tokens[token_count], "amp=", 4) + == 0) { + if (!wm_isdigit(line_tokens[token_count][4])) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + tmp_patch->amp = (atoi( + &line_tokens[token_count][4]) << 10) + / 100; + } + } else if (strncasecmp(line_tokens[token_count], + "note=", 5) == 0) { + if (!wm_isdigit(line_tokens[token_count][5])) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + tmp_patch->note = atoi( + &line_tokens[token_count][5]); + } + } else if (strncasecmp(line_tokens[token_count], + "env_time", 8) == 0) { + if ((!wm_isdigit(line_tokens[token_count][8])) + || (!wm_isdigit( + line_tokens[token_count][10])) + || (line_tokens[token_count][9] != '=')) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + unsigned int env_no = atoi( + &line_tokens[token_count][8]); + if (env_no > 5) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", + 0); + } else { + tmp_patch->env[env_no].time = + (float) atof( + &line_tokens[token_count][10]); + if ((tmp_patch->env[env_no].time + > 45000.0f) + || (tmp_patch->env[env_no].time + < 1.47f)) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(range error in patch line)", + 0); + tmp_patch->env[env_no].set &= 0xFE; + } else { + tmp_patch->env[env_no].set |= 0x01; + } + } + } + } else if (strncasecmp(line_tokens[token_count], + "env_level", 9) == 0) { + if ((!wm_isdigit(line_tokens[token_count][9])) + || (!wm_isdigit( + line_tokens[token_count][11])) + || (line_tokens[token_count][10] != '=')) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + unsigned int env_no = atoi( + &line_tokens[token_count][9]); + if (env_no > 5) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", + 0); + } else { + tmp_patch->env[env_no].level = + (float) atof( + &line_tokens[token_count][11]); + if ((tmp_patch->env[env_no].level > 1.0f) + || (tmp_patch->env[env_no].level + < 0.0f)) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(range error in patch line)", + 0); + tmp_patch->env[env_no].set &= 0xFD; + } else { + tmp_patch->env[env_no].set |= 0x02; + } + } + } + } else if (strcasecmp(line_tokens[token_count], + "keep=loop") == 0) { + tmp_patch->keep |= SAMPLE_LOOP; + } else if (strcasecmp(line_tokens[token_count], + "keep=env") == 0) { + tmp_patch->keep |= SAMPLE_ENVELOPE; + } else if (strcasecmp(line_tokens[token_count], + "remove=sustain") == 0) { + tmp_patch->remove |= SAMPLE_SUSTAIN; + } else if (strcasecmp(line_tokens[token_count], + "remove=clamped") == 0) { + tmp_patch->remove |= SAMPLE_CLAMPED; + } + token_count++; + } + } + } + /* free up tokens */ + free(line_tokens); + } + line_start_ptr = config_ptr + 1; + } + config_ptr++; + } + + free(config_buffer); + free(config_dir); + + return 0; +} + +/* sample loading */ + +static int load_sample(struct _patch *sample_patch) { + struct _sample *guspat = NULL; + struct _sample *tmp_sample = NULL; + unsigned int i = 0; + + /* we only want to try loading the guspat once. */ + sample_patch->loaded = 1; + + if ((guspat = _WM_load_gus_pat(sample_patch->filename, fix_release)) == NULL) { + return -1; + } + + if (auto_amp) { + signed short int tmp_max = 0; + signed short int tmp_min = 0; + signed short samp_max = 0; + signed short samp_min = 0; + tmp_sample = guspat; + do { + samp_max = 0; + samp_min = 0; + for (i = 0; i < (tmp_sample->data_length >> 10); i++) { + if (tmp_sample->data[i] > samp_max) + samp_max = tmp_sample->data[i]; + if (tmp_sample->data[i] < samp_min) + samp_min = tmp_sample->data[i]; + + } + if (samp_max > tmp_max) + tmp_max = samp_max; + if (samp_min < tmp_min) + tmp_min = samp_min; + tmp_sample = tmp_sample->next; + } while (tmp_sample); + if (auto_amp_with_amp) { + if (tmp_max >= -tmp_min) { + sample_patch->amp = (sample_patch->amp + * ((32767 << 10) / tmp_max)) >> 10; + } else { + sample_patch->amp = (sample_patch->amp + * ((32768 << 10) / -tmp_min)) >> 10; + } + } else { + if (tmp_max >= -tmp_min) { + sample_patch->amp = (32767 << 10) / tmp_max; + } else { + sample_patch->amp = (32768 << 10) / -tmp_min; + } + } + } + + sample_patch->first_sample = guspat; + + if (sample_patch->patchid & 0x0080) { + if (!(sample_patch->keep & SAMPLE_LOOP)) { + do { + guspat->modes &= 0xFB; + guspat = guspat->next; + } while (guspat); + } + guspat = sample_patch->first_sample; + if (!(sample_patch->keep & SAMPLE_ENVELOPE)) { + do { + guspat->modes &= 0xBF; + guspat = guspat->next; + } while (guspat); + } + guspat = sample_patch->first_sample; + } + + if (sample_patch->patchid == 47) { + do { + if (!(guspat->modes & SAMPLE_LOOP)) { + for (i = 3; i < 6; i++) { + guspat->env_target[i] = guspat->env_target[2]; + guspat->env_rate[i] = guspat->env_rate[2]; + } + } + guspat = guspat->next; + } while (guspat); + guspat = sample_patch->first_sample; + } + + do { + if ((sample_patch->remove & SAMPLE_SUSTAIN) + && (guspat->modes & SAMPLE_SUSTAIN)) { + guspat->modes ^= SAMPLE_SUSTAIN; + } + if ((sample_patch->remove & SAMPLE_CLAMPED) + && (guspat->modes & SAMPLE_CLAMPED)) { + guspat->modes ^= SAMPLE_CLAMPED; + } + if (sample_patch->keep & SAMPLE_ENVELOPE) { + guspat->modes |= SAMPLE_ENVELOPE; + } + + for (i = 0; i < 6; i++) { + if (guspat->modes & SAMPLE_ENVELOPE) { + if (sample_patch->env[i].set & 0x02) { + guspat->env_target[i] = 16448 + * (signed long int) (255.0 + * sample_patch->env[i].level); + } + + if (sample_patch->env[i].set & 0x01) { + guspat->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate + * (sample_patch->env[i].time / 1000.0))); + } + } else { + guspat->env_target[i] = 4194303; + guspat->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + } + } + + guspat = guspat->next; + } while (guspat); + return 0; +} + +static struct _patch * +get_patch_data(unsigned short patchid) { + struct _patch *search_patch; + + _WM_Lock(&patch_lock); + + search_patch = patch[patchid & 0x007F]; + + if (search_patch == NULL) { + _WM_Unlock(&patch_lock); + return NULL; + } + + while (search_patch) { + if (search_patch->patchid == patchid) { + _WM_Unlock(&patch_lock); + return search_patch; + } + search_patch = search_patch->next; + } + if ((patchid >> 8) != 0) { + _WM_Unlock(&patch_lock); + return (get_patch_data(patchid & 0x00FF)); + } + _WM_Unlock(&patch_lock); + return NULL; +} + +static void load_patch(struct _mdi *mdi, unsigned short patchid) { + unsigned int i; + struct _patch *tmp_patch = NULL; + + for (i = 0; i < mdi->patch_count; i++) { + if (mdi->patches[i]->patchid == patchid) { + return; + } + } + + tmp_patch = get_patch_data(patchid); + if (tmp_patch == NULL) { + return; + } + + _WM_Lock(&patch_lock); + if (!tmp_patch->loaded) { + if (load_sample(tmp_patch) == -1) { + _WM_Unlock(&patch_lock); + return; + } + } + + if (tmp_patch->first_sample == NULL) { + _WM_Unlock(&patch_lock); + return; + } + + mdi->patch_count++; + mdi->patches = (struct _patch**)realloc(mdi->patches, + (sizeof(struct _patch*) * mdi->patch_count)); + mdi->patches[mdi->patch_count - 1] = tmp_patch; + tmp_patch->inuse_count++; + _WM_Unlock(&patch_lock); +} + +static struct _sample * +get_sample_data(struct _patch *sample_patch, unsigned long int freq) { + struct _sample *last_sample = NULL; + struct _sample *return_sample = NULL; + + _WM_Lock(&patch_lock); + if (sample_patch == NULL) { + _WM_Unlock(&patch_lock); + return NULL; + } + if (sample_patch->first_sample == NULL) { + _WM_Unlock(&patch_lock); + return NULL; + } + if (freq == 0) { + _WM_Unlock(&patch_lock); + return sample_patch->first_sample; + } + + return_sample = sample_patch->first_sample; + last_sample = sample_patch->first_sample; + while (last_sample) { + if (freq > last_sample->freq_low) { + if (freq < last_sample->freq_high) { + _WM_Unlock(&patch_lock); + return last_sample; + } else { + return_sample = last_sample; + } + } + last_sample = last_sample->next; + } + _WM_Unlock(&patch_lock); + return return_sample; +} + +static void do_note_off_extra(struct _note *nte) { + + nte->is_off = 0; + + if (nte->hold) { + nte->hold |= HOLD_OFF; + } else { + if (!(nte->modes & SAMPLE_ENVELOPE)) { + if (nte->modes & SAMPLE_LOOP) { + nte->modes ^= SAMPLE_LOOP; + } + nte->env_inc = 0; + + } else if (nte->modes & SAMPLE_CLAMPED) { + if (nte->env < 5) { + nte->env = 5; + if (nte->env_level > nte->sample->env_target[5]) { + nte->env_inc = -nte->sample->env_rate[5]; + } else { + nte->env_inc = nte->sample->env_rate[5]; + } + } +#if 1 + } else if (nte->modes & SAMPLE_SUSTAIN) { + if (nte->env < 3) { + nte->env = 3; + if (nte->env_level > nte->sample->env_target[3]) { + nte->env_inc = -nte->sample->env_rate[3]; + } else { + nte->env_inc = nte->sample->env_rate[3]; + } + } +#endif + } else if (nte->env < 4) { + nte->env = 4; + if (nte->env_level > nte->sample->env_target[4]) { + nte->env_inc = -nte->sample->env_rate[4]; + } else { + nte->env_inc = nte->sample->env_rate[4]; + } + } + } +} + +static void do_note_off(struct _mdi *mdi, struct _event_data *data) { + struct _note *nte; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + nte = &mdi->note_table[0][ch][(data->data >> 8)]; + if (!nte->active) + nte = &mdi->note_table[1][ch][(data->data >> 8)]; + if (!nte->active) { + return; + } + + if ((mdi->channel[ch].isdrum) && (!(nte->modes & SAMPLE_LOOP))) { + return; + } + + if (nte->env == 0) { + nte->is_off = 1; + } else { + do_note_off_extra(nte); + } +} + +static inline unsigned long int get_inc(struct _mdi *mdi, struct _note *nte) { + int ch = nte->noteid >> 8; + signed long int note_f; + unsigned long int freq; + + if (__builtin_expect((nte->patch->note != 0), 0)) { + note_f = nte->patch->note * 100; + } else { + note_f = (nte->noteid & 0x7f) * 100; + } + note_f += mdi->channel[ch].pitch_adjust; + if (__builtin_expect((note_f < 0), 0)) { + note_f = 0; + } else if (__builtin_expect((note_f > 12700), 0)) { + note_f = 12700; + } + freq = freq_table[(note_f % 1200)] >> (10 - (note_f / 1200)); + return (((freq / ((_WM_SampleRate * 100) / 1024)) * 1024 + / nte->sample->inc_div)); +} + +static inline unsigned long int get_volume(struct _mdi *mdi, unsigned char ch, + struct _note *nte) { + signed long int volume; + + if (mdi->info.mixer_options & WM_MO_LOG_VOLUME) { + volume = (sqr_volume[mdi->channel[ch].volume] + * sqr_volume[mdi->channel[ch].expression] + * sqr_volume[nte->velocity]) / 1048576; + } else { + volume = (lin_volume[mdi->channel[ch].volume] + * lin_volume[mdi->channel[ch].expression] + * lin_volume[nte->velocity]) / 1048576; + } + + volume = volume * nte->patch->amp / 100; + return (volume); +} + +static void do_note_on(struct _mdi *mdi, struct _event_data *data) { + struct _note *nte; + struct _note *prev_nte; + struct _note *nte_array; + unsigned long int freq = 0; + struct _patch *patch; + struct _sample *sample; + unsigned char ch = data->channel; + unsigned char note = (unsigned char)(data->data >> 8); + unsigned char velocity = (data->data & 0xFF); + + if (velocity == 0x00) { + do_note_off(mdi, data); + return; + } + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + if (!mdi->channel[ch].isdrum) { + patch = mdi->channel[ch].patch; + if (patch == NULL) { + return; + } + freq = freq_table[(note % 12) * 100] >> (10 - (note / 12)); + } else { + patch = get_patch_data(((mdi->channel[ch].bank << 8) | note | 0x80)); + if (patch == NULL) { + return; + } + if (patch->note) { + freq = freq_table[(patch->note % 12) * 100] + >> (10 - (patch->note / 12)); + } else { + freq = freq_table[(note % 12) * 100] >> (10 - (note / 12)); + } + } + + sample = get_sample_data(patch, (freq / 100)); + if (sample == NULL) { + return; + } + + nte = &mdi->note_table[0][ch][note]; + + if (nte->active) { + if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3) + && (!(nte->hold & HOLD_OFF))) + return; + nte->replay = &mdi->note_table[1][ch][note]; + nte->env = 6; + nte->env_inc = -nte->sample->env_rate[6]; + nte = nte->replay; + } else { + if (mdi->note_table[1][ch][note].active) { + if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3) + && (!(nte->hold & HOLD_OFF))) + return; + mdi->note_table[1][ch][note].replay = nte; + mdi->note_table[1][ch][note].env = 6; + mdi->note_table[1][ch][note].env_inc = + -mdi->note_table[1][ch][note].sample->env_rate[6]; + } else { + nte_array = mdi->note; + if (nte_array == NULL) { + mdi->note = nte; + } else { + do { + prev_nte = nte_array; + nte_array = nte_array->next; + } while (nte_array); + prev_nte->next = nte; + } + nte->active = 1; + nte->next = NULL; + } + } + nte->noteid = (ch << 8) | note; + nte->patch = patch; + nte->sample = sample; + nte->sample_pos = 0; + nte->sample_inc = get_inc(mdi, nte); + nte->velocity = velocity; + nte->env = 0; + nte->env_inc = nte->sample->env_rate[0]; + nte->env_level = 0; + nte->modes = sample->modes; + nte->hold = mdi->channel[ch].hold; + nte->vol_lvl = get_volume(mdi, ch, nte); + nte->replay = NULL; + nte->is_off = 0; +} + +static void do_aftertouch(struct _mdi *mdi, struct _event_data *data) { + struct _note *nte; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + nte = &mdi->note_table[0][ch][(data->data >> 8)]; + if (!nte->active) { + nte = &mdi->note_table[1][ch][(data->data >> 8)]; + if (!nte->active) { + return; + } + } + + nte->velocity = data->data & 0xff; + nte->vol_lvl = get_volume(mdi, ch, nte); + + if (nte->replay) { + nte->replay->velocity = data->data & 0xff; + nte->replay->vol_lvl = get_volume(mdi, ch, nte->replay); + } +} + +static void do_pan_adjust(struct _mdi *mdi, unsigned char ch) { + signed short int pan_adjust = mdi->channel[ch].balance + + mdi->channel[ch].pan; + signed short int left, right; + int amp = 32; + + if (pan_adjust > 63) { + pan_adjust = 63; + } else if (pan_adjust < -64) { + pan_adjust = -64; + } + + pan_adjust += 64; +/* if (mdi->info.mixer_options & WM_MO_LOG_VOLUME) {*/ + left = (pan_volume[127 - pan_adjust] * WM_MasterVolume * amp) / 1048576; + right = (pan_volume[pan_adjust] * WM_MasterVolume * amp) / 1048576; +/* } else { + left = (lin_volume[127 - pan_adjust] * WM_MasterVolume * amp) / 1048576; + right= (lin_volume[pan_adjust] * WM_MasterVolume * amp) / 1048576; + }*/ + + mdi->channel[ch].left_adjust = left; + mdi->channel[ch].right_adjust = right; +} + +static void do_control_bank_select(struct _mdi *mdi, struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].bank = (unsigned char)data->data; +} + +static void do_control_data_entry_course(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + int data_tmp; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + data_tmp = mdi->channel[ch].pitch_range % 100; + mdi->channel[ch].pitch_range = short(data->data * 100 + data_tmp); + /* printf("Data Entry Course: pitch_range: %i\n\r",mdi->channel[ch].pitch_range);*/ + /* printf("Data Entry Course: data %li\n\r",data->data);*/ + } +} + +static void do_control_channel_volume(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + mdi->channel[ch].volume = (unsigned char)data->data; + + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + note_data->vol_lvl = get_volume(mdi, ch, note_data); + if (note_data->replay) + note_data->replay->vol_lvl = get_volume(mdi, ch, + note_data->replay); + } + note_data = note_data->next; + } while (note_data); + } +} + +static void do_control_channel_balance(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + + mdi->channel[ch].balance = (signed char)(data->data - 64); + do_pan_adjust(mdi, ch); +} + +static void do_control_channel_pan(struct _mdi *mdi, struct _event_data *data) { + unsigned char ch = data->channel; + + mdi->channel[ch].pan = signed char(data->data - 64); + do_pan_adjust(mdi, ch); +} + +static void do_control_channel_expression(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + mdi->channel[ch].expression = (unsigned char)data->data; + + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + note_data->vol_lvl = get_volume(mdi, ch, note_data); + if (note_data->replay) + note_data->replay->vol_lvl = get_volume(mdi, ch, + note_data->replay); + } + note_data = note_data->next; + } while (note_data); + } +} + +static void do_control_data_entry_fine(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + int data_tmp; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + data_tmp = mdi->channel[ch].pitch_range / 100; + mdi->channel[ch].pitch_range = short((data_tmp * 100) + data->data); + /* printf("Data Entry Fine: pitch_range: %i\n\r",mdi->channel[ch].pitch_range);*/ + /* printf("Data Entry Fine: data: %li\n\r", data->data);*/ + } + +} + +static void do_control_channel_hold(struct _mdi *mdi, struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + if (data->data > 63) { + mdi->channel[ch].hold = 1; + } else { + mdi->channel[ch].hold = 0; + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + if (note_data->hold & HOLD_OFF) { + if (note_data->modes & SAMPLE_ENVELOPE) { + if (note_data->modes & SAMPLE_CLAMPED) { + if (note_data->env < 5) { + note_data->env = 5; + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + } + } else if (note_data->env < 4) { + note_data->env = 4; + if (note_data->env_level + > note_data->sample->env_target[4]) { + note_data->env_inc = + -note_data->sample->env_rate[4]; + } else { + note_data->env_inc = + note_data->sample->env_rate[4]; + } + } + } else { + if (note_data->modes & SAMPLE_LOOP) { + note_data->modes ^= SAMPLE_LOOP; + } + note_data->env_inc = 0; + } + } + note_data->hold = 0x00; + } + note_data = note_data->next; + } while (note_data); + } + } +} + +static void do_control_data_increment(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + if (mdi->channel[ch].pitch_range < 0x3FFF) + mdi->channel[ch].pitch_range++; + } +} + +static void do_control_data_decrement(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + if (mdi->channel[ch].pitch_range > 0) + mdi->channel[ch].pitch_range--; + } +} +static void do_control_non_registered_param(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].reg_non = 1; +} + +static void do_control_registered_param_fine(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].reg_data = (unsigned short) ((mdi->channel[ch].reg_data & 0x3F80) + | data->data); + mdi->channel[ch].reg_non = 0; +} + +static void do_control_registered_param_course(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].reg_data = (unsigned short) ((mdi->channel[ch].reg_data & 0x7F) + | (data->data << 7)); + mdi->channel[ch].reg_non = 0; +} + +static void do_control_channel_sound_off(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + note_data->active = 0; + if (note_data->replay) { + note_data->replay = NULL; + } + } + note_data = note_data->next; + } while (note_data); + } +} + +static void do_control_channel_controllers_off(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + mdi->channel[ch].expression = 127; + mdi->channel[ch].pressure = 127; + mdi->channel[ch].volume = 100; + mdi->channel[ch].pan = 0; + mdi->channel[ch].balance = 0; + mdi->channel[ch].reg_data = 0xffff; + mdi->channel[ch].pitch_range = 200; + mdi->channel[ch].pitch = 0; + mdi->channel[ch].pitch_adjust = 0; + mdi->channel[ch].hold = 0; + do_pan_adjust(mdi, ch); + + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + note_data->sample_inc = get_inc(mdi, note_data); + note_data->velocity = 0; + note_data->vol_lvl = get_volume(mdi, ch, note_data); + note_data->hold = 0; + + if (note_data->replay) { + note_data->replay->velocity = (unsigned char)data->data; + note_data->replay->vol_lvl = get_volume(mdi, ch, + note_data->replay); + } + } + note_data = note_data->next; + } while (note_data); + } +} + +static void do_control_channel_notes_off(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + if (mdi->channel[ch].isdrum) + return; + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + if (!note_data->hold) { + if (note_data->modes & SAMPLE_ENVELOPE) { + if (note_data->env < 5) { + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + note_data->env = 5; + } + } + } else { + note_data->hold |= HOLD_OFF; + } + } + note_data = note_data->next; + } while (note_data); + } +} + +static void do_patch(struct _mdi *mdi, struct _event_data *data) { + unsigned char ch = data->channel; + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + if (!mdi->channel[ch].isdrum) { + mdi->channel[ch].patch = get_patch_data((unsigned short)(((mdi->channel[ch].bank << 8) | data->data))); + } else { + mdi->channel[ch].bank = (unsigned char)data->data; + } +} + +static void do_channel_pressure(struct _mdi *mdi, struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + note_data->velocity = (unsigned char)data->data; + note_data->vol_lvl = get_volume(mdi, ch, note_data); + + if (note_data->replay) { + note_data->replay->velocity = (unsigned char)data->data; + note_data->replay->vol_lvl = get_volume(mdi, ch, + note_data->replay); + } + } + note_data = note_data->next; + } while (note_data); + } +} + +static void do_pitch(struct _mdi *mdi, struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + mdi->channel[ch].pitch = short(data->data - 0x2000); + + if (mdi->channel[ch].pitch < 0) { + mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range + * mdi->channel[ch].pitch / 8192; + } else { + mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range + * mdi->channel[ch].pitch / 8191; + } + + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + note_data->sample_inc = get_inc(mdi, note_data); + } + note_data = note_data->next; + } while (note_data); + } +} + +static void do_sysex_roland_drum_track(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + if (data->data > 0) { + mdi->channel[ch].isdrum = 1; + mdi->channel[ch].patch = NULL; + } else { + mdi->channel[ch].isdrum = 0; + mdi->channel[ch].patch = get_patch_data(0); + } +} + +static void do_sysex_roland_reset(struct _mdi *mdi, struct _event_data *data) { + int i; + for (i = 0; i < 16; i++) { + mdi->channel[i].bank = 0; + if (i != 9) { + mdi->channel[i].patch = get_patch_data(0); + } else { + mdi->channel[i].patch = NULL; + } + mdi->channel[i].hold = 0; + mdi->channel[i].volume = 100; + mdi->channel[i].pressure = 127; + mdi->channel[i].expression = 127; + mdi->channel[i].balance = 0; + mdi->channel[i].pan = 0; + mdi->channel[i].left_adjust = 1; + mdi->channel[i].right_adjust = 1; + mdi->channel[i].pitch = 0; + mdi->channel[i].pitch_range = 200; + mdi->channel[i].reg_data = 0xFFFF; + mdi->channel[i].isdrum = 0; + do_pan_adjust(mdi, i); + } + mdi->channel[9].isdrum = 1; + UNUSED(data); /* NOOP, to please the compiler gods */ +} + +static void WM_ResetToStart(midi * handle) { + struct _mdi *mdi = (struct _mdi *) handle; + + mdi->current_event = mdi->events; + mdi->samples_to_mix = 0; + mdi->info.current_sample = 0; + + do_sysex_roland_reset(mdi, NULL); +} + +static int midi_setup_noteoff(struct _mdi *mdi, unsigned char channel, + unsigned char note, unsigned char velocity) { + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].do_event = *do_note_off; + mdi->events[mdi->event_count - 1].event_data.channel = channel; + mdi->events[mdi->event_count - 1].event_data.data = (note << 8) + | velocity; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = *do_note_off; + mdi->events[mdi->event_count].event_data.channel = channel; + mdi->events[mdi->event_count].event_data.data = (note << 8) | velocity; + mdi->events[mdi->event_count].samples_to_next = 0; + mdi->event_count++; + } + return 0; +} + +static int midi_setup_noteon(struct _mdi *mdi, unsigned char channel, + unsigned char note, unsigned char velocity) { + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].do_event = *do_note_on; + mdi->events[mdi->event_count - 1].event_data.channel = channel; + mdi->events[mdi->event_count - 1].event_data.data = (note << 8) + | velocity; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = *do_note_on; + mdi->events[mdi->event_count].event_data.channel = channel; + mdi->events[mdi->event_count].event_data.data = (note << 8) | velocity; + mdi->events[mdi->event_count].samples_to_next = 0; + mdi->event_count++; + } + + if (mdi->channel[channel].isdrum) + load_patch(mdi, ((mdi->channel[channel].bank << 8) | (note | 0x80))); + return 0; +} + +static int midi_setup_aftertouch(struct _mdi *mdi, unsigned char channel, + unsigned char note, unsigned char pressure) { + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].do_event = *do_aftertouch; + mdi->events[mdi->event_count - 1].event_data.channel = channel; + mdi->events[mdi->event_count - 1].event_data.data = (note << 8) + | pressure; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = *do_aftertouch; + mdi->events[mdi->event_count].event_data.channel = channel; + mdi->events[mdi->event_count].event_data.data = (note << 8) | pressure; + mdi->events[mdi->event_count].samples_to_next = 0; + mdi->event_count++; + } + return 0; +} + +static int midi_setup_control(struct _mdi *mdi, unsigned char channel, + unsigned char controller, unsigned char setting) { + void (*tmp_event)(struct _mdi *mdi, struct _event_data *data) = NULL; + + switch (controller) { + case 0: + tmp_event = *do_control_bank_select; + mdi->channel[channel].bank = setting; + break; + case 6: + tmp_event = *do_control_data_entry_course; + break; + case 7: + tmp_event = *do_control_channel_volume; + mdi->channel[channel].volume = setting; + break; + case 8: + tmp_event = *do_control_channel_balance; + break; + case 10: + tmp_event = *do_control_channel_pan; + break; + case 11: + tmp_event = *do_control_channel_expression; + break; + case 38: + tmp_event = *do_control_data_entry_fine; + break; + case 64: + tmp_event = *do_control_channel_hold; + break; + case 96: + tmp_event = *do_control_data_increment; + break; + case 97: + tmp_event = *do_control_data_decrement; + break; + case 98: + case 99: + tmp_event = *do_control_non_registered_param; + break; + case 100: + tmp_event = *do_control_registered_param_fine; + break; + case 101: + tmp_event = *do_control_registered_param_course; + break; + case 120: + tmp_event = *do_control_channel_sound_off; + break; + case 121: + tmp_event = *do_control_channel_controllers_off; + break; + case 123: + tmp_event = *do_control_channel_notes_off; + break; + default: + return 0; + } + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].do_event = tmp_event; + mdi->events[mdi->event_count - 1].event_data.channel = channel; + mdi->events[mdi->event_count - 1].event_data.data = setting; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = tmp_event; + mdi->events[mdi->event_count].event_data.channel = channel; + mdi->events[mdi->event_count].event_data.data = setting; + mdi->events[mdi->event_count].samples_to_next = 0; + mdi->event_count++; + } + return 0; +} + +static int midi_setup_patch(struct _mdi *mdi, unsigned char channel, + unsigned char patch) { + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].do_event = *do_patch; + mdi->events[mdi->event_count - 1].event_data.channel = channel; + mdi->events[mdi->event_count - 1].event_data.data = patch; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = *do_patch; + mdi->events[mdi->event_count].event_data.channel = channel; + mdi->events[mdi->event_count].event_data.data = patch; + mdi->events[mdi->event_count].samples_to_next = 0; + mdi->event_count++; + } + if (mdi->channel[channel].isdrum) { + mdi->channel[channel].bank = patch; + } else { + load_patch(mdi, ((mdi->channel[channel].bank << 8) | patch)); + mdi->channel[channel].patch = get_patch_data( + ((mdi->channel[channel].bank << 8) | patch)); + } + return 0; +} + +static int midi_setup_channel_pressure(struct _mdi *mdi, unsigned char channel, + unsigned char pressure) { + + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].do_event = *do_channel_pressure; + mdi->events[mdi->event_count - 1].event_data.channel = channel; + mdi->events[mdi->event_count - 1].event_data.data = pressure; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = *do_channel_pressure; + mdi->events[mdi->event_count].event_data.channel = channel; + mdi->events[mdi->event_count].event_data.data = pressure; + mdi->events[mdi->event_count].samples_to_next = 0; + mdi->event_count++; + } + + return 0; +} + +static int midi_setup_pitch(struct _mdi *mdi, unsigned char channel, + unsigned short pitch) { + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].do_event = *do_pitch; + mdi->events[mdi->event_count - 1].event_data.channel = channel; + mdi->events[mdi->event_count - 1].event_data.data = pitch; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = *do_pitch; + mdi->events[mdi->event_count].event_data.channel = channel; + mdi->events[mdi->event_count].event_data.data = pitch; + mdi->events[mdi->event_count].samples_to_next = 0; + mdi->event_count++; + } + return 0; +} + +static int midi_setup_sysex_roland_drum_track(struct _mdi *mdi, + unsigned char channel, unsigned short setting) { + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].do_event = + *do_sysex_roland_drum_track; + mdi->events[mdi->event_count - 1].event_data.channel = channel; + mdi->events[mdi->event_count - 1].event_data.data = setting; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = *do_sysex_roland_drum_track; + mdi->events[mdi->event_count].event_data.channel = channel; + mdi->events[mdi->event_count].event_data.data = setting; + mdi->events[mdi->event_count].samples_to_next = 0; + mdi->event_count++; + } + + if (setting > 0) { + mdi->channel[channel].isdrum = 1; + } else { + mdi->channel[channel].isdrum = 0; + } + + return 0; +} + +static int midi_setup_sysex_roland_reset(struct _mdi *mdi) { + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].do_event = *do_sysex_roland_reset; + mdi->events[mdi->event_count - 1].event_data.channel = 0; + mdi->events[mdi->event_count - 1].event_data.data = 0; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = *do_sysex_roland_reset; + mdi->events[mdi->event_count].event_data.channel = 0; + mdi->events[mdi->event_count].event_data.data = 0; + mdi->events[mdi->event_count].samples_to_next = 0; + mdi->event_count++; + } + return 0; +} + +static int add_handle(void * handle) { + struct _hndl *tmp_handle = NULL; + + if (first_handle == NULL) { + first_handle = (struct _hndl*)malloc(sizeof(struct _hndl)); + if (first_handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, " to get ram", errno); + return -1; + } + first_handle->handle = handle; + first_handle->prev = NULL; + first_handle->next = NULL; + } else { + tmp_handle = first_handle; + if (tmp_handle->next) { + while (tmp_handle->next) + tmp_handle = tmp_handle->next; + } + tmp_handle->next = (struct _hndl*)malloc(sizeof(struct _hndl)); + if (tmp_handle->next == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, " to get ram", errno); + return -1; + } + tmp_handle->next->prev = tmp_handle; + tmp_handle = tmp_handle->next; + tmp_handle->next = NULL; + tmp_handle->handle = handle; + } + return 0; +} + +static struct _mdi * +Init_MDI(void) { + struct _mdi *mdi; + + mdi = (struct _mdi*)malloc(sizeof(struct _mdi)); + memset(mdi, 0, (sizeof(struct _mdi))); + + mdi->info.copyright = NULL; + mdi->info.mixer_options = WM_MixerOptions; + + load_patch(mdi, 0x0000); + + mdi->events_size = MEM_CHUNK; + mdi->events = (struct _event*)malloc(mdi->events_size * sizeof(struct _event)); + mdi->events[0].do_event = NULL; + mdi->events[0].event_data.channel = 0; + mdi->events[0].event_data.data = 0; + mdi->events[0].samples_to_next = 0; + mdi->event_count++; + + mdi->current_event = mdi->events; + mdi->samples_to_mix = 0; + mdi->info.current_sample = 0; + mdi->info.total_midi_time = 0; + mdi->info.approx_total_samples = 0; + + do_sysex_roland_reset(mdi, NULL); + + return mdi; +} + +static void freeMDI(struct _mdi *mdi) { + struct _sample *tmp_sample; + unsigned long int i; + + if (mdi->patch_count != 0) { + _WM_Lock(&patch_lock); + for (i = 0; i < mdi->patch_count; i++) { + mdi->patches[i]->inuse_count--; + if (mdi->patches[i]->inuse_count == 0) { + /* free samples here */ + while (mdi->patches[i]->first_sample) { + tmp_sample = mdi->patches[i]->first_sample->next; + free(mdi->patches[i]->first_sample->data); + free(mdi->patches[i]->first_sample); + mdi->patches[i]->first_sample = tmp_sample; + } + mdi->patches[i]->loaded = 0; + } + } + _WM_Unlock(&patch_lock); + free(mdi->patches); + } + + free(mdi->events); + free(mdi->tmp_info); + _WM_free_reverb(mdi->reverb); + free(mdi->mix_buffer); + free(mdi); +} + +static unsigned long int get_decay_samples(struct _patch *patch, unsigned char note) { + + struct _sample *sample = NULL; + unsigned long int freq = 0; + unsigned long int decay_samples = 0; + + if (patch == NULL) + return 0; + + /* first get the freq we need so we can check the right sample */ + if (patch->patchid & 0x80) { + /* is a drum patch */ + if (patch->note) { + freq = freq_table[(patch->note % 12) * 100] + >> (10 - (patch->note / 12)); + } else { + freq = freq_table[(note % 12) * 100] >> (10 - (note / 12)); + } + } else { + freq = freq_table[(note % 12) * 100] >> (10 - (note / 12)); + } + + /* get the sample */ + sample = get_sample_data(patch, (freq / 100)); + if (sample == NULL) + return 0; + + if (patch->patchid & 0x80) { + float sratedata = ((float) sample->rate / (float) _WM_SampleRate) + * (float) (sample->data_length >> 10); + decay_samples = (unsigned long int) sratedata; + /* printf("Drums (%i / %i) * %lu = %f\n", sample->rate, _WM_SampleRate, (sample->data_length >> 10), sratedata);*/ + } else if (sample->modes & SAMPLE_CLAMPED) { + decay_samples = (4194303 / sample->env_rate[5]); + /* printf("clamped 4194303 / %lu = %lu\n", sample->env_rate[5], decay_samples);*/ + } else { + decay_samples = + ((4194303 - sample->env_target[4]) / sample->env_rate[4]) + + (sample->env_target[4] / sample->env_rate[5]); + /* printf("NOT clamped ((4194303 - %lu) / %lu) + (%lu / %lu)) = %lu\n", sample->env_target[4], sample->env_rate[4], sample->env_target[4], sample->env_rate[5], decay_samples);*/ + } + return decay_samples; +} + +static struct _mdi * +WM_ParseNewMidi(unsigned char *midi_data, unsigned int midi_size) { + struct _mdi *mdi; + unsigned int tmp_val; + unsigned int midi_type; + unsigned int track_size; + unsigned char **tracks; + unsigned int end_of_tracks = 0; + unsigned int no_tracks; + unsigned int i; + unsigned int divisions = 96; + unsigned int tempo = 500000; + float samples_per_delta_f = 0.0; + float microseconds_per_pulse = 0.0; + float pulses_per_second = 0.0; + + unsigned long int sample_count = 0; + float sample_count_tmp = 0; + float sample_remainder = 0; + unsigned char *sysex_store = NULL; + unsigned long int sysex_store_len = 0; + + unsigned long int *track_delta; + unsigned char *track_end; + unsigned long int smallest_delta = 0; + unsigned long int subtract_delta = 0; + unsigned long int tmp_length = 0; + unsigned char current_event = 0; + unsigned char current_event_ch = 0; + unsigned char *running_event; + unsigned long int decay_samples = 0; + + if (memcmp(midi_data, "RIFF", 4) == 0) { + midi_data += 20; + midi_size -= 20; + } + if (memcmp(midi_data, "MThd", 4)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MIDI, NULL, 0); + return NULL; + } + midi_data += 4; + midi_size -= 4; + + if (midi_size < 10) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + return NULL; + } + + /* + * Get Midi Header Size - must always be 6 + */ + tmp_val = *midi_data++ << 24; + tmp_val |= *midi_data++ << 16; + tmp_val |= *midi_data++ << 8; + tmp_val |= *midi_data++; + midi_size -= 4; + if (tmp_val != 6) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, NULL, 0); + return NULL; + } + + /* + * Get Midi Format - we only support 0, 1 & 2 + */ + tmp_val = *midi_data++ << 8; + tmp_val |= *midi_data++; + midi_size -= 2; + if (tmp_val > 2) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0); + return NULL; + } + midi_type = tmp_val; + + /* + * Get No. of Tracks + */ + tmp_val = *midi_data++ << 8; + tmp_val |= *midi_data++; + midi_size -= 2; + if (tmp_val < 1) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(no tracks)", 0); + return NULL; + } + no_tracks = tmp_val; + + /* + * Check that type 0 midi file has only 1 track + */ + if ((midi_type == 0) && (no_tracks > 1)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(expected 1 track for type 0 midi file, found more)", 0); + return NULL; + } + + /* + * Get Divisions + */ + divisions = *midi_data++ << 8; + divisions |= *midi_data++; + midi_size -= 2; + if (divisions & 0x00008000) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0); + return NULL; + } + + if ((WM_MixerOptions & WM_MO_WHOLETEMPO)) { + float bpm_f = (float) (60000000 / tempo); + tempo = 60000000 / (unsigned long int) bpm_f; + } else if ((WM_MixerOptions & WM_MO_ROUNDTEMPO)) { + float bpm_fr = (float) (60000000 / tempo) + 0.5f; + tempo = 60000000 / (unsigned long int) bpm_fr; + } + /* Slow but needed for accuracy */ + microseconds_per_pulse = (float) tempo / (float) divisions; + pulses_per_second = 1000000.0f / microseconds_per_pulse; + samples_per_delta_f = (float) _WM_SampleRate / pulses_per_second; + + mdi = Init_MDI(); + + tracks = (unsigned char**)malloc(sizeof(unsigned char *) * no_tracks); + track_delta = (unsigned long*)malloc(sizeof(unsigned long int) * no_tracks); + track_end = (unsigned char*)malloc(sizeof(unsigned char) * no_tracks); + running_event = (unsigned char*)malloc(sizeof(unsigned char) * no_tracks); + + for (i = 0; i < no_tracks; i++) { + if (midi_size < 8) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + goto _end; + } + if (memcmp(midi_data, "MTrk", 4) != 0) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing track header)", 0); + goto _end; + } + midi_data += 4; + midi_size -= 4; + + track_size = *midi_data++ << 24; + track_size |= *midi_data++ << 16; + track_size |= *midi_data++ << 8; + track_size |= *midi_data++; + midi_size -= 4; + if (midi_size < track_size) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + goto _end; + } + if ((midi_data[track_size - 3] != 0xFF) + || (midi_data[track_size - 2] != 0x2F) + || (midi_data[track_size - 1] != 0x00)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing EOT)", 0); + goto _end; + } + tracks[i] = midi_data; + midi_data += track_size; + midi_size -= track_size; + track_end[i] = 0; + running_event[i] = 0; + track_delta[i] = 0; + decay_samples = 0; + while (*tracks[i] > 0x7F) { + track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F); + tracks[i]++; + } + track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F); + tracks[i]++; + } + + /* + * Handle type 0 & 1 the same, but type 2 differently + */ + switch (midi_type) { + case 0: + case 1: + /* Type 0 & 1 can use the same code */ + while (end_of_tracks != no_tracks) { + smallest_delta = 0; + for (i = 0; i < no_tracks; i++) { + if (track_end[i]) + continue; + + if (track_delta[i]) { + track_delta[i] -= subtract_delta; + if (track_delta[i]) { + if ((!smallest_delta) + || (smallest_delta > track_delta[i])) { + smallest_delta = track_delta[i]; + } + continue; + } + } + do { + if (*tracks[i] > 0x7F) { + current_event = *tracks[i]; + tracks[i]++; + } else { + current_event = running_event[i]; + if (running_event[i] < 0x80) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing event)", 0); + goto _end; + } + } + current_event_ch = current_event & 0x0F; + switch (current_event >> 4) { + case 0x8: + NOTEOFF: midi_setup_noteoff(mdi, current_event_ch, + tracks[i][0], tracks[i][1]); + /* To better calculate samples needed after the end of midi, + * we calculate samples for decay for note off */ + { + unsigned long int tmp_decay_samples = 0; + struct _patch *tmp_patch = NULL; + if (mdi->channel[current_event_ch].isdrum) { + tmp_patch = get_patch_data( + ((mdi->channel[current_event_ch].bank << 8) + | tracks[i][0] | 0x80)); + /* if (tmp_patch == NULL) + printf("Drum patch not loaded 0x%02x on channel %i\n",((mdi->channel[current_event_ch].bank << 8) | tracks[i][0] | 0x80),current_event_ch);*/ + } else { + tmp_patch = mdi->channel[current_event_ch].patch; + /* if (tmp_patch == NULL) + printf("Channel %i patch not loaded\n", current_event_ch);*/ + } + tmp_decay_samples = get_decay_samples(tmp_patch, + tracks[i][0]); + /* if the note off decay is more than the decay we currently tracking then + * we set it to new decay. */ + if (tmp_decay_samples > decay_samples) { + decay_samples = tmp_decay_samples; + } + } + + tracks[i] += 2; + running_event[i] = current_event; + break; + case 0x9: + if (tracks[i][1] == 0) { + goto NOTEOFF; + } + midi_setup_noteon(mdi, (current_event & 0x0F), tracks[i][0], + tracks[i][1]); + tracks[i] += 2; + running_event[i] = current_event; + break; + case 0xA: + midi_setup_aftertouch(mdi, (current_event & 0x0F), + tracks[i][0], tracks[i][1]); + tracks[i] += 2; + running_event[i] = current_event; + break; + case 0xB: + midi_setup_control(mdi, (current_event & 0x0F), + tracks[i][0], tracks[i][1]); + tracks[i] += 2; + running_event[i] = current_event; + break; + case 0xC: + midi_setup_patch(mdi, (current_event & 0x0F), *tracks[i]); + tracks[i]++; + running_event[i] = current_event; + break; + case 0xD: + midi_setup_channel_pressure(mdi, (current_event & 0x0F), + *tracks[i]); + tracks[i]++; + running_event[i] = current_event; + break; + case 0xE: + midi_setup_pitch(mdi, (current_event & 0x0F), + ((tracks[i][1] << 7) | (tracks[i][0] & 0x7F))); + tracks[i] += 2; + running_event[i] = current_event; + break; + case 0xF: /* Meta Event */ + if (current_event == 0xFF) { + if (tracks[i][0] == 0x02) { /* Copyright Event */ + /* Get Length */ + tmp_length = 0; + tracks[i]++; + while (*tracks[i] > 0x7f) { + tmp_length = (tmp_length << 7) + + (*tracks[i] & 0x7f); + tracks[i]++; + } + tmp_length = (tmp_length << 7) + + (*tracks[i] & 0x7f); + /* Copy copyright info in the getinfo struct */ + if (mdi->info.copyright) { + mdi->info.copyright = (char*)realloc( + mdi->info.copyright, + (strlen(mdi->info.copyright) + 1 + + tmp_length + 1)); + strncpy( + &mdi->info.copyright[strlen( + mdi->info.copyright) + 1], + (char *) tracks[i], tmp_length); + mdi->info.copyright[strlen(mdi->info.copyright) + + 1 + tmp_length] = '\0'; + mdi->info.copyright[strlen(mdi->info.copyright)] = '\n'; + + } else { + mdi->info.copyright = (char*)malloc(tmp_length + 1); + strncpy(mdi->info.copyright, (char *) tracks[i], + tmp_length); + mdi->info.copyright[tmp_length] = '\0'; + } + tracks[i] += tmp_length + 1; + } else if ((tracks[i][0] == 0x2F) + && (tracks[i][1] == 0x00)) { + /* End of Track */ + end_of_tracks++; + track_end[i] = 1; + goto NEXT_TRACK; + } else if ((tracks[i][0] == 0x51) + && (tracks[i][1] == 0x03)) { + /* Tempo */ + tempo = (tracks[i][2] << 16) + (tracks[i][3] << 8) + + tracks[i][4]; + tracks[i] += 5; + if (!tempo) + tempo = 500000; + + if ((WM_MixerOptions & WM_MO_WHOLETEMPO)) { + float bpm_f = (float) (60000000 / tempo); + tempo = 60000000 + / (unsigned long int) bpm_f; + } else if ((WM_MixerOptions & WM_MO_ROUNDTEMPO)) { + float bpm_fr = (float) (60000000 / tempo) + + 0.5f; + tempo = 60000000 + / (unsigned long int) bpm_fr; + } + /* Slow but needed for accuracy */ + microseconds_per_pulse = (float) tempo + / (float) divisions; + pulses_per_second = 1000000.0f + / microseconds_per_pulse; + samples_per_delta_f = (float) _WM_SampleRate + / pulses_per_second; + + } else { + tmp_length = 0; + tracks[i]++; + while (*tracks[i] > 0x7f) { + tmp_length = (tmp_length << 7) + + (*tracks[i] & 0x7f); + tracks[i]++; + } + tmp_length = (tmp_length << 7) + + (*tracks[i] & 0x7f); + tracks[i] += tmp_length + 1; + } + } else if ((current_event == 0xF0) + || (current_event == 0xF7)) { + /* Roland Sysex Events */ + unsigned long int sysex_len = 0; + while (*tracks[i] > 0x7F) { + sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F); + tracks[i]++; + } + sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F); + tracks[i]++; + + running_event[i] = 0; + + sysex_store = (unsigned char*)realloc(sysex_store, + sizeof(unsigned char) + * (sysex_store_len + sysex_len)); + memcpy(&sysex_store[sysex_store_len], tracks[i], + sysex_len); + sysex_store_len += sysex_len; + + if (sysex_store[sysex_store_len - 1] == 0xF7) { + unsigned char tmpsysexdata[] = { 0x41, 0x10, 0x42, 0x12 }; + if (memcmp(tmpsysexdata, sysex_store, 4) == 0) { + /* checksum */ + unsigned char sysex_cs = 0; + unsigned int sysex_ofs = 4; + do { + sysex_cs += sysex_store[sysex_ofs]; + if (sysex_cs > 0x7F) { + sysex_cs -= 0x80; + } + sysex_ofs++; + } while (sysex_store[sysex_ofs + 1] != 0xF7); + sysex_cs = 128 - sysex_cs; + /* is roland sysex message valid */ + if (sysex_cs == sysex_store[sysex_ofs]) { + /* process roland sysex event */ + if (sysex_store[4] == 0x40) { + if (((sysex_store[5] & 0xF0) == 0x10) + && (sysex_store[6] == 0x15)) { + /* Roland Drum Track Setting */ + unsigned char sysex_ch = 0x0F + & sysex_store[5]; + if (sysex_ch == 0x00) { + sysex_ch = 0x09; + } else if (sysex_ch <= 0x09) { + sysex_ch -= 1; + } + midi_setup_sysex_roland_drum_track( + mdi, sysex_ch, + sysex_store[7]); + } else if ((sysex_store[5] == 0x00) + && (sysex_store[6] == 0x7F) + && (sysex_store[7] == 0x00)) { + /* Roland GS Reset */ + midi_setup_sysex_roland_reset(mdi); + } + } + } + } + free(sysex_store); + sysex_store = NULL; + sysex_store_len = 0; + } + tracks[i] += sysex_len; + } else { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized meta event)", 0); + goto _end; + } + break; + default: + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized event)", 0); + goto _end; + } + while (*tracks[i] > 0x7F) { + track_delta[i] = (track_delta[i] << 7) + + (*tracks[i] & 0x7F); + tracks[i]++; + } + track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F); + tracks[i]++; + } while (!track_delta[i]); + if ((!smallest_delta) || (smallest_delta > track_delta[i])) { + smallest_delta = track_delta[i]; + } + NEXT_TRACK: continue; + } + + subtract_delta = smallest_delta; + sample_count_tmp = (((float) smallest_delta * samples_per_delta_f) + + sample_remainder); + sample_count = (unsigned long int) sample_count_tmp; + sample_remainder = sample_count_tmp - (float) sample_count; + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].samples_to_next += sample_count; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = NULL; + mdi->events[mdi->event_count].event_data.channel = 0; + mdi->events[mdi->event_count].event_data.data = 0; + mdi->events[mdi->event_count].samples_to_next = sample_count; + mdi->event_count++; + } + mdi->info.approx_total_samples += sample_count; + /* printf("Decay Samples = %lu\n",decay_samples);*/ + if (decay_samples > sample_count) { + decay_samples -= sample_count; + } else { + decay_samples = 0; + } + } + break; + + case 2: /* Type 2 has to be handled differently */ + for (i = 0; i < no_tracks; i++) { + sample_remainder = 0.0; + decay_samples = 0; + track_delta[i] = 0; + do { + if(track_delta[i]) { + sample_count_tmp = (((float) track_delta[i] * samples_per_delta_f) + + sample_remainder); + sample_count = (unsigned long int) sample_count_tmp; + sample_remainder = sample_count_tmp - (float) sample_count; + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].samples_to_next += sample_count; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = NULL; + mdi->events[mdi->event_count].event_data.channel = 0; + mdi->events[mdi->event_count].event_data.data = 0; + mdi->events[mdi->event_count].samples_to_next = sample_count; + mdi->event_count++; + } + mdi->info.approx_total_samples += sample_count; + /* printf("Decay Samples = %lu\n",decay_samples);*/ + if (decay_samples > sample_count) { + decay_samples -= sample_count; + } else { + decay_samples = 0; + } + } + if (*tracks[i] > 0x7F) { + current_event = *tracks[i]; + tracks[i]++; + } else { + current_event = running_event[i]; + if (running_event[i] < 0x80) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing event)", 0); + goto _end; + } + } + current_event_ch = current_event & 0x0F; + switch (current_event >> 4) { + case 0x8: + NOTEOFF2: midi_setup_noteoff(mdi, current_event_ch, + tracks[i][0], tracks[i][1]); + /* To better calculate samples needed after the end of midi, + * we calculate samples for decay for note off */ + { + unsigned long int tmp_decay_samples = 0; + struct _patch *tmp_patch = NULL; + + if (mdi->channel[current_event_ch].isdrum) { + tmp_patch = get_patch_data( + ((mdi->channel[current_event_ch].bank << 8) + | tracks[i][0] | 0x80)); + /* if (tmp_patch == NULL) + printf("Drum patch not loaded 0x%02x on channel %i\n",((mdi->channel[current_event_ch].bank << 8) | tracks[i][0] | 0x80),current_event_ch);*/ + } else { + tmp_patch = mdi->channel[current_event_ch].patch; + /* if (tmp_patch == NULL) + printf("Channel %i patch not loaded\n", current_event_ch);*/ + } + tmp_decay_samples = get_decay_samples(tmp_patch, + tracks[i][0]); + /* if the note off decay is more than the decay we currently tracking then + * we set it to new decay. */ + if (tmp_decay_samples > decay_samples) { + decay_samples = tmp_decay_samples; + } + } + + tracks[i] += 2; + running_event[i] = current_event; + break; + case 0x9: + if (tracks[i][1] == 0) { + goto NOTEOFF2; + } + midi_setup_noteon(mdi, (current_event & 0x0F), tracks[i][0], + tracks[i][1]); + tracks[i] += 2; + running_event[i] = current_event; + break; + case 0xA: + midi_setup_aftertouch(mdi, (current_event & 0x0F), + tracks[i][0], tracks[i][1]); + tracks[i] += 2; + running_event[i] = current_event; + break; + case 0xB: + midi_setup_control(mdi, (current_event & 0x0F), + tracks[i][0], tracks[i][1]); + tracks[i] += 2; + running_event[i] = current_event; + break; + case 0xC: + midi_setup_patch(mdi, (current_event & 0x0F), *tracks[i]); + tracks[i]++; + running_event[i] = current_event; + break; + case 0xD: + midi_setup_channel_pressure(mdi, (current_event & 0x0F), + *tracks[i]); + tracks[i]++; + running_event[i] = current_event; + break; + case 0xE: + midi_setup_pitch(mdi, (current_event & 0x0F), + ((tracks[i][1] << 7) | (tracks[i][0] & 0x7F))); + tracks[i] += 2; + running_event[i] = current_event; + break; + case 0xF: /* Meta Event */ + if (current_event == 0xFF) { + if (tracks[i][0] == 0x02) { /* Copyright Event */ + /* Get Length */ + tmp_length = 0; + tracks[i]++; + while (*tracks[i] > 0x7f) { + tmp_length = (tmp_length << 7) + + (*tracks[i] & 0x7f); + tracks[i]++; + } + tmp_length = (tmp_length << 7) + + (*tracks[i] & 0x7f); + /* Copy copyright info in the getinfo struct */ + if (mdi->info.copyright) { + mdi->info.copyright = (char*)realloc( + mdi->info.copyright, + (strlen(mdi->info.copyright) + 1 + + tmp_length + 1)); + strncpy( + &mdi->info.copyright[strlen( + mdi->info.copyright) + 1], + (char *) tracks[i], tmp_length); + mdi->info.copyright[strlen(mdi->info.copyright) + + 1 + tmp_length] = '\0'; + mdi->info.copyright[strlen(mdi->info.copyright)] = '\n'; + + } else { + mdi->info.copyright = (char*)malloc(tmp_length + 1); + strncpy(mdi->info.copyright, (char *) tracks[i], + tmp_length); + mdi->info.copyright[tmp_length] = '\0'; + } + tracks[i] += tmp_length + 1; + } else if ((tracks[i][0] == 0x2F) + && (tracks[i][1] == 0x00)) { + /* End of Track */ + end_of_tracks++; + track_end[i] = 1; + goto NEXT_TRACK2; + } else if ((tracks[i][0] == 0x51) + && (tracks[i][1] == 0x03)) { + /* Tempo */ + tempo = (tracks[i][2] << 16) + (tracks[i][3] << 8) + + tracks[i][4]; + tracks[i] += 5; + if (!tempo) + tempo = 500000; + + if ((WM_MixerOptions & WM_MO_WHOLETEMPO)) { + float bpm_f = (float) (60000000 / tempo); + tempo = 60000000 + / (unsigned long int) bpm_f; + } else if ((WM_MixerOptions & WM_MO_ROUNDTEMPO)) { + float bpm_fr = (float) (60000000 / tempo) + + 0.5f; + tempo = 60000000 + / (unsigned long int) bpm_fr; + } + /* Slow but needed for accuracy */ + microseconds_per_pulse = (float) tempo + / (float) divisions; + pulses_per_second = 1000000.0f + / microseconds_per_pulse; + samples_per_delta_f = (float) _WM_SampleRate + / pulses_per_second; + + } else { + tmp_length = 0; + tracks[i]++; + while (*tracks[i] > 0x7f) { + tmp_length = (tmp_length << 7) + + (*tracks[i] & 0x7f); + tracks[i]++; + } + tmp_length = (tmp_length << 7) + + (*tracks[i] & 0x7f); + tracks[i] += tmp_length + 1; + } + } else if ((current_event == 0xF0) + || (current_event == 0xF7)) { + /* Roland Sysex Events */ + unsigned long int sysex_len = 0; + while (*tracks[i] > 0x7F) { + sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F); + tracks[i]++; + } + sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F); + tracks[i]++; + + running_event[i] = 0; + + sysex_store = (unsigned char*)realloc(sysex_store, + sizeof(unsigned char) + * (sysex_store_len + sysex_len)); + memcpy(&sysex_store[sysex_store_len], tracks[i], + sysex_len); + sysex_store_len += sysex_len; + + if (sysex_store[sysex_store_len - 1] == 0xF7) { + unsigned char tmpsysexdata[] = { 0x41, 0x10, 0x42, 0x12 }; + if (memcmp(tmpsysexdata, sysex_store, 4) == 0) { + /* checksum */ + unsigned char sysex_cs = 0; + unsigned int sysex_ofs = 4; + do { + sysex_cs += sysex_store[sysex_ofs]; + if (sysex_cs > 0x7F) { + sysex_cs -= 0x80; + } + sysex_ofs++; + } while (sysex_store[sysex_ofs + 1] != 0xF7); + sysex_cs = 128 - sysex_cs; + /* is roland sysex message valid */ + if (sysex_cs == sysex_store[sysex_ofs]) { + /* process roland sysex event */ + if (sysex_store[4] == 0x40) { + if (((sysex_store[5] & 0xF0) == 0x10) + && (sysex_store[6] == 0x15)) { + /* Roland Drum Track Setting */ + unsigned char sysex_ch = 0x0F + & sysex_store[5]; + if (sysex_ch == 0x00) { + sysex_ch = 0x09; + } else if (sysex_ch <= 0x09) { + sysex_ch -= 1; + } + midi_setup_sysex_roland_drum_track( + mdi, sysex_ch, + sysex_store[7]); + } else if ((sysex_store[5] == 0x00) + && (sysex_store[6] == 0x7F) + && (sysex_store[7] == 0x00)) { + /* Roland GS Reset */ + midi_setup_sysex_roland_reset(mdi); + } + } + } + } + free(sysex_store); + sysex_store = NULL; + sysex_store_len = 0; + } + tracks[i] += sysex_len; + } else { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized meta event)", 0); + goto _end; + } + break; + default: + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized event)", 0); + goto _end; + } + track_delta[i] = 0; + while (*tracks[i] > 0x7F) { + track_delta[i] = (track_delta[i] << 7) + + (*tracks[i] & 0x7F); + tracks[i]++; + } + track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F); + tracks[i]++; + NEXT_TRACK2: + smallest_delta = track_delta[i]; /* Added just to keep Xcode happy */ + UNUSED(smallest_delta); /* Added to just keep clang happy */ + } while (track_end[i] == 0); + /* + * Add decay at the end of each song + */ + if (decay_samples) { + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->events[mdi->event_count - 1].samples_to_next += decay_samples; + } else { + WM_CheckEventMemoryPool(mdi); + mdi->events[mdi->event_count].do_event = NULL; + mdi->events[mdi->event_count].event_data.channel = 0; + mdi->events[mdi->event_count].event_data.data = 0; + mdi->events[mdi->event_count].samples_to_next = decay_samples; + mdi->event_count++; + } + } + } + break; + + default: break; /* Don't expect to get here, added for completeness */ + } + + if ((mdi->event_count) + && (mdi->events[mdi->event_count - 1].do_event == NULL)) { + mdi->info.approx_total_samples -= + mdi->events[mdi->event_count - 1].samples_to_next; + mdi->event_count--; + } + /* Set total MIDI time to 1/1000's seconds */ + mdi->info.total_midi_time = (mdi->info.approx_total_samples * 1000) + / _WM_SampleRate; + /*mdi->info.approx_total_samples += _WM_SampleRate * 3;*/ + + /* Add additional samples needed for decay */ + mdi->info.approx_total_samples += decay_samples; + /*printf("decay_samples = %lu\n",decay_samples);*/ + + if ((mdi->reverb = _WM_init_reverb(_WM_SampleRate, reverb_room_width, + reverb_room_length, reverb_listen_posx, reverb_listen_posy)) + == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to init reverb", 0); + goto _end; + } + + mdi->info.current_sample = 0; + mdi->current_event = &mdi->events[0]; + mdi->samples_to_mix = 0; + mdi->note = NULL; + + WM_ResetToStart(mdi); + +_end: free(sysex_store); + free(track_end); + free(track_delta); + free(running_event); + free(tracks); + if (mdi->reverb) return mdi; + freeMDI(mdi); + return NULL; +} + +static int WM_GetOutput_Linear(midi * handle, char * buffer, + unsigned long int size) { + unsigned long int buffer_used = 0; + unsigned long int i; + struct _mdi *mdi = (struct _mdi *) handle; + unsigned long int real_samples_to_mix = 0; + unsigned long int data_pos; + signed long int premix, left_mix, right_mix; + signed long int vol_mul; + struct _note *note_data = NULL; + unsigned long int count; + struct _event *event = mdi->current_event; + signed long int *tmp_buffer; + signed long int *out_buffer; + + _WM_Lock(&mdi->lock); + + buffer_used = 0; + memset(buffer, 0, size); + + if ( (size / 2) > mdi->mix_buffer_size) { + if ( (size / 2) <= ( mdi->mix_buffer_size * 2 )) { + mdi->mix_buffer_size += MEM_CHUNK; + } else { + mdi->mix_buffer_size = size / 2; + } + mdi->mix_buffer = (long*)realloc(mdi->mix_buffer, mdi->mix_buffer_size * sizeof(signed long int)); + } + + tmp_buffer = mdi->mix_buffer; + + memset(tmp_buffer, 0, ((size / 2) * sizeof(signed long int))); + out_buffer = tmp_buffer; + + do { + if (__builtin_expect((!mdi->samples_to_mix), 0)) { + while ((!mdi->samples_to_mix) && (event->do_event)) { + event->do_event(mdi, &event->event_data); + event++; + mdi->samples_to_mix = event->samples_to_next; + mdi->current_event = event; + } + + if (!mdi->samples_to_mix) { + if (mdi->info.current_sample + >= mdi->info.approx_total_samples) { + break; + } else if ((mdi->info.approx_total_samples + - mdi->info.current_sample) > (size >> 2)) { + mdi->samples_to_mix = size >> 2; + } else { + mdi->samples_to_mix = mdi->info.approx_total_samples + - mdi->info.current_sample; + } + } + } + if (__builtin_expect((mdi->samples_to_mix > (size >> 2)), 1)) { + real_samples_to_mix = size >> 2; + } else { + real_samples_to_mix = mdi->samples_to_mix; + if (real_samples_to_mix == 0) { + continue; + } + } + + /* do mixing here */ + count = real_samples_to_mix; + do { + note_data = mdi->note; + left_mix = right_mix = 0; + if (__builtin_expect((note_data != NULL), 1)) { + while (note_data) { + /* + * =================== + * resample the sample + * =================== + */ + data_pos = note_data->sample_pos >> FPBITS; + vol_mul = ((note_data->vol_lvl + * (note_data->env_level >> 12)) >> FPBITS); + + premix = (note_data->sample->data[data_pos] + + ((note_data->sample->data[data_pos + 1] + - note_data->sample->data[data_pos]) + * (signed long int) (note_data->sample_pos + & FPMASK)>> FPBITS)) * vol_mul + / 1024; + + left_mix += premix + * mdi->channel[note_data->noteid >> 8].left_adjust; + right_mix += premix + * mdi->channel[note_data->noteid >> 8].right_adjust; + + /* + * ======================== + * sample position checking + * ======================== + */ + note_data->sample_pos += note_data->sample_inc; + if (__builtin_expect( + (note_data->sample_pos > note_data->sample->loop_end), + 0)) { + if (note_data->modes & SAMPLE_LOOP) { + note_data->sample_pos = + note_data->sample->loop_start + + ((note_data->sample_pos + - note_data->sample->loop_start) + % note_data->sample->loop_size); + } else if (__builtin_expect( + (note_data->sample_pos + >= note_data->sample->data_length), + 0)) { + if (__builtin_expect((note_data->replay == NULL), 1)) { + goto KILL_NOTE; + } + goto RESTART_NOTE; + } + } + + if (__builtin_expect((note_data->env_inc == 0), 0)) { + note_data = note_data->next; + continue; + } + + note_data->env_level += note_data->env_inc; + if (__builtin_expect((note_data->env_level > 4194304), 0)) { + note_data->env_level = + note_data->sample->env_target[note_data->env]; + } + if (__builtin_expect( + ((note_data->env_inc < 0) + && (note_data->env_level + > note_data->sample->env_target[note_data->env])) + || ((note_data->env_inc > 0) + && (note_data->env_level + < note_data->sample->env_target[note_data->env])), + 1)) { + note_data = note_data->next; + continue; + } + + note_data->env_level = + note_data->sample->env_target[note_data->env]; + switch (note_data->env) { + case 0: +#if 0 + if (!(note_data->modes & SAMPLE_ENVELOPE)) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } +#endif + break; + case 2: + if (note_data->modes & SAMPLE_SUSTAIN) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } else if (note_data->modes & SAMPLE_CLAMPED) { + note_data->env = 5; + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + continue; + } + break; + case 5: + if (__builtin_expect((note_data->env_level == 0), 1)) { + goto KILL_NOTE; + } + /* sample release */ + if (note_data->modes & SAMPLE_LOOP) + note_data->modes ^= SAMPLE_LOOP; + note_data->env_inc = 0; + note_data = note_data->next; + continue; + case 6: + if (__builtin_expect((note_data->replay != NULL), 1)) { + RESTART_NOTE: note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while (nte_array != note_data); + } + if (prev_note) { + prev_note->next = note_data->replay; + } else { + mdi->note = note_data->replay; + } + note_data->replay->next = note_data->next; + note_data = note_data->replay; + note_data->active = 1; + } + } else { + KILL_NOTE: note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while ((nte_array != note_data) + && (nte_array)); + } + if (prev_note) { + prev_note->next = note_data->next; + } else { + mdi->note = note_data->next; + } + note_data = note_data->next; + } + } + continue; + } + note_data->env++; + + if (note_data->is_off == 1) { + do_note_off_extra(note_data); + } + + if (note_data->env_level + > note_data->sample->env_target[note_data->env]) { + note_data->env_inc = + -note_data->sample->env_rate[note_data->env]; + } else { + note_data->env_inc = + note_data->sample->env_rate[note_data->env]; + } + note_data = note_data->next; + continue; + } + + /* + * ========================= + * mix the channels together + * ========================= + */ + left_mix /= 1024; + right_mix /= 1024; + } + + *tmp_buffer++ = left_mix; + *tmp_buffer++ = right_mix; + } while (--count); + + buffer_used += real_samples_to_mix * 4; + size -= (real_samples_to_mix << 2); + mdi->info.current_sample += real_samples_to_mix; + mdi->samples_to_mix -= real_samples_to_mix; + } while (size); + + tmp_buffer = out_buffer; + + if (mdi->info.mixer_options & WM_MO_REVERB) { + _WM_do_reverb(mdi->reverb, tmp_buffer, (buffer_used / 2)); + } + + for (i = 0; i < buffer_used; i += 4) { + left_mix = *tmp_buffer++; + right_mix = *tmp_buffer++; + + if (left_mix > 32767) { + left_mix = 32767; + } else if (left_mix < -32768) { + left_mix = -32768; + } + + if (right_mix > 32767) { + right_mix = 32767; + } else if (right_mix < -32768) { + right_mix = -32768; + } + + /* + * =================== + * Write to the buffer + * =================== + */ + (*buffer++) = left_mix & 0xff; + (*buffer++) = ((left_mix >> 8) & 0x7f) | ((left_mix >> 24) & 0x80); + (*buffer++) = right_mix & 0xff; + (*buffer++) = ((right_mix >> 8) & 0x7f) | ((right_mix >> 24) & 0x80); + } + + _WM_Unlock(&mdi->lock); + return buffer_used; +} + +static int WM_GetOutput_Gauss(midi * handle, char * buffer, + unsigned long int size) { + unsigned long int buffer_used = 0; + unsigned long int i; + struct _mdi *mdi = (struct _mdi *) handle; + unsigned long int real_samples_to_mix = 0; + unsigned long int data_pos; + signed long int premix, left_mix, right_mix; + signed long int vol_mul; + struct _note *note_data = NULL; + unsigned long int count; + signed short int *sptr; + double y, xd; + double *gptr, *gend; + int left, right, temp_n; + int ii, jj; + struct _event *event = mdi->current_event; + signed long int *tmp_buffer; + signed long int *out_buffer; + + _WM_Lock(&mdi->lock); + + buffer_used = 0; + memset(buffer, 0, size); + if ( (size / 2) > mdi->mix_buffer_size) { + if ( (size / 2) <= ( mdi->mix_buffer_size * 2 )) { + mdi->mix_buffer_size += MEM_CHUNK; + } else { + mdi->mix_buffer_size = size / 2; + } + mdi->mix_buffer = (long*)realloc(mdi->mix_buffer, mdi->mix_buffer_size * sizeof(signed long int)); + } + tmp_buffer = mdi->mix_buffer; + memset(tmp_buffer, 0, ((size / 2) * sizeof(signed long int))); + out_buffer = tmp_buffer; + + do { + if (__builtin_expect((!mdi->samples_to_mix), 0)) { + while ((!mdi->samples_to_mix) && (event->do_event)) { + event->do_event(mdi, &event->event_data); + event++; + mdi->samples_to_mix = event->samples_to_next; + mdi->current_event = event; + } + + if (!mdi->samples_to_mix) { + if (mdi->info.current_sample + >= mdi->info.approx_total_samples) { + break; + } else if ((mdi->info.approx_total_samples + - mdi->info.current_sample) > (size >> 2)) { + mdi->samples_to_mix = size >> 2; + } else { + mdi->samples_to_mix = mdi->info.approx_total_samples + - mdi->info.current_sample; + } + } + } + if (__builtin_expect((mdi->samples_to_mix > (size >> 2)), 1)) { + real_samples_to_mix = size >> 2; + } else { + real_samples_to_mix = mdi->samples_to_mix; + if (real_samples_to_mix == 0) { + continue; + } + } + + /* do mixing here */ + count = real_samples_to_mix; + do { + note_data = mdi->note; + left_mix = right_mix = 0; + if (__builtin_expect((note_data != NULL), 1)) { + while (note_data) { + /* + * =================== + * resample the sample + * =================== + */ + data_pos = note_data->sample_pos >> FPBITS; + vol_mul = ((note_data->vol_lvl + * (note_data->env_level >> 12)) >> FPBITS); + + /* check to see if we're near one of the ends */ + left = data_pos; + right = (note_data->sample->data_length >> FPBITS) - left + - 1; + temp_n = (right << 1) - 1; + if (temp_n <= 0) + temp_n = 1; + if (temp_n > (left << 1) + 1) + temp_n = (left << 1) + 1; + + /* use Newton if we can't fill the window */ + if (temp_n < gauss_n) { + xd = note_data->sample_pos & FPMASK; + xd /= (1L << FPBITS); + xd += temp_n >> 1; + y = 0; + sptr = note_data->sample->data + + (note_data->sample_pos >> FPBITS) + - (temp_n >> 1); + for (ii = temp_n; ii;) { + for (jj = 0; jj <= ii; jj++) + y += sptr[jj] * newt_coeffs[ii][jj]; + y *= xd - --ii; + } + y += *sptr; + } else { /* otherwise, use Gauss as usual */ + y = 0; + gptr = &gauss_table[(note_data->sample_pos & FPMASK) * + (gauss_n + 1)]; + gend = gptr + gauss_n; + sptr = note_data->sample->data + + (note_data->sample_pos >> FPBITS) + - (gauss_n >> 1); + do { + y += *(sptr++) * *(gptr++); + } while (gptr <= gend); + } + + premix = (long) (y * vol_mul / 1024); + + left_mix += premix + * mdi->channel[note_data->noteid >> 8].left_adjust; + right_mix += premix + * mdi->channel[note_data->noteid >> 8].right_adjust; + + /* + * ======================== + * sample position checking + * ======================== + */ + note_data->sample_pos += note_data->sample_inc; + if (__builtin_expect( + (note_data->sample_pos > note_data->sample->loop_end), + 0)) { + if (note_data->modes & SAMPLE_LOOP) { + note_data->sample_pos = + note_data->sample->loop_start + + ((note_data->sample_pos + - note_data->sample->loop_start) + % note_data->sample->loop_size); + } else if (__builtin_expect( + (note_data->sample_pos + >= note_data->sample->data_length), + 0)) { + if (__builtin_expect((note_data->replay == NULL), 1)) { + goto KILL_NOTE; + } + goto RESTART_NOTE; + } + } + + if (__builtin_expect((note_data->env_inc == 0), 0)) { + note_data = note_data->next; + continue; + } + + note_data->env_level += note_data->env_inc; + if (__builtin_expect((note_data->env_level > 4194304), 0)) { + note_data->env_level = + note_data->sample->env_target[note_data->env]; + } + if (__builtin_expect( + ((note_data->env_inc < 0) + && (note_data->env_level + > note_data->sample->env_target[note_data->env])) + || ((note_data->env_inc > 0) + && (note_data->env_level + < note_data->sample->env_target[note_data->env])), + 1)) { + note_data = note_data->next; + continue; + } + + note_data->env_level = + note_data->sample->env_target[note_data->env]; + switch (note_data->env) { + case 0: +#if 0 + if (!(note_data->modes & SAMPLE_ENVELOPE)) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } +#endif + break; + case 2: + if (note_data->modes & SAMPLE_SUSTAIN) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } else if (note_data->modes & SAMPLE_CLAMPED) { + note_data->env = 5; + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + continue; + } + break; + case 5: + if (__builtin_expect((note_data->env_level == 0), 1)) { + goto KILL_NOTE; + } + /* sample release */ + if (note_data->modes & SAMPLE_LOOP) + note_data->modes ^= SAMPLE_LOOP; + note_data->env_inc = 0; + note_data = note_data->next; + continue; + case 6: + if (__builtin_expect((note_data->replay != NULL), 1)) { + RESTART_NOTE: note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while (nte_array != note_data); + } + if (prev_note) { + prev_note->next = note_data->replay; + } else { + mdi->note = note_data->replay; + } + note_data->replay->next = note_data->next; + note_data = note_data->replay; + note_data->active = 1; + } + } else { + KILL_NOTE: note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while ((nte_array != note_data) + && (nte_array)); + } + if (prev_note) { + prev_note->next = note_data->next; + } else { + mdi->note = note_data->next; + } + note_data = note_data->next; + } + } + continue; + } + note_data->env++; + + if (note_data->is_off == 1) { + do_note_off_extra(note_data); + } + + if (note_data->env_level + > note_data->sample->env_target[note_data->env]) { + note_data->env_inc = + -note_data->sample->env_rate[note_data->env]; + } else { + note_data->env_inc = + note_data->sample->env_rate[note_data->env]; + } + note_data = note_data->next; + continue; + } + + /* + * ========================= + * mix the channels together + * ========================= + */ + left_mix /= 1024; + right_mix /= 1024; + } + + *tmp_buffer++ = left_mix; + *tmp_buffer++ = right_mix; + } while (--count); + + buffer_used += real_samples_to_mix * 4; + size -= (real_samples_to_mix << 2); + mdi->info.current_sample += real_samples_to_mix; + mdi->samples_to_mix -= real_samples_to_mix; + } while (size); + + tmp_buffer = out_buffer; + + if (mdi->info.mixer_options & WM_MO_REVERB) { + _WM_do_reverb(mdi->reverb, tmp_buffer, (buffer_used / 2)); + } + + for (i = 0; i < buffer_used; i += 4) { + left_mix = *tmp_buffer++; + right_mix = *tmp_buffer++; + + if (left_mix > 32767) { + left_mix = 32767; + } else if (left_mix < -32768) { + left_mix = -32768; + } + + if (right_mix > 32767) { + right_mix = 32767; + } else if (right_mix < -32768) { + right_mix = -32768; + } + + /* + * =================== + * Write to the buffer + * =================== + */ + (*buffer++) = left_mix & 0xff; + (*buffer++) = ((left_mix >> 8) & 0x7f) | ((left_mix >> 24) & 0x80); + (*buffer++) = right_mix & 0xff; + (*buffer++) = ((right_mix >> 8) & 0x7f) | ((right_mix >> 24) & 0x80); + } + _WM_Unlock(&mdi->lock); + return buffer_used; +} + +/* + * ========================= + * External Functions + * ========================= + */ + +WM_SYMBOL const char * +WildMidi_GetString(unsigned short int info) { + switch (info) { + case WM_GS_VERSION: + return WM_Version; + } + return NULL; +} + +WM_SYMBOL int WildMidi_Init(const char * config_file, unsigned short int rate, + unsigned short int options) { + if (WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_ALR_INIT, NULL, 0); + return -1; + } + + if (config_file == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(NULL config file pointer)", 0); + return -1; + } + WM_InitPatches(); + if (WM_LoadConfig(config_file) == -1) { + return -1; + } + + if (options & 0x5FF8) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", + 0); + WM_FreePatches(); + return -1; + } + WM_MixerOptions = options; + + if (rate < 11025) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(rate out of bounds, range is 11025 - 65535)", 0); + WM_FreePatches(); + return -1; + } + _WM_SampleRate = rate; + + gauss_lock = 0; + patch_lock = 0; + WM_Initialized = 1; + + return 0; +} + +WM_SYMBOL int WildMidi_MasterVolume(unsigned char master_volume) { + struct _mdi *mdi = NULL; + struct _hndl * tmp_handle = first_handle; + int i = 0; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (master_volume > 127) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(master volume out of range, range is 0-127)", 0); + return -1; + } + + WM_MasterVolume = lin_volume[master_volume]; + + if (tmp_handle) { + while (tmp_handle) { + mdi = (struct _mdi *) tmp_handle->handle; + for (i = 0; i < 16; i++) { + do_pan_adjust(mdi, i); + } + tmp_handle = tmp_handle->next; + } + } + + return 0; +} + +WM_SYMBOL int WildMidi_Close(midi * handle) { + struct _mdi *mdi = (struct _mdi *) handle; + struct _hndl * tmp_handle; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", + 0); + return -1; + } + if (first_handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(no midi's open)", + 0); + return -1; + } + _WM_Lock(&mdi->lock); + if (first_handle->handle == handle) { + tmp_handle = first_handle->next; + free(first_handle); + first_handle = tmp_handle; + if (first_handle) + first_handle->prev = NULL; + } else { + tmp_handle = first_handle; + while (tmp_handle->handle != handle) { + tmp_handle = tmp_handle->next; + if (tmp_handle == NULL) { + break; + } + } + if (tmp_handle) { + tmp_handle->prev->next = tmp_handle->next; + if (tmp_handle->next) { + tmp_handle->next->prev = tmp_handle->prev; + } + free(tmp_handle); + } + } + + freeMDI(mdi); + + return 0; +} + +WM_SYMBOL midi * +WildMidi_Open(const char *midifile) { + unsigned char *mididata = NULL; + unsigned long int midisize = 0; + midi * ret = NULL; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return NULL; + } + if (midifile == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL filename)", + 0); + return NULL; + } + + if ((mididata = _WM_BufferFile(midifile, &midisize)) == NULL) { + return NULL; + } + + ret = (void *) WM_ParseNewMidi(mididata, midisize); + free(mididata); + + if (ret) { + if (add_handle(ret) != 0) { + WildMidi_Close(ret); + ret = NULL; + } + } + + return ret; +} + +WM_SYMBOL midi * +WildMidi_OpenBuffer(unsigned char *midibuffer, unsigned long int size) { + midi * ret = NULL; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return NULL; + } + if (midibuffer == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(NULL midi data buffer)", 0); + return NULL; + } + if (size > WM_MAXFILESIZE) { + /* don't bother loading suspiciously long files */ + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LONGFIL, NULL, 0); + return NULL; + } + ret = (void *) WM_ParseNewMidi(midibuffer, size); + + 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; + struct _note *note_data; + unsigned long int real_samples_to_mix; + unsigned long int count; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", + 0); + return -1; + } + if (sample_pos == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(NULL seek position pointer)", 0); + return -1; + } + + mdi = (struct _mdi *) handle; + _WM_Lock(&mdi->lock); + event = mdi->current_event; + + /* make sure we havent asked for a positions beyond the end of the song. */ + if (*sample_pos > mdi->info.approx_total_samples) { + /* if so set the position to the end of the song */ + *sample_pos = mdi->info.approx_total_samples; + } + + /* was end of song requested and are we are there? */ + if (*sample_pos == mdi->info.current_sample) { + /* yes */ + _WM_Unlock(&mdi->lock); + return 0; + } + + /* did we want to fast forward? */ + if (mdi->info.current_sample < *sample_pos) { + /* yes */ + count = *sample_pos - mdi->info.current_sample; + } else { + /* no, reset values to start as the beginning */ + count = *sample_pos; + WM_ResetToStart(handle); + event = mdi->current_event; + } + + /* clear the reverb buffers since we not gonna be using them here */ + _WM_reset_reverb(mdi->reverb); + + while (count) { + if (__builtin_expect((!mdi->samples_to_mix), 0)) { + while ((!mdi->samples_to_mix) && (event->do_event)) { + event->do_event(mdi, &event->event_data); + event++; + mdi->samples_to_mix = event->samples_to_next; + mdi->current_event = event; + } + + if (!mdi->samples_to_mix) { + if (event->do_event == NULL) { + mdi->samples_to_mix = mdi->info.approx_total_samples + - *sample_pos; + } else { + mdi->samples_to_mix = count; + } + } + } + + if (__builtin_expect((mdi->samples_to_mix > count), 0)) { + real_samples_to_mix = count; + } else { + real_samples_to_mix = mdi->samples_to_mix; + } + + if (real_samples_to_mix == 0) { + break; + } + + count -= real_samples_to_mix; + mdi->info.current_sample += real_samples_to_mix; + mdi->samples_to_mix -= real_samples_to_mix; + } + + note_data = mdi->note; + if (note_data) { + do { + note_data->active = 0; + if (note_data->replay) { + note_data->replay = NULL; + } + note_data = note_data->next; + } while (note_data); + } + mdi->note = NULL; + + _WM_Unlock(&mdi->lock); + return 0; +} + +WM_SYMBOL int WildMidi_GetOutput(midi * handle, char *buffer, unsigned long int size) { + if (__builtin_expect((!WM_Initialized), 0)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (__builtin_expect((handle == NULL), 0)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", + 0); + return -1; + } + if (__builtin_expect((buffer == NULL), 0)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(NULL buffer pointer)", 0); + return -1; + } + if (__builtin_expect((size == 0), 0)) { + return 0; + } + if (__builtin_expect((!!(size % 4)), 0)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(size not a multiple of 4)", 0); + return -1; + } + if (((struct _mdi *) handle)->info.mixer_options & WM_MO_ENHANCED_RESAMPLING) { + if (!gauss_table) init_gauss(); + return WM_GetOutput_Gauss(handle, buffer, size); + } + return WM_GetOutput_Linear(handle, buffer, size); +} + +WM_SYMBOL int WildMidi_SetOption(midi * handle, unsigned short int options, + unsigned short int setting) { + struct _mdi *mdi; + int i; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", + 0); + return -1; + } + + mdi = (struct _mdi *) handle; + _WM_Lock(&mdi->lock); + if ((!(options & 0x0007)) || (options & 0xFFF8)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", + 0); + _WM_Unlock(&mdi->lock); + return -1; + } + if (setting & 0xFFF8) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(invalid setting)", 0); + _WM_Unlock(&mdi->lock); + return -1; + } + + mdi->info.mixer_options = ((mdi->info.mixer_options & (0x00FF ^ options)) + | (options & setting)); + + if (options & WM_MO_LOG_VOLUME) { + struct _note *note_data = mdi->note; + + for (i = 0; i < 16; i++) { + do_pan_adjust(mdi, i); + } + + if (note_data) { + do { + note_data->vol_lvl = get_volume(mdi, (note_data->noteid >> 8), + note_data); + if (note_data->replay) + note_data->replay->vol_lvl = get_volume(mdi, + (note_data->noteid >> 8), note_data->replay); + note_data = note_data->next; + } while (note_data); + } + } else if (options & WM_MO_REVERB) { + _WM_reset_reverb(mdi->reverb); + } + + _WM_Unlock(&mdi->lock); + return 0; +} + +WM_SYMBOL struct _WM_Info * +WildMidi_GetInfo(midi * handle) { + struct _mdi *mdi = (struct _mdi *) handle; + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return NULL; + } + if (handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", + 0); + return NULL; + } + _WM_Lock(&mdi->lock); + if (mdi->tmp_info == NULL) { + mdi->tmp_info = (struct _WM_Info*)malloc(sizeof(struct _WM_Info)); + if (mdi->tmp_info == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to set info", 0); + _WM_Unlock(&mdi->lock); + return NULL; + } + mdi->tmp_info->copyright = NULL; + } + mdi->tmp_info->current_sample = mdi->info.current_sample; + mdi->tmp_info->approx_total_samples = mdi->info.approx_total_samples; + mdi->tmp_info->mixer_options = mdi->info.mixer_options; + if (mdi->info.copyright) { + free(mdi->tmp_info->copyright); + mdi->tmp_info->copyright = (char*)malloc(strlen(mdi->info.copyright) + 1); + strcpy(mdi->tmp_info->copyright, mdi->info.copyright); + } else { + mdi->tmp_info->copyright = NULL; + } + _WM_Unlock(&mdi->lock); + return mdi->tmp_info; +} + +WM_SYMBOL int WildMidi_Shutdown(void) { + if (!WM_Initialized) { + // No error if trying to shut down an uninitialized device. + return 0; + } + while (first_handle) { + /* closes open handle and rotates the handles list. */ + WildMidi_Close((struct _mdi *) first_handle->handle); + } + WM_FreePatches(); + free_gauss(); + + /* reset the globals */ + WM_MasterVolume = 948; + WM_MixerOptions = 0; + fix_release = 0; + auto_amp = 0; + auto_amp_with_amp = 0; + reverb_room_width = 16.875f; + reverb_room_length = 22.5f; + reverb_listen_posx = 8.4375f; + reverb_listen_posy = 16.875f; + + WM_Initialized = 0; + + return 0; +} diff --git a/src/wildmidi/wildmidi_lib.h b/src/wildmidi/wildmidi_lib.h new file mode 100644 index 000000000..e6317a271 --- /dev/null +++ b/src/wildmidi/wildmidi_lib.h @@ -0,0 +1,75 @@ +/* + wildmidi_lib.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef WILDMIDI_LIB_H +#define WILDMIDI_LIB_H + +#define WM_MO_LOG_VOLUME 0x0001 +#define WM_MO_ENHANCED_RESAMPLING 0x0002 +#define WM_MO_REVERB 0x0004 +#define WM_MO_WHOLETEMPO 0x8000 +#define WM_MO_ROUNDTEMPO 0x2000 + +#define WM_GS_VERSION 0x0001 + +#define WM_SYMBOL // we do not need this in ZDoom + +/* +#if defined(__cplusplus) +extern "C" { +#endif +*/ + +struct _WM_Info { + char *copyright; + unsigned long int current_sample; + unsigned long int approx_total_samples; + unsigned short int mixer_options; + unsigned long int total_midi_time; +}; + +typedef void midi; + +WM_SYMBOL const char * WildMidi_GetString (unsigned short int info); +WM_SYMBOL int WildMidi_Init (const char * config_file, unsigned short int rate, unsigned short int options); +WM_SYMBOL int WildMidi_MasterVolume (unsigned char master_volume); +WM_SYMBOL midi * WildMidi_Open (const char *midifile); +WM_SYMBOL midi * WildMidi_OpenBuffer (unsigned char *midibuffer, unsigned long int size); +WM_SYMBOL int WildMidi_GetOutput (midi * handle, char * buffer, unsigned long int size); +WM_SYMBOL int WildMidi_SetOption (midi * handle, unsigned short int options, unsigned short int setting); +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); + +/* +#if defined(__cplusplus) +} +#endif +*/ + +#endif /* WILDMIDI_LIB_H */ + diff --git a/src/wildmidi/wm_error.cpp b/src/wildmidi/wm_error.cpp new file mode 100644 index 000000000..9fabdff84 --- /dev/null +++ b/src/wildmidi/wm_error.cpp @@ -0,0 +1,86 @@ +/* + wm_error.c + error reporting + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +//#include "config.h" + +#include +#include +#include +#include "wm_error.h" +#include "doomtype.h" +#include "v_text.h" + +void _WM_ERROR_NEW(const char * wmfmt, ...) { + va_list args; + fprintf(stderr, "\r"); + va_start(args, wmfmt); + vfprintf(stderr, wmfmt, args); + va_end(args); + fprintf(stderr, "\n"); +} + +void _WM_ERROR(const char * func, unsigned int lne, int wmerno, + const char * wmfor, int error) { + + static const char *errors[WM_ERR_MAX+1] = { + "No error", + "Unable to obtain memory", + "Unable to stat", + "Unable to load", + "Unable to open", + "Unable to read", + "Invalid or Unsuported file format", + "File corrupt", + "Library not Initialized", + "Invalid argument", + "Library Already Initialized", + "Not a midi file", + "Refusing to load unusually long file", + + "Invalid error code" + }; + + if (wmerno < 0 || wmerno > WM_ERR_MAX) + wmerno = WM_ERR_MAX; + + if (wmfor != NULL) { + if (error != 0) { + Printf(TEXTCOLOR_RED "\rlibWildMidi(%s:%u): ERROR %s %s (%s)\n", func, + lne, errors[wmerno], wmfor, strerror(error)); + } else { + Printf(TEXTCOLOR_RED "\rlibWildMidi(%s:%u): ERROR %s %s\n", func, lne, + errors[wmerno], wmfor); + } + } else { + if (error != 0) { + Printf(TEXTCOLOR_RED "\rlibWildMidi(%s:%u): ERROR %s (%s)\n", func, lne, + errors[wmerno], strerror(error)); + } else { + Printf(TEXTCOLOR_RED "\rlibWildMidi(%s:%u): ERROR %s\n", func, lne, + errors[wmerno]); + } + } +} + diff --git a/src/wildmidi/wm_error.h b/src/wildmidi/wm_error.h new file mode 100644 index 000000000..316924648 --- /dev/null +++ b/src/wildmidi/wm_error.h @@ -0,0 +1,56 @@ +/* + wm_error.h + + error reporting + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +#ifndef __WM_ERROR_H +#define __WM_ERROR_H + +enum { + WM_ERR_NONE = 0, + WM_ERR_MEM, + WM_ERR_STAT, + WM_ERR_LOAD, + WM_ERR_OPEN, + WM_ERR_READ, + WM_ERR_INVALID, + WM_ERR_CORUPT, + WM_ERR_NOT_INIT, + WM_ERR_INVALID_ARG, + WM_ERR_ALR_INIT, + WM_ERR_NOT_MIDI, + WM_ERR_LONGFIL, + + WM_ERR_MAX +}; + + extern void _WM_ERROR_NEW(const char * wmfmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 1, 2))) +#endif + ; + extern void _WM_ERROR(const char * func, unsigned int lne, int wmerno, + const char * wmfor, int error); + +#endif /* __WM_ERROR_H */ diff --git a/zdoom.vcproj b/zdoom.vcproj index d802fc61c..db4660d3b 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -106,6 +106,7 @@ MapExports="true" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" OptimizeReferences="2" EnableCOMDATFolding="2" @@ -223,6 +224,7 @@ MapExports="true" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" OptimizeReferences="2" EnableCOMDATFolding="2" @@ -332,6 +334,7 @@ ProgramDatabaseFile=".\Debug/zdoomd.pdb" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" SetChecksum="false" TargetMachine="0" @@ -440,6 +443,7 @@ ProgramDatabaseFile=".\Debug/zdoomd.pdb" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" SetChecksum="false" TargetMachine="17"