diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 782e4bac0..614d2fb50 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1128,6 +1128,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
@@ -1265,6 +1271,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/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..9ae3ad1be
--- /dev/null
+++ b/src/wildmidi/wildmidi_lib.cpp
@@ -0,0 +1,4290 @@
+/*
+ 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(struct _mdi *mdi, 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(mdi, 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(mdi, 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,
+ ((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(mdi,
+ (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(mdi, 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(mdi, 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,
+ ((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,
+ ((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,
+ ((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) {
+ _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
+ return -1;
+ }
+ 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 */