From eff2286bc905c8891928513a7415cb24985365bc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 28 Nov 2015 17:38:40 +0100 Subject: [PATCH 01/56] - added WildMidi library sources - all converted to C++ so that they can later interface with ZDoom's own MIDI code. Also redid the file loading function to use ZDoom's FileReader instead of low level IO. --- src/CMakeLists.txt | 8 + src/wildmidi/common.h | 94 + src/wildmidi/file_io.cpp | 71 + src/wildmidi/file_io.h | 33 + src/wildmidi/gus_pat.cpp | 900 +++++++ src/wildmidi/gus_pat.h | 77 + src/wildmidi/lock.cpp | 87 + src/wildmidi/lock.h | 36 + src/wildmidi/reverb.cpp | 398 +++ src/wildmidi/reverb.h | 57 + src/wildmidi/wildmidi_lib.cpp | 4290 +++++++++++++++++++++++++++++++++ src/wildmidi/wildmidi_lib.h | 75 + src/wildmidi/wm_error.cpp | 86 + src/wildmidi/wm_error.h | 56 + 14 files changed, 6268 insertions(+) create mode 100644 src/wildmidi/common.h create mode 100644 src/wildmidi/file_io.cpp create mode 100644 src/wildmidi/file_io.h create mode 100644 src/wildmidi/gus_pat.cpp create mode 100644 src/wildmidi/gus_pat.h create mode 100644 src/wildmidi/lock.cpp create mode 100644 src/wildmidi/lock.h create mode 100644 src/wildmidi/reverb.cpp create mode 100644 src/wildmidi/reverb.h create mode 100644 src/wildmidi/wildmidi_lib.cpp create mode 100644 src/wildmidi/wildmidi_lib.h create mode 100644 src/wildmidi/wm_error.cpp create mode 100644 src/wildmidi/wm_error.h 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 */ From a03b9477295743b038e9af141525e818eda6943c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 28 Nov 2015 20:58:14 +0100 Subject: [PATCH 02/56] - WildMidi generally working, some cleanup left to do... --- src/CMakeLists.txt | 1 + src/s_advsound.cpp | 1 + src/s_sound.h | 1 + src/sound/i_musicinterns.h | 19 +++++++++++++++++++ src/sound/music_midi_base.cpp | 13 ++++++++----- src/sound/music_midistream.cpp | 4 ++++ 6 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 614d2fb50..5bc88249c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1080,6 +1080,7 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE sound/music_midistream.cpp sound/music_midi_base.cpp sound/music_midi_timidity.cpp + sound/music_midi_wildmidi.cpp sound/music_mus_opl.cpp sound/music_stream.cpp sound/music_fluidsynth_mididevice.cpp diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index c72005813..cb0f26ae6 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -1372,6 +1372,7 @@ static void S_AddSNDINFO (int lump) else if (sc.Compare("default")) MidiDevices[nm] = MDEV_DEFAULT; else if (sc.Compare("fluidsynth")) MidiDevices[nm] = MDEV_FLUIDSYNTH; else if (sc.Compare("gus")) MidiDevices[nm] = MDEV_GUS; + else if (sc.Compare("wildmidi")) MidiDevices[nm] = MDEV_WILDMIDI; else sc.ScriptError("Unknown MIDI device %s\n", sc.String); } break; diff --git a/src/s_sound.h b/src/s_sound.h index 49fb81fb3..16aa8b333 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -394,6 +394,7 @@ enum EMidiDevice MDEV_TIMIDITY = 3, MDEV_FLUIDSYNTH = 4, MDEV_GUS = 5, + MDEV_WILDMIDI = 6, }; typedef TMap MusicAliasMap; diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 77aa31312..4aa5f8254 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -24,6 +24,7 @@ #include "i_music.h" #include "s_sound.h" #include "files.h" +#include "wildmidi/wildmidi_lib.h" void I_InitMusicWin32 (); void I_ShutdownMusicWin32 (); @@ -218,6 +219,24 @@ protected: #endif }; +class WildMidiMIDIDevice : public PseudoMIDIDevice +{ +public: + WildMidiMIDIDevice(); + ~WildMidiMIDIDevice(); + + int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + bool Preprocess(MIDIStreamer *song, bool looping); + bool IsOpen() const; + +protected: + + midi *mMidi; + bool mLoop; + + static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); +}; + // Base class for software synthesizer MIDI output devices ------------------ diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index 649f9002e..e1afa09a8 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -11,9 +11,9 @@ static DWORD nummididevices; static bool nummididevicesset; #ifdef HAVE_FLUIDSYNTH -#define NUM_DEF_DEVICES 5 +#define NUM_DEF_DEVICES 6 #else -#define NUM_DEF_DEVICES 4 +#define NUM_DEF_DEVICES 5 #endif static void AddDefaultMidiDevices(FOptionValues *opt) @@ -33,8 +33,10 @@ static void AddDefaultMidiDevices(FOptionValues *opt) pair[p+1].Value = -3.0; pair[p+2].Text = "TiMidity++"; pair[p+2].Value = -2.0; - pair[p+3].Text = "Sound System"; - pair[p+3].Value = -1.0; + pair[p+3].Text = "WildMidi"; + pair[p+3].Value = -6.0; + pair[p+4].Text = "Sound System"; + pair[p+4].Value = -1.0; } @@ -70,7 +72,7 @@ CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) if (!nummididevicesset) return; - if ((self >= (signed)nummididevices) || (self < -5)) + if ((self >= (signed)nummididevices) || (self < -6)) { Printf ("ID out of range. Using default device.\n"); self = 0; @@ -211,6 +213,7 @@ void I_BuildMIDIMenuList (FOptionValues *opt) CCMD (snd_listmididevices) { + Printf("%s-6. WildMidi\n", -6 == snd_mididevice ? TEXTCOLOR_BOLD : ""); #ifdef HAVE_FLUIDSYNTH Printf("%s-5. FluidSynth\n", -5 == snd_mididevice ? TEXTCOLOR_BOLD : ""); #endif diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 6c85c06af..42e8b5926 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -240,6 +240,7 @@ EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device) #ifdef HAVE_FLUIDSYNTH case -5: return MDEV_FLUIDSYNTH; #endif + case -6: return MDEV_WILDMIDI; default: #ifdef _WIN32 return MDEV_MMAPI; @@ -292,6 +293,9 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const case MDEV_TIMIDITY: return new TimidityPPMIDIDevice; + case MDEV_WILDMIDI: + return new WildMidiMIDIDevice; + default: return NULL; } From 3682924249390acb4cb0ab3c0b4daa1ba2b44eba Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 29 Nov 2015 11:27:08 +0100 Subject: [PATCH 03/56] - removed redundant parameter --- src/wildmidi/wildmidi_lib.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 9ae3ad1be..84c2f382a 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -1299,7 +1299,7 @@ static int load_sample(struct _patch *sample_patch) { } static struct _patch * -get_patch_data(struct _mdi *mdi, unsigned short patchid) { +get_patch_data(unsigned short patchid) { struct _patch *search_patch; _WM_Lock(&patch_lock); @@ -1320,7 +1320,7 @@ get_patch_data(struct _mdi *mdi, unsigned short patchid) { } if ((patchid >> 8) != 0) { _WM_Unlock(&patch_lock); - return (get_patch_data(mdi, patchid & 0x00FF)); + return (get_patch_data(patchid & 0x00FF)); } _WM_Unlock(&patch_lock); return NULL; @@ -1336,7 +1336,7 @@ static void load_patch(struct _mdi *mdi, unsigned short patchid) { } } - tmp_patch = get_patch_data(mdi, patchid); + tmp_patch = get_patch_data(patchid); if (tmp_patch == NULL) { return; } @@ -1530,8 +1530,7 @@ static void do_note_on(struct _mdi *mdi, struct _event_data *data) { } freq = freq_table[(note % 12) * 100] >> (10 - (note / 12)); } else { - patch = get_patch_data(mdi, - ((mdi->channel[ch].bank << 8) | note | 0x80)); + patch = get_patch_data(((mdi->channel[ch].bank << 8) | note | 0x80)); if (patch == NULL) { return; } @@ -1920,8 +1919,7 @@ 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))); + mdi->channel[ch].patch = get_patch_data((unsigned short)(((mdi->channel[ch].bank << 8) | data->data))); } else { mdi->channel[ch].bank = (unsigned char)data->data; } @@ -1986,7 +1984,7 @@ static void do_sysex_roland_drum_track(struct _mdi *mdi, mdi->channel[ch].patch = NULL; } else { mdi->channel[ch].isdrum = 0; - mdi->channel[ch].patch = get_patch_data(mdi, 0); + mdi->channel[ch].patch = get_patch_data(0); } } @@ -1995,7 +1993,7 @@ static void do_sysex_roland_reset(struct _mdi *mdi, struct _event_data *data) { for (i = 0; i < 16; i++) { mdi->channel[i].bank = 0; if (i != 9) { - mdi->channel[i].patch = get_patch_data(mdi, 0); + mdi->channel[i].patch = get_patch_data(0); } else { mdi->channel[i].patch = NULL; } @@ -2181,7 +2179,7 @@ static int midi_setup_patch(struct _mdi *mdi, unsigned char channel, 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].patch = get_patch_data( ((mdi->channel[channel].bank << 8) | patch)); } return 0; @@ -2614,7 +2612,7 @@ WM_ParseNewMidi(unsigned char *midi_data, unsigned int midi_size) { 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, + tmp_patch = get_patch_data( ((mdi->channel[current_event_ch].bank << 8) | tracks[i][0] | 0x80)); /* if (tmp_patch == NULL) @@ -2919,7 +2917,7 @@ WM_ParseNewMidi(unsigned char *midi_data, unsigned int midi_size) { struct _patch *tmp_patch = NULL; if (mdi->channel[current_event_ch].isdrum) { - tmp_patch = get_patch_data(mdi, + tmp_patch = get_patch_data( ((mdi->channel[current_event_ch].bank << 8) | tracks[i][0] | 0x80)); /* if (tmp_patch == NULL) From 724445354ca1e2bc0517a4a0cf8d0dae52c2a295 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 1 Dec 2015 10:39:22 +0100 Subject: [PATCH 04/56] - add the WildMidiMIDIDevice CPP file. --- src/sound/music_midi_wildmidi.cpp | 163 ++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 src/sound/music_midi_wildmidi.cpp diff --git a/src/sound/music_midi_wildmidi.cpp b/src/sound/music_midi_wildmidi.cpp new file mode 100644 index 000000000..f6df48459 --- /dev/null +++ b/src/sound/music_midi_wildmidi.cpp @@ -0,0 +1,163 @@ +#include "i_musicinterns.h" +#include "c_cvars.h" +#include "cmdlib.h" +#include "templates.h" +#include "version.h" + + +CVAR(String, wildmidi_config, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +static FString currentConfig; + +// added because Timidity's output is rather loud. +CUSTOM_CVAR (Float, wildmidi_mastervolume, 1.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (self < 0.f) + self = 0.f; + else if (self > 1.f) + self = 1.f; +} + +CUSTOM_CVAR (Int, wildmidi_frequency, 44100, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ // Clamp frequency to Timidity's limits + if (self < 11000) + self = 11000; + else if (self > 65000) + self = 65000; +} + +//========================================================================== +// +// WildMidiMIDIDevice Constructor +// +//========================================================================== + +WildMidiMIDIDevice::WildMidiMIDIDevice() +{ + mMidi = NULL; + mLoop = false; +} + +//========================================================================== +// +// WildMidiMIDIDevice Destructor +// +//========================================================================== + +WildMidiMIDIDevice::~WildMidiMIDIDevice () +{ + if (mMidi != NULL) WildMidi_Close(mMidi); + // do not shut down the device so that it can be reused for the next song being played. +} + +//========================================================================== +// +// WildMidiMIDIDevice :: Preprocess +// +//========================================================================== + +bool WildMidiMIDIDevice::Preprocess(MIDIStreamer *song, bool looping) +{ + TArray midi; + + // Write MIDI song to temporary file + song->CreateSMF(midi, looping ? 0 : 1); + + mMidi = WildMidi_OpenBuffer(&midi[0], midi.Size()); + if (mMidi == NULL) + { + Printf(PRINT_BOLD, "Could not open temp music file\n"); + } + mLoop = looping; + return false; +} + +//========================================================================== +// +// WildMidiMIDIDevice :: Open +// +//========================================================================== + +int WildMidiMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +{ + if (currentConfig.CompareNoCase(wildmidi_config) != 0) + { + if (currentConfig.IsNotEmpty()) WildMidi_Shutdown(); + currentConfig = ""; + if (!WildMidi_Init(wildmidi_config, wildmidi_frequency, WM_MO_ENHANCED_RESAMPLING)) + { + currentConfig = wildmidi_config; + } + else + { + return 1; + } + } + + Stream = GSnd->CreateStream(FillStream, 32 * 1024, 0, wildmidi_frequency, this); + if (Stream == NULL) + { + Printf(PRINT_BOLD, "Could not create music stream.\n"); + return 1; + } + + return 0; +} + + +//========================================================================== +// +// WildMidiMIDIDevice :: FillStream +// +//========================================================================== + +bool WildMidiMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata) +{ + char *buffer = (char*)buff; + WildMidiMIDIDevice *song = (WildMidiMIDIDevice *)userdata; + if (song->mMidi != NULL) + { + while (len > 0) + { + int written = WildMidi_GetOutput(song->mMidi, buffer, len); + if (written < 0) + { + // error + memset(buffer, 0, len); + return false; + } + buffer += written; + len -= written; + + if (len > 0) + { + if (!song->mLoop) + { + memset(buffer, 0, len); + return written > 0; + } + else + { + // loop the sound (i.e. go back to start.) + unsigned long spos = 0; + WildMidi_FastSeek(song->mMidi, &spos); + } + + } + } + } + + return true; +} + +//========================================================================== +// +// WildMidiMIDIDevice :: IsOpen +// +//========================================================================== + +bool WildMidiMIDIDevice::IsOpen() const +{ + return mMidi != NULL; +} + From 7c6237e1343915f26c84010bf668e1133e7a8395 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Thu, 10 Dec 2015 21:24:37 -0600 Subject: [PATCH 05/56] has replaced on FreeBSD as well --- src/sound/fmodsound.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 33ca0495d..4042ca421 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -44,7 +44,7 @@ extern HWND Window; #define FALSE 0 #define TRUE 1 #endif -#ifdef __APPLE__ +#if defined(__FreeBSD__) || defined(__APPLE__) #include #elif __sun #include From 841ddb0d63a60b3ca97443bcfd9c9eed856d2478 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Thu, 10 Dec 2015 23:01:59 -0600 Subject: [PATCH 06/56] has replaced on FreeBSD as well --- src/gl/system/gl_system.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl/system/gl_system.h b/src/gl/system/gl_system.h index 5cfd7fe5c..ed52ab7ef 100644 --- a/src/gl/system/gl_system.h +++ b/src/gl/system/gl_system.h @@ -49,7 +49,7 @@ #include #include #include -#if !defined(__APPLE__) +#if !defined(__APPLE__) && !defined(__FreeBSD__) #include #endif #include From 37ea94abf3ddc126fb67c79e94f87b069640c309 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 11 Dec 2015 22:26:10 +0100 Subject: [PATCH 07/56] - fixed: translucent walls did not set up their dynamic lights in the GL4 render path. --- src/gl/scene/gl_walls_draw.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 801bebcc5..5135a5947 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -322,6 +322,11 @@ void GLWall::RenderTranslucentWall() // and until that changes I won't fix this code for the new blending modes! bool isadditive = RenderStyle == STYLE_Add; + if (gl_fixedcolormap == CM_DEFAULT && gl_lights && (gl.flags & RFL_BUFFER_STORAGE)) + { + SetupLights(); + } + if (!transparent) gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold); else gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f); if (isadditive) gl_RenderState.BlendFunc(GL_SRC_ALPHA,GL_ONE); From af2ce6ef427d2d175bd5c19403f7bc26dcb5ad02 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 14 Dec 2015 09:06:13 +0100 Subject: [PATCH 08/56] - fixed: The 'mindefaults' game configuration must define the player starts 5-8. --- wadsrc/static/mapinfo/mindefaults.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wadsrc/static/mapinfo/mindefaults.txt b/wadsrc/static/mapinfo/mindefaults.txt index cbb3fa06b..0d7b16209 100644 --- a/wadsrc/static/mapinfo/mindefaults.txt +++ b/wadsrc/static/mapinfo/mindefaults.txt @@ -56,4 +56,12 @@ gameinfo statscreen_enteringpatch = "WIENTER" } +DoomEdNums +{ + 4001 = "$Player5Start" + 4002 = "$Player6Start" + 4003 = "$Player7Start" + 4004 = "$Player8Start" +} + include "mapinfo/common.txt" From 9176d75580d10a7d9ad4f8ab77ef18d6ffbe247e Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 14 Dec 2015 11:47:46 +0200 Subject: [PATCH 09/56] Fix incorrect small font rendering with Hexen Mac IWAD Unused high resolution font lumps broke composite font logic Small font had doubled height because of that, at least alternate HUD and inter-hub text messages had noticeable visual issues --- src/w_wad.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/w_wad.h | 1 + 2 files changed, 37 insertions(+) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index efeb38571..552228557 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -184,6 +184,7 @@ void FWadCollection::InitMultipleFiles (TArray &filenames) } RenameNerve(); RenameSprites(); + FixMacHexen(); // [RH] Set up hash table FirstLumpIndex = new DWORD[NumLumps]; @@ -956,6 +957,41 @@ void FWadCollection::RenameNerve () } } +//========================================================================== +// +// FixMacHexen +// +// Rename unused high resolution font lumps because they are incorrectly +// treated as extended characters +// +//========================================================================== + +void FWadCollection::FixMacHexen() +{ + if (GAME_Hexen != gameinfo.gametype) + { + return; + } + + for (int i = GetFirstLump(IWAD_FILENUM), last = GetLastLump(IWAD_FILENUM); i <= last; ++i) + { + assert(IWAD_FILENUM == LumpInfo[i].wadnum); + + FResourceLump* const lump = LumpInfo[i].lump; + char* const name = lump->Name; + + // Unwanted lumps are named like FONTA??1 + + if (8 == strlen(name) + && MAKE_ID('F', 'O', 'N', 'T') == lump->dwName + && 'A' == name[4] && '1' == name[7] + && isdigit(name[5]) && isdigit(name[6])) + { + name[0] = '\0'; + } + } +} + //========================================================================== // // W_FindLump diff --git a/src/w_wad.h b/src/w_wad.h index 323f12df2..dcac6a1b2 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -238,6 +238,7 @@ protected: private: void RenameSprites(); void RenameNerve(); + void FixMacHexen(); void DeleteAll(); }; From 06bb75576c45ae315c2ab37745503eb08b65ab0a Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 15 Dec 2015 14:13:54 -0600 Subject: [PATCH 10/56] Revert "Fixed timekeeping when starting a sigrenderer with a time offset" This reverts commit cf2577d4bc284fb5c5b71377413a47c72a1362dc. --- dumb/src/it/itrender.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dumb/src/it/itrender.c b/dumb/src/it/itrender.c index 0491e7e59..fd8ccae13 100644 --- a/dumb/src/it/itrender.c +++ b/dumb/src/it/itrender.c @@ -5482,10 +5482,6 @@ static sigrenderer_t *it_start_sigrenderer(DUH *duh, sigdata_t *vsigdata, int n_ while (pos > 0 && pos >= sigrenderer->time_left) { render(sigrenderer, 0, 1.0f, 0, sigrenderer->time_left, NULL); -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->time_played += (LONG_LONG)sigrenderer->time_left << 16; -#endif - pos -= sigrenderer->time_left; sigrenderer->time_left = 0; @@ -5498,10 +5494,6 @@ static sigrenderer_t *it_start_sigrenderer(DUH *duh, sigdata_t *vsigdata, int n_ render(sigrenderer, 0, 1.0f, 0, pos, NULL); sigrenderer->time_left -= pos; -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->time_played += (LONG_LONG)pos << 16; -#endif - return sigrenderer; } From d3000fd838495bc1eb5a72def26172e374d1c3c9 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 15 Dec 2015 14:16:34 -0600 Subject: [PATCH 11/56] Revert "Fixed timekeeping" This reverts commit 68f8a3aa8fb53b98625232d99cc5bd040e67dd96. Conflicts: dumb/src/it/itrender.c --- dumb/src/it/itrender.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/dumb/src/it/itrender.c b/dumb/src/it/itrender.c index fd8ccae13..a82649268 100644 --- a/dumb/src/it/itrender.c +++ b/dumb/src/it/itrender.c @@ -4244,6 +4244,10 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) */ #endif bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row); + if (sigrenderer->looped == 0) { + timekeeping_array_push(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row, sigrenderer->time_played); + } + timekeeping_array_bump(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); { int n; for (n = 0; n < DUMB_IT_N_CHANNELS; n++) @@ -4413,13 +4417,6 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) } } -#ifdef BIT_ARRAY_BULLSHIT - if (sigrenderer->looped == 0) { - timekeeping_array_push(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row, sigrenderer->time_played); - } - timekeeping_array_bump(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); -#endif - if (!(sigdata->flags & IT_WAS_A_669)) reset_effects(sigrenderer); @@ -5510,7 +5507,7 @@ static int32 it_sigrenderer_get_samples( int dt; int32 todo; int ret; - LONG_LONG t; + LONG_LONG time_left, t; if (sigrenderer->order < 0) return 0; // problematic @@ -5523,7 +5520,8 @@ static int32 it_sigrenderer_get_samples( if (!samples) volume = 0; for (;;) { - todo = (int32)((((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left) / dt); + time_left = ((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left; + todo = (long)(time_left / dt); if (todo >= size) break; @@ -5538,7 +5536,7 @@ static int32 it_sigrenderer_get_samples( sigrenderer->time_left += (int32)(t >> 16); #ifdef BIT_ARRAY_BULLSHIT - sigrenderer->time_played += (LONG_LONG)todo * dt; + sigrenderer->time_played += time_left; #endif ret = process_tick(sigrenderer); From d0f1df113223182b2ad75accfbb0dc3511cb996d Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 15 Dec 2015 14:17:02 -0600 Subject: [PATCH 12/56] Revert "Fixed duplicating some timekeeping state variables" This reverts commit 381ce8ea4237a64d63ac447d1e42463edde356da. --- dumb/src/it/itrender.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dumb/src/it/itrender.c b/dumb/src/it/itrender.c index a82649268..99f8bdc13 100644 --- a/dumb/src/it/itrender.c +++ b/dumb/src/it/itrender.c @@ -353,8 +353,6 @@ static DUMB_IT_SIGRENDERER *dup_sigrenderer(DUMB_IT_SIGRENDERER *src, int n_chan #ifdef BIT_ARRAY_BULLSHIT dst->played = bit_array_dup(src->played); - dst->looped = src->looped; - dst->time_played = src->time_played; dst->row_timekeeper = timekeeping_array_dup(src->row_timekeeper); #endif From 8a6dfabedb104e7ae37e0c4f03c7c3b44397b048 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 15 Dec 2015 14:29:51 -0600 Subject: [PATCH 13/56] Revert "- Implemented loop-accurate time position reporting into DUMB" This reverts commit 153721b1c9f4cde62fa17d6aef56f782b37384bf. Conflicts: dumb/include/dumb.h dumb/include/internal/tarray.h dumb/src/helpers/tarray.c dumb/src/it/itrender.c --- dumb/CMakeLists.txt | 1 - dumb/include/dumb.h | 5 - dumb/include/internal/it.h | 16 --- dumb/include/internal/tarray.h | 31 ----- dumb/prj/dumb/dumb.pro | 2 - dumb/src/core/rendsig.c | 10 +- dumb/src/helpers/tarray.c | 175 ----------------------------- dumb/src/it/itrender.c | 76 ++----------- dumb/vc6/dumb/dumb.vcxproj | 2 - dumb/vc6/dumb/dumb.vcxproj.filters | 6 - 10 files changed, 8 insertions(+), 316 deletions(-) delete mode 100644 dumb/include/internal/tarray.h delete mode 100644 dumb/src/helpers/tarray.c diff --git a/dumb/CMakeLists.txt b/dumb/CMakeLists.txt index e95c69154..b590aa165 100644 --- a/dumb/CMakeLists.txt +++ b/dumb/CMakeLists.txt @@ -39,7 +39,6 @@ add_library( dumb src/helpers/memfile.c src/helpers/clickrem.c src/helpers/barray.c - src/helpers/tarray.c src/it/xmeffect.c src/it/readxm2.c src/it/readxm.c diff --git a/dumb/include/dumb.h b/dumb/include/dumb.h index c2c0aaa32..2b6ac4879 100644 --- a/dumb/include/dumb.h +++ b/dumb/include/dumb.h @@ -606,10 +606,6 @@ typedef void (*DUH_SIGRENDERER_GET_CURRENT_SAMPLE)( sample_t *samples ); -typedef int32 (*DUH_SIGRENDERER_GET_POSITION)( - sigrenderer_t *sigrenderer -); - typedef void (*DUH_END_SIGRENDERER)(sigrenderer_t *sigrenderer); typedef void (*DUH_UNLOAD_SIGDATA)(sigdata_t *sigdata); @@ -625,7 +621,6 @@ typedef struct DUH_SIGTYPE_DESC DUH_SIGRENDERER_SET_SIGPARAM sigrenderer_set_sigparam; DUH_SIGRENDERER_GENERATE_SAMPLES sigrenderer_generate_samples; DUH_SIGRENDERER_GET_CURRENT_SAMPLE sigrenderer_get_current_sample; - DUH_SIGRENDERER_GET_POSITION sigrenderer_get_position; DUH_END_SIGRENDERER end_sigrenderer; DUH_UNLOAD_SIGDATA unload_sigdata; } diff --git a/dumb/include/internal/it.h b/dumb/include/internal/it.h index 6defa759a..a9196b316 100644 --- a/dumb/include/internal/it.h +++ b/dumb/include/internal/it.h @@ -33,7 +33,6 @@ #include #include "barray.h" -#include "tarray.h" /** TO DO: THINK ABOUT THE FOLLOWING: @@ -724,21 +723,6 @@ struct DUMB_IT_SIGRENDERER #ifdef BIT_ARRAY_BULLSHIT /* bit array, which rows are played, only checked by pattern break or loop commands */ void * played; - - /* - Loop indicator for internal processes, may also be useful for external processes - 0 - Not looped - 1 - Looped - -1 - Continued past loop - */ - int looped; - - /* - Kept until looped - */ - LONG_LONG time_played; - - void * row_timekeeper; #endif int32 gvz_time; diff --git a/dumb/include/internal/tarray.h b/dumb/include/internal/tarray.h deleted file mode 100644 index 7eb3af7c6..000000000 --- a/dumb/include/internal/tarray.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _T_ARRAY_H_ -#define _T_ARRAY_H_ - -#include - -#ifndef LONG_LONG -#if defined __GNUC__ || defined __INTEL_COMPILER || defined __MWERKS__ -#define LONG_LONG long long -#elif defined _MSC_VER || defined __WATCOMC__ -#define LONG_LONG __int64 -#elif defined __sgi -#define LONG_LONG long long -#else -#error 64-bit integer type unknown -#endif -#endif - -void * timekeeping_array_create(size_t size); -void timekeeping_array_destroy(void * array); -void * timekeeping_array_dup(void * array); - -void timekeeping_array_reset(void * array, size_t loop_start); - -void timekeeping_array_push(void * array, size_t index, LONG_LONG time); -void timekeeping_array_bump(void * array, size_t index); - -unsigned int timekeeping_array_get_count(void * array, size_t index); - -LONG_LONG timekeeping_array_get_item(void * array, size_t index); - -#endif diff --git a/dumb/prj/dumb/dumb.pro b/dumb/prj/dumb/dumb.pro index 629a9294a..9244ce4bd 100644 --- a/dumb/prj/dumb/dumb.pro +++ b/dumb/prj/dumb/dumb.pro @@ -37,7 +37,6 @@ SOURCES += \ ../../src/helpers/memfile.c \ ../../src/helpers/clickrem.c \ ../../src/helpers/barray.c \ - ../../src/helpers/tarray.c \ ../../src/it/xmeffect.c \ ../../src/it/readxm2.c \ ../../src/it/readxm.c \ @@ -109,7 +108,6 @@ HEADERS += \ ../../include/internal/it.h \ ../../include/internal/dumb.h \ ../../include/internal/barray.h \ - ../../include/internal/tarray.h \ ../../include/internal/aldumb.h \ ../../include/internal/sinc_resampler.h \ ../../include/internal/stack_alloc.h \ diff --git a/dumb/src/core/rendsig.c b/dumb/src/core/rendsig.c index 053011a11..72da173c5 100644 --- a/dumb/src/core/rendsig.c +++ b/dumb/src/core/rendsig.c @@ -147,15 +147,7 @@ int DUMBEXPORT duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer) int32 DUMBEXPORT duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer) { - DUH_SIGRENDERER_GET_POSITION proc; - - if (!sigrenderer) return -1; - - proc = sigrenderer->desc->sigrenderer_get_position; - if (proc) - return (*proc)(sigrenderer->sigrenderer); - else - return sigrenderer->pos; + return sigrenderer ? sigrenderer->pos : -1; } diff --git a/dumb/src/helpers/tarray.c b/dumb/src/helpers/tarray.c deleted file mode 100644 index f3ba422d8..000000000 --- a/dumb/src/helpers/tarray.c +++ /dev/null @@ -1,175 +0,0 @@ -#include "internal/tarray.h" - -#include - - /* - Structures which contain the play times of each pattern and row combination in the song, - not guaranteed to be valid for the whole song until the loop status is no longer zero. - The initial count and restart count will both be zero on song start, then both will be - incremented until the song loops. Restart count will be reset to zero on loop for all - rows which have a time equal to or greater than the loop start point, so time keeping - functions will know which timestamp the song is currently located at. - - Timestamp lists are guaranteed to be allocated in blocks of 16 timestamps at a time. - */ - - /* - We don't need full timekeeping because the player loop only wants the first play time - of the loop start order/row. We also don't really want full timekeeping because it - involves a lot of memory allocations, which is also slow. - */ - -#undef FULL_TIMEKEEPING - -typedef struct DUMB_IT_ROW_TIME -{ - unsigned int count, restart_count; -#ifndef FULL_TIMEKEEPING - LONG_LONG first_time; -#else - LONG_LONG * times; -#endif -} DUMB_IT_ROW_TIME; - -void * timekeeping_array_create(size_t size) -{ - size_t * _size = (size_t *) calloc( 1, sizeof(size_t) + sizeof(DUMB_IT_ROW_TIME) * size ); - if ( _size ) { - *_size = size; - } - return _size; -} - -void timekeeping_array_destroy(void * array) -{ -#ifdef FULL_TIMEKEEPING - size_t i; - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - for (i = 0; i < *size; i++) { - if (s[i].times) free(s[i].times); - } -#endif - - free(array); -} - -void * timekeeping_array_dup(void * array) -{ - size_t i; - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - size_t * new_size = (size_t *) calloc( 1, sizeof(size_t) + sizeof(DUMB_IT_ROW_TIME) * *size ); - if ( new_size ) { - DUMB_IT_ROW_TIME * new_s = (DUMB_IT_ROW_TIME *)(new_size + 1); - - *new_size = *size; - - for (i = 0; i < *size; i++) { - new_s[i].count = s[i].count; - new_s[i].restart_count = s[i].restart_count; - -#ifndef FULL_TIMEKEEPING - new_s[i].first_time = s[i].first_time; -#else - if ( s[i].times ) { - size_t time_count = ( s[i].count + 15 ) & ~15; - new_s[i].times = (LONG_LONG *) malloc( sizeof(LONG_LONG) * time_count ); - if ( new_s[i].times == (void *)0 ) { - timekeeping_array_destroy( new_size ); - return (void *) 0; - } - memcpy( new_s[i].times, s[i].times, sizeof(LONG_LONG) * s[i].count ); - } -#endif - } - } - - return new_size; -} - -void timekeeping_array_reset(void * array, size_t loop_start) -{ - size_t i; - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - DUMB_IT_ROW_TIME * s_loop_start = s + loop_start; - LONG_LONG loop_start_time; - - if ( loop_start >= *size || s_loop_start->count < 1 ) return; - -#ifndef FULL_TIMEKEEPING - loop_start_time = s_loop_start->first_time; -#else - loop_start_time = s_loop_start->times[0]; -#endif - - for ( i = 0; i < *size; i++ ) { -#ifndef FULL_TIMEKEEPING - if ( s[i].count && s[i].first_time >= loop_start_time ) { -#else - if ( s[i].count && s[i].times[0] >= loop_start_time ) { -#endif - s[i].restart_count = 0; - } - } -} - -void timekeeping_array_push(void * array, size_t index, LONG_LONG time) -{ -#ifdef FULL_TIMEKEEPING - size_t i; - size_t time_count; -#endif - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - if (index >= *size) return; - -#ifndef FULL_TIMEKEEPING - if ( !s[index].count++ ) - s[index].first_time = time; -#else - time_count = ( s[index].count + 16 ) & ~15; - - s[index].times = (LONG_LONG *) realloc( s[index].times, sizeof(LONG_LONG) * time_count ); - - s[index].times[s[index].count++] = time; -#endif -} - -void timekeeping_array_bump(void * array, size_t index) -{ - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - if (index >= *size) return; - - s[index].restart_count++; -} - -unsigned int timekeeping_array_get_count(void * array, size_t index) -{ - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - if (index >= *size) return 0; - - return s[index].count; -} - -LONG_LONG timekeeping_array_get_item(void * array, size_t index) -{ - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - if (index >= *size || s[index].restart_count >= s[index].count) return 0; - -#ifndef FULL_TIMEKEEPING - return s[index].first_time; -#else - return s[index].times[s[index].restart_count]; -#endif -} diff --git a/dumb/src/it/itrender.c b/dumb/src/it/itrender.c index 99f8bdc13..0a7feae3c 100644 --- a/dumb/src/it/itrender.c +++ b/dumb/src/it/itrender.c @@ -352,8 +352,6 @@ static DUMB_IT_SIGRENDERER *dup_sigrenderer(DUMB_IT_SIGRENDERER *src, int n_chan #ifdef BIT_ARRAY_BULLSHIT dst->played = bit_array_dup(src->played); - - dst->row_timekeeper = timekeeping_array_dup(src->row_timekeeper); #endif dst->gvz_time = src->gvz_time; @@ -2219,9 +2217,6 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row); #endif sigrenderer->speed = 0; -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->looped = 1; -#endif if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data)) return 1; } @@ -4242,10 +4237,6 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) */ #endif bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row); - if (sigrenderer->looped == 0) { - timekeeping_array_push(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row, sigrenderer->time_played); - } - timekeeping_array_bump(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); { int n; for (n = 0; n < DUMB_IT_N_CHANNELS; n++) @@ -4343,8 +4334,6 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) /* Fix play tracking and timekeeping for orders containing skip commands */ for (n = 0; n < 256; n++) { bit_array_set(sigrenderer->played, sigrenderer->processorder * 256 + n); - timekeeping_array_push(sigrenderer->row_timekeeper, sigrenderer->processorder * 256 + n, sigrenderer->time_played); - timekeeping_array_bump(sigrenderer->row_timekeeper, sigrenderer->processorder * 256 + n); } #endif } @@ -4369,9 +4358,6 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) && bit_array_test(sigrenderer->played, sigrenderer->processorder * 256 + sigrenderer->processrow) #endif ) { -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->looped = 1; -#endif if (sigrenderer->callbacks->loop) { if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data)) return 1; @@ -4466,9 +4452,6 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) sigrenderer->gvz_time += (int)(t >> 16); sigrenderer->gvz_sub_time = (int)t & 65535; if (sigrenderer->gvz_time >= 65536 * 12) { -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->looped = 1; -#endif if ((*sigrenderer->callbacks->global_volume_zero)(sigrenderer->callbacks->global_volume_zero_data)) return 1; } @@ -5283,10 +5266,6 @@ static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_cha #ifdef BIT_ARRAY_BULLSHIT sigrenderer->played = bit_array_create(sigdata->n_orders * 256); - - sigrenderer->looped = 0; - sigrenderer->time_played = 0; - sigrenderer->row_timekeeper = timekeeping_array_create(sigdata->n_orders * 256); #endif { @@ -5305,8 +5284,6 @@ static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_cha /* Fix for played order detection for songs which have skips at the start of the orders list */ for (n = 0; n < 256; n++) { bit_array_set(sigrenderer->played, order * 256 + n); - timekeeping_array_push(sigrenderer->row_timekeeper, order * 256 + n, 0); - timekeeping_array_bump(sigrenderer->row_timekeeper, order * 256 + n); } #endif } @@ -5319,6 +5296,10 @@ static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_cha sigrenderer->time_left = 0; sigrenderer->sub_time_left = 0; +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->played = bit_array_create(sigdata->n_orders * 256); +#endif + sigrenderer->gvz_time = 0; sigrenderer->gvz_sub_time = 0; @@ -5504,8 +5485,7 @@ static int32 it_sigrenderer_get_samples( int32 pos; int dt; int32 todo; - int ret; - LONG_LONG time_left, t; + LONG_LONG t; if (sigrenderer->order < 0) return 0; // problematic @@ -5518,8 +5498,7 @@ static int32 it_sigrenderer_get_samples( if (!samples) volume = 0; for (;;) { - time_left = ((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left; - todo = (long)(time_left / dt); + todo = (long)((((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left) / dt); if (todo >= size) break; @@ -5533,28 +5512,9 @@ static int32 it_sigrenderer_get_samples( sigrenderer->sub_time_left = (int32)t & 65535; sigrenderer->time_left += (int32)(t >> 16); -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->time_played += time_left; -#endif - - ret = process_tick(sigrenderer); - - if (ret) { + if (process_tick(sigrenderer)) { sigrenderer->order = -1; sigrenderer->row = -1; - } - -#ifdef BIT_ARRAY_BULLSHIT - if (sigrenderer->looped == 1) { - sigrenderer->looped = -1; - size = 0; - timekeeping_array_reset(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); - sigrenderer->time_played = timekeeping_array_get_item(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); - break; - } -#endif - - if (ret) { return pos; } } @@ -5567,10 +5527,6 @@ static int32 it_sigrenderer_get_samples( sigrenderer->sub_time_left = (int32)t & 65535; sigrenderer->time_left += (int32)(t >> 16); -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->time_played += (LONG_LONG)size * dt; -#endif - if (samples) dumb_remove_clicks_array(sigrenderer->n_channels, sigrenderer->click_remover, samples, pos, 512.0f / delta); @@ -5622,8 +5578,6 @@ void _dumb_it_end_sigrenderer(sigrenderer_t *vsigrenderer) #ifdef BIT_ARRAY_BULLSHIT bit_array_destroy(sigrenderer->played); - - timekeeping_array_destroy(sigrenderer->row_timekeeper); #endif free(vsigrenderer); @@ -5632,17 +5586,6 @@ void _dumb_it_end_sigrenderer(sigrenderer_t *vsigrenderer) -#ifdef BIT_ARRAY_BULLSHIT -static int32 it_sigrenderer_get_position(sigrenderer_t *vsigrenderer) -{ - DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; - - return (int32)(sigrenderer->time_played >> 16); -} -#endif - - - DUH_SIGTYPE_DESC _dumb_sigtype_it = { SIGTYPE_IT, NULL, @@ -5650,11 +5593,6 @@ DUH_SIGTYPE_DESC _dumb_sigtype_it = { NULL, &it_sigrenderer_get_samples, &it_sigrenderer_get_current_sample, -#ifdef BIT_ARRAY_BULLSHIT - &it_sigrenderer_get_position, -#else - NULL, -#endif &_dumb_it_end_sigrenderer, &_dumb_it_unload_sigdata }; diff --git a/dumb/vc6/dumb/dumb.vcxproj b/dumb/vc6/dumb/dumb.vcxproj index 6e49557cf..ae8ebdb0b 100644 --- a/dumb/vc6/dumb/dumb.vcxproj +++ b/dumb/vc6/dumb/dumb.vcxproj @@ -118,7 +118,6 @@ - @@ -210,7 +209,6 @@ - diff --git a/dumb/vc6/dumb/dumb.vcxproj.filters b/dumb/vc6/dumb/dumb.vcxproj.filters index 167393748..422556dc2 100644 --- a/dumb/vc6/dumb/dumb.vcxproj.filters +++ b/dumb/vc6/dumb/dumb.vcxproj.filters @@ -279,9 +279,6 @@ src\helpers - - src\helpers - @@ -314,9 +311,6 @@ include\internal - - include\internal - From 3d83ed2ee5d7ff219a8f8895c148fe5ae2a040e0 Mon Sep 17 00:00:00 2001 From: Tuomas Virtanen Date: Thu, 25 Jun 2015 16:00:13 +0300 Subject: [PATCH 14/56] Change dumb.h version information to match release version Conflicts: dumb/include/dumb.h --- dumb/include/dumb.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/dumb/include/dumb.h b/dumb/include/dumb.h index 2b6ac4879..385335da7 100644 --- a/dumb/include/dumb.h +++ b/dumb/include/dumb.h @@ -36,24 +36,24 @@ #endif -#define DUMB_MAJOR_VERSION 0 -#define DUMB_MINOR_VERSION 9 -#define DUMB_REVISION_VERSION 3 +#define DUMB_MAJOR_VERSION 1 +#define DUMB_MINOR_VERSION 0 +#define DUMB_REVISION_VERSION 0 #define DUMB_VERSION (DUMB_MAJOR_VERSION*10000 + DUMB_MINOR_VERSION*100 + DUMB_REVISION_VERSION) -#define DUMB_VERSION_STR "0.9.3" +#define DUMB_VERSION_STR "1.0.0" #define DUMB_NAME "DUMB v" DUMB_VERSION_STR -#define DUMB_YEAR 2005 -#define DUMB_MONTH 8 -#define DUMB_DAY 7 +#define DUMB_YEAR 2015 +#define DUMB_MONTH 1 +#define DUMB_DAY 17 -#define DUMB_YEAR_STR2 "05" -#define DUMB_YEAR_STR4 "2005" -#define DUMB_MONTH_STR1 "8" -#define DUMB_DAY_STR1 "7" +#define DUMB_YEAR_STR2 "15" +#define DUMB_YEAR_STR4 "2015" +#define DUMB_MONTH_STR1 "1" +#define DUMB_DAY_STR1 "17" #if DUMB_MONTH < 10 #define DUMB_MONTH_STR2 "0" DUMB_MONTH_STR1 From 865f083128744bbc02cb9b1164dae0d3a91f9fa7 Mon Sep 17 00:00:00 2001 From: Chris Spiegel Date: Mon, 10 Aug 2015 21:07:17 -0700 Subject: [PATCH 15/56] Fix memory leaks. --- dumb/src/it/readptm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dumb/src/it/readptm.c b/dumb/src/it/readptm.c index 885929e42..9b34861db 100644 --- a/dumb/src/it/readptm.c +++ b/dumb/src/it/readptm.c @@ -439,6 +439,7 @@ static DUMB_IT_SIGDATA *it_ptm_load_sigdata(DUMBFILE *f) } if (dumbfile_seek(f, 352, DFS_SEEK_SET)) { + free(component); _dumb_it_unload_sigdata(sigdata); return NULL; } @@ -451,12 +452,14 @@ static DUMB_IT_SIGDATA *it_ptm_load_sigdata(DUMBFILE *f) } if (dumbfile_seek(f, 608, DFS_SEEK_SET)) { + free(component); _dumb_it_unload_sigdata(sigdata); return NULL; } for (n = 0; n < sigdata->n_samples; n++) { if (it_ptm_read_sample_header(&sigdata->sample[n], &component[n_components].offset, f)) { + free(component); _dumb_it_unload_sigdata(sigdata); return NULL; } From 45e031170e55cd06ecd4ed657a3f458151669a48 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Sat, 8 Aug 2015 21:26:05 -0700 Subject: [PATCH 16/56] Implement missing n_pchannels for AMF format reader --- dumb/src/it/readamf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dumb/src/it/readamf.c b/dumb/src/it/readamf.c index 83f6075e7..820709e9d 100644 --- a/dumb/src/it/readamf.c +++ b/dumb/src/it/readamf.c @@ -320,6 +320,8 @@ static DUMB_IT_SIGDATA *it_amf_load_sigdata(DUMBFILE *f, int * version) free( sigdata ); return NULL; } + + sigdata->n_pchannels = nchannels; memset( sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS ); From e5a4031a7010ac42559bfbcab5ffb71d1440e805 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Fri, 14 Aug 2015 20:07:02 -0700 Subject: [PATCH 17/56] Fixed another memory leak in an error handler --- dumb/src/it/readxm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dumb/src/it/readxm.c b/dumb/src/it/readxm.c index a06fd1e99..0c838ade8 100644 --- a/dumb/src/it/readxm.c +++ b/dumb/src/it/readxm.c @@ -1197,6 +1197,7 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version) sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument)); if (!sigdata->instrument) { + free(roguebytes); _dumb_it_unload_sigdata(sigdata); return NULL; } From 17a216c832f1584355a777343b60202cff2342fa Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Fri, 9 Oct 2015 17:59:30 -0700 Subject: [PATCH 18/56] Fix issue #15 / CVE-2006-3668 --- dumb/src/it/itread.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dumb/src/it/itread.c b/dumb/src/it/itread.c index e8661807e..ca1dde55d 100644 --- a/dumb/src/it/itread.c +++ b/dumb/src/it/itread.c @@ -290,12 +290,15 @@ static int it_read_envelope(IT_ENVELOPE *envelope, DUMBFILE *f) envelope->flags = dumbfile_getc(f); envelope->n_nodes = dumbfile_getc(f); + if(envelope->n_nodes > 25) { + TRACE("IT error: wrong number of envelope nodes (%d)\n", envelope->n_nodes); + envelope->n_nodes = 0; + return -1; + } envelope->loop_start = dumbfile_getc(f); envelope->loop_end = dumbfile_getc(f); envelope->sus_loop_start = dumbfile_getc(f); envelope->sus_loop_end = dumbfile_getc(f); - if (envelope->n_nodes > 25) - envelope->n_nodes = 25; for (n = 0; n < envelope->n_nodes; n++) { envelope->node_y[n] = dumbfile_getc(f); envelope->node_t[n] = dumbfile_igetw(f); From 792d3906fd2be890a2f17c8a90f097981cdbc143 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 19 Dec 2015 20:32:41 +0100 Subject: [PATCH 19/56] - fixed: line activation checks for monster activation could be skipped if the lines also were flagged for player activation. --- src/p_spec.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 9bc0d4a9b..e2cc0025b 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -288,16 +288,16 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType) } } + if (activationType == SPAC_Use && (lineActivation & SPAC_MUse) && !mo->player && mo->flags4 & MF4_CANUSEWALLS) + { + return true; + } + if (activationType == SPAC_Push && (lineActivation & SPAC_MPush) && !mo->player && mo->flags2 & MF2_PUSHWALL) + { + return true; + } if ((lineActivation & activationType) == 0) { - if (activationType == SPAC_Use && (lineActivation & SPAC_MUse) && !mo->player && mo->flags4 & MF4_CANUSEWALLS) - { - return true; - } - if (activationType == SPAC_Push && (lineActivation & SPAC_MPush) && !mo->player && mo->flags2 & MF2_PUSHWALL) - { - return true; - } if (activationType != SPAC_MCross || lineActivation != SPAC_Cross) { return false; From 03ccf03b8f3161a601cd223a3abfcf345da79f16 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 21 Dec 2015 01:13:21 +0100 Subject: [PATCH 20/56] - fixed: UDMF with Doom format specials used the line's ID, not the first arg as the tag parameter for its special. --- src/p_udmf.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 269ce7e3d..db0c952cf 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -785,7 +785,7 @@ public: bool strifetrans = false; bool strifetrans2 = false; FString arg0str, arg1str; - int lineid; // forZDoomTranslated namespace + int lineid = -1; // forZDoomTranslated namespace FString tagstring; memset(ld, 0, sizeof(*ld)); @@ -1082,7 +1082,7 @@ public: maplinedef_t mld; memset(&mld, 0, sizeof(mld)); mld.special = ld->special; - mld.tag = lineid; + mld.tag = ld->args[0]; P_TranslateLineDef(ld, &mld); ld->flags = saved | (ld->flags&(ML_MONSTERSCANACTIVATE|ML_REPEAT_SPECIAL|ML_FIRSTSIDEONLY)); } From c51abb01610fc6eb399da8139eca83856fd3a5b5 Mon Sep 17 00:00:00 2001 From: Blue-Shadow Date: Tue, 22 Dec 2015 19:01:09 +0300 Subject: [PATCH 21/56] Added GetMaxInventory ACS function --- src/p_acs.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 30733eac8..b94d63b79 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1264,7 +1264,7 @@ static int UseInventory (AActor *activator, const char *type) // //============================================================================ -static int CheckInventory (AActor *activator, const char *type) +static int CheckInventory (AActor *activator, const char *type, bool max) { if (activator == NULL || type == NULL) return 0; @@ -1275,11 +1275,26 @@ static int CheckInventory (AActor *activator, const char *type) } else if (stricmp (type, "Health") == 0) { + if (max) + { + if (activator->IsKindOf (RUNTIME_CLASS (APlayerPawn))) + return static_cast(activator)->MaxHealth; + else + return activator->SpawnHealth(); + } return activator->health; } const PClass *info = PClass::FindClass (type); AInventory *item = activator->FindInventory (info); + + if (max) + { + if (item) + return item->MaxAmount; + else + return ((AInventory *)GetDefaultByType (info))->MaxAmount; + } return item ? item->Amount : 0; } @@ -4442,6 +4457,7 @@ enum EACSFunctions ACSF_GetActorRoll, ACSF_QuakeEx, ACSF_Warp, // 92 + ACSF_GetMaxInventory, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -5915,6 +5931,13 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } return false; } + case ACSF_GetMaxInventory: + actor = SingleActorFromTID(args[0], activator); + if (actor != NULL) + { + return CheckInventory(actor, FBehavior::StaticLookupString(args[1]), true); + } + break; default: break; @@ -8339,17 +8362,17 @@ scriptwait: break; case PCD_CHECKINVENTORY: - STACK(1) = CheckInventory (activator, FBehavior::StaticLookupString (STACK(1))); + STACK(1) = CheckInventory (activator, FBehavior::StaticLookupString (STACK(1)), false); break; case PCD_CHECKACTORINVENTORY: STACK(2) = CheckInventory (SingleActorFromTID(STACK(2), NULL), - FBehavior::StaticLookupString (STACK(1))); + FBehavior::StaticLookupString (STACK(1)), false); sp--; break; case PCD_CHECKINVENTORYDIRECT: - PushToStack (CheckInventory (activator, FBehavior::StaticLookupString (TAGSTR(uallong(pc[0]))))); + PushToStack (CheckInventory (activator, FBehavior::StaticLookupString (TAGSTR(uallong(pc[0]))), false)); pc += 1; break; From beb7a8e4a2ace28fda777d59f5fcbda785713e65 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 22 Dec 2015 22:21:59 +0100 Subject: [PATCH 22/56] - added /LARGEADDRESSAWARE linker flag to CMake project. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd2c7730f..3e375a2df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,7 +123,7 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS( GME if( MSVC ) # Eliminate unreferenced functions and data # Perform identical COMDAT folding - set( REL_LINKER_FLAGS "/opt:ref /opt:icf /nodefaultlib:msvcrt /TSAWARE" ) + set( REL_LINKER_FLAGS "/opt:ref /opt:icf /nodefaultlib:msvcrt /TSAWARE /LARGEADDRESSAWARE" ) # String pooling # Function-level linking From c8810db5fe7b8ded8bcbf257d23f77ffaa10653b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 24 Dec 2015 16:34:00 +0100 Subject: [PATCH 23/56] - fixed: clipping swimmable against non-swimmable translucent 3D-floors was broken due to an incorrect flag check. --- src/gl/scene/gl_walls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl/scene/gl_walls.cpp b/src/gl/scene/gl_walls.cpp index b5317dc2a..9856f1d02 100644 --- a/src/gl/scene/gl_walls.cpp +++ b/src/gl/scene/gl_walls.cpp @@ -1261,7 +1261,7 @@ void GLWall::ClipFFloors(seg_t * seg, F3DFloor * ffloor, sector_t * frontsector, F3DFloor * rover=frontffloors[i]; if (!(rover->flags&FF_EXISTS)) continue; if (!(rover->flags&FF_RENDERSIDES)) continue; - if ((rover->flags&flags)!=flags) continue; + if ((rover->flags&(FF_SWIMMABLE|FF_TRANSLUCENT))!=flags) continue; fixed_t ff_topleft; fixed_t ff_topright; From 571f7a4eb6b85f6ac24e92156df92693f27a36a6 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Thu, 24 Dec 2015 10:32:19 +0200 Subject: [PATCH 24/56] - fixed: debug assertion failure in texture precaching loading of level from command line attempted to use uninitialized time value --- src/textures/texturemanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 3de5016fd..0612fa792 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -1239,7 +1239,7 @@ void FTextureManager::PrecacheLevel (void) if (demoplayback) return; - precacheTime = I_MSTime(); + precacheTime = I_FPSTime(); hitlist = new BYTE[cnt]; memset (hitlist, 0, cnt); From 19ae244f66fdda01de08b0db458aa0a5a486d024 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Thu, 24 Dec 2015 10:33:30 +0200 Subject: [PATCH 25/56] - fixed: allow to use all hqNx texture upscale modes --- src/gl/textures/gl_hqresize.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gl/textures/gl_hqresize.cpp b/src/gl/textures/gl_hqresize.cpp index a4f192ce8..621f444bc 100644 --- a/src/gl/textures/gl_hqresize.cpp +++ b/src/gl/textures/gl_hqresize.cpp @@ -46,7 +46,11 @@ CUSTOM_CVAR(Int, gl_texture_hqresize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { +#ifdef _MSC_VER + if (self < 0 || self > 9) +#else if (self < 0 || self > 6) +#endif self = 0; GLRenderer->FlushTextures(); } From 69813993b0e023b2452da1a9097cac1a393e7c23 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Thu, 24 Dec 2015 11:13:31 +0200 Subject: [PATCH 26/56] - added missing hqNx modes to options menu --- src/gl/system/gl_menu.cpp | 14 ++++++++++++++ wadsrc/static/menudef.z | 3 +++ 2 files changed, 17 insertions(+) diff --git a/src/gl/system/gl_menu.cpp b/src/gl/system/gl_menu.cpp index a9167b7a6..41304be0f 100644 --- a/src/gl/system/gl_menu.cpp +++ b/src/gl/system/gl_menu.cpp @@ -57,4 +57,18 @@ CUSTOM_CVAR (Float, vid_contrast, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // when they are actually valid. void gl_SetupMenu() { +#ifndef _MSC_VER + FOptionValues **opt = OptionValues.CheckKey("HqResizeModes"); + if (opt != NULL) + { + for(int i = (*opt)->mValues.Size()-1; i>=0; i--) + { + // Delete HQnX resize modes for non MSVC targets + if ((*opt)->mValues[i].Value >= 7.0) + { + (*opt)->mValues.Delete(i); + } + } + } +#endif } diff --git a/wadsrc/static/menudef.z b/wadsrc/static/menudef.z index 75ea1bf5f..ffe67ee51 100644 --- a/wadsrc/static/menudef.z +++ b/wadsrc/static/menudef.z @@ -103,6 +103,9 @@ OptionValue "HqResizeModes" 4, "hq2x" 5, "hq3x" 6, "hq4x" + 7, "hq2x MMX" + 8, "hq3x MMX" + 9, "hq4x MMX" } OptionValue "FogMode" From 1c5d0ccd65e8a8ae631fd3160abf808fb26635d6 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Fri, 25 Dec 2015 15:41:06 +0200 Subject: [PATCH 27/56] - enabled hqNx MMX on all platforms with Intel intrinsics support --- src/CMakeLists.txt | 39 +++++++++++++++++++++++++++++---- src/gl/system/gl_menu.cpp | 2 +- src/gl/textures/gl_hqresize.cpp | 11 +++++----- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9332e4919..d4da7e7ce 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -449,6 +449,22 @@ else( SSE_MATTERS ) set( BACKPATCH 0 ) endif( SSE_MATTERS ) +if( X64 ) + set( HAVE_MMX 1 ) +else( X64 ) + set( SAFE_CMAKE_C_FLAGS ${CMAKE_C_FLAGS} ) + + if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) + set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmmx") + endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) + + CHECK_CXX_SOURCE_COMPILES("#include + int main(void) { __m64 v = _m_from_int(0); }" + HAVE_MMX) + + set( CMAKE_C_FLAGS ${SAFE_CMAKE_C_FLAGS} ) +endif( X64 ) + # Set up flags for GCC if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) @@ -581,10 +597,6 @@ endif( NOT DYN_FLUIDSYNTH ) # Start defining source files for ZDoom set( PLAT_WIN32_SOURCES - gl/hqnx_asm/hq2x_asm.cpp - gl/hqnx_asm/hq3x_asm.cpp - gl/hqnx_asm/hq4x_asm.cpp - gl/hqnx_asm/hqnx_asm_Image.cpp win32/eaxedit.cpp win32/fb_d3d9.cpp win32/fb_d3d9_wipe.cpp @@ -668,6 +680,25 @@ else( WIN32 ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ) endif( WIN32 ) +if( HAVE_MMX ) + add_definitions( -DHAVE_MMX=1 ) + + set( SYSTEM_SOURCES ${SYSTEM_SOURCES} + gl/hqnx_asm/hq2x_asm.cpp + gl/hqnx_asm/hq3x_asm.cpp + gl/hqnx_asm/hq4x_asm.cpp + gl/hqnx_asm/hqnx_asm_Image.cpp) + + if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) + set_source_files_properties( + gl/hqnx_asm/hq2x_asm.cpp + gl/hqnx_asm/hq3x_asm.cpp + gl/hqnx_asm/hq4x_asm.cpp + gl/textures/gl_hqresize.cpp + PROPERTIES COMPILE_FLAGS "-mmmx" ) + endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) +endif( HAVE_MMX ) + if( NOT ASM_SOURCES ) set( ASM_SOURCES "" ) endif( NOT ASM_SOURCES ) diff --git a/src/gl/system/gl_menu.cpp b/src/gl/system/gl_menu.cpp index 41304be0f..d4688cfd1 100644 --- a/src/gl/system/gl_menu.cpp +++ b/src/gl/system/gl_menu.cpp @@ -57,7 +57,7 @@ CUSTOM_CVAR (Float, vid_contrast, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // when they are actually valid. void gl_SetupMenu() { -#ifndef _MSC_VER +#ifndef HAVE_MMX FOptionValues **opt = OptionValues.CheckKey("HqResizeModes"); if (opt != NULL) { diff --git a/src/gl/textures/gl_hqresize.cpp b/src/gl/textures/gl_hqresize.cpp index 621f444bc..261f66e6a 100644 --- a/src/gl/textures/gl_hqresize.cpp +++ b/src/gl/textures/gl_hqresize.cpp @@ -40,13 +40,13 @@ #include "gl/textures/gl_texture.h" #include "c_cvars.h" #include "gl/hqnx/hqx.h" -#ifdef _MSC_VER +#ifdef HAVE_MMX #include "gl/hqnx_asm/hqnx_asm.h" #endif CUSTOM_CVAR(Int, gl_texture_hqresize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { -#ifdef _MSC_VER +#ifdef HAVE_MMX if (self < 0 || self > 9) #else if (self < 0 || self > 6) @@ -186,8 +186,7 @@ static unsigned char *scaleNxHelper( void (*scaleNxFunction) ( uint32* , uint32* return newBuffer; } -// [BB] hqnx scaling is only supported with the MS compiler. -#ifdef _MSC_VER +#ifdef HAVE_MMX static unsigned char *hqNxAsmHelper( void (*hqNxFunction) ( int*, unsigned char*, int, int, int ), const int N, unsigned char *inputBuffer, @@ -285,7 +284,7 @@ unsigned char *gl_CreateUpsampledTextureBuffer ( const FTexture *inputTexture, u outWidth = inWidth; outHeight = inHeight; int type = gl_texture_hqresize; -#ifdef _MSC_VER +#ifdef HAVE_MMX // ASM-hqNx does not preserve the alpha channel so fall back to C-version for such textures if (!hasAlpha && type > 3 && type <= 6) { @@ -307,7 +306,7 @@ unsigned char *gl_CreateUpsampledTextureBuffer ( const FTexture *inputTexture, u return hqNxHelper( &hq3x_32, 3, inputBuffer, inWidth, inHeight, outWidth, outHeight ); case 6: return hqNxHelper( &hq4x_32, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight ); -#ifdef _MSC_VER +#ifdef HAVE_MMX case 7: return hqNxAsmHelper( &HQnX_asm::hq2x_32, 2, inputBuffer, inWidth, inHeight, outWidth, outHeight ); case 8: From f8c38a0bbe9571dee33ed722d96b67ac016074c5 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Fri, 25 Dec 2015 15:42:13 +0200 Subject: [PATCH 28/56] - fixed GCC's 'unknown pragma' warnings in hqNx MMX implementation --- src/gl/hqnx_asm/hqnx_asm.h | 2 ++ src/gl/hqnx_asm/hqnx_asm_Image.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/gl/hqnx_asm/hqnx_asm.h b/src/gl/hqnx_asm/hqnx_asm.h index 2580f80ef..9ced349fa 100644 --- a/src/gl/hqnx_asm/hqnx_asm.h +++ b/src/gl/hqnx_asm/hqnx_asm.h @@ -21,7 +21,9 @@ #ifndef __HQNX_H__ #define __HQNX_H__ +#ifdef _MSC_VER #pragma warning(disable:4799) +#endif // _MSC_VER #include "hqnx_asm_Image.h" diff --git a/src/gl/hqnx_asm/hqnx_asm_Image.h b/src/gl/hqnx_asm/hqnx_asm_Image.h index 8f57d7865..e4157a7b5 100644 --- a/src/gl/hqnx_asm/hqnx_asm_Image.h +++ b/src/gl/hqnx_asm/hqnx_asm_Image.h @@ -24,7 +24,9 @@ #include #pragma once +#ifdef _MSC_VER #pragma warning(disable: 4103) +#endif // _MSC_VER #pragma pack(1) namespace HQnX_asm From 400038643cab557e828881c716d0f08820accd69 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 26 Dec 2015 15:31:59 +0100 Subject: [PATCH 29/56] - fixed: Strife dialogues could crash on invalid links. --- src/p_conversation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 069fca6e1..85edc53ab 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -1347,9 +1347,10 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply int rootnode = npc->ConversationRoot; if (reply->NextNode < 0) { - npc->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1]; - if (gameaction != ga_slideshow) + unsigned next = (unsigned)(rootnode - reply->NextNode - 1); + if (gameaction != ga_slideshow && next < StrifeDialogues.Size()) { + npc->Conversation = StrifeDialogues[next]; P_StartConversation (npc, player->mo, player->ConversationFaceTalker, false); return; } From 1070bd9beb9e17de359486fbed201dd330b29a6f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 26 Dec 2015 16:17:56 +0100 Subject: [PATCH 30/56] - fixed: APlayerPawn::ViewHeight wasn't stored in savegames. --- src/p_user.cpp | 4 ++++ src/version.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index 0d5cdce20..331a978b7 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -566,6 +566,10 @@ void APlayerPawn::Serialize (FArchive &arc) { arc << AirCapacity; } + if (SaveVersion >= 4526) + { + arc << ViewHeight; + } } //=========================================================================== diff --git a/src/version.h b/src/version.h index 168cb7519..c1288f02f 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4525 +#define SAVEVER 4526 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From 5346b813da90a59d2a7a7c47195e9346fc6c23da Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Sun, 27 Dec 2015 09:48:22 -0600 Subject: [PATCH 31/56] Re-do GTK2_LIBARY_DIRS inclusion on a separate branch --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bd353a2ed..bd4f625dc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -187,6 +187,7 @@ else( WIN32 ) if( GTK2_FOUND ) set( ZDOOM_LIBS ${ZDOOM_LIBS} ${GTK2_LIBRARIES} ) include_directories( ${GTK2_INCLUDE_DIRS} ) + link_directories( ${GTK2_LIBRARY_DIRS} ) else( GTK2_FOUND ) set( NO_GTK ON ) endif( GTK2_FOUND ) From 33a252bf4bcd6189a655c4f44ab44f8aa9e95a7c Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 27 Dec 2015 21:52:44 -0600 Subject: [PATCH 32/56] Add /LARGEADDRESSAWARE to VC2005 project --- zdoom.vcproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zdoom.vcproj b/zdoom.vcproj index d802fc61c..db4660d3b 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -106,6 +106,7 @@ MapExports="true" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" OptimizeReferences="2" EnableCOMDATFolding="2" @@ -223,6 +224,7 @@ MapExports="true" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" OptimizeReferences="2" EnableCOMDATFolding="2" @@ -332,6 +334,7 @@ ProgramDatabaseFile=".\Debug/zdoomd.pdb" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" SetChecksum="false" TargetMachine="0" @@ -440,6 +443,7 @@ ProgramDatabaseFile=".\Debug/zdoomd.pdb" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" SetChecksum="false" TargetMachine="17" From 060a6b2ff2b9716eee79f09187f59a41eacda616 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 28 Dec 2015 21:23:21 +0100 Subject: [PATCH 33/56] - shut down WildMidi when exiting. The pointless error message in WildMidi_Shutdown was removed to keep the rest of the code simple and allowing to call this even when the device never was used. --- src/sound/i_music.cpp | 1 + src/wildmidi/wildmidi_lib.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 3f8fac0da..721be5192 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -189,6 +189,7 @@ void I_ShutdownMusic(void) assert (currSong == NULL); } Timidity::FreeAll(); + WildMidi_Shutdown(); #ifdef _WIN32 I_ShutdownMusicWin32(); #endif // _WIN32 diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 84c2f382a..b5ea6b887 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -4261,8 +4261,8 @@ WildMidi_GetInfo(midi * handle) { WM_SYMBOL int WildMidi_Shutdown(void) { if (!WM_Initialized) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); - return -1; + // No error if trying to shut down an uninitialized device. + return 0; } while (first_handle) { /* closes open handle and rotates the handles list. */ From a2ebf771d30ed75413e746128a6057ea249ddae9 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Mon, 28 Dec 2015 19:13:34 -0600 Subject: [PATCH 34/56] Remove '\r' character from beginning of WildMidi error messages --- src/wildmidi/wm_error.cpp | 8 ++--- zdoom.vcproj | 68 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/wildmidi/wm_error.cpp b/src/wildmidi/wm_error.cpp index 9fabdff84..eab56c437 100644 --- a/src/wildmidi/wm_error.cpp +++ b/src/wildmidi/wm_error.cpp @@ -67,18 +67,18 @@ void _WM_ERROR(const char * func, unsigned int lne, int wmerno, if (wmfor != NULL) { if (error != 0) { - Printf(TEXTCOLOR_RED "\rlibWildMidi(%s:%u): ERROR %s %s (%s)\n", func, + Printf(TEXTCOLOR_RED "libWildMidi(%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, + Printf(TEXTCOLOR_RED "libWildMidi(%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, + Printf(TEXTCOLOR_RED "libWildMidi(%s:%u): ERROR %s (%s)\n", func, lne, errors[wmerno], strerror(error)); } else { - Printf(TEXTCOLOR_RED "\rlibWildMidi(%s:%u): ERROR %s\n", func, lne, + Printf(TEXTCOLOR_RED "libWildMidi(%s:%u): ERROR %s\n", func, lne, errors[wmerno]); } } diff --git a/zdoom.vcproj b/zdoom.vcproj index db4660d3b..126efb7f5 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -2553,6 +2553,10 @@ RelativePath="src\sound\music_midi_timidity.cpp" > + + @@ -2789,6 +2793,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Mon, 28 Dec 2015 20:33:41 -0600 Subject: [PATCH 35/56] Separate WildMidi mixing from event handling - In order to use ZDoom's own MIDI sequencer event handling must be completely separate from mixing, but WildMidi had them intertwined because it wasn't designed for external sequencers. - Also remove all 'long's defining the output buffers to avoid having something that's 32 bits wide on Windows and 64 bits wide on Linux. --- src/wildmidi/reverb.cpp | 32 +- src/wildmidi/reverb.h | 16 +- src/wildmidi/wildmidi_lib.cpp | 884 +++++++++++++++++----------------- 3 files changed, 473 insertions(+), 459 deletions(-) diff --git a/src/wildmidi/reverb.cpp b/src/wildmidi/reverb.cpp index d183a1c71..6c6af88d4 100644 --- a/src/wildmidi/reverb.cpp +++ b/src/wildmidi/reverb.cpp @@ -269,23 +269,23 @@ _WM_init_reverb(int rate, float room_x, float room_y, float listen_x, 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); + rtn_rvb->coeff[j][i][0] = (signed int) ((b0 / a0) * 1024.0); + rtn_rvb->coeff[j][i][1] = (signed int) ((b1 / a0) * 1024.0); + rtn_rvb->coeff[j][i][2] = (signed int) ((b2 / a0) * 1024.0); + rtn_rvb->coeff[j][i][3] = (signed int) ((a1 / a0) * 1024.0); + rtn_rvb->coeff[j][i][4] = (signed 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_buf = (int*)malloc( + sizeof(signed 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_buf = (int*)malloc( + sizeof(signed int) * (rtn_rvb->r_buf_size + 1)); rtn_rvb->r_out = 0; for (i = 0; i < 4; i++) { @@ -313,17 +313,17 @@ void _WM_free_reverb(struct _rvb *rvb) { free(rvb); } -void _WM_do_reverb(struct _rvb *rvb, signed long int *buffer, int size) { +void _WM_do_reverb(struct _rvb *rvb, signed 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; + signed int l_buf_flt = 0; + signed int r_buf_flt = 0; + signed int l_rfl = 0; + signed 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; + signed int tmp_l_val = 0; + signed int tmp_r_val = 0; /* add the initial reflections from each speaker, 4 to go the left, 4 go to the right buffers diff --git a/src/wildmidi/reverb.h b/src/wildmidi/reverb.h index b8f5a6333..162de61eb 100644 --- a/src/wildmidi/reverb.h +++ b/src/wildmidi/reverb.h @@ -29,14 +29,14 @@ 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]; + signed int l_buf_flt_in[8][6][2]; + signed int l_buf_flt_out[8][6][2]; + signed int r_buf_flt_in[8][6][2]; + signed int r_buf_flt_out[8][6][2]; + signed int coeff[8][6][5]; /* buffer data */ - signed long int *l_buf; - signed long int *r_buf; + signed int *l_buf; + signed int *r_buf; int l_buf_size; int r_buf_size; int l_out; @@ -52,6 +52,6 @@ struct _rvb { 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); + extern void _WM_do_reverb (struct _rvb *rvb, signed int *buffer, int size); #endif /* __REVERB_H */ diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index b5ea6b887..b1ec8b459 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -182,7 +182,7 @@ struct _mdi { unsigned long int patch_count; signed short int amp; - signed long int *mix_buffer; + signed int *mix_buffer; unsigned long int mix_buffer_size; struct _rvb *reverb; @@ -3203,20 +3203,216 @@ _end: free(sysex_store); return NULL; } +static int *WM_Mix_Linear(midi * handle, int * buffer, unsigned long int count) +{ + struct _mdi *mdi = (struct _mdi *)handle; + unsigned long int data_pos; + signed int premix, left_mix, right_mix; + signed int vol_mul; + struct _note *note_data = NULL; + + 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; + } + + *buffer++ = left_mix; + *buffer++ = right_mix; + } while (--count); + return buffer; +} + 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; + int *tmp_buffer; + int *out_buffer; + int left_mix, right_mix; _WM_Lock(&mdi->lock); @@ -3229,7 +3425,7 @@ static int WM_GetOutput_Linear(midi * handle, char * buffer, } else { mdi->mix_buffer_size = size / 2; } - mdi->mix_buffer = (long*)realloc(mdi->mix_buffer, mdi->mix_buffer_size * sizeof(signed long int)); + mdi->mix_buffer = (int*)realloc(mdi->mix_buffer, mdi->mix_buffer_size * sizeof(signed int)); } tmp_buffer = mdi->mix_buffer; @@ -3269,196 +3465,7 @@ static int WM_GetOutput_Linear(midi * handle, char * buffer, } /* 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); + tmp_buffer = WM_Mix_Linear(handle, tmp_buffer, real_samples_to_mix); buffer_used += real_samples_to_mix * 4; size -= (real_samples_to_mix << 2); @@ -3503,25 +3510,254 @@ static int WM_GetOutput_Linear(midi * handle, char * buffer, return buffer_used; } +static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count) +{ + struct _mdi *mdi = (struct _mdi *)handle; + unsigned long int data_pos; + signed int premix, left_mix, right_mix; + signed int vol_mul; + struct _note *note_data = NULL; + signed short int *sptr; + double y, xd; + double *gptr, *gend; + int left, right, temp_n; + int ii, jj; + + 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; + } + + *buffer++ = left_mix; + *buffer++ = right_mix; + } while (--count); + return buffer; +} + 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; + signed int *tmp_buffer; + signed int *out_buffer; + signed int left_mix, right_mix; _WM_Lock(&mdi->lock); @@ -3533,7 +3769,7 @@ static int WM_GetOutput_Gauss(midi * handle, char * buffer, } else { mdi->mix_buffer_size = size / 2; } - mdi->mix_buffer = (long*)realloc(mdi->mix_buffer, mdi->mix_buffer_size * sizeof(signed long int)); + mdi->mix_buffer = (int*)realloc(mdi->mix_buffer, mdi->mix_buffer_size * sizeof(signed int)); } tmp_buffer = mdi->mix_buffer; memset(tmp_buffer, 0, ((size / 2) * sizeof(signed long int))); @@ -3571,229 +3807,7 @@ static int WM_GetOutput_Gauss(midi * handle, char * buffer, } /* 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); + tmp_buffer = WM_Mix_Gauss(handle, tmp_buffer, real_samples_to_mix); buffer_used += real_samples_to_mix * 4; size -= (real_samples_to_mix << 2); From b1405921bf0503895ef25439e5ffbd1b5a293936 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Mon, 28 Dec 2015 20:44:10 -0600 Subject: [PATCH 36/56] Merged WM_GetOutput_Linear and WM_GetOutput_Gauss into WM_DoGetOutput - With mixing moved into separate functions, these two functions became identical except for the function they called to do mixing. --- src/wildmidi/wildmidi_lib.cpp | 126 ++++------------------------------ 1 file changed, 12 insertions(+), 114 deletions(-) diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index b1ec8b459..6adaa0421 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -3403,115 +3403,10 @@ static int *WM_Mix_Linear(midi * handle, int * buffer, unsigned long int count) return buffer; } -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; - struct _event *event = mdi->current_event; - int *tmp_buffer; - int *out_buffer; - int left_mix, right_mix; - - _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 = (int*)realloc(mdi->mix_buffer, mdi->mix_buffer_size * sizeof(signed 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 */ - tmp_buffer = WM_Mix_Linear(handle, tmp_buffer, real_samples_to_mix); - - 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_Mix_Gauss(midi * handle, int * buffer, unsigned long int count) { + if (!gauss_table) init_gauss(); + struct _mdi *mdi = (struct _mdi *)handle; unsigned long int data_pos; signed int premix, left_mix, right_mix; @@ -3748,7 +3643,7 @@ static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count) return buffer; } -static int WM_GetOutput_Gauss(midi * handle, char * buffer, +static int WM_DoGetOutput(midi * handle, char * buffer, unsigned long int size) { unsigned long int buffer_used = 0; unsigned long int i; @@ -3807,7 +3702,14 @@ static int WM_GetOutput_Gauss(midi * handle, char * buffer, } /* do mixing here */ - tmp_buffer = WM_Mix_Gauss(handle, tmp_buffer, real_samples_to_mix); + if (mdi->info.mixer_options & WM_MO_ENHANCED_RESAMPLING) + { + tmp_buffer = WM_Mix_Gauss(handle, tmp_buffer, real_samples_to_mix); + } + else + { + tmp_buffer = WM_Mix_Linear(handle, tmp_buffer, real_samples_to_mix); + } buffer_used += real_samples_to_mix * 4; size -= (real_samples_to_mix << 2); @@ -4172,11 +4074,7 @@ WM_SYMBOL int WildMidi_GetOutput(midi * handle, char *buffer, unsigned long int "(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); + return WM_DoGetOutput(handle, buffer, size); } WM_SYMBOL int WildMidi_SetOption(midi * handle, unsigned short int options, From ee46799d9ea7adec7aea5dee4e32df10bca4de96 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Mon, 28 Dec 2015 20:51:53 -0600 Subject: [PATCH 37/56] Fix WM_DoGetOutput for big-endian machines - Besides being little-endian centric, this bit shifting madness was unneccessary since the values were already clamped to a 16-bit range, so all we need to do is cast them to a short. --- src/wildmidi/wildmidi_lib.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 6adaa0421..385ac6c1d 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -3744,10 +3744,9 @@ static int WM_DoGetOutput(midi * handle, char * buffer, * 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); + ((short *)buffer)[0] = (short)left_mix; + ((short *)buffer)[1] = (short)right_mix; + buffer += 4; } _WM_Unlock(&mdi->lock); return buffer_used; From afc36544b7e3ffdfe378343b42e2c6773a3514a5 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Mon, 28 Dec 2015 22:07:51 -0600 Subject: [PATCH 38/56] Add a WildMidi softsynth device - This removes the preceding psuedo MIDI device for WildMidi. --- src/CMakeLists.txt | 2 +- src/sound/i_musicinterns.h | 39 ++--- src/sound/music_midi_wildmidi.cpp | 163 ------------------- src/sound/music_midistream.cpp | 2 +- src/sound/music_wildmidi_mididevice.cpp | 206 ++++++++++++++++++++++++ src/wildmidi/wildmidi_lib.cpp | 150 ++++++++++++++++- src/wildmidi/wildmidi_lib.h | 16 ++ zdoom.vcproj | 8 +- 8 files changed, 390 insertions(+), 196 deletions(-) delete mode 100644 src/sound/music_midi_wildmidi.cpp create mode 100644 src/sound/music_wildmidi_mididevice.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a86e96f01..18fdb992c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1086,12 +1086,12 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE sound/music_midistream.cpp sound/music_midi_base.cpp sound/music_midi_timidity.cpp - sound/music_midi_wildmidi.cpp sound/music_mus_opl.cpp sound/music_stream.cpp sound/music_fluidsynth_mididevice.cpp sound/music_softsynth_mididevice.cpp sound/music_timidity_mididevice.cpp + sound/music_wildmidi_mididevice.cpp sound/music_win_mididevice.cpp sound/oalsound.cpp sound/sndfile_decoder.cpp diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 4aa5f8254..03282ef37 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -219,25 +219,6 @@ protected: #endif }; -class WildMidiMIDIDevice : public PseudoMIDIDevice -{ -public: - WildMidiMIDIDevice(); - ~WildMidiMIDIDevice(); - - int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); - bool Preprocess(MIDIStreamer *song, bool looping); - bool IsOpen() const; - -protected: - - midi *mMidi; - bool mLoop; - - static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); -}; - - // Base class for software synthesizer MIDI output devices ------------------ class SoftSynthMIDIDevice : public MIDIDevice @@ -350,6 +331,26 @@ protected: FILE *File; }; +// WildMidi implementation of a MIDI device --------------------------------- + +class WildMIDIDevice : public SoftSynthMIDIDevice +{ +public: + WildMIDIDevice(); + ~WildMIDIDevice(); + + int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + void PrecacheInstruments(const WORD *instruments, int count); + FString GetStats(); + +protected: + WildMidi_Renderer *Renderer; + + void HandleEvent(int status, int parm1, int parm2); + void HandleLongEvent(const BYTE *data, int len); + void ComputeOutput(float *buffer, int len); +}; + // FluidSynth implementation of a MIDI device ------------------------------- #ifdef HAVE_FLUIDSYNTH diff --git a/src/sound/music_midi_wildmidi.cpp b/src/sound/music_midi_wildmidi.cpp deleted file mode 100644 index f6df48459..000000000 --- a/src/sound/music_midi_wildmidi.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "i_musicinterns.h" -#include "c_cvars.h" -#include "cmdlib.h" -#include "templates.h" -#include "version.h" - - -CVAR(String, wildmidi_config, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) - -static FString currentConfig; - -// added because Timidity's output is rather loud. -CUSTOM_CVAR (Float, wildmidi_mastervolume, 1.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (self < 0.f) - self = 0.f; - else if (self > 1.f) - self = 1.f; -} - -CUSTOM_CVAR (Int, wildmidi_frequency, 44100, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ // Clamp frequency to Timidity's limits - if (self < 11000) - self = 11000; - else if (self > 65000) - self = 65000; -} - -//========================================================================== -// -// WildMidiMIDIDevice Constructor -// -//========================================================================== - -WildMidiMIDIDevice::WildMidiMIDIDevice() -{ - mMidi = NULL; - mLoop = false; -} - -//========================================================================== -// -// WildMidiMIDIDevice Destructor -// -//========================================================================== - -WildMidiMIDIDevice::~WildMidiMIDIDevice () -{ - if (mMidi != NULL) WildMidi_Close(mMidi); - // do not shut down the device so that it can be reused for the next song being played. -} - -//========================================================================== -// -// WildMidiMIDIDevice :: Preprocess -// -//========================================================================== - -bool WildMidiMIDIDevice::Preprocess(MIDIStreamer *song, bool looping) -{ - TArray midi; - - // Write MIDI song to temporary file - song->CreateSMF(midi, looping ? 0 : 1); - - mMidi = WildMidi_OpenBuffer(&midi[0], midi.Size()); - if (mMidi == NULL) - { - Printf(PRINT_BOLD, "Could not open temp music file\n"); - } - mLoop = looping; - return false; -} - -//========================================================================== -// -// WildMidiMIDIDevice :: Open -// -//========================================================================== - -int WildMidiMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) -{ - if (currentConfig.CompareNoCase(wildmidi_config) != 0) - { - if (currentConfig.IsNotEmpty()) WildMidi_Shutdown(); - currentConfig = ""; - if (!WildMidi_Init(wildmidi_config, wildmidi_frequency, WM_MO_ENHANCED_RESAMPLING)) - { - currentConfig = wildmidi_config; - } - else - { - return 1; - } - } - - Stream = GSnd->CreateStream(FillStream, 32 * 1024, 0, wildmidi_frequency, this); - if (Stream == NULL) - { - Printf(PRINT_BOLD, "Could not create music stream.\n"); - return 1; - } - - return 0; -} - - -//========================================================================== -// -// WildMidiMIDIDevice :: FillStream -// -//========================================================================== - -bool WildMidiMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata) -{ - char *buffer = (char*)buff; - WildMidiMIDIDevice *song = (WildMidiMIDIDevice *)userdata; - if (song->mMidi != NULL) - { - while (len > 0) - { - int written = WildMidi_GetOutput(song->mMidi, buffer, len); - if (written < 0) - { - // error - memset(buffer, 0, len); - return false; - } - buffer += written; - len -= written; - - if (len > 0) - { - if (!song->mLoop) - { - memset(buffer, 0, len); - return written > 0; - } - else - { - // loop the sound (i.e. go back to start.) - unsigned long spos = 0; - WildMidi_FastSeek(song->mMidi, &spos); - } - - } - } - } - - return true; -} - -//========================================================================== -// -// WildMidiMIDIDevice :: IsOpen -// -//========================================================================== - -bool WildMidiMIDIDevice::IsOpen() const -{ - return mMidi != NULL; -} - diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 42e8b5926..37616b9a6 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -294,7 +294,7 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const return new TimidityPPMIDIDevice; case MDEV_WILDMIDI: - return new WildMidiMIDIDevice; + return new WildMIDIDevice; default: return NULL; diff --git a/src/sound/music_wildmidi_mididevice.cpp b/src/sound/music_wildmidi_mididevice.cpp new file mode 100644 index 000000000..44c060f26 --- /dev/null +++ b/src/sound/music_wildmidi_mididevice.cpp @@ -0,0 +1,206 @@ +/* +** music_wildmidi_mididevice.cpp +** Provides access to WildMidi as a generic MIDI device. +** +**--------------------------------------------------------------------------- +** Copyright 2015 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "i_musicinterns.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "w_wad.h" +#include "v_text.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static FString CurrentConfig; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +CVAR(String, wildmidi_config, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, wildmidi_frequency, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// WildMIDIDevice Constructor +// +//========================================================================== + +WildMIDIDevice::WildMIDIDevice() +{ + Renderer = NULL; + + if (wildmidi_frequency >= 11025 && wildmidi_frequency < 65536) + { // Use our own sample rate instead of the global one + SampleRate = wildmidi_frequency; + } + else + { // Else make sure we're not outside of WildMidi's range + SampleRate = clamp(SampleRate, 11025, 65535); + } + + if (CurrentConfig.CompareNoCase(wildmidi_config) != 0 || SampleRate != WildMidi_GetSampleRate()) + { + if (CurrentConfig.IsNotEmpty()) + { + WildMidi_Shutdown(); + CurrentConfig = ""; + } + if (!WildMidi_Init(wildmidi_config, SampleRate, WM_MO_ENHANCED_RESAMPLING)) + { + CurrentConfig = wildmidi_config; + } + } + if (CurrentConfig.IsNotEmpty()) + { + Renderer = new WildMidi_Renderer(); + } +} + +//========================================================================== +// +// WildMIDIDevice Destructor +// +//========================================================================== + +WildMIDIDevice::~WildMIDIDevice() +{ + Close(); + if (Renderer != NULL) + { + delete Renderer; + } + // Do not shut down the device so that it can be reused for the next song being played. +} + +//========================================================================== +// +// WildMIDIDevice :: Open +// +// Returns 0 on success. +// +//========================================================================== + +int WildMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +{ + if (Renderer == NULL) + { + return 1; + } + int ret = OpenStream(2, 0, callback, userdata); + if (ret == 0) + { +// Renderer->Reset(); + } + return ret; +} + +//========================================================================== +// +// WildMIDIDevice :: PrecacheInstruments +// +// Each entry is packed as follows: +// Bits 0- 6: Instrument number +// Bits 7-13: Bank number +// Bit 14: Select drum set if 1, tone bank if 0 +// +//========================================================================== + +void WildMIDIDevice::PrecacheInstruments(const WORD *instruments, int count) +{ + for (int i = 0; i < count; ++i) + { + Renderer->LoadInstrument((instruments[i] >> 7) & 127, instruments[i] >> 14, instruments[i] & 127); + } +} + + +//========================================================================== +// +// WildMIDIDevice :: HandleEvent +// +//========================================================================== + +void WildMIDIDevice::HandleEvent(int status, int parm1, int parm2) +{ + Renderer->ShortEvent(status, parm1, parm2); +} + +//========================================================================== +// +// WildMIDIDevice :: HandleLongEvent +// +//========================================================================== + +void WildMIDIDevice::HandleLongEvent(const BYTE *data, int len) +{ + Renderer->LongEvent((const char *)data, len); +} + +//========================================================================== +// +// WildMIDIDevice :: ComputeOutput +// +//========================================================================== + +void WildMIDIDevice::ComputeOutput(float *buffer, int len) +{ + Renderer->ComputeOutput(buffer, len); +} + +//========================================================================== +// +// WildMIDIDevice :: GetStats +// +//========================================================================== + +FString WildMIDIDevice::GetStats() +{ + FString out; + out.Format("%3d voices", Renderer->GetVoiceCount()); + return out; +} diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 385ac6c1d..f20420e75 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -3643,6 +3643,18 @@ static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count) return buffer; } +int *WM_Mix(midi *handle, int *buffer, unsigned long count) +{ + if (((struct _mdi *)handle)->info.mixer_options & WM_MO_ENHANCED_RESAMPLING) + { + return WM_Mix_Gauss(handle, buffer, count); + } + else + { + return WM_Mix_Linear(handle, buffer, count); + } +} + static int WM_DoGetOutput(midi * handle, char * buffer, unsigned long int size) { unsigned long int buffer_used = 0; @@ -3702,14 +3714,7 @@ static int WM_DoGetOutput(midi * handle, char * buffer, } /* do mixing here */ - if (mdi->info.mixer_options & WM_MO_ENHANCED_RESAMPLING) - { - tmp_buffer = WM_Mix_Gauss(handle, tmp_buffer, real_samples_to_mix); - } - else - { - tmp_buffer = WM_Mix_Linear(handle, tmp_buffer, real_samples_to_mix); - } + tmp_buffer = WM_Mix(handle, tmp_buffer, real_samples_to_mix); buffer_used += real_samples_to_mix * 4; size -= (real_samples_to_mix << 2); @@ -3807,6 +3812,11 @@ WM_SYMBOL int WildMidi_Init(const char * config_file, unsigned short int rate, return 0; } +WM_SYMBOL int WildMidi_GetSampleRate(void) +{ + return _WM_SampleRate; +} + WM_SYMBOL int WildMidi_MasterVolume(unsigned char master_volume) { struct _mdi *mdi = NULL; struct _hndl * tmp_handle = first_handle; @@ -3947,6 +3957,23 @@ WildMidi_OpenBuffer(unsigned char *midibuffer, unsigned long int size) { return ret; } +midi *WildMidi_NewMidi() { + midi * ret = NULL; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return NULL; + } + ret = Init_MDI(); + if (ret) { + if (add_handle(ret) != 0) { + WildMidi_Close(ret); + ret = NULL; + } + } + return ret; +} + WM_SYMBOL int WildMidi_FastSeek(midi * handle, unsigned long int *sample_pos) { struct _mdi *mdi; struct _event *event; @@ -4197,3 +4224,110 @@ WM_SYMBOL int WildMidi_Shutdown(void) { return 0; } + +WildMidi_Renderer::WildMidi_Renderer() +{ + handle = WildMidi_NewMidi(); +} + +WildMidi_Renderer::~WildMidi_Renderer() +{ + WildMidi_Close((midi *)handle); +} + +void WildMidi_Renderer::ShortEvent(int status, int parm1, int parm2) +{ + _mdi *mdi = (_mdi *)handle; + _event_data ev; + + ev.channel = status & 0x0F; + switch ((status & 0xF0) >> 4) // command + { + case 0x8: + ev.data = (parm1 << 8) | parm2; + do_note_off(mdi, &ev); + break; + + case 0x9: + ev.data = (parm1 << 8) | parm2; + do_note_on(mdi, &ev); + break; + + case 0xA: + ev.data = (parm1 << 8) | parm2; + do_aftertouch(mdi, &ev); + break; + + case 0xC: + ev.data = parm1; + do_patch(mdi, &ev); + break; + + case 0xD: + ev.data = parm1; + do_channel_pressure(mdi, &ev); + break; + + case 0xE: + ev.data = parm1 | (parm2 << 7); + do_pitch(mdi, &ev); + break; + + case 0xB: // Controllers + ev.data = parm2; + switch (parm1) + { + case 0: do_control_bank_select(mdi, &ev); break; + case 6: do_control_data_entry_course(mdi, &ev); break; // [sic] + case 7: do_control_channel_volume(mdi, &ev); break; + case 8: do_control_channel_balance(mdi, &ev); break; + case 10: do_control_channel_pan(mdi, &ev); break; + case 11: do_control_channel_expression(mdi, &ev); break; + case 38: do_control_data_entry_fine(mdi, &ev); break; + case 64: do_control_channel_hold(mdi, &ev); break; + case 96: do_control_data_increment(mdi, &ev); break; + case 97: do_control_data_decrement(mdi, &ev); break; + case 98: + case 99: do_control_non_registered_param(mdi, &ev); break; + case 100: do_control_registered_param_fine(mdi, &ev); break; + case 101: do_control_registered_param_course(mdi, &ev); break; // [sic] + case 120: do_control_channel_sound_off(mdi, &ev); break; + case 121: do_control_channel_controllers_off(mdi, &ev); break; + case 123: do_control_channel_notes_off(mdi, &ev); break; + } + } +} + +void WildMidi_Renderer::LongEvent(const char *data, int len) +{ +} + +void WildMidi_Renderer::ComputeOutput(float *fbuffer, int len) +{ + _mdi *mdi = (_mdi *)handle; + int *buffer = (int *)fbuffer; + int *newbuf = WM_Mix(handle, buffer, len); +// assert(newbuf - buffer == len); + if (mdi->info.mixer_options & WM_MO_REVERB) { + _WM_do_reverb(mdi->reverb, buffer, len * 2); + } + for (; buffer < newbuf; ++buffer) + { + *(float *)buffer = (float)*buffer / 32768.f; + } +} + +void WildMidi_Renderer::LoadInstrument(int bank, int percussion, int instr) +{ + load_patch((_mdi *)handle, (bank << 8) | instr | (percussion ? 0x80 : 0)); +} + +int WildMidi_Renderer::GetVoiceCount() +{ + int count = 0; + for (_note *note_data = ((_mdi *)handle)->note; note_data != NULL; note_data = note_data->next) + { + count++; + } + return count; +} diff --git a/src/wildmidi/wildmidi_lib.h b/src/wildmidi/wildmidi_lib.h index e6317a271..46a517d90 100644 --- a/src/wildmidi/wildmidi_lib.h +++ b/src/wildmidi/wildmidi_lib.h @@ -64,6 +64,7 @@ WM_SYMBOL struct _WM_Info * WildMidi_GetInfo (midi * handle); WM_SYMBOL int WildMidi_FastSeek (midi * handle, unsigned long int *sample_pos); WM_SYMBOL int WildMidi_Close (midi * handle); WM_SYMBOL int WildMidi_Shutdown (void); +WM_SYMBOL int WildMidi_GetSampleRate (void); /* #if defined(__cplusplus) @@ -71,5 +72,20 @@ WM_SYMBOL int WildMidi_Shutdown (void); #endif */ +class WildMidi_Renderer +{ +public: + WildMidi_Renderer(); + ~WildMidi_Renderer(); + + void ShortEvent(int status, int parm1, int parm2); + void LongEvent(const char *data, int len); + void ComputeOutput(float *buffer, int len); + void LoadInstrument(int bank, int percussion, int instr); + int GetVoiceCount(); +private: + void *handle; +}; + #endif /* WILDMIDI_LIB_H */ diff --git a/zdoom.vcproj b/zdoom.vcproj index 126efb7f5..a5d7bfb8e 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -2553,10 +2553,6 @@ RelativePath="src\sound\music_midi_timidity.cpp" > - - @@ -2597,6 +2593,10 @@ RelativePath=".\src\sound\music_timidity_mididevice.cpp" > + + From 7c82c576a3e3e4522dd528c3643a2396b9bee2eb Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Tue, 29 Dec 2015 10:33:20 +0100 Subject: [PATCH 39/56] - Fixed Linux compiler errors and snd_mididevice. -- errno.h is required for 'errno'; -- don't use str(n)casecmp and rely on ZDoom CMake handling; -- add a missing parenthesis around a 'signed char' cast; -- remove an unneeded GNU_SOURCE redefinition; -- the non-MSVC side of snd_mididevice was not adapted to the new code, making wildmidi unavailable through the menu. --- src/sound/music_midi_base.cpp | 4 ++-- src/wildmidi/file_io.cpp | 2 ++ src/wildmidi/lock.cpp | 1 - src/wildmidi/wildmidi_lib.cpp | 44 +++++++++++++++++------------------ 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index e1afa09a8..048e52e3f 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -198,8 +198,8 @@ CCMD (snd_listmididevices) CUSTOM_CVAR(Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { - if (self < -5) - self = -5; + if (self < -6) + self = -6; else if (self > -1) self = -1; else diff --git a/src/wildmidi/file_io.cpp b/src/wildmidi/file_io.cpp index f14e22bbf..c2ef55715 100644 --- a/src/wildmidi/file_io.cpp +++ b/src/wildmidi/file_io.cpp @@ -33,6 +33,8 @@ ** */ +#include + #include "../files.h" #include "wm_error.h" #include "file_io.h" diff --git a/src/wildmidi/lock.cpp b/src/wildmidi/lock.cpp index f8abfe926..c5d3fc7da 100644 --- a/src/wildmidi/lock.cpp +++ b/src/wildmidi/lock.cpp @@ -29,7 +29,6 @@ #ifdef _WIN32 #include #else -#define _GNU_SOURCE #include #endif diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index f20420e75..8f8d558b9 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -47,11 +47,11 @@ #ifdef _WIN32 #include #include -*/ #undef strcasecmp #define strcasecmp _stricmp #undef strncasecmp #define strncasecmp _strnicmp +*/ @@ -699,7 +699,7 @@ static int WM_LoadConfig(const char *config_file) { 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) { + if (stricmp(line_tokens[0], "dir") == 0) { free(config_dir); if (!line_tokens[1]) { _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, @@ -724,7 +724,7 @@ static int WM_LoadConfig(const char *config_file) { config_dir[strlen(config_dir) + 1] = '\0'; config_dir[strlen(config_dir)] = '/'; } - } else if (strcasecmp(line_tokens[0], "source") == 0) { + } else if (stricmp(line_tokens[0], "source") == 0) { char *new_config = NULL; if (!line_tokens[1]) { _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, @@ -772,7 +772,7 @@ static int WM_LoadConfig(const char *config_file) { return -1; } free(new_config); - } else if (strcasecmp(line_tokens[0], "bank") == 0) { + } else if (stricmp(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); @@ -785,7 +785,7 @@ static int WM_LoadConfig(const char *config_file) { return -1; } patchid = (atoi(line_tokens[1]) & 0xFF) << 8; - } else if (strcasecmp(line_tokens[0], "drumset") == 0) { + } else if (stricmp(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); @@ -798,7 +798,7 @@ static int WM_LoadConfig(const char *config_file) { return -1; } patchid = ((atoi(line_tokens[1]) & 0xFF) << 8) | 0x80; - } else if (strcasecmp(line_tokens[0], "reverb_room_width") == 0) { + } else if (stricmp(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)", @@ -823,7 +823,7 @@ static int WM_LoadConfig(const char *config_file) { 0); reverb_room_width = 100.0f; } - } else if (strcasecmp(line_tokens[0], "reverb_room_length") == 0) { + } else if (stricmp(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)", @@ -848,7 +848,7 @@ static int WM_LoadConfig(const char *config_file) { 0); reverb_room_length = 100.0f; } - } else if (strcasecmp(line_tokens[0], "reverb_listener_posx") == 0) { + } else if (stricmp(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)", @@ -869,7 +869,7 @@ static int WM_LoadConfig(const char *config_file) { 0); reverb_listen_posx = reverb_room_width / 2.0f; } - } else if (strcasecmp(line_tokens[0], + } else if (stricmp(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, @@ -891,13 +891,13 @@ static int WM_LoadConfig(const char *config_file) { 0); reverb_listen_posy = reverb_room_length * 0.75f; } - } else if (strcasecmp(line_tokens[0], + } else if (stricmp(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) { + } else if (stricmp(line_tokens[0], "auto_amp") == 0) { auto_amp = 1; - } else if (strcasecmp(line_tokens[0], "auto_amp_with_amp") + } else if (stricmp(line_tokens[0], "auto_amp_with_amp") == 0) { auto_amp = 1; auto_amp_with_amp = 1; @@ -1038,7 +1038,7 @@ static int WM_LoadConfig(const char *config_file) { return -1; } } - if (strncasecmp( + if (strnicmp( &tmp_patch->filename[strlen(tmp_patch->filename) - 4], ".pat", 4) != 0) { strcat(tmp_patch->filename, ".pat"); @@ -1054,7 +1054,7 @@ static int WM_LoadConfig(const char *config_file) { token_count = 0; while (line_tokens[token_count]) { - if (strncasecmp(line_tokens[token_count], "amp=", 4) + if (strnicmp(line_tokens[token_count], "amp=", 4) == 0) { if (!wm_isdigit(line_tokens[token_count][4])) { _WM_ERROR(__FUNCTION__, __LINE__, @@ -1065,7 +1065,7 @@ static int WM_LoadConfig(const char *config_file) { &line_tokens[token_count][4]) << 10) / 100; } - } else if (strncasecmp(line_tokens[token_count], + } else if (strnicmp(line_tokens[token_count], "note=", 5) == 0) { if (!wm_isdigit(line_tokens[token_count][5])) { _WM_ERROR(__FUNCTION__, __LINE__, @@ -1075,7 +1075,7 @@ static int WM_LoadConfig(const char *config_file) { tmp_patch->note = atoi( &line_tokens[token_count][5]); } - } else if (strncasecmp(line_tokens[token_count], + } else if (strnicmp(line_tokens[token_count], "env_time", 8) == 0) { if ((!wm_isdigit(line_tokens[token_count][8])) || (!wm_isdigit( @@ -1110,7 +1110,7 @@ static int WM_LoadConfig(const char *config_file) { } } } - } else if (strncasecmp(line_tokens[token_count], + } else if (strnicmp(line_tokens[token_count], "env_level", 9) == 0) { if ((!wm_isdigit(line_tokens[token_count][9])) || (!wm_isdigit( @@ -1144,16 +1144,16 @@ static int WM_LoadConfig(const char *config_file) { } } } - } else if (strcasecmp(line_tokens[token_count], + } else if (stricmp(line_tokens[token_count], "keep=loop") == 0) { tmp_patch->keep |= SAMPLE_LOOP; - } else if (strcasecmp(line_tokens[token_count], + } else if (stricmp(line_tokens[token_count], "keep=env") == 0) { tmp_patch->keep |= SAMPLE_ENVELOPE; - } else if (strcasecmp(line_tokens[token_count], + } else if (stricmp(line_tokens[token_count], "remove=sustain") == 0) { tmp_patch->remove |= SAMPLE_SUSTAIN; - } else if (strcasecmp(line_tokens[token_count], + } else if (stricmp(line_tokens[token_count], "remove=clamped") == 0) { tmp_patch->remove |= SAMPLE_CLAMPED; } @@ -1695,7 +1695,7 @@ static void do_control_channel_balance(struct _mdi *mdi, 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); + mdi->channel[ch].pan = (signed char)(data->data - 64); do_pan_adjust(mdi, ch); } From 944360557f79480b4b39571d06fbe4b97e58b87e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 29 Dec 2015 11:23:43 +0100 Subject: [PATCH 40/56] - removed unused header stuff. --- src/wildmidi/wildmidi_lib.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 8f8d558b9..1e78f7feb 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -43,18 +43,6 @@ #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" From 7fa289109b9fdcdd1cecf3f72708b7ea4ee95820 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 29 Dec 2015 11:36:56 +0100 Subject: [PATCH 41/56] - removed all uses of __builtin_expect from WildMidi code. --- src/wildmidi/wildmidi_lib.cpp | 91 +++++++++++++++-------------------- 1 file changed, 39 insertions(+), 52 deletions(-) diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 1e78f7feb..26e4965ba 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -60,9 +60,6 @@ #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 @@ -1459,15 +1456,15 @@ static inline unsigned long int get_inc(struct _mdi *mdi, struct _note *nte) { signed long int note_f; unsigned long int freq; - if (__builtin_expect((nte->patch->note != 0), 0)) { + if (nte->patch->note != 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)) { + if (note_f < 0) { note_f = 0; - } else if (__builtin_expect((note_f > 12700), 0)) { + } else if (note_f > 12700) { note_f = 12700; } freq = freq_table[(note_f % 1200)] >> (10 - (note_f / 1200)); @@ -3202,7 +3199,7 @@ static int *WM_Mix_Linear(midi * handle, int * buffer, unsigned long int count) do { note_data = mdi->note; left_mix = right_mix = 0; - if (__builtin_expect((note_data != NULL), 1)) { + if (note_data != NULL) { while (note_data) { /* * =================== @@ -3231,44 +3228,38 @@ static int *WM_Mix_Linear(midi * handle, int * buffer, unsigned long int count) * ======================== */ note_data->sample_pos += note_data->sample_inc; - if (__builtin_expect( - (note_data->sample_pos > note_data->sample->loop_end), - 0)) { + if (note_data->sample_pos > note_data->sample->loop_end) { 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)) { + } else if (note_data->sample_pos >= note_data->sample->data_length) { + if (note_data->replay == NULL) { goto KILL_NOTE; } goto RESTART_NOTE; } } - if (__builtin_expect((note_data->env_inc == 0), 0)) { + if (note_data->env_inc == 0) { note_data = note_data->next; continue; } note_data->env_level += note_data->env_inc; - if (__builtin_expect((note_data->env_level > 4194304), 0)) { + if (note_data->env_level > 4194304) { 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)) { + if (((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]))) + { note_data = note_data->next; continue; } @@ -3304,7 +3295,7 @@ static int *WM_Mix_Linear(midi * handle, int * buffer, unsigned long int count) } break; case 5: - if (__builtin_expect((note_data->env_level == 0), 1)) { + if (note_data->env_level == 0) { goto KILL_NOTE; } /* sample release */ @@ -3314,7 +3305,7 @@ static int *WM_Mix_Linear(midi * handle, int * buffer, unsigned long int count) note_data = note_data->next; continue; case 6: - if (__builtin_expect((note_data->replay != NULL), 1)) { + if (note_data->replay != NULL) { RESTART_NOTE: note_data->active = 0; { struct _note *prev_note = NULL; @@ -3409,7 +3400,7 @@ static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count) do { note_data = mdi->note; left_mix = right_mix = 0; - if (__builtin_expect((note_data != NULL), 1)) { + if (note_data != NULL) { while (note_data) { /* * =================== @@ -3471,44 +3462,40 @@ static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count) * ======================== */ note_data->sample_pos += note_data->sample_inc; - if (__builtin_expect( - (note_data->sample_pos > note_data->sample->loop_end), - 0)) { + if (note_data->sample_pos > note_data->sample->loop_end) + { 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)) { + } else if (note_data->sample_pos >= note_data->sample->data_length) { + if (note_data->replay == NULL) { goto KILL_NOTE; } goto RESTART_NOTE; } } - if (__builtin_expect((note_data->env_inc == 0), 0)) { + if (note_data->env_inc == 0) { note_data = note_data->next; continue; } note_data->env_level += note_data->env_inc; - if (__builtin_expect((note_data->env_level > 4194304), 0)) { + if (note_data->env_level > 4194304) { note_data->env_level = note_data->sample->env_target[note_data->env]; } - if (__builtin_expect( + if ( ((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->sample->env_target[note_data->env])) + ) { note_data = note_data->next; continue; } @@ -3544,7 +3531,7 @@ static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count) } break; case 5: - if (__builtin_expect((note_data->env_level == 0), 1)) { + if (note_data->env_level == 0) { goto KILL_NOTE; } /* sample release */ @@ -3554,7 +3541,7 @@ static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count) note_data = note_data->next; continue; case 6: - if (__builtin_expect((note_data->replay != NULL), 1)) { + if (note_data->replay != NULL) { RESTART_NOTE: note_data->active = 0; { struct _note *prev_note = NULL; @@ -3671,7 +3658,7 @@ static int WM_DoGetOutput(midi * handle, char * buffer, out_buffer = tmp_buffer; do { - if (__builtin_expect((!mdi->samples_to_mix), 0)) { + if (!mdi->samples_to_mix) { while ((!mdi->samples_to_mix) && (event->do_event)) { event->do_event(mdi, &event->event_data); event++; @@ -3692,7 +3679,7 @@ static int WM_DoGetOutput(midi * handle, char * buffer, } } } - if (__builtin_expect((mdi->samples_to_mix > (size >> 2)), 1)) { + if (mdi->samples_to_mix > (size >> 2)) { real_samples_to_mix = size >> 2; } else { real_samples_to_mix = mdi->samples_to_mix; @@ -4016,7 +4003,7 @@ WM_SYMBOL int WildMidi_FastSeek(midi * handle, unsigned long int *sample_pos) { _WM_reset_reverb(mdi->reverb); while (count) { - if (__builtin_expect((!mdi->samples_to_mix), 0)) { + if (!mdi->samples_to_mix) { while ((!mdi->samples_to_mix) && (event->do_event)) { event->do_event(mdi, &event->event_data); event++; @@ -4034,7 +4021,7 @@ WM_SYMBOL int WildMidi_FastSeek(midi * handle, unsigned long int *sample_pos) { } } - if (__builtin_expect((mdi->samples_to_mix > count), 0)) { + if (mdi->samples_to_mix > count) { real_samples_to_mix = count; } else { real_samples_to_mix = mdi->samples_to_mix; @@ -4066,24 +4053,24 @@ WM_SYMBOL int WildMidi_FastSeek(midi * handle, unsigned long int *sample_pos) { } WM_SYMBOL int WildMidi_GetOutput(midi * handle, char *buffer, unsigned long int size) { - if (__builtin_expect((!WM_Initialized), 0)) { + if (!WM_Initialized) { _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); return -1; } - if (__builtin_expect((handle == NULL), 0)) { + if (handle == NULL) { _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); return -1; } - if (__builtin_expect((buffer == NULL), 0)) { + if (buffer == NULL) { _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL buffer pointer)", 0); return -1; } - if (__builtin_expect((size == 0), 0)) { + if (size == 0) { return 0; } - if (__builtin_expect((!!(size % 4)), 0)) { + if (!!(size % 4)) { _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(size not a multiple of 4)", 0); return -1; From 0634205d7f4bd0e9cc507a3e0ff13670d1ac80b6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 29 Dec 2015 18:01:15 +0100 Subject: [PATCH 42/56] - let WildMidi tokenizer handle quoted strings. --- src/wildmidi/wildmidi_lib.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 26e4965ba..19e290f6b 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -573,13 +573,15 @@ static inline int wm_isdigit(int c) { } #define TOKEN_CNT_INC 8 -static char** WM_LC_Tokenize_Line(char *line_data) { +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; + bool in_quotes = false; if (line_length == 0) return NULL; @@ -589,8 +591,11 @@ static char** WM_LC_Tokenize_Line(char *line_data) { if (line_data[line_ofs] == '#') { break; } - - if ((line_data[line_ofs] == ' ') || (line_data[line_ofs] == '\t')) { + if (line_data[line_ofs] == '"') + { + in_quotes = !in_quotes; + } + else if (!in_quotes && ((line_data[line_ofs] == ' ') || (line_data[line_ofs] == '\t'))) { /* whitespace means we aren't in a token */ if (token_start) { token_start = 0; From fe2dcfd588596d5311b31193f0e70630815a9932 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 29 Dec 2015 20:38:08 +0100 Subject: [PATCH 43/56] - refactored the GUS/Timidity player's path building code so that it can also be used by WildMidi. - fixed crash during sound reset - in this case I_ShutdownMusic should not close the WildMidi player. --- src/CMakeLists.txt | 1 + src/pathexpander.cpp | 134 ++++++++++++++++++++++++++++++++++ src/pathexpander.h | 31 ++++++++ src/sound/i_music.cpp | 11 ++- src/sound/i_music.h | 3 +- src/timidity/common.cpp | 88 ---------------------- src/timidity/instrum.cpp | 4 +- src/timidity/instrum_font.cpp | 2 +- src/timidity/instrum_sf2.cpp | 2 +- src/timidity/timidity.cpp | 32 ++++---- src/timidity/timidity.h | 11 +-- 11 files changed, 198 insertions(+), 121 deletions(-) create mode 100644 src/pathexpander.cpp create mode 100644 src/pathexpander.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 18fdb992c..f1975a8d8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -915,6 +915,7 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE nodebuild_extract.cpp nodebuild_gl.cpp nodebuild_utility.cpp + pathexpander.cpp p_3dfloors.cpp p_3dmidtex.cpp p_acs.cpp diff --git a/src/pathexpander.cpp b/src/pathexpander.cpp new file mode 100644 index 000000000..d8b912b65 --- /dev/null +++ b/src/pathexpander.cpp @@ -0,0 +1,134 @@ +/* +** pathexpander.cpp +** Utility class for expanding a given path with a range of directories +** +**--------------------------------------------------------------------------- +** Copyright 2015 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 "pathexpander.h" +#include "cmdlib.h" +#include "w_wad.h" + +//============================================================================ +// +// +// +//============================================================================ + +static FString BuildPath(const FString &base, const char *name) +{ + FString current; + if (base.IsNotEmpty()) + { + current = base; + if (current[current.Len() - 1] != '/') current += '/'; + } + current += name; + return current; +} + +//============================================================================ +// +// This is meant to find and open files for reading. +// +//============================================================================ + +FileReader *PathExpander::openFileReader(const char *name, int *plumpnum) +{ + FileReader *fp; + FString current_filename; + + if (!name || !(*name)) + { + return 0; + } + + /* First try the given name */ + current_filename = name; + FixPathSeperator(current_filename); + + int lumpnum = Wads.CheckNumForFullName(current_filename); + + if (openmode != OM_FILE) + { + if (lumpnum >= 0) + { + fp = Wads.ReopenLumpNum(lumpnum); + if (plumpnum) *plumpnum = lumpnum; + return fp; + } + if (openmode == OM_LUMP) // search the path list when not loading the main config + { + for (unsigned int plp = PathList.Size(); plp-- != 0; ) + { /* Try along the path then */ + current_filename = BuildPath(PathList[plp], name); + lumpnum = Wads.CheckNumForFullName(current_filename); + if (lumpnum >= 0) + { + fp = Wads.ReopenLumpNum(lumpnum); + if (plumpnum) *plumpnum = lumpnum; + return fp; + } + } + return NULL; + } + } + if (plumpnum) *plumpnum = -1; + + + fp = new FileReader; + if (fp->Open(current_filename)) return fp; + + if (name[0] != '/') + { + for (unsigned int plp = PathList.Size(); plp-- != 0; ) + { /* Try along the path then */ + current_filename = BuildPath(PathList[plp], name); + if (fp->Open(current_filename)) return fp; + } + } + delete fp; + + /* Nothing could be opened. */ + return NULL; +} + +/* This adds a directory to the path list */ +void PathExpander::addToPathlist(const char *s) +{ + FString copy = s; + FixPathSeperator(copy); + PathList.Push(copy); +} + +void PathExpander::clearPathlist() +{ + PathList.Clear(); +} diff --git a/src/pathexpander.h b/src/pathexpander.h new file mode 100644 index 000000000..556598071 --- /dev/null +++ b/src/pathexpander.h @@ -0,0 +1,31 @@ +#ifndef __PATHEXPANDER_H +#define __PATHEXPANDER_H + +#include "tarray.h" +#include "zstring.h" +#include "files.h" + +class PathExpander +{ + TArray PathList; + +public: + int openmode; + + enum + { + OM_FILEORLUMP = 0, + OM_LUMP, + OM_FILE + }; + + PathExpander(int om = OM_FILEORLUMP) + { + openmode = om; + } + void addToPathlist(const char *s); + void clearPathlist(); + FileReader *openFileReader(const char *name, int *plumpnum); +}; + +#endif diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 721be5192..121c0b9fb 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -162,7 +162,7 @@ void I_InitMusic (void) if (!setatterm) { setatterm = true; - atterm (I_ShutdownMusic); + atterm (I_ShutdownMusicExit); #ifndef _WIN32 signal (SIGCHLD, ChildSigHandler); @@ -178,7 +178,7 @@ void I_InitMusic (void) // //========================================================================== -void I_ShutdownMusic(void) +void I_ShutdownMusic(bool onexit) { if (MusicDown) return; @@ -189,12 +189,17 @@ void I_ShutdownMusic(void) assert (currSong == NULL); } Timidity::FreeAll(); - WildMidi_Shutdown(); + if (onexit) WildMidi_Shutdown(); #ifdef _WIN32 I_ShutdownMusicWin32(); #endif // _WIN32 } +void I_ShutdownMusicExit() +{ + I_ShutdownMusic(true); +} + //========================================================================== // diff --git a/src/sound/i_music.h b/src/sound/i_music.h index f6a6dbadd..ee05d8caf 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -43,7 +43,8 @@ struct FOptionValues; // MUSIC I/O // void I_InitMusic (); -void I_ShutdownMusic (); +void I_ShutdownMusic (bool onexit = false); +void I_ShutdownMusicExit (); void I_BuildMIDIMenuList (FOptionValues *); void I_UpdateMusic (); diff --git a/src/timidity/common.cpp b/src/timidity/common.cpp index 92d9b009a..57fd26e0c 100644 --- a/src/timidity/common.cpp +++ b/src/timidity/common.cpp @@ -35,82 +35,6 @@ namespace Timidity { -static TArray PathList; - -FString BuildPath(FString base, const char *name) -{ - FString current; - if (base.IsNotEmpty()) - { - current = base; - if (current[current.Len() - 1] != '/') current += '/'; - } - current += name; - return current; -} - -/* This is meant to find and open files for reading. */ -FileReader *open_filereader(const char *name, int open, int *plumpnum) -{ - FileReader *fp; - FString current_filename; - - if (!name || !(*name)) - { - return 0; - } - - /* First try the given name */ - current_filename = name; - FixPathSeperator(current_filename); - - int lumpnum = Wads.CheckNumForFullName(current_filename); - - if (open != OM_FILE) - { - if (lumpnum >= 0) - { - fp = Wads.ReopenLumpNum(lumpnum); - if (plumpnum) *plumpnum = lumpnum; - return fp; - } - if (open == OM_LUMP) // search the path list when not loading the main config - { - for (unsigned int plp = PathList.Size(); plp-- != 0; ) - { /* Try along the path then */ - current_filename = BuildPath(PathList[plp], name); - lumpnum = Wads.CheckNumForFullName(current_filename); - if (lumpnum >= 0) - { - fp = Wads.ReopenLumpNum(lumpnum); - if (plumpnum) *plumpnum = lumpnum; - return fp; - } - } - return NULL; - } - } - if (plumpnum) *plumpnum = -1; - - - fp = new FileReader; - if (fp->Open(current_filename)) return fp; - - if (name[0] != '/') - { - for (unsigned int plp = PathList.Size(); plp-- != 0; ) - { /* Try along the path then */ - current_filename = BuildPath(PathList[plp], name); - if (fp->Open(current_filename)) return fp; - } - } - delete fp; - - /* Nothing could be opened. */ - current_filename = ""; - return NULL; -} - /* This'll allocate memory or die. */ @@ -132,17 +56,5 @@ void *safe_malloc(size_t count) return 0; // Unreachable. } -/* This adds a directory to the path list */ -void add_to_pathlist(const char *s) -{ - FString copy = s; - FixPathSeperator(copy); - PathList.Push(copy); -} - -void clear_pathlist() -{ - PathList.Clear(); -} } diff --git a/src/timidity/instrum.cpp b/src/timidity/instrum.cpp index 7454ca7a4..a8c7b5f7c 100644 --- a/src/timidity/instrum.cpp +++ b/src/timidity/instrum.cpp @@ -159,12 +159,12 @@ static Instrument *load_instrument(Renderer *song, const char *name, int percuss if (!name) return 0; /* Open patch file */ - if ((fp = open_filereader(name, openmode, NULL)) == NULL) + if ((fp = pathExpander.openFileReader(name, NULL)) == NULL) { /* Try with various extensions */ FString tmp = name; tmp += ".pat"; - if ((fp = open_filereader(tmp, openmode, NULL)) == NULL) + if ((fp = pathExpander.openFileReader(tmp, NULL)) == NULL) { #ifdef __unix__ // Windows isn't case-sensitive. tmp.ToUpper(); diff --git a/src/timidity/instrum_font.cpp b/src/timidity/instrum_font.cpp index 995558bd4..069eb9c84 100644 --- a/src/timidity/instrum_font.cpp +++ b/src/timidity/instrum_font.cpp @@ -54,7 +54,7 @@ void font_add(const char *filename, int load_order) } else { - FileReader *fp = open_filereader(filename, openmode, NULL); + FileReader *fp = pathExpander.openFileReader(filename, NULL); if (fp != NULL) { diff --git a/src/timidity/instrum_sf2.cpp b/src/timidity/instrum_sf2.cpp index dae330644..c4cf0bc81 100644 --- a/src/timidity/instrum_sf2.cpp +++ b/src/timidity/instrum_sf2.cpp @@ -1502,7 +1502,7 @@ void SFFile::ApplyGeneratorsToRegion(SFGenComposite *gen, SFSample *sfsamp, Rend void SFFile::LoadSample(SFSample *sample) { - FileReader *fp = open_filereader(Filename, openmode, NULL); + FileReader *fp = pathExpander.openFileReader(Filename, NULL); DWORD i; if (fp == NULL) diff --git a/src/timidity/timidity.cpp b/src/timidity/timidity.cpp index 0f0fcdeb5..1d74c5afc 100644 --- a/src/timidity/timidity.cpp +++ b/src/timidity/timidity.cpp @@ -42,10 +42,10 @@ CVAR(Int, gus_memsize, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) namespace Timidity { +PathExpander pathExpander; ToneBank *tonebank[MAXBANK], *drumset[MAXBANK]; static FString def_instr_name; -int openmode = OM_FILEORLUMP; static int read_config_file(const char *name, bool ismain) { @@ -62,21 +62,21 @@ static int read_config_file(const char *name, bool ismain) return (-1); } - if (ismain) openmode = OM_FILEORLUMP; + if (ismain) pathExpander.openmode = PathExpander::OM_FILEORLUMP; - if (!(fp = open_filereader(name, openmode, &lumpnum))) + if (!(fp = pathExpander.openFileReader(name, &lumpnum))) return -1; if (ismain) { if (lumpnum > 0) { - openmode = OM_LUMP; - clear_pathlist(); // when reading from a PK3 we won't want to use any external path + pathExpander.openmode = PathExpander::OM_LUMP; + pathExpander.clearPathlist(); // when reading from a PK3 we don't want to use any external path } else { - openmode = OM_FILE; + pathExpander.openmode = PathExpander::OM_FILE; } } @@ -288,7 +288,7 @@ static int read_config_file(const char *name, bool ismain) return -2; } for (i = 1; i < words; i++) - add_to_pathlist(w[i]); + pathExpander.addToPathlist(w[i]); } else if (!strcmp(w[0], "source")) { @@ -532,15 +532,15 @@ int LoadConfig(const char *filename) * file itself since that file should contain any other directory * that needs to be added to the search path. */ - clear_pathlist(); + pathExpander.clearPathlist(); #ifdef _WIN32 - add_to_pathlist("C:\\TIMIDITY"); - add_to_pathlist("\\TIMIDITY"); - add_to_pathlist(progdir); + pathExpander.addToPathlist("C:\\TIMIDITY"); + pathExpander.addToPathlist("\\TIMIDITY"); + pathExpander.addToPathlist(progdir); #else - add_to_pathlist("/usr/local/lib/timidity"); - add_to_pathlist("/etc/timidity"); - add_to_pathlist("/etc"); + pathExpander.addToPathlist("/usr/local/lib/timidity"); + pathExpander.addToPathlist("/etc/timidity"); + pathExpander.addToPathlist("/etc"); #endif /* Some functions get aggravated if not even the standard banks are available. */ @@ -579,10 +579,10 @@ int LoadDMXGUS() if (ultradir.IsNotEmpty()) { ultradir += "/midi"; - add_to_pathlist(ultradir.GetChars()); + pathExpander.addToPathlist(ultradir.GetChars()); } // Load DMXGUS lump and patches from gus_patchdir - add_to_pathlist(gus_patchdir); + pathExpander.addToPathlist(gus_patchdir); char readbuffer[1024]; long size = data.GetLength(); diff --git a/src/timidity/timidity.h b/src/timidity/timidity.h index 6c46317dd..0a21a41b9 100644 --- a/src/timidity/timidity.h +++ b/src/timidity/timidity.h @@ -21,6 +21,7 @@ #define TIMIDITY_H #include "doomtype.h" +#include "pathexpander.h" class FileReader; @@ -172,17 +173,8 @@ extern __inline__ double pow_x87_inline(double x,double y) common.h */ -#define OM_FILEORLUMP 0 -#define OM_LUMP 1 -#define OM_FILE 2 - -extern void add_to_pathlist(const char *s); -extern void clear_pathlist(); extern void *safe_malloc(size_t count); -FileReader *open_filereader(const char *name, int open, int *plumpnum); -extern int openmode; - /* controls.h */ @@ -627,6 +619,7 @@ int LoadConfig(const char *filename); int LoadDMXGUS(); extern int LoadConfig(); extern void FreeAll(); +extern PathExpander pathExpander; extern ToneBank *tonebank[MAXBANK]; extern ToneBank *drumset[MAXBANK]; From c3862d910142ac726358f0838aeb5d9e535cefd0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 29 Dec 2015 21:18:46 +0100 Subject: [PATCH 44/56] - use PathExpander class for WildMidi's file access functions so that it can find Timdity's data on its own. --- src/wildmidi/file_io.cpp | 49 +++++++++++++++++++++++++++++++---- src/wildmidi/file_io.h | 2 +- src/wildmidi/wildmidi_lib.cpp | 2 +- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/wildmidi/file_io.cpp b/src/wildmidi/file_io.cpp index c2ef55715..50a2a6b6c 100644 --- a/src/wildmidi/file_io.cpp +++ b/src/wildmidi/file_io.cpp @@ -38,18 +38,56 @@ #include "../files.h" #include "wm_error.h" #include "file_io.h" +#include "pathexpander.h" +#include "cmdlib.h" -unsigned char *_WM_BufferFile(const char *filename, unsigned long int *size) +static PathExpander wmPathExpander; + +unsigned char *_WM_BufferFile(const char *filename, unsigned long int *size, bool ismain) { - FileReader file; + FileReader *fp; + int lumpnum; - if (!file.Open(filename)) + if (ismain) + { + wmPathExpander.openmode = PathExpander::OM_FILEORLUMP; + wmPathExpander.clearPathlist(); +#ifdef _WIN32 + wmPathExpander.addToPathlist("C:\\TIMIDITY"); + wmPathExpander.addToPathlist("\\TIMIDITY"); + wmPathExpander.addToPathlist(progdir); +#else + wmPathExpander.addToPathlist("/usr/local/lib/timidity"); + wmPathExpander.addToPathlist("/etc/timidity"); + wmPathExpander.addToPathlist("/etc"); +#endif + } + + if (!(fp = wmPathExpander.openFileReader(filename, &lumpnum))) + return NULL; + + if (ismain) + { + if (lumpnum > 0) + { + wmPathExpander.openmode = PathExpander::OM_LUMP; + wmPathExpander.clearPathlist(); // when reading from a PK3 we don't want to use any external path + } + else + { + wmPathExpander.openmode = PathExpander::OM_FILE; + } + } + + + + if (fp == NULL) { _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); return NULL; } - long fsize = file.GetLength(); + long fsize = fp->GetLength(); if (fsize > WM_MAXFILESIZE) { @@ -66,7 +104,8 @@ unsigned char *_WM_BufferFile(const char *filename, unsigned long int *size) return NULL; } - file.Read(data, fsize); + fp->Read(data, fsize); + delete fp; data[fsize] = 0; *size = fsize; return data; diff --git a/src/wildmidi/file_io.h b/src/wildmidi/file_io.h index d38caffbb..550a8a4b2 100644 --- a/src/wildmidi/file_io.h +++ b/src/wildmidi/file_io.h @@ -28,6 +28,6 @@ #define __FILE_IO_H #define WM_MAXFILESIZE 0x1fffffff -extern unsigned char *_WM_BufferFile (const char *filename, unsigned long int *size); +extern unsigned char *_WM_BufferFile (const char *filename, unsigned long int *size, bool mainfile = false); #endif /* __FILE_IO_H */ diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 19e290f6b..eebd42830 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -645,7 +645,7 @@ static int WM_LoadConfig(const char *config_file) { char **line_tokens = NULL; int token_count = 0; - config_buffer = (char *) _WM_BufferFile(config_file, &config_size); + config_buffer = (char *) _WM_BufferFile(config_file, &config_size, true); if (!config_buffer) { WM_FreePatches(); return -1; From e0f9a59a9ac00291abdbda2c19e602a7e29937dd Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 29 Dec 2015 22:14:40 +0100 Subject: [PATCH 45/56] - add WildMidi config file CVAR to menu. --- wadsrc/static/menudef.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index eef2629d3..94de2b96c 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1608,6 +1608,9 @@ OptionMenu AdvSoundOptions Option "Reverb", "timidity_reverb", "OnOff" Option "Chorus", "timidity_chorus", "OnOff" Slider "Relative volume", "timidity_mastervolume", 0, 4, 0.2, 1 + StaticText " " + StaticText "WildMidi", 1 + TextField "WildMidi config file", "wildmidi_config" } /*======================================= From 0cc4bd56d189958f1b7652312de634eea49494e2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 29 Dec 2015 23:18:39 +0100 Subject: [PATCH 46/56] - removed the original WildMidi loader and the main playback function because none of those is actually being used anymore. --- src/wildmidi/wildmidi_lib.cpp | 1374 --------------------------------- src/wildmidi/wildmidi_lib.h | 5 - 2 files changed, 1379 deletions(-) diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index eebd42830..4372f8436 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -151,10 +151,6 @@ struct _event_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; @@ -173,13 +169,6 @@ struct _mdi { 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<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++) { @@ -2005,257 +1986,6 @@ static void do_sysex_roland_reset(struct _mdi *mdi, struct _event_data *data) { UNUSED(data); /* NOOP, to please the compiler gods */ } -static void WM_ResetToStart(midi * handle) { - struct _mdi *mdi = (struct _mdi *) handle; - - mdi->current_event = mdi->events; - mdi->samples_to_mix = 0; - mdi->info.current_sample = 0; - - do_sysex_roland_reset(mdi, NULL); -} - -static int midi_setup_noteoff(struct _mdi *mdi, unsigned char channel, - unsigned char note, unsigned char velocity) { - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].do_event = *do_note_off; - mdi->events[mdi->event_count - 1].event_data.channel = channel; - mdi->events[mdi->event_count - 1].event_data.data = (note << 8) - | velocity; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = *do_note_off; - mdi->events[mdi->event_count].event_data.channel = channel; - mdi->events[mdi->event_count].event_data.data = (note << 8) | velocity; - mdi->events[mdi->event_count].samples_to_next = 0; - mdi->event_count++; - } - return 0; -} - -static int midi_setup_noteon(struct _mdi *mdi, unsigned char channel, - unsigned char note, unsigned char velocity) { - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].do_event = *do_note_on; - mdi->events[mdi->event_count - 1].event_data.channel = channel; - mdi->events[mdi->event_count - 1].event_data.data = (note << 8) - | velocity; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = *do_note_on; - mdi->events[mdi->event_count].event_data.channel = channel; - mdi->events[mdi->event_count].event_data.data = (note << 8) | velocity; - mdi->events[mdi->event_count].samples_to_next = 0; - mdi->event_count++; - } - - if (mdi->channel[channel].isdrum) - load_patch(mdi, ((mdi->channel[channel].bank << 8) | (note | 0x80))); - return 0; -} - -static int midi_setup_aftertouch(struct _mdi *mdi, unsigned char channel, - unsigned char note, unsigned char pressure) { - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].do_event = *do_aftertouch; - mdi->events[mdi->event_count - 1].event_data.channel = channel; - mdi->events[mdi->event_count - 1].event_data.data = (note << 8) - | pressure; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = *do_aftertouch; - mdi->events[mdi->event_count].event_data.channel = channel; - mdi->events[mdi->event_count].event_data.data = (note << 8) | pressure; - mdi->events[mdi->event_count].samples_to_next = 0; - mdi->event_count++; - } - return 0; -} - -static int midi_setup_control(struct _mdi *mdi, unsigned char channel, - unsigned char controller, unsigned char setting) { - void (*tmp_event)(struct _mdi *mdi, struct _event_data *data) = NULL; - - switch (controller) { - case 0: - tmp_event = *do_control_bank_select; - mdi->channel[channel].bank = setting; - break; - case 6: - tmp_event = *do_control_data_entry_course; - break; - case 7: - tmp_event = *do_control_channel_volume; - mdi->channel[channel].volume = setting; - break; - case 8: - tmp_event = *do_control_channel_balance; - break; - case 10: - tmp_event = *do_control_channel_pan; - break; - case 11: - tmp_event = *do_control_channel_expression; - break; - case 38: - tmp_event = *do_control_data_entry_fine; - break; - case 64: - tmp_event = *do_control_channel_hold; - break; - case 96: - tmp_event = *do_control_data_increment; - break; - case 97: - tmp_event = *do_control_data_decrement; - break; - case 98: - case 99: - tmp_event = *do_control_non_registered_param; - break; - case 100: - tmp_event = *do_control_registered_param_fine; - break; - case 101: - tmp_event = *do_control_registered_param_course; - break; - case 120: - tmp_event = *do_control_channel_sound_off; - break; - case 121: - tmp_event = *do_control_channel_controllers_off; - break; - case 123: - tmp_event = *do_control_channel_notes_off; - break; - default: - return 0; - } - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].do_event = tmp_event; - mdi->events[mdi->event_count - 1].event_data.channel = channel; - mdi->events[mdi->event_count - 1].event_data.data = setting; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = tmp_event; - mdi->events[mdi->event_count].event_data.channel = channel; - mdi->events[mdi->event_count].event_data.data = setting; - mdi->events[mdi->event_count].samples_to_next = 0; - mdi->event_count++; - } - return 0; -} - -static int midi_setup_patch(struct _mdi *mdi, unsigned char channel, - unsigned char patch) { - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].do_event = *do_patch; - mdi->events[mdi->event_count - 1].event_data.channel = channel; - mdi->events[mdi->event_count - 1].event_data.data = patch; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = *do_patch; - mdi->events[mdi->event_count].event_data.channel = channel; - mdi->events[mdi->event_count].event_data.data = patch; - mdi->events[mdi->event_count].samples_to_next = 0; - mdi->event_count++; - } - if (mdi->channel[channel].isdrum) { - mdi->channel[channel].bank = patch; - } else { - load_patch(mdi, ((mdi->channel[channel].bank << 8) | patch)); - mdi->channel[channel].patch = get_patch_data( - ((mdi->channel[channel].bank << 8) | patch)); - } - return 0; -} - -static int midi_setup_channel_pressure(struct _mdi *mdi, unsigned char channel, - unsigned char pressure) { - - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].do_event = *do_channel_pressure; - mdi->events[mdi->event_count - 1].event_data.channel = channel; - mdi->events[mdi->event_count - 1].event_data.data = pressure; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = *do_channel_pressure; - mdi->events[mdi->event_count].event_data.channel = channel; - mdi->events[mdi->event_count].event_data.data = pressure; - mdi->events[mdi->event_count].samples_to_next = 0; - mdi->event_count++; - } - - return 0; -} - -static int midi_setup_pitch(struct _mdi *mdi, unsigned char channel, - unsigned short pitch) { - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].do_event = *do_pitch; - mdi->events[mdi->event_count - 1].event_data.channel = channel; - mdi->events[mdi->event_count - 1].event_data.data = pitch; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = *do_pitch; - mdi->events[mdi->event_count].event_data.channel = channel; - mdi->events[mdi->event_count].event_data.data = pitch; - mdi->events[mdi->event_count].samples_to_next = 0; - mdi->event_count++; - } - return 0; -} - -static int midi_setup_sysex_roland_drum_track(struct _mdi *mdi, - unsigned char channel, unsigned short setting) { - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].do_event = - *do_sysex_roland_drum_track; - mdi->events[mdi->event_count - 1].event_data.channel = channel; - mdi->events[mdi->event_count - 1].event_data.data = setting; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = *do_sysex_roland_drum_track; - mdi->events[mdi->event_count].event_data.channel = channel; - mdi->events[mdi->event_count].event_data.data = setting; - mdi->events[mdi->event_count].samples_to_next = 0; - mdi->event_count++; - } - - if (setting > 0) { - mdi->channel[channel].isdrum = 1; - } else { - mdi->channel[channel].isdrum = 0; - } - - return 0; -} - -static int midi_setup_sysex_roland_reset(struct _mdi *mdi) { - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].do_event = *do_sysex_roland_reset; - mdi->events[mdi->event_count - 1].event_data.channel = 0; - mdi->events[mdi->event_count - 1].event_data.data = 0; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = *do_sysex_roland_reset; - mdi->events[mdi->event_count].event_data.channel = 0; - mdi->events[mdi->event_count].event_data.data = 0; - mdi->events[mdi->event_count].samples_to_next = 0; - mdi->event_count++; - } - return 0; -} - static int add_handle(void * handle) { struct _hndl *tmp_handle = NULL; @@ -2299,15 +2029,6 @@ Init_MDI(void) { 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; @@ -2341,7 +2062,6 @@ static void freeMDI(struct _mdi *mdi) { free(mdi->patches); } - free(mdi->events); free(mdi->tmp_info); _WM_free_reverb(mdi->reverb); free(mdi->mix_buffer); @@ -2392,807 +2112,6 @@ static unsigned long int get_decay_samples(struct _patch *patch, unsigned char n return decay_samples; } -static struct _mdi * -WM_ParseNewMidi(unsigned char *midi_data, unsigned int midi_size) { - struct _mdi *mdi; - unsigned int tmp_val; - unsigned int midi_type; - unsigned int track_size; - unsigned char **tracks; - unsigned int end_of_tracks = 0; - unsigned int no_tracks; - unsigned int i; - unsigned int divisions = 96; - unsigned int tempo = 500000; - float samples_per_delta_f = 0.0; - float microseconds_per_pulse = 0.0; - float pulses_per_second = 0.0; - - unsigned long int sample_count = 0; - float sample_count_tmp = 0; - float sample_remainder = 0; - unsigned char *sysex_store = NULL; - unsigned long int sysex_store_len = 0; - - unsigned long int *track_delta; - unsigned char *track_end; - unsigned long int smallest_delta = 0; - unsigned long int subtract_delta = 0; - unsigned long int tmp_length = 0; - unsigned char current_event = 0; - unsigned char current_event_ch = 0; - unsigned char *running_event; - unsigned long int decay_samples = 0; - - if (memcmp(midi_data, "RIFF", 4) == 0) { - midi_data += 20; - midi_size -= 20; - } - if (memcmp(midi_data, "MThd", 4)) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MIDI, NULL, 0); - return NULL; - } - midi_data += 4; - midi_size -= 4; - - if (midi_size < 10) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); - return NULL; - } - - /* - * Get Midi Header Size - must always be 6 - */ - tmp_val = *midi_data++ << 24; - tmp_val |= *midi_data++ << 16; - tmp_val |= *midi_data++ << 8; - tmp_val |= *midi_data++; - midi_size -= 4; - if (tmp_val != 6) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, NULL, 0); - return NULL; - } - - /* - * Get Midi Format - we only support 0, 1 & 2 - */ - tmp_val = *midi_data++ << 8; - tmp_val |= *midi_data++; - midi_size -= 2; - if (tmp_val > 2) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0); - return NULL; - } - midi_type = tmp_val; - - /* - * Get No. of Tracks - */ - tmp_val = *midi_data++ << 8; - tmp_val |= *midi_data++; - midi_size -= 2; - if (tmp_val < 1) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(no tracks)", 0); - return NULL; - } - no_tracks = tmp_val; - - /* - * Check that type 0 midi file has only 1 track - */ - if ((midi_type == 0) && (no_tracks > 1)) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(expected 1 track for type 0 midi file, found more)", 0); - return NULL; - } - - /* - * Get Divisions - */ - divisions = *midi_data++ << 8; - divisions |= *midi_data++; - midi_size -= 2; - if (divisions & 0x00008000) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0); - return NULL; - } - - if ((WM_MixerOptions & WM_MO_WHOLETEMPO)) { - float bpm_f = (float) (60000000 / tempo); - tempo = 60000000 / (unsigned long int) bpm_f; - } else if ((WM_MixerOptions & WM_MO_ROUNDTEMPO)) { - float bpm_fr = (float) (60000000 / tempo) + 0.5f; - tempo = 60000000 / (unsigned long int) bpm_fr; - } - /* Slow but needed for accuracy */ - microseconds_per_pulse = (float) tempo / (float) divisions; - pulses_per_second = 1000000.0f / microseconds_per_pulse; - samples_per_delta_f = (float) _WM_SampleRate / pulses_per_second; - - mdi = Init_MDI(); - - tracks = (unsigned char**)malloc(sizeof(unsigned char *) * no_tracks); - track_delta = (unsigned long*)malloc(sizeof(unsigned long int) * no_tracks); - track_end = (unsigned char*)malloc(sizeof(unsigned char) * no_tracks); - running_event = (unsigned char*)malloc(sizeof(unsigned char) * no_tracks); - - for (i = 0; i < no_tracks; i++) { - if (midi_size < 8) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); - goto _end; - } - if (memcmp(midi_data, "MTrk", 4) != 0) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing track header)", 0); - goto _end; - } - midi_data += 4; - midi_size -= 4; - - track_size = *midi_data++ << 24; - track_size |= *midi_data++ << 16; - track_size |= *midi_data++ << 8; - track_size |= *midi_data++; - midi_size -= 4; - if (midi_size < track_size) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); - goto _end; - } - if ((midi_data[track_size - 3] != 0xFF) - || (midi_data[track_size - 2] != 0x2F) - || (midi_data[track_size - 1] != 0x00)) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing EOT)", 0); - goto _end; - } - tracks[i] = midi_data; - midi_data += track_size; - midi_size -= track_size; - track_end[i] = 0; - running_event[i] = 0; - track_delta[i] = 0; - decay_samples = 0; - while (*tracks[i] > 0x7F) { - track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F); - tracks[i]++; - } - track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F); - tracks[i]++; - } - - /* - * Handle type 0 & 1 the same, but type 2 differently - */ - switch (midi_type) { - case 0: - case 1: - /* Type 0 & 1 can use the same code */ - while (end_of_tracks != no_tracks) { - smallest_delta = 0; - for (i = 0; i < no_tracks; i++) { - if (track_end[i]) - continue; - - if (track_delta[i]) { - track_delta[i] -= subtract_delta; - if (track_delta[i]) { - if ((!smallest_delta) - || (smallest_delta > track_delta[i])) { - smallest_delta = track_delta[i]; - } - continue; - } - } - do { - if (*tracks[i] > 0x7F) { - current_event = *tracks[i]; - tracks[i]++; - } else { - current_event = running_event[i]; - if (running_event[i] < 0x80) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing event)", 0); - goto _end; - } - } - current_event_ch = current_event & 0x0F; - switch (current_event >> 4) { - case 0x8: - NOTEOFF: midi_setup_noteoff(mdi, current_event_ch, - tracks[i][0], tracks[i][1]); - /* To better calculate samples needed after the end of midi, - * we calculate samples for decay for note off */ - { - unsigned long int tmp_decay_samples = 0; - struct _patch *tmp_patch = NULL; - if (mdi->channel[current_event_ch].isdrum) { - tmp_patch = get_patch_data( - ((mdi->channel[current_event_ch].bank << 8) - | tracks[i][0] | 0x80)); - /* if (tmp_patch == NULL) - printf("Drum patch not loaded 0x%02x on channel %i\n",((mdi->channel[current_event_ch].bank << 8) | tracks[i][0] | 0x80),current_event_ch);*/ - } else { - tmp_patch = mdi->channel[current_event_ch].patch; - /* if (tmp_patch == NULL) - printf("Channel %i patch not loaded\n", current_event_ch);*/ - } - tmp_decay_samples = get_decay_samples(tmp_patch, - tracks[i][0]); - /* if the note off decay is more than the decay we currently tracking then - * we set it to new decay. */ - if (tmp_decay_samples > decay_samples) { - decay_samples = tmp_decay_samples; - } - } - - tracks[i] += 2; - running_event[i] = current_event; - break; - case 0x9: - if (tracks[i][1] == 0) { - goto NOTEOFF; - } - midi_setup_noteon(mdi, (current_event & 0x0F), tracks[i][0], - tracks[i][1]); - tracks[i] += 2; - running_event[i] = current_event; - break; - case 0xA: - midi_setup_aftertouch(mdi, (current_event & 0x0F), - tracks[i][0], tracks[i][1]); - tracks[i] += 2; - running_event[i] = current_event; - break; - case 0xB: - midi_setup_control(mdi, (current_event & 0x0F), - tracks[i][0], tracks[i][1]); - tracks[i] += 2; - running_event[i] = current_event; - break; - case 0xC: - midi_setup_patch(mdi, (current_event & 0x0F), *tracks[i]); - tracks[i]++; - running_event[i] = current_event; - break; - case 0xD: - midi_setup_channel_pressure(mdi, (current_event & 0x0F), - *tracks[i]); - tracks[i]++; - running_event[i] = current_event; - break; - case 0xE: - midi_setup_pitch(mdi, (current_event & 0x0F), - ((tracks[i][1] << 7) | (tracks[i][0] & 0x7F))); - tracks[i] += 2; - running_event[i] = current_event; - break; - case 0xF: /* Meta Event */ - if (current_event == 0xFF) { - if (tracks[i][0] == 0x02) { /* Copyright Event */ - /* Get Length */ - tmp_length = 0; - tracks[i]++; - while (*tracks[i] > 0x7f) { - tmp_length = (tmp_length << 7) - + (*tracks[i] & 0x7f); - tracks[i]++; - } - tmp_length = (tmp_length << 7) - + (*tracks[i] & 0x7f); - /* Copy copyright info in the getinfo struct */ - if (mdi->info.copyright) { - mdi->info.copyright = (char*)realloc( - mdi->info.copyright, - (strlen(mdi->info.copyright) + 1 - + tmp_length + 1)); - strncpy( - &mdi->info.copyright[strlen( - mdi->info.copyright) + 1], - (char *) tracks[i], tmp_length); - mdi->info.copyright[strlen(mdi->info.copyright) - + 1 + tmp_length] = '\0'; - mdi->info.copyright[strlen(mdi->info.copyright)] = '\n'; - - } else { - mdi->info.copyright = (char*)malloc(tmp_length + 1); - strncpy(mdi->info.copyright, (char *) tracks[i], - tmp_length); - mdi->info.copyright[tmp_length] = '\0'; - } - tracks[i] += tmp_length + 1; - } else if ((tracks[i][0] == 0x2F) - && (tracks[i][1] == 0x00)) { - /* End of Track */ - end_of_tracks++; - track_end[i] = 1; - goto NEXT_TRACK; - } else if ((tracks[i][0] == 0x51) - && (tracks[i][1] == 0x03)) { - /* Tempo */ - tempo = (tracks[i][2] << 16) + (tracks[i][3] << 8) - + tracks[i][4]; - tracks[i] += 5; - if (!tempo) - tempo = 500000; - - if ((WM_MixerOptions & WM_MO_WHOLETEMPO)) { - float bpm_f = (float) (60000000 / tempo); - tempo = 60000000 - / (unsigned long int) bpm_f; - } else if ((WM_MixerOptions & WM_MO_ROUNDTEMPO)) { - float bpm_fr = (float) (60000000 / tempo) - + 0.5f; - tempo = 60000000 - / (unsigned long int) bpm_fr; - } - /* Slow but needed for accuracy */ - microseconds_per_pulse = (float) tempo - / (float) divisions; - pulses_per_second = 1000000.0f - / microseconds_per_pulse; - samples_per_delta_f = (float) _WM_SampleRate - / pulses_per_second; - - } else { - tmp_length = 0; - tracks[i]++; - while (*tracks[i] > 0x7f) { - tmp_length = (tmp_length << 7) - + (*tracks[i] & 0x7f); - tracks[i]++; - } - tmp_length = (tmp_length << 7) - + (*tracks[i] & 0x7f); - tracks[i] += tmp_length + 1; - } - } else if ((current_event == 0xF0) - || (current_event == 0xF7)) { - /* Roland Sysex Events */ - unsigned long int sysex_len = 0; - while (*tracks[i] > 0x7F) { - sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F); - tracks[i]++; - } - sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F); - tracks[i]++; - - running_event[i] = 0; - - sysex_store = (unsigned char*)realloc(sysex_store, - sizeof(unsigned char) - * (sysex_store_len + sysex_len)); - memcpy(&sysex_store[sysex_store_len], tracks[i], - sysex_len); - sysex_store_len += sysex_len; - - if (sysex_store[sysex_store_len - 1] == 0xF7) { - unsigned char tmpsysexdata[] = { 0x41, 0x10, 0x42, 0x12 }; - if (memcmp(tmpsysexdata, sysex_store, 4) == 0) { - /* checksum */ - unsigned char sysex_cs = 0; - unsigned int sysex_ofs = 4; - do { - sysex_cs += sysex_store[sysex_ofs]; - if (sysex_cs > 0x7F) { - sysex_cs -= 0x80; - } - sysex_ofs++; - } while (sysex_store[sysex_ofs + 1] != 0xF7); - sysex_cs = 128 - sysex_cs; - /* is roland sysex message valid */ - if (sysex_cs == sysex_store[sysex_ofs]) { - /* process roland sysex event */ - if (sysex_store[4] == 0x40) { - if (((sysex_store[5] & 0xF0) == 0x10) - && (sysex_store[6] == 0x15)) { - /* Roland Drum Track Setting */ - unsigned char sysex_ch = 0x0F - & sysex_store[5]; - if (sysex_ch == 0x00) { - sysex_ch = 0x09; - } else if (sysex_ch <= 0x09) { - sysex_ch -= 1; - } - midi_setup_sysex_roland_drum_track( - mdi, sysex_ch, - sysex_store[7]); - } else if ((sysex_store[5] == 0x00) - && (sysex_store[6] == 0x7F) - && (sysex_store[7] == 0x00)) { - /* Roland GS Reset */ - midi_setup_sysex_roland_reset(mdi); - } - } - } - } - free(sysex_store); - sysex_store = NULL; - sysex_store_len = 0; - } - tracks[i] += sysex_len; - } else { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized meta event)", 0); - goto _end; - } - break; - default: - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized event)", 0); - goto _end; - } - while (*tracks[i] > 0x7F) { - track_delta[i] = (track_delta[i] << 7) - + (*tracks[i] & 0x7F); - tracks[i]++; - } - track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F); - tracks[i]++; - } while (!track_delta[i]); - if ((!smallest_delta) || (smallest_delta > track_delta[i])) { - smallest_delta = track_delta[i]; - } - NEXT_TRACK: continue; - } - - subtract_delta = smallest_delta; - sample_count_tmp = (((float) smallest_delta * samples_per_delta_f) - + sample_remainder); - sample_count = (unsigned long int) sample_count_tmp; - sample_remainder = sample_count_tmp - (float) sample_count; - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].samples_to_next += sample_count; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = NULL; - mdi->events[mdi->event_count].event_data.channel = 0; - mdi->events[mdi->event_count].event_data.data = 0; - mdi->events[mdi->event_count].samples_to_next = sample_count; - mdi->event_count++; - } - mdi->info.approx_total_samples += sample_count; - /* printf("Decay Samples = %lu\n",decay_samples);*/ - if (decay_samples > sample_count) { - decay_samples -= sample_count; - } else { - decay_samples = 0; - } - } - break; - - case 2: /* Type 2 has to be handled differently */ - for (i = 0; i < no_tracks; i++) { - sample_remainder = 0.0; - decay_samples = 0; - track_delta[i] = 0; - do { - if(track_delta[i]) { - sample_count_tmp = (((float) track_delta[i] * samples_per_delta_f) - + sample_remainder); - sample_count = (unsigned long int) sample_count_tmp; - sample_remainder = sample_count_tmp - (float) sample_count; - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].samples_to_next += sample_count; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = NULL; - mdi->events[mdi->event_count].event_data.channel = 0; - mdi->events[mdi->event_count].event_data.data = 0; - mdi->events[mdi->event_count].samples_to_next = sample_count; - mdi->event_count++; - } - mdi->info.approx_total_samples += sample_count; - /* printf("Decay Samples = %lu\n",decay_samples);*/ - if (decay_samples > sample_count) { - decay_samples -= sample_count; - } else { - decay_samples = 0; - } - } - if (*tracks[i] > 0x7F) { - current_event = *tracks[i]; - tracks[i]++; - } else { - current_event = running_event[i]; - if (running_event[i] < 0x80) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing event)", 0); - goto _end; - } - } - current_event_ch = current_event & 0x0F; - switch (current_event >> 4) { - case 0x8: - NOTEOFF2: midi_setup_noteoff(mdi, current_event_ch, - tracks[i][0], tracks[i][1]); - /* To better calculate samples needed after the end of midi, - * we calculate samples for decay for note off */ - { - unsigned long int tmp_decay_samples = 0; - struct _patch *tmp_patch = NULL; - - if (mdi->channel[current_event_ch].isdrum) { - tmp_patch = get_patch_data( - ((mdi->channel[current_event_ch].bank << 8) - | tracks[i][0] | 0x80)); - /* if (tmp_patch == NULL) - printf("Drum patch not loaded 0x%02x on channel %i\n",((mdi->channel[current_event_ch].bank << 8) | tracks[i][0] | 0x80),current_event_ch);*/ - } else { - tmp_patch = mdi->channel[current_event_ch].patch; - /* if (tmp_patch == NULL) - printf("Channel %i patch not loaded\n", current_event_ch);*/ - } - tmp_decay_samples = get_decay_samples(tmp_patch, - tracks[i][0]); - /* if the note off decay is more than the decay we currently tracking then - * we set it to new decay. */ - if (tmp_decay_samples > decay_samples) { - decay_samples = tmp_decay_samples; - } - } - - tracks[i] += 2; - running_event[i] = current_event; - break; - case 0x9: - if (tracks[i][1] == 0) { - goto NOTEOFF2; - } - midi_setup_noteon(mdi, (current_event & 0x0F), tracks[i][0], - tracks[i][1]); - tracks[i] += 2; - running_event[i] = current_event; - break; - case 0xA: - midi_setup_aftertouch(mdi, (current_event & 0x0F), - tracks[i][0], tracks[i][1]); - tracks[i] += 2; - running_event[i] = current_event; - break; - case 0xB: - midi_setup_control(mdi, (current_event & 0x0F), - tracks[i][0], tracks[i][1]); - tracks[i] += 2; - running_event[i] = current_event; - break; - case 0xC: - midi_setup_patch(mdi, (current_event & 0x0F), *tracks[i]); - tracks[i]++; - running_event[i] = current_event; - break; - case 0xD: - midi_setup_channel_pressure(mdi, (current_event & 0x0F), - *tracks[i]); - tracks[i]++; - running_event[i] = current_event; - break; - case 0xE: - midi_setup_pitch(mdi, (current_event & 0x0F), - ((tracks[i][1] << 7) | (tracks[i][0] & 0x7F))); - tracks[i] += 2; - running_event[i] = current_event; - break; - case 0xF: /* Meta Event */ - if (current_event == 0xFF) { - if (tracks[i][0] == 0x02) { /* Copyright Event */ - /* Get Length */ - tmp_length = 0; - tracks[i]++; - while (*tracks[i] > 0x7f) { - tmp_length = (tmp_length << 7) - + (*tracks[i] & 0x7f); - tracks[i]++; - } - tmp_length = (tmp_length << 7) - + (*tracks[i] & 0x7f); - /* Copy copyright info in the getinfo struct */ - if (mdi->info.copyright) { - mdi->info.copyright = (char*)realloc( - mdi->info.copyright, - (strlen(mdi->info.copyright) + 1 - + tmp_length + 1)); - strncpy( - &mdi->info.copyright[strlen( - mdi->info.copyright) + 1], - (char *) tracks[i], tmp_length); - mdi->info.copyright[strlen(mdi->info.copyright) - + 1 + tmp_length] = '\0'; - mdi->info.copyright[strlen(mdi->info.copyright)] = '\n'; - - } else { - mdi->info.copyright = (char*)malloc(tmp_length + 1); - strncpy(mdi->info.copyright, (char *) tracks[i], - tmp_length); - mdi->info.copyright[tmp_length] = '\0'; - } - tracks[i] += tmp_length + 1; - } else if ((tracks[i][0] == 0x2F) - && (tracks[i][1] == 0x00)) { - /* End of Track */ - end_of_tracks++; - track_end[i] = 1; - goto NEXT_TRACK2; - } else if ((tracks[i][0] == 0x51) - && (tracks[i][1] == 0x03)) { - /* Tempo */ - tempo = (tracks[i][2] << 16) + (tracks[i][3] << 8) - + tracks[i][4]; - tracks[i] += 5; - if (!tempo) - tempo = 500000; - - if ((WM_MixerOptions & WM_MO_WHOLETEMPO)) { - float bpm_f = (float) (60000000 / tempo); - tempo = 60000000 - / (unsigned long int) bpm_f; - } else if ((WM_MixerOptions & WM_MO_ROUNDTEMPO)) { - float bpm_fr = (float) (60000000 / tempo) - + 0.5f; - tempo = 60000000 - / (unsigned long int) bpm_fr; - } - /* Slow but needed for accuracy */ - microseconds_per_pulse = (float) tempo - / (float) divisions; - pulses_per_second = 1000000.0f - / microseconds_per_pulse; - samples_per_delta_f = (float) _WM_SampleRate - / pulses_per_second; - - } else { - tmp_length = 0; - tracks[i]++; - while (*tracks[i] > 0x7f) { - tmp_length = (tmp_length << 7) - + (*tracks[i] & 0x7f); - tracks[i]++; - } - tmp_length = (tmp_length << 7) - + (*tracks[i] & 0x7f); - tracks[i] += tmp_length + 1; - } - } else if ((current_event == 0xF0) - || (current_event == 0xF7)) { - /* Roland Sysex Events */ - unsigned long int sysex_len = 0; - while (*tracks[i] > 0x7F) { - sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F); - tracks[i]++; - } - sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F); - tracks[i]++; - - running_event[i] = 0; - - sysex_store = (unsigned char*)realloc(sysex_store, - sizeof(unsigned char) - * (sysex_store_len + sysex_len)); - memcpy(&sysex_store[sysex_store_len], tracks[i], - sysex_len); - sysex_store_len += sysex_len; - - if (sysex_store[sysex_store_len - 1] == 0xF7) { - unsigned char tmpsysexdata[] = { 0x41, 0x10, 0x42, 0x12 }; - if (memcmp(tmpsysexdata, sysex_store, 4) == 0) { - /* checksum */ - unsigned char sysex_cs = 0; - unsigned int sysex_ofs = 4; - do { - sysex_cs += sysex_store[sysex_ofs]; - if (sysex_cs > 0x7F) { - sysex_cs -= 0x80; - } - sysex_ofs++; - } while (sysex_store[sysex_ofs + 1] != 0xF7); - sysex_cs = 128 - sysex_cs; - /* is roland sysex message valid */ - if (sysex_cs == sysex_store[sysex_ofs]) { - /* process roland sysex event */ - if (sysex_store[4] == 0x40) { - if (((sysex_store[5] & 0xF0) == 0x10) - && (sysex_store[6] == 0x15)) { - /* Roland Drum Track Setting */ - unsigned char sysex_ch = 0x0F - & sysex_store[5]; - if (sysex_ch == 0x00) { - sysex_ch = 0x09; - } else if (sysex_ch <= 0x09) { - sysex_ch -= 1; - } - midi_setup_sysex_roland_drum_track( - mdi, sysex_ch, - sysex_store[7]); - } else if ((sysex_store[5] == 0x00) - && (sysex_store[6] == 0x7F) - && (sysex_store[7] == 0x00)) { - /* Roland GS Reset */ - midi_setup_sysex_roland_reset(mdi); - } - } - } - } - free(sysex_store); - sysex_store = NULL; - sysex_store_len = 0; - } - tracks[i] += sysex_len; - } else { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized meta event)", 0); - goto _end; - } - break; - default: - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized event)", 0); - goto _end; - } - track_delta[i] = 0; - while (*tracks[i] > 0x7F) { - track_delta[i] = (track_delta[i] << 7) - + (*tracks[i] & 0x7F); - tracks[i]++; - } - track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F); - tracks[i]++; - NEXT_TRACK2: - smallest_delta = track_delta[i]; /* Added just to keep Xcode happy */ - UNUSED(smallest_delta); /* Added to just keep clang happy */ - } while (track_end[i] == 0); - /* - * Add decay at the end of each song - */ - if (decay_samples) { - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->events[mdi->event_count - 1].samples_to_next += decay_samples; - } else { - WM_CheckEventMemoryPool(mdi); - mdi->events[mdi->event_count].do_event = NULL; - mdi->events[mdi->event_count].event_data.channel = 0; - mdi->events[mdi->event_count].event_data.data = 0; - mdi->events[mdi->event_count].samples_to_next = decay_samples; - mdi->event_count++; - } - } - } - break; - - default: break; /* Don't expect to get here, added for completeness */ - } - - if ((mdi->event_count) - && (mdi->events[mdi->event_count - 1].do_event == NULL)) { - mdi->info.approx_total_samples -= - mdi->events[mdi->event_count - 1].samples_to_next; - mdi->event_count--; - } - /* Set total MIDI time to 1/1000's seconds */ - mdi->info.total_midi_time = (mdi->info.approx_total_samples * 1000) - / _WM_SampleRate; - /*mdi->info.approx_total_samples += _WM_SampleRate * 3;*/ - - /* Add additional samples needed for decay */ - mdi->info.approx_total_samples += decay_samples; - /*printf("decay_samples = %lu\n",decay_samples);*/ - - if ((mdi->reverb = _WM_init_reverb(_WM_SampleRate, reverb_room_width, - reverb_room_length, reverb_listen_posx, reverb_listen_posy)) - == NULL) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to init reverb", 0); - goto _end; - } - - mdi->info.current_sample = 0; - mdi->current_event = &mdi->events[0]; - mdi->samples_to_mix = 0; - mdi->note = NULL; - - WM_ResetToStart(mdi); - -_end: free(sysex_store); - free(track_end); - free(track_delta); - free(running_event); - free(tracks); - if (mdi->reverb) return mdi; - freeMDI(mdi); - return NULL; -} - static int *WM_Mix_Linear(midi * handle, int * buffer, unsigned long int count) { struct _mdi *mdi = (struct _mdi *)handle; @@ -3635,107 +2554,6 @@ int *WM_Mix(midi *handle, int *buffer, unsigned long count) } } -static int WM_DoGetOutput(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; - struct _event *event = mdi->current_event; - signed int *tmp_buffer; - signed int *out_buffer; - signed int left_mix, right_mix; - - _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 = (int*)realloc(mdi->mix_buffer, mdi->mix_buffer_size * sizeof(signed int)); - } - tmp_buffer = mdi->mix_buffer; - memset(tmp_buffer, 0, ((size / 2) * sizeof(signed long int))); - out_buffer = tmp_buffer; - - do { - if (!mdi->samples_to_mix) { - 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 (mdi->samples_to_mix > (size >> 2)) { - 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 */ - tmp_buffer = WM_Mix(handle, tmp_buffer, real_samples_to_mix); - - 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 - * =================== - */ - ((short *)buffer)[0] = (short)left_mix; - ((short *)buffer)[1] = (short)right_mix; - buffer += 4; - } - _WM_Unlock(&mdi->lock); - return buffer_used; -} /* * ========================= @@ -3874,69 +2692,6 @@ WM_SYMBOL int WildMidi_Close(midi * handle) { 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; -} - midi *WildMidi_NewMidi() { midi * ret = NULL; @@ -3954,135 +2709,6 @@ midi *WildMidi_NewMidi() { 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 (!mdi->samples_to_mix) { - 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 (mdi->samples_to_mix > count) { - 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 (!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 (buffer == NULL) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, - "(NULL buffer pointer)", 0); - return -1; - } - if (size == 0) { - return 0; - } - if (!!(size % 4)) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, - "(size not a multiple of 4)", 0); - return -1; - } - return WM_DoGetOutput(handle, buffer, size); -} - WM_SYMBOL int WildMidi_SetOption(midi * handle, unsigned short int options, unsigned short int setting) { struct _mdi *mdi; diff --git a/src/wildmidi/wildmidi_lib.h b/src/wildmidi/wildmidi_lib.h index 46a517d90..ec8dea3c1 100644 --- a/src/wildmidi/wildmidi_lib.h +++ b/src/wildmidi/wildmidi_lib.h @@ -56,12 +56,7 @@ 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); WM_SYMBOL int WildMidi_GetSampleRate (void); From 14361d93138e2695d5fc6d7ed83d5e60aae6a09a Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Dec 2015 16:04:26 -0600 Subject: [PATCH 47/56] Remove midi_timiditylike - Did anybody actually use this? Use WildMidi instead if you want something that sounds more like Timidity++ without actually being Timidity++, since not even the old Timidity manages that. --- src/timidity/instrum.cpp | 60 ++----------------------------- src/timidity/instrum_dls.cpp | 1 - src/timidity/instrum_sf2.cpp | 1 - src/timidity/mix.cpp | 69 ++++++------------------------------ src/timidity/playmidi.cpp | 23 +++--------- src/timidity/resample.cpp | 6 ++-- src/timidity/timidity.cpp | 14 ++------ src/timidity/timidity.h | 16 ++------- zdoom.vcproj | 8 +++++ 9 files changed, 33 insertions(+), 165 deletions(-) diff --git a/src/timidity/instrum.cpp b/src/timidity/instrum.cpp index a8c7b5f7c..fdfa4fd64 100644 --- a/src/timidity/instrum.cpp +++ b/src/timidity/instrum.cpp @@ -142,7 +142,7 @@ static void reverse_data(sample_t *sp, int ls, int le) TODO: do reverse loops right */ static Instrument *load_instrument(Renderer *song, const char *name, int percussion, - int panning, int amp, int note_to_use, + int panning, int note_to_use, int strip_loop, int strip_envelope, int strip_tail) { @@ -372,7 +372,6 @@ fail: { sp->modes &= ~(PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD); } - sp->modes |= PATCH_T_NO_LOOP; } if (strip_envelope == 1) @@ -398,37 +397,6 @@ fail: patch_data.EnvelopeOffset[k] = patch_data.EnvelopeOffset[0]; } } - sp->modes |= PATCH_T_NO_ENVELOPE; - } - else if (strip_envelope != 0) - { - /* Have to make a guess. */ - if (!(sp->modes & (PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD))) - { - /* No loop? Then what's there to sustain? No envelope needed either... */ - sp->modes |= PATCH_T_NO_ENVELOPE; - cmsg(CMSG_INFO, VERB_DEBUG, " - No loop, removing sustain and envelope\n"); - } - else if (memcmp(patch_data.EnvelopeRate, "??????", 6) == 0 || patch_data.EnvelopeOffset[GF1_RELEASEC] >= 100) - { - /* Envelope rates all maxed out? Envelope end at a high "offset"? - That's a weird envelope. Take it out. */ - sp->modes |= PATCH_T_NO_ENVELOPE; - cmsg(CMSG_INFO, VERB_DEBUG, " - Weirdness, removing envelope\n"); - } - else if (!(sp->modes & PATCH_SUSTAIN)) - { - /* No sustain? Then no envelope. I don't know if this is - justified, but patches without sustain usually don't need the - envelope either... at least the Gravis ones. They're mostly - drums. I think. */ - sp->modes |= PATCH_T_NO_ENVELOPE; - cmsg(CMSG_INFO, VERB_DEBUG, " - No sustain, removing envelope\n"); - } - } - if (!(sp->modes & PATCH_NO_SRELEASE)) - { // TiMidity thinks that this is an envelope enable flag. - sp->modes |= PATCH_T_NO_ENVELOPE; } for (j = 0; j < 6; j++) @@ -470,29 +438,6 @@ fail: sp->modes |= PATCH_LOOPEN; /* just in case */ } - if (amp != -1) - { - sp->volume = (amp) / 100.f; - } - else - { - /* Try to determine a volume scaling factor for the sample. - This is a very crude adjustment, but things sound more - balanced with it. Still, this should be a runtime option. - (This is ignored unless midi_timiditylike is turned on.) */ - int i; - sample_t maxamp = 0, a; - sample_t *tmp; - for (i = sp->data_length, tmp = sp->data; i; --i) - { - a = fabsf(*tmp++); - if (a > maxamp) - maxamp = a; - } - sp->volume = 1 / maxamp; - cmsg(CMSG_INFO, VERB_DEBUG, " * volume comp: %f\n", sp->volume); - } - /* Then fractional samples */ sp->data_length <<= FRACTION_BITS; sp->loop_start <<= FRACTION_BITS; @@ -653,7 +598,6 @@ static int fill_bank(Renderer *song, int dr, int b) ip = load_instrument(song, bank->tone[i].name, (dr) ? 1 : 0, bank->tone[i].pan, - bank->tone[i].amp, (bank->tone[i].note != -1) ? bank->tone[i].note : ((dr) ? i : -1), (bank->tone[i].strip_loop != -1) ? bank->tone[i].strip_loop : ((dr) ? 1 : -1), (bank->tone[i].strip_envelope != -1) ? bank->tone[i].strip_envelope : ((dr) ? 1 : -1), @@ -731,7 +675,7 @@ void free_instruments() int Renderer::set_default_instrument(const char *name) { Instrument *ip; - if ((ip = load_instrument(this, name, 0, -1, -1, -1, 0, 0, 0)) == NULL) + if ((ip = load_instrument(this, name, 0, -1, -1, 0, 0, 0)) == NULL) { return -1; } diff --git a/src/timidity/instrum_dls.cpp b/src/timidity/instrum_dls.cpp index fe5e4fdcd..286fcb1a3 100644 --- a/src/timidity/instrum_dls.cpp +++ b/src/timidity/instrum_dls.cpp @@ -1141,7 +1141,6 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, sample->loop_start = rgn->wsmp_loop->ulStart / 2; sample->loop_end = sample->loop_start + (rgn->wsmp_loop->ulLength / 2); } - sample->volume = 1.0f; if (sample->modes & PATCH_SUSTAIN) { diff --git a/src/timidity/instrum_sf2.cpp b/src/timidity/instrum_sf2.cpp index c4cf0bc81..01576e64c 100644 --- a/src/timidity/instrum_sf2.cpp +++ b/src/timidity/instrum_sf2.cpp @@ -1441,7 +1441,6 @@ void SFFile::ApplyGeneratorsToRegion(SFGenComposite *gen, SFSample *sfsamp, Rend sp->root_freq = note_to_freq(sp->scale_note); sp->sample_rate = sfsamp->SampleRate; sp->key_group = gen->exclusiveClass; - sp->volume = 1; // Set key scaling if (gen->keynum >= 0 && gen->keynum <= 127) diff --git a/src/timidity/mix.cpp b/src/timidity/mix.cpp index b4fc0f818..f97710326 100644 --- a/src/timidity/mix.cpp +++ b/src/timidity/mix.cpp @@ -29,8 +29,6 @@ #include "templates.h" #include "c_cvars.h" -EXTERN_CVAR(Bool, midi_timiditylike) - namespace Timidity { @@ -78,16 +76,7 @@ void GF1Envelope::Init(Renderer *song, Voice *v) void GF1Envelope::Release(Voice *v) { - if (midi_timiditylike) - { - if (!(v->sample->modes & PATCH_T_NO_ENVELOPE)) - { - stage = GF1_RELEASE; - Recompute(v); - } - // else ... loop was already turned off by the caller - } - else if (!(v->sample->modes & PATCH_NO_SRELEASE) || (v->sample->modes & PATCH_FAST_REL)) + if (!(v->sample->modes & PATCH_NO_SRELEASE) || (v->sample->modes & PATCH_FAST_REL)) { /* ramp out to minimum volume with rate from final release stage */ stage = GF1_RELEASEC+1; @@ -119,23 +108,11 @@ bool GF1Envelope::Recompute(Voice *v) bUpdating = false; v->status &= ~(VOICE_SUSTAINING | VOICE_LPE); v->status |= VOICE_RELEASING; - if (midi_timiditylike) - { /* kill the voice ... or not */ - if (volume <= 0) - { - v->status |= VOICE_STOPPING; - } - return 1; - } - else - { /* play sampled release */ - } + /* play sampled release */ return 0; } - if (newstage == GF1_RELEASE && !(v->status & VOICE_RELEASING) && - ((!midi_timiditylike && (v->sample->modes & PATCH_SUSTAIN)) || - (midi_timiditylike && !(v->sample->modes & PATCH_T_NO_ENVELOPE)))) + if (newstage == GF1_RELEASE && !(v->status & VOICE_RELEASING) && (v->sample->modes & PATCH_SUSTAIN)) { v->status |= VOICE_SUSTAINING; /* Freeze envelope until note turns off. Trumpets want this. */ @@ -161,10 +138,6 @@ bool GF1Envelope::Recompute(Voice *v) bool GF1Envelope::Update(Voice *v) { - if (midi_timiditylike && (v->sample->modes & PATCH_T_NO_ENVELOPE)) - { - return 0; - } volume += increment; if (((increment < 0) && (volume <= target)) || ((increment > 0) && (volume >= target))) { @@ -182,36 +155,14 @@ void GF1Envelope::ApplyToAmp(Voice *v) double env_vol = v->attenuation; double final_amp; - if (midi_timiditylike) - { - final_amp = v->sample->volume * FINAL_MIX_TIMIDITY_SCALE; - if (v->tremolo_phase_increment != 0) - { - env_vol *= v->tremolo_volume; - } - if (!(v->sample->modes & PATCH_T_NO_ENVELOPE)) - { - if (stage > GF1_ATTACK) - { - env_vol *= pow(2.0, volume * (6.0 / (1 << 30)) - 6.0); - } - else - { - env_vol *= volume / float(1 << 30); - } - } - } - else - { - final_amp = FINAL_MIX_SCALE; - if (v->tremolo_phase_increment != 0) - { // [RH] FIXME: This is wrong. Tremolo should offset the - // envelope volume, not scale it. - env_vol *= v->tremolo_volume; - } - env_vol *= volume / float(1 << 30); - env_vol = calc_gf1_amp(env_vol); + final_amp = FINAL_MIX_SCALE; + if (v->tremolo_phase_increment != 0) + { // [RH] FIXME: This is wrong. Tremolo should offset the + // envelope volume, not scale it. + env_vol *= v->tremolo_volume; } + env_vol *= volume / float(1 << 30); + env_vol = calc_gf1_amp(env_vol); env_vol *= final_amp; v->left_mix = float(env_vol * v->left_offset); v->right_mix = float(env_vol * v->right_offset); diff --git a/src/timidity/playmidi.cpp b/src/timidity/playmidi.cpp index 5db4f61bc..7d8294384 100644 --- a/src/timidity/playmidi.cpp +++ b/src/timidity/playmidi.cpp @@ -29,8 +29,6 @@ #include "timidity.h" #include "c_cvars.h" -EXTERN_CVAR(Bool, midi_timiditylike) - namespace Timidity { @@ -118,7 +116,7 @@ void Renderer::recompute_freq(int v) voice[v].sample_increment = (int)(a); } -static BYTE vol_table[] = { +static const BYTE vol_table[] = { 000 /* 000 */, 129 /* 001 */, 145 /* 002 */, 155 /* 003 */, 161 /* 004 */, 166 /* 005 */, 171 /* 006 */, 174 /* 007 */, 177 /* 008 */, 180 /* 009 */, 182 /* 010 */, 185 /* 011 */, @@ -161,16 +159,7 @@ void Renderer::recompute_amp(Voice *v) if (v->sample->type == INST_GUS) { - if (midi_timiditylike) - { - v->attenuation = float(timidityxx_perceived_vol(v->velocity / 127.0) * - timidityxx_perceived_vol(chanvol / 127.0) * - timidityxx_perceived_vol(chanexpr / 127.0)); - } - else - { - v->attenuation = (vol_table[(chanvol * chanexpr) / 127] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f); - } + v->attenuation = (vol_table[(chanvol * chanexpr) / 127] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f); } else { @@ -199,7 +188,7 @@ void Renderer::compute_pan(double pan, int type, float &left_offset, float &righ } else { - if (type == INST_GUS && !midi_timiditylike) + if (type == INST_GUS) { /* Original amp equation looks like this: * calc_gf1_amp(atten + offset) @@ -218,9 +207,7 @@ void Renderer::compute_pan(double pan, int type, float &left_offset, float &righ } else { - /* I have no idea what equation, if any, will reproduce the sc_pan_table - * that TiMidity++ uses, so midi_timiditylike gets the same Equal Power - * Panning as SF2/DLS. + /* Equal Power Panning for SF2/DLS. */ left_offset = (float)sqrt(1 - pan); right_offset = (float)sqrt(pan); @@ -561,7 +548,7 @@ void Renderer::finish_note(int i) v->status &= ~VOICE_SUSTAINING; v->status |= VOICE_RELEASING; - if (!(v->sample->modes & PATCH_NO_SRELEASE) || midi_timiditylike) + if (!(v->sample->modes & PATCH_NO_SRELEASE)) { v->status &= ~VOICE_LPE; /* sampled release */ } diff --git a/src/timidity/resample.cpp b/src/timidity/resample.cpp index 0ee6aa856..6b9bdb9f5 100644 --- a/src/timidity/resample.cpp +++ b/src/timidity/resample.cpp @@ -28,8 +28,6 @@ #include "timidity.h" #include "c_cvars.h" -EXTERN_CVAR(Bool, midi_timiditylike) - namespace Timidity { @@ -522,7 +520,7 @@ sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr) if (vp->vibrato_control_ratio) { - if (vp->status & VOICE_LPE && !(midi_timiditylike && vp->sample->modes & PATCH_T_NO_LOOP)) + if (vp->status & VOICE_LPE) { if (modes & PATCH_BIDIR) return rs_vib_bidir(song->resample_buffer, song->rate, vp, *countptr); @@ -536,7 +534,7 @@ sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr) } else { - if (vp->status & VOICE_LPE && !(midi_timiditylike && vp->sample->modes & PATCH_T_NO_LOOP)) + if (vp->status & VOICE_LPE) { if (modes & PATCH_BIDIR) return rs_bidir(song->resample_buffer, vp, *countptr); diff --git a/src/timidity/timidity.cpp b/src/timidity/timidity.cpp index 1d74c5afc..09b5ae7b5 100644 --- a/src/timidity/timidity.cpp +++ b/src/timidity/timidity.cpp @@ -34,7 +34,6 @@ CVAR(String, midi_config, CONFIG_FILE, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Int, midi_voices, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR(Bool, midi_timiditylike, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(String, gus_patchdir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, midi_dmxgus, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Int, gus_memsize, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -377,7 +376,7 @@ static int read_config_file(const char *name, bool ismain) delete fp; return -2; } - bank->tone[i].note = bank->tone[i].amp = bank->tone[i].pan = + bank->tone[i].note = bank->tone[i].pan = bank->tone[i].fontbank = bank->tone[i].fontpreset = bank->tone[i].fontnote = bank->tone[i].strip_loop = bank->tone[i].strip_envelope = bank->tone[i].strip_tail = -1; @@ -415,14 +414,7 @@ static int read_config_file(const char *name, bool ismain) *cp++ = 0; if (!strcmp(w[j], "amp")) { - k = atoi(cp); - if ((k < 0 || k > MAX_AMPLIFICATION) || (*cp < '0' || *cp > '9')) - { - Printf("%s: line %d: amplification must be between 0 and %d\n", name, line, MAX_AMPLIFICATION); - delete fp; - return -2; - } - bank->tone[i].amp = k; + /* Ignored */ } else if (!strcmp(w[j], "note")) { @@ -676,7 +668,7 @@ int LoadDMXGUS() continue; int val = k % 128; - bank->tone[val].note = bank->tone[val].amp = bank->tone[val].pan = + bank->tone[val].note = bank->tone[val].pan = bank->tone[val].fontbank = bank->tone[val].fontpreset = bank->tone[val].fontnote = bank->tone[val].strip_loop = bank->tone[val].strip_envelope = bank->tone[val].strip_tail = -1; diff --git a/src/timidity/timidity.h b/src/timidity/timidity.h index 0a21a41b9..59ba5f8ad 100644 --- a/src/timidity/timidity.h +++ b/src/timidity/timidity.h @@ -55,10 +55,6 @@ config.h volume level of FMOD's built-in MIDI player. */ #define FINAL_MIX_SCALE 0.5 -/* This value is used instead when midi_timiditylike is turned on, - because TiMidity++ is louder than a GUS. */ -#define FINAL_MIX_TIMIDITY_SCALE 0.3 - /* How many bits to use for the fractional part of sample positions. This affects tonal accuracy. The entire position counter must fit in 32 bits, so with FRACTION_BITS equal to 12, the maximum size of @@ -211,9 +207,6 @@ enum PATCH_SUSTAIN = (1<<5), PATCH_NO_SRELEASE = (1<<6), PATCH_FAST_REL = (1<<7), - - PATCH_T_NO_ENVELOPE = (1<<8), - PATCH_T_NO_LOOP = (1<<9) }; struct Sample @@ -239,8 +232,6 @@ struct Sample short release_vol; } sf2; } envelope; - float - volume; sample_t *data; SDWORD tremolo_sweep_increment, tremolo_phase_increment, @@ -316,11 +307,12 @@ struct Instrument struct ToneBankElement { ToneBankElement() : - note(0), amp(0), pan(0), strip_loop(0), strip_envelope(0), strip_tail(0) + note(0), pan(0), strip_loop(0), strip_envelope(0), strip_tail(0) {} FString name; - int note, amp, pan, fontbank, fontpreset, fontnote, strip_loop, strip_envelope, strip_tail; + int note, pan, fontbank, fontpreset, fontnote; + SBYTE strip_loop, strip_envelope, strip_tail; }; /* A hack to delay instrument loading until after reading the entire MIDI file. */ @@ -608,8 +600,6 @@ const double log_of_2 = 0.69314718055994529; #define calc_gf1_amp(x) (pow(2.0,((x)*16.0 - 16.0))) // Actual GUS equation #define cb_to_amp(x) (pow(10.0, (x) * (1 / -200.0))) // centibels to amp -#define db_to_amp(x) (pow(10.0, (x) * (1 / -20.0))) // decibels to map -#define timidityxx_perceived_vol(x) (pow((x), 1.66096404744)) /* timidity.h diff --git a/zdoom.vcproj b/zdoom.vcproj index a5d7bfb8e..2e425f5b4 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -942,6 +942,10 @@ RelativePath=".\src\parsecontext.cpp" > + + @@ -1479,6 +1483,10 @@ RelativePath=".\src\parsecontext.h" > + + From 900937929e6e045ea568982741491812d092a1b4 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Dec 2015 16:24:16 -0600 Subject: [PATCH 48/56] Use critical sections for WildMidi locking ...because when you're trying to be thread-safe, it's generally a good idea to use mechanisms that work across multiple processor cores. --- src/CMakeLists.txt | 1 - src/wildmidi/lock.cpp | 86 ------------------------------- src/wildmidi/lock.h | 36 ------------- src/wildmidi/wildmidi_lib.cpp | 96 ++++++++++++++++++++--------------- zdoom.vcproj | 8 --- 5 files changed, 54 insertions(+), 173 deletions(-) delete mode 100644 src/wildmidi/lock.cpp delete mode 100644 src/wildmidi/lock.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f1975a8d8..8503fabcf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1138,7 +1138,6 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE 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 diff --git a/src/wildmidi/lock.cpp b/src/wildmidi/lock.cpp deleted file mode 100644 index c5d3fc7da..000000000 --- a/src/wildmidi/lock.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - 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 -#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 deleted file mode 100644 index 6504bbecf..000000000 --- a/src/wildmidi/lock.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - 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/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 4372f8436..fe97ad7e8 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -46,10 +46,10 @@ #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" +#include "critsec.h" #define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\') #ifdef _WIN32 @@ -88,7 +88,7 @@ static int fix_release = 0; static int auto_amp = 0; static int auto_amp_with_amp = 0; -static int patch_lock; +static FCriticalSection patch_lock; struct _channel { unsigned char bank; @@ -149,7 +149,24 @@ struct _event_data { }; struct _mdi { - int lock; + _mdi() + { + samples_to_mix = 0; + midi_master_vol = 0; + memset(&info, 0, sizeof(info)); + tmp_info = NULL; + memset(&channel, 0, sizeof(channel)); + note = NULL; + memset(note_table, 0, sizeof(note_table)); + patches = NULL; + patch_count = 0; + amp = 0; + mix_buffer = NULL; + mix_buffer_size = NULL; + reverb = NULL; + } + + FCriticalSection lock; unsigned long int samples_to_mix; unsigned short midi_master_vol; @@ -177,7 +194,7 @@ static double newt_coeffs[58][58]; /* for start/end of samples */ #define MAX_GAUSS_ORDER 34 /* 34 is as high as we can go before errors crop up */ static double *gauss_table = NULL; /* *gauss_table[1<first_sample) { @@ -535,7 +552,7 @@ static void WM_FreePatches(void) { patch[i] = tmp_patch; } } - _WM_Unlock(&patch_lock); + patch_lock.Leave(); } /* wm_strdup -- adds extra space for appending up to 4 chars */ @@ -1273,27 +1290,27 @@ static struct _patch * get_patch_data(unsigned short patchid) { struct _patch *search_patch; - _WM_Lock(&patch_lock); + patch_lock.Enter(); search_patch = patch[patchid & 0x007F]; if (search_patch == NULL) { - _WM_Unlock(&patch_lock); + patch_lock.Leave(); return NULL; } while (search_patch) { if (search_patch->patchid == patchid) { - _WM_Unlock(&patch_lock); + patch_lock.Leave(); return search_patch; } search_patch = search_patch->next; } if ((patchid >> 8) != 0) { - _WM_Unlock(&patch_lock); + patch_lock.Leave(); return (get_patch_data(patchid & 0x00FF)); } - _WM_Unlock(&patch_lock); + patch_lock.Leave(); return NULL; } @@ -1312,16 +1329,16 @@ static void load_patch(struct _mdi *mdi, unsigned short patchid) { return; } - _WM_Lock(&patch_lock); + patch_lock.Enter(); if (!tmp_patch->loaded) { if (load_sample(tmp_patch) == -1) { - _WM_Unlock(&patch_lock); + patch_lock.Leave(); return; } } if (tmp_patch->first_sample == NULL) { - _WM_Unlock(&patch_lock); + patch_lock.Leave(); return; } @@ -1330,7 +1347,7 @@ static void load_patch(struct _mdi *mdi, unsigned short patchid) { (sizeof(struct _patch*) * mdi->patch_count)); mdi->patches[mdi->patch_count - 1] = tmp_patch; tmp_patch->inuse_count++; - _WM_Unlock(&patch_lock); + patch_lock.Leave(); } static struct _sample * @@ -1338,17 +1355,17 @@ 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); + patch_lock.Enter(); if (sample_patch == NULL) { - _WM_Unlock(&patch_lock); + patch_lock.Leave(); return NULL; } if (sample_patch->first_sample == NULL) { - _WM_Unlock(&patch_lock); + patch_lock.Leave(); return NULL; } if (freq == 0) { - _WM_Unlock(&patch_lock); + patch_lock.Leave(); return sample_patch->first_sample; } @@ -1357,7 +1374,7 @@ get_sample_data(struct _patch *sample_patch, unsigned long int freq) { while (last_sample) { if (freq > last_sample->freq_low) { if (freq < last_sample->freq_high) { - _WM_Unlock(&patch_lock); + patch_lock.Leave(); return last_sample; } else { return_sample = last_sample; @@ -1365,7 +1382,7 @@ get_sample_data(struct _patch *sample_patch, unsigned long int freq) { } last_sample = last_sample->next; } - _WM_Unlock(&patch_lock); + patch_lock.Leave(); return return_sample; } @@ -2021,8 +2038,7 @@ static struct _mdi * Init_MDI(void) { struct _mdi *mdi; - mdi = (struct _mdi*)malloc(sizeof(struct _mdi)); - memset(mdi, 0, (sizeof(struct _mdi))); + mdi = new _mdi; mdi->info.copyright = NULL; mdi->info.mixer_options = WM_MixerOptions; @@ -2044,7 +2060,7 @@ static void freeMDI(struct _mdi *mdi) { unsigned long int i; if (mdi->patch_count != 0) { - _WM_Lock(&patch_lock); + patch_lock.Enter(); for (i = 0; i < mdi->patch_count; i++) { mdi->patches[i]->inuse_count--; if (mdi->patches[i]->inuse_count == 0) { @@ -2058,7 +2074,7 @@ static void freeMDI(struct _mdi *mdi) { mdi->patches[i]->loaded = 0; } } - _WM_Unlock(&patch_lock); + patch_lock.Leave(); free(mdi->patches); } @@ -2554,7 +2570,6 @@ int *WM_Mix(midi *handle, int *buffer, unsigned long count) } } - /* * ========================= * External Functions @@ -2602,9 +2617,6 @@ WM_SYMBOL int WildMidi_Init(const char * config_file, unsigned short int rate, return -1; } _WM_SampleRate = rate; - - gauss_lock = 0; - patch_lock = 0; WM_Initialized = 1; return 0; @@ -2663,7 +2675,7 @@ WM_SYMBOL int WildMidi_Close(midi * handle) { 0); return -1; } - _WM_Lock(&mdi->lock); + mdi->lock.Enter(); if (first_handle->handle == handle) { tmp_handle = first_handle->next; free(first_handle); @@ -2725,17 +2737,17 @@ WM_SYMBOL int WildMidi_SetOption(midi * handle, unsigned short int options, } mdi = (struct _mdi *) handle; - _WM_Lock(&mdi->lock); + mdi->lock.Enter(); if ((!(options & 0x0007)) || (options & 0xFFF8)) { _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", 0); - _WM_Unlock(&mdi->lock); + mdi->lock.Leave(); return -1; } if (setting & 0xFFF8) { _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid setting)", 0); - _WM_Unlock(&mdi->lock); + mdi->lock.Leave(); return -1; } @@ -2763,7 +2775,7 @@ WM_SYMBOL int WildMidi_SetOption(midi * handle, unsigned short int options, _WM_reset_reverb(mdi->reverb); } - _WM_Unlock(&mdi->lock); + mdi->lock.Leave(); return 0; } @@ -2779,12 +2791,12 @@ WildMidi_GetInfo(midi * handle) { 0); return NULL; } - _WM_Lock(&mdi->lock); + mdi->lock.Enter(); 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); + mdi->lock.Leave(); return NULL; } mdi->tmp_info->copyright = NULL; @@ -2799,7 +2811,7 @@ WildMidi_GetInfo(midi * handle) { } else { mdi->tmp_info->copyright = NULL; } - _WM_Unlock(&mdi->lock); + mdi->lock.Leave(); return mdi->tmp_info; } diff --git a/zdoom.vcproj b/zdoom.vcproj index 2e425f5b4..a2587840f 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -2819,10 +2819,6 @@ RelativePath=".\src\wildmidi\gus_pat.h" > - - @@ -2847,10 +2843,6 @@ RelativePath=".\src\wildmidi\gus_pat.cpp" > - - From 545e2f7c69a3648680811e27228a845ecac5398f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Dec 2015 16:30:01 -0600 Subject: [PATCH 49/56] Slap WildMidi onto snd_listmididevices's output for Windows --- src/sound/music_midi_base.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index 048e52e3f..260f635b2 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -168,6 +168,7 @@ CCMD (snd_listmididevices) MIDIOUTCAPS caps; MMRESULT res; + PrintMidiDevice (-6, "WildMidi", MOD_SWSYNTH, 0); #ifdef HAVE_FLUIDSYNTH PrintMidiDevice (-5, "FluidSynth", MOD_SWSYNTH, 0); #endif From 6d2e93254fcea18ea37c6eb21f9ba116084172d1 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Dec 2015 17:14:13 -0600 Subject: [PATCH 50/56] Fix CreateSMF's SysEx writing - It was wrong before. It might still be wrong, but at least it doesn't look obviously wrong anymore. --- src/sound/music_midistream.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 37616b9a6..064606d0f 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -1196,9 +1196,15 @@ void MIDIStreamer::CreateSMF(TArray &file, int looplimit) len--; file.Push(MIDI_SYSEX); WriteVarLen(file, len); - memcpy(&file[file.Reserve(len - 1)], bytes, len); - running_status = 255; + memcpy(&file[file.Reserve(len)], bytes + 1, len); } + else + { + file.Push(MIDI_SYSEXEND); + WriteVarLen(file, len); + memcpy(&file[file.Reserve(len)], bytes, len); + } + running_status = 255; } else if (MEVT_EVENTTYPE(event[2]) == 0) { From 92e0bbeee9e2440999990f6e21fa0a1312d21d0f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Dec 2015 17:49:43 -0600 Subject: [PATCH 51/56] Handle WildMidi's supported SysEx messages --- src/wildmidi/wildmidi_lib.cpp | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index fe97ad7e8..68ab89933 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -2918,6 +2918,45 @@ void WildMidi_Renderer::ShortEvent(int status, int parm1, int parm2) void WildMidi_Renderer::LongEvent(const char *data, int len) { + // Check for Roland SysEx + if (len >= 11 && // Must be at least 11 bytes + data[len-1] == 0xF7 && // SysEx end + data[0] == 0xF0 && // SysEx + data[1] == 0x41 && // Roland + data[2] == 0x10 && // Device ID, defaults to 0x10 + data[3] == 0x42 && // Model ID, 0x42 indicates a GS synth + data[4] == 0x12 && // The other end is sending data to us + data[5] == 0x40) // We only care about addresses with this first byte + { + // Calculate checksum + int cksum = 0; + for (int i = 5; i < len - 2; ++i) + { + cksum += data[i]; + } + cksum = 128 - (cksum & 0x7F); + if (data[len-2] == cksum) + { // Check destination address + if (((data[6] & 0xF0) == 0x10) && data[7] == 0x15) + { // Roland drum track setting + int sysex_ch = data[6] & 0x0F; + if (sysex_ch == 0) + { + sysex_ch = 9; + } + else if (sysex_ch <= 9) + { + sysex_ch -= 1; + } + _event_data ev = { sysex_ch, data[8] }; + do_sysex_roland_drum_track((_mdi *)handle, &ev); + } + else if (data[6] == 0x00 && data[7] == 0x7F && data[8] == 0x00) + { // Roland GS reset + do_sysex_roland_reset((_mdi *)handle, NULL); + } + } + } } void WildMidi_Renderer::ComputeOutput(float *fbuffer, int len) From 3ec6ad5018941bde5dd0802afc28f70ffc8674ee Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Dec 2015 22:39:38 -0600 Subject: [PATCH 52/56] Add SysEx retrieval to the MIDI file reader --- src/sound/i_musicinterns.h | 2 +- src/sound/music_smf_midiout.cpp | 53 ++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 03282ef37..dd5fd1606 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -566,7 +566,7 @@ protected: struct TrackInfo; void ProcessInitialMetaEvents (); - DWORD *SendCommand (DWORD *event, TrackInfo *track, DWORD delay); + DWORD *SendCommand (DWORD *event, TrackInfo *track, DWORD delay, ptrdiff_t room, bool &sysex_noroom); TrackInfo *FindNextDue (); BYTE *MusHeader; diff --git a/src/sound/music_smf_midiout.cpp b/src/sound/music_smf_midiout.cpp index d5d307ec7..49fd12502 100644 --- a/src/sound/music_smf_midiout.cpp +++ b/src/sound/music_smf_midiout.cpp @@ -322,7 +322,12 @@ DWORD *MIDISong2::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) // Play all events for this tick. do { - DWORD *new_events = SendCommand(events, TrackDue, time); + bool sysex_noroom = false; + DWORD *new_events = SendCommand(events, TrackDue, time, max_event_p - events, sysex_noroom); + if (sysex_noroom) + { + return events; + } TrackDue = FindNextDue(); if (new_events != events) { @@ -366,12 +371,15 @@ void MIDISong2::AdvanceTracks(DWORD time) // //========================================================================== -DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) +DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptrdiff_t room, bool &sysex_noroom) { DWORD len; BYTE event, data1 = 0, data2 = 0; int i; + sysex_noroom = false; + size_t start_p = track->TrackP; + CHECK_FINISHED event = track->TrackBegin[track->TrackP++]; CHECK_FINISHED @@ -588,13 +596,44 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) } else { - // Skip SysEx events just because I don't want to bother with them. - // The old MIDI player ignored them too, so this won't break - // anything that played before. + // SysEx events could potentially not have enough room in the buffer... if (event == MIDI_SYSEX || event == MIDI_SYSEXEND) { - len = track->ReadVarLen (); - track->TrackP += len; + len = track->ReadVarLen(); + if (len >= (MAX_EVENTS-1)*3*4) + { // This message will never fit. Throw it away. + track->TrackP += len; + } + else if (len + 12 >= (size_t)room * 4) + { // Not enough room left in this buffer. Backup and wait for the next one. + track->TrackP = start_p; + sysex_noroom = true; + return events; + } + else + { + events[0] = delay; + events[1] = 0; + BYTE *msg = (BYTE *)&events[3]; + if (event == MIDI_SYSEX) + { // Need to add the SysEx marker to the message. + events[2] = (MEVT_LONGMSG << 24) | (len + 1); + *msg++ = MIDI_SYSEX; + } + else + { + events[2] = (MEVT_LONGMSG << 24) | len; + } + memcpy(msg, &track->TrackBegin[track->TrackP], len); + msg += len; + // Must pad with 0 + while ((size_t)msg & 3) + { + *msg++ = 0; + } + events = (DWORD *)msg; + track->TrackP += len; + } } else if (event == MIDI_META) { From 6c13ba40ac57136d63ba617f6e5f48a191a36696 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 30 Dec 2015 01:09:11 -0600 Subject: [PATCH 53/56] Fix for __unix__ compilation -- section not changed with the rest of fe2dcfd588596d53 --- src/timidity/instrum.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timidity/instrum.cpp b/src/timidity/instrum.cpp index fdfa4fd64..fa9360267 100644 --- a/src/timidity/instrum.cpp +++ b/src/timidity/instrum.cpp @@ -168,7 +168,7 @@ static Instrument *load_instrument(Renderer *song, const char *name, int percuss { #ifdef __unix__ // Windows isn't case-sensitive. tmp.ToUpper(); - if ((fp = open_filereader(tmp, openmode, NULL)) == NULL) + if ((fp = pathExpander.openFileReader(tmp, NULL)) == NULL) #endif { noluck = true; From 1def61e3e37e9bd0baf23102e9cc19f915e72859 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 30 Dec 2015 10:14:18 +0100 Subject: [PATCH 54/56] - allow changing the reverb and resampling mode setting for WildMidi. - fixed: WildMidi did not initialize the reverb data structures. - removed the menu option for midi_timiditylike. --- src/sound/i_music.cpp | 4 ++++ src/sound/i_music.h | 1 + src/sound/i_musicinterns.h | 3 +++ src/sound/music_midistream.cpp | 25 ++++++++++++++++++++++ src/sound/music_wildmidi_mididevice.cpp | 28 ++++++++++++++++++++++++- src/wildmidi/wildmidi_lib.cpp | 15 +++++++++++++ src/wildmidi/wildmidi_lib.h | 1 + wadsrc/static/menudef.txt | 2 +- 8 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 121c0b9fb..a46401f3e 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -286,6 +286,10 @@ void MusInfo::FluidSettingStr(const char *, const char *) { } +void MusInfo::WildMidiSetOption(int opt, int set) +{ +} + FString MusInfo::GetStats() { return "No stats available for this song"; diff --git a/src/sound/i_music.h b/src/sound/i_music.h index ee05d8caf..03e0a3212 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -82,6 +82,7 @@ public: virtual void FluidSettingInt(const char *setting, int value); // FluidSynth settings virtual void FluidSettingNum(const char *setting, double value); // " virtual void FluidSettingStr(const char *setting, const char *value); // " + virtual void WildMidiSetOption(int opt, int set); void Start(bool loop, float rel_vol = -1.f, int subsong = 0); diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index dd5fd1606..275253d84 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -102,6 +102,7 @@ public: virtual void FluidSettingInt(const char *setting, int value); virtual void FluidSettingNum(const char *setting, double value); virtual void FluidSettingStr(const char *setting, const char *value); + virtual void WildMidiSetOption(int opt, int set); virtual bool Preprocess(MIDIStreamer *song, bool looping); virtual FString GetStats(); }; @@ -349,6 +350,7 @@ protected: void HandleEvent(int status, int parm1, int parm2); void HandleLongEvent(const BYTE *data, int len); void ComputeOutput(float *buffer, int len); + void WildMidiSetOption(int opt, int set); }; // FluidSynth implementation of a MIDI device ------------------------------- @@ -447,6 +449,7 @@ public: void FluidSettingInt(const char *setting, int value); void FluidSettingNum(const char *setting, double value); void FluidSettingStr(const char *setting, const char *value); + void WildMidiSetOption(int opt, int set); void CreateSMF(TArray &file, int looplimit=0); protected: diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 064606d0f..a6fb5f4d8 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -626,6 +626,21 @@ void MIDIStreamer::FluidSettingStr(const char *setting, const char *value) } +//========================================================================== +// +// MIDIDeviceStreamer :: WildMidiSetOption +// +//========================================================================== + +void MIDIStreamer::WildMidiSetOption(int opt, int set) +{ + if (MIDI != NULL) + { + MIDI->WildMidiSetOption(opt, set); + } +} + + //========================================================================== // // MIDIStreamer :: OutputVolume @@ -1522,6 +1537,16 @@ void MIDIDevice::FluidSettingStr(const char *setting, const char *value) { } +//========================================================================== +// +// MIDIDevice :: WildMidiSetOption +// +//========================================================================== + +void MIDIDevice::WildMidiSetOption(int opt, int set) +{ +} + //========================================================================== // // MIDIDevice :: GetStats diff --git a/src/sound/music_wildmidi_mididevice.cpp b/src/sound/music_wildmidi_mididevice.cpp index 44c060f26..80bf46e2b 100644 --- a/src/sound/music_wildmidi_mididevice.cpp +++ b/src/sound/music_wildmidi_mididevice.cpp @@ -61,6 +61,17 @@ static FString CurrentConfig; CVAR(String, wildmidi_config, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Int, wildmidi_frequency, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Bool, wildmidi_reverb, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (currSong != NULL) + currSong->WildMidiSetOption(WM_MO_REVERB, *self? WM_MO_REVERB:0); +} + +CUSTOM_CVAR(Bool, wildmidi_enhanced_resampling, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (currSong != NULL) + currSong->WildMidiSetOption(WM_MO_ENHANCED_RESAMPLING, *self? WM_MO_ENHANCED_RESAMPLING:0); +} // CODE -------------------------------------------------------------------- @@ -90,7 +101,7 @@ WildMIDIDevice::WildMIDIDevice() WildMidi_Shutdown(); CurrentConfig = ""; } - if (!WildMidi_Init(wildmidi_config, SampleRate, WM_MO_ENHANCED_RESAMPLING)) + if (!WildMidi_Init(wildmidi_config, SampleRate, 0)) { CurrentConfig = wildmidi_config; } @@ -98,6 +109,10 @@ WildMIDIDevice::WildMIDIDevice() if (CurrentConfig.IsNotEmpty()) { Renderer = new WildMidi_Renderer(); + int flags = 0; + if (wildmidi_enhanced_resampling) flags |= WM_MO_ENHANCED_RESAMPLING; + if (wildmidi_reverb) flags |= WM_MO_REVERB; + Renderer->SetOption(WM_MO_ENHANCED_RESAMPLING | WM_MO_REVERB, flags); } } @@ -204,3 +219,14 @@ FString WildMIDIDevice::GetStats() out.Format("%3d voices", Renderer->GetVoiceCount()); return out; } + +//========================================================================== +// +// WildMIDIDevice :: GetStats +// +//========================================================================== + +void WildMIDIDevice::WildMidiSetOption(int opt, int set) +{ + Renderer->SetOption(opt, set); +} diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 68ab89933..40e462b62 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -2718,6 +2718,16 @@ midi *WildMidi_NewMidi() { ret = NULL; } } + + if ((((_mdi*)ret)->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); + WildMidi_Close(ret); + ret = NULL; + } + + return ret; } @@ -2988,3 +2998,8 @@ int WildMidi_Renderer::GetVoiceCount() } return count; } + +void WildMidi_Renderer::SetOption(int opt, int set) +{ + WildMidi_SetOption((_mdi*)handle, (unsigned short)opt, (unsigned short)set); +} diff --git a/src/wildmidi/wildmidi_lib.h b/src/wildmidi/wildmidi_lib.h index ec8dea3c1..8f021d426 100644 --- a/src/wildmidi/wildmidi_lib.h +++ b/src/wildmidi/wildmidi_lib.h @@ -78,6 +78,7 @@ public: void ComputeOutput(float *buffer, int len); void LoadInstrument(int bank, int percussion, int instr); int GetVoiceCount(); + void SetOption(int opt, int set); private: void *handle; }; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 94de2b96c..244b14691 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1592,7 +1592,6 @@ OptionMenu AdvSoundOptions StaticText "GUS Emulation", 1 TextField "GUS config file", "midi_config" Slider "MIDI voices", "midi_voices", 16, 256, 4, 0 - Option "Emulate TiMidity", "midi_timiditylike", "OnOff" Option "Read DMXGUS lumps", "midi_dmxgus", "OnOff" Option "GUS memory size", "gus_memsize", "GusMemory" StaticText " " @@ -1611,6 +1610,7 @@ OptionMenu AdvSoundOptions StaticText " " StaticText "WildMidi", 1 TextField "WildMidi config file", "wildmidi_config" + Option "Reverb", "wildmidi_reverb", "OnOff" } /*======================================= From aff42a6186073abf7ebf6abbe73c5ecc18cced07 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 30 Dec 2015 10:21:17 +0100 Subject: [PATCH 55/56] - don't look up a lump name in PathExpander if we are only looking for real files. --- src/pathexpander.cpp | 2 +- src/sound/music_wildmidi_mididevice.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pathexpander.cpp b/src/pathexpander.cpp index d8b912b65..bb89a0555 100644 --- a/src/pathexpander.cpp +++ b/src/pathexpander.cpp @@ -74,10 +74,10 @@ FileReader *PathExpander::openFileReader(const char *name, int *plumpnum) current_filename = name; FixPathSeperator(current_filename); - int lumpnum = Wads.CheckNumForFullName(current_filename); if (openmode != OM_FILE) { + int lumpnum = Wads.CheckNumForFullName(current_filename); if (lumpnum >= 0) { fp = Wads.ReopenLumpNum(lumpnum); diff --git a/src/sound/music_wildmidi_mididevice.cpp b/src/sound/music_wildmidi_mididevice.cpp index 80bf46e2b..332a97abe 100644 --- a/src/sound/music_wildmidi_mididevice.cpp +++ b/src/sound/music_wildmidi_mididevice.cpp @@ -226,7 +226,7 @@ FString WildMIDIDevice::GetStats() // //========================================================================== -void WildMIDIDevice::WildMidiSetOption(int opt, int set) -{ - Renderer->SetOption(opt, set); -} +void WildMIDIDevice::WildMidiSetOption(int opt, int set) +{ + Renderer->SetOption(opt, set); +} From a2b377c58039bdf896dea0f79da1d6844b2e135b Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Wed, 30 Dec 2015 10:58:52 +0100 Subject: [PATCH 56/56] - Fixed Clang errors/warnings on wildMIDI code. --- src/sound/music_wildmidi_mididevice.cpp | 2 +- src/wildmidi/wildmidi_lib.cpp | 8 ++++---- src/wildmidi/wildmidi_lib.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sound/music_wildmidi_mididevice.cpp b/src/sound/music_wildmidi_mididevice.cpp index 332a97abe..c3fd674c9 100644 --- a/src/sound/music_wildmidi_mididevice.cpp +++ b/src/sound/music_wildmidi_mididevice.cpp @@ -193,7 +193,7 @@ void WildMIDIDevice::HandleEvent(int status, int parm1, int parm2) void WildMIDIDevice::HandleLongEvent(const BYTE *data, int len) { - Renderer->LongEvent((const char *)data, len); + Renderer->LongEvent(data, len); } //========================================================================== diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 40e462b62..94a446e17 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -162,7 +162,7 @@ struct _mdi { patch_count = 0; amp = 0; mix_buffer = NULL; - mix_buffer_size = NULL; + mix_buffer_size = 0; reverb = NULL; } @@ -2926,7 +2926,7 @@ void WildMidi_Renderer::ShortEvent(int status, int parm1, int parm2) } } -void WildMidi_Renderer::LongEvent(const char *data, int len) +void WildMidi_Renderer::LongEvent(const unsigned char *data, int len) { // Check for Roland SysEx if (len >= 11 && // Must be at least 11 bytes @@ -2949,7 +2949,7 @@ void WildMidi_Renderer::LongEvent(const char *data, int len) { // Check destination address if (((data[6] & 0xF0) == 0x10) && data[7] == 0x15) { // Roland drum track setting - int sysex_ch = data[6] & 0x0F; + unsigned char sysex_ch = data[6] & 0x0F; if (sysex_ch == 0) { sysex_ch = 9; @@ -2958,7 +2958,7 @@ void WildMidi_Renderer::LongEvent(const char *data, int len) { sysex_ch -= 1; } - _event_data ev = { sysex_ch, data[8] }; + _event_data ev = { sysex_ch, static_cast(data[8]) }; do_sysex_roland_drum_track((_mdi *)handle, &ev); } else if (data[6] == 0x00 && data[7] == 0x7F && data[8] == 0x00) diff --git a/src/wildmidi/wildmidi_lib.h b/src/wildmidi/wildmidi_lib.h index 8f021d426..b5aa79849 100644 --- a/src/wildmidi/wildmidi_lib.h +++ b/src/wildmidi/wildmidi_lib.h @@ -74,7 +74,7 @@ public: ~WildMidi_Renderer(); void ShortEvent(int status, int parm1, int parm2); - void LongEvent(const char *data, int len); + void LongEvent(const unsigned char *data, int len); void ComputeOutput(float *buffer, int len); void LoadInstrument(int bank, int percussion, int instr); int GetVoiceCount();