From 2f027504b6716f35739038eb3073fedc9c1a86d1 Mon Sep 17 00:00:00 2001 From: Teemu Piippo Date: Sat, 12 Sep 2015 04:47:49 +0300 Subject: [PATCH 01/75] Show sector action boundaries on the automap like line specials are. --- src/am_map.cpp | 154 +++++++++++++++++++++++++------- src/g_shared/a_sectoraction.cpp | 23 ++++- src/r_defs.h | 5 ++ 3 files changed, 148 insertions(+), 34 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index 045d48589..3430d3182 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -2224,6 +2224,124 @@ bool AM_Check3DFloors(line_t *line) return false; } +// [TP] Check whether a sector can trigger a special that satisfies the provided function. +// If found, specialptr and argsptr will be filled by the special and the arguments +// If needUseActivated is true, the special must be activated by use. +bool AM_checkSectorActions (sector_t *sector, bool (*function)(int, int *), int *specialptr, int **argsptr, bool needUseActivated) +{ + for (ASectorAction* action = sector->SecActTarget; action; action = barrier_cast(action->tracer)) + { + if ((action->IsActivatedByUse() || false == needUseActivated) + && (*function)(action->special, action->args) + && action->CanTrigger (players[consoleplayer].mo)) + { + *specialptr = action->special; + *argsptr = action->args; + return true; + } + } + + return false; +} + +// [TP] Check whether there's a boundary on the provided line for a special that satisfies the provided function. +// It's a boundary if the line can activate the special or the line's bordering sectors can activate it. +// If found, specialptr and argsptr will be filled with special and args if given. +bool AM_checkSpecialBoundary (line_t &line, bool (*function)(int, int*), int *specialptr = NULL, int **argsptr = NULL) +{ + if (specialptr == NULL) + { + static int sink; + specialptr = &sink; + } + + if (argsptr == NULL) + { + static int* sink; + argsptr = &sink; + } + + // Check if the line special qualifies for this + if ((*function)(line.special, line.args) && (line.activation & SPAC_PlayerActivate)) + { + *specialptr = line.special; + *argsptr = line.args; + return true; + } + + // Check sector actions in the line's front sector -- the action has to be use-activated in order to + // show up if this is a one-sided line, because the player cannot trigger sector actions by crossing + // a one-sided line (since that's impossible, duh). + if (AM_checkSectorActions(line.frontsector, function, specialptr, argsptr, line.backsector == NULL)) + return true; + + // If it has a back sector, check sector actions in that. + return (line.backsector && AM_checkSectorActions(line.backsector, function, specialptr, argsptr, false)); +} + +bool AM_isTeleportSpecial (int special, int*) +{ + return (special == Teleport || + special == Teleport_NoFog || + special == Teleport_ZombieChanger || + special == Teleport_Line); +} + +bool AM_isTeleportBoundary (line_t& line) +{ + return AM_checkSpecialBoundary(line, &AM_isTeleportSpecial); +} + +bool AM_isExitSpecial (int special, int*) +{ + return (special == Teleport_NewMap || + special == Teleport_EndGame || + special == Exit_Normal || + special == Exit_Secret); +} + +bool AM_isExitBoundary (line_t& line) +{ + return AM_checkSpecialBoundary(line, &AM_isExitSpecial); +} + +bool AM_isTriggerSpecial (int special, int*) +{ + return special != 0 + && special != Door_Open + && special != Door_Close + && special != Door_CloseWaitOpen + && special != Door_Raise + && special != Door_Animated + && special != Generic_Door; +} + +bool AM_isTriggerBoundary (line_t& line) +{ + return AM_checkSpecialBoundary(line, &AM_isTriggerSpecial); +} + +bool AM_isLockSpecial (int special, int* args) +{ + return special == Door_LockedRaise || + special == ACS_LockedExecute || + special == ACS_LockedExecuteDoor || + (special == Door_Animated && args[3] != 0) || + (special == Generic_Door && args[4] != 0); +} + +bool AM_isLockBoundary (line_t &line, int *lockptr = NULL) +{ + int special; + int *args; + bool result = AM_checkSpecialBoundary(line, &AM_isLockSpecial, &special, &args); + + if (lockptr && result) + *lockptr = (special==Door_LockedRaise || special==Door_Animated) ? args[3] : args[4]; + + return result; +} + //============================================================================= // // Determines visible lines, draws them. @@ -2283,37 +2401,18 @@ void AM_drawWalls (bool allmap) AM_drawMline (&l, c); } - else if ((lines[i].special == Teleport || - lines[i].special == Teleport_NoFog || - lines[i].special == Teleport_ZombieChanger || - lines[i].special == Teleport_Line) && - (lines[i].activation & SPAC_PlayerActivate) && - AMColors.isValid(AMColors.IntraTeleportColor)) + else if (AM_isTeleportBoundary(lines[i]) && AMColors.isValid(AMColors.IntraTeleportColor)) { // intra-level teleporters AM_drawMline(&l, AMColors.IntraTeleportColor); } - else if ((lines[i].special == Teleport_NewMap || - lines[i].special == Teleport_EndGame || - lines[i].special == Exit_Normal || - lines[i].special == Exit_Secret) && - AMColors.isValid(AMColors.InterTeleportColor)) + else if (AM_isExitBoundary(lines[i]) && AMColors.isValid(AMColors.InterTeleportColor)) { // inter-level/game-ending teleporters AM_drawMline(&l, AMColors.InterTeleportColor); } - else if (lines[i].special == Door_LockedRaise || - lines[i].special == ACS_LockedExecute || - lines[i].special == ACS_LockedExecuteDoor || - (lines[i].special == Door_Animated && lines[i].args[3] != 0) || - (lines[i].special == Generic_Door && lines[i].args[4] != 0)) + else if (AM_isLockBoundary(lines[i], &lock)) { if (AMColors.displayLocks) { - int P_GetMapColorForLock(int lock); - - if (lines[i].special==Door_LockedRaise || lines[i].special==Door_Animated) - lock=lines[i].args[3]; - else lock=lines[i].args[4]; - color = P_GetMapColorForLock(lock); AMColor c; @@ -2328,14 +2427,9 @@ void AM_drawWalls (bool allmap) AM_drawMline (&l, AMColors.LockedColor); // locked special } } - else if (am_showtriggerlines && AMColors.isValid(AMColors.SpecialWallColor) && lines[i].special != 0 - && lines[i].special != Door_Open - && lines[i].special != Door_Close - && lines[i].special != Door_CloseWaitOpen - && lines[i].special != Door_Raise - && lines[i].special != Door_Animated - && lines[i].special != Generic_Door - && (lines[i].activation & SPAC_PlayerActivate)) + else if (am_showtriggerlines + && AMColors.isValid(AMColors.SpecialWallColor) + && AM_isTriggerBoundary(lines[i])) { AM_drawMline(&l, AMColors.SpecialWallColor); // wall with special non-door action the player can do } diff --git a/src/g_shared/a_sectoraction.cpp b/src/g_shared/a_sectoraction.cpp index 570818e19..bb582dd28 100644 --- a/src/g_shared/a_sectoraction.cpp +++ b/src/g_shared/a_sectoraction.cpp @@ -39,6 +39,14 @@ IMPLEMENT_CLASS (ASectorAction) +ASectorAction::ASectorAction (bool activatedByUse) : + ActivatedByUse (activatedByUse) {} + +bool ASectorAction::IsActivatedByUse() const +{ + return ActivatedByUse; +} + void ASectorAction::Destroy () { // Remove ourself from this sector's list of actions @@ -102,12 +110,17 @@ bool ASectorAction::DoTriggerAction (AActor *triggerer, int activationType) return false; } +bool ASectorAction::CanTrigger (AActor *triggerer) const +{ + return special && + ((triggerer->player && !(flags & MF_FRIENDLY)) || + ((flags & MF_AMBUSH) && (triggerer->flags2 & MF2_MCROSS)) || + ((flags2 & MF2_DORMANT) && (triggerer->flags2 & MF2_PCROSS))); +} + bool ASectorAction::CheckTrigger (AActor *triggerer) const { - if (special && - ((triggerer->player && !(flags & MF_FRIENDLY)) || - ((flags & MF_AMBUSH) && (triggerer->flags2 & MF2_MCROSS)) || - ((flags2 & MF2_DORMANT) && (triggerer->flags2 & MF2_PCROSS)))) + if (CanTrigger(triggerer)) { bool res = !!P_ExecuteSpecial(special, NULL, triggerer, false, args[0], args[1], args[2], args[3], args[4]); @@ -196,6 +209,7 @@ class ASecActUse : public ASectorAction { DECLARE_CLASS (ASecActUse, ASectorAction) public: + ASecActUse() : ASectorAction (true) {} bool DoTriggerAction (AActor *triggerer, int activationType); }; @@ -214,6 +228,7 @@ class ASecActUseWall : public ASectorAction { DECLARE_CLASS (ASecActUseWall, ASectorAction) public: + ASecActUseWall() : ASectorAction (true) {} bool DoTriggerAction (AActor *triggerer, int activationType); }; diff --git a/src/r_defs.h b/src/r_defs.h index afda92089..ff6957783 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -194,14 +194,19 @@ class ASectorAction : public AActor { DECLARE_CLASS (ASectorAction, AActor) public: + ASectorAction (bool activatedByUse = false); void Destroy (); void BeginPlay (); void Activate (AActor *source); void Deactivate (AActor *source); bool TriggerAction(AActor *triggerer, int activationType); + bool CanTrigger (AActor *triggerer) const; + bool IsActivatedByUse() const; protected: virtual bool DoTriggerAction(AActor *triggerer, int activationType); bool CheckTrigger(AActor *triggerer) const; +private: + bool ActivatedByUse; }; class ASkyViewpoint; From 67a7f48ca3cdd8c6e83a58c27dba6fd3f31fbbac Mon Sep 17 00:00:00 2001 From: Teemu Piippo Date: Sat, 12 Sep 2015 14:02:07 +0300 Subject: [PATCH 02/75] Handle locknumber in boundary checks, check for FS_Execute --- src/am_map.cpp | 56 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index 3430d3182..279deb8a4 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -2323,21 +2323,50 @@ bool AM_isTriggerBoundary (line_t& line) bool AM_isLockSpecial (int special, int* args) { - return special == Door_LockedRaise || - special == ACS_LockedExecute || - special == ACS_LockedExecuteDoor || - (special == Door_Animated && args[3] != 0) || - (special == Generic_Door && args[4] != 0); + return special == Door_LockedRaise + || special == ACS_LockedExecute + || special == ACS_LockedExecuteDoor + || (special == Door_Animated && args[3] != 0) + || (special == Generic_Door && args[4] != 0) + || (special == FS_Execute && args[2] != 0); } bool AM_isLockBoundary (line_t &line, int *lockptr = NULL) { + if (lockptr == NULL) + { + static int sink; + lockptr = &sink; + } + + if (line.locknumber) + { + *lockptr = line.locknumber; + return true; + } + int special; int *args; bool result = AM_checkSpecialBoundary(line, &AM_isLockSpecial, &special, &args); - if (lockptr && result) - *lockptr = (special==Door_LockedRaise || special==Door_Animated) ? args[3] : args[4]; + if (result) + { + switch (special) + { + case FS_Execute: + *lockptr = args[2]; + break; + + case Door_Animated: + case Door_LockedRaise: + *lockptr = args[3]; + break; + + default: + *lockptr = args[4]; + break; + } + } return result; } @@ -2389,18 +2418,7 @@ void AM_drawWalls (bool allmap) AM_drawMline(&l, AMColors.SecretWallColor); else AM_drawMline(&l, AMColors.WallColor); - } - else if (lines[i].locknumber > 0 && AMColors.displayLocks) - { // [Dusk] specials w/ locknumbers - lock = lines[i].locknumber; - color = P_GetMapColorForLock(lock); - - AMColor c; - if (color >= 0) c.FromRGB(RPART(color), GPART(color), BPART(color)); - else c = AMColors[AMColors.LockedColor]; - - AM_drawMline (&l, c); - } + } else if (AM_isTeleportBoundary(lines[i]) && AMColors.isValid(AMColors.IntraTeleportColor)) { // intra-level teleporters AM_drawMline(&l, AMColors.IntraTeleportColor); From 69fd0e6eb484fddd4a55333b4fa74377c6e0e992 Mon Sep 17 00:00:00 2001 From: Teemu Piippo Date: Sat, 12 Sep 2015 14:07:40 +0300 Subject: [PATCH 03/75] Stylistical coherence.. --- src/am_map.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index 279deb8a4..16fd67111 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -2247,7 +2247,7 @@ bool AM_checkSectorActions (sector_t *sector, bool (*function)(int, int *), int // [TP] Check whether there's a boundary on the provided line for a special that satisfies the provided function. // It's a boundary if the line can activate the special or the line's bordering sectors can activate it. // If found, specialptr and argsptr will be filled with special and args if given. -bool AM_checkSpecialBoundary (line_t &line, bool (*function)(int, int*), int *specialptr = NULL, int **argsptr = NULL) +bool AM_checkSpecialBoundary (line_t &line, bool (*function)(int, int *), int *specialptr = NULL, int **argsptr = NULL) { if (specialptr == NULL) { @@ -2257,7 +2257,7 @@ bool AM_checkSpecialBoundary (line_t &line, bool (*function)(int, int*), int *sp if (argsptr == NULL) { - static int* sink; + static int *sink; argsptr = &sink; } @@ -2279,7 +2279,7 @@ bool AM_checkSpecialBoundary (line_t &line, bool (*function)(int, int*), int *sp return (line.backsector && AM_checkSectorActions(line.backsector, function, specialptr, argsptr, false)); } -bool AM_isTeleportSpecial (int special, int*) +bool AM_isTeleportSpecial (int special, int *) { return (special == Teleport || special == Teleport_NoFog || @@ -2287,12 +2287,12 @@ bool AM_isTeleportSpecial (int special, int*) special == Teleport_Line); } -bool AM_isTeleportBoundary (line_t& line) +bool AM_isTeleportBoundary (line_t &line) { return AM_checkSpecialBoundary(line, &AM_isTeleportSpecial); } -bool AM_isExitSpecial (int special, int*) +bool AM_isExitSpecial (int special, int *) { return (special == Teleport_NewMap || special == Teleport_EndGame || @@ -2305,7 +2305,7 @@ bool AM_isExitBoundary (line_t& line) return AM_checkSpecialBoundary(line, &AM_isExitSpecial); } -bool AM_isTriggerSpecial (int special, int*) +bool AM_isTriggerSpecial (int special, int *) { return special != 0 && special != Door_Open @@ -2316,7 +2316,7 @@ bool AM_isTriggerSpecial (int special, int*) && special != Generic_Door; } -bool AM_isTriggerBoundary (line_t& line) +bool AM_isTriggerBoundary (line_t &line) { return AM_checkSpecialBoundary(line, &AM_isTriggerSpecial); } From eff2286bc905c8891928513a7415cb24985365bc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 28 Nov 2015 17:38:40 +0100 Subject: [PATCH 04/75] - 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 4fb48b332b8faab1ab731653a84c941abed27609 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 28 Nov 2015 10:53:34 -0600 Subject: [PATCH 05/75] Added A_CheckProximity. - Checks to see if a certain actor class, in numbers, is close to the actor/pointer via distance, based upon count. Can check for ancestry, disable Z searching, perform less than or equal to instead of greater or equal to, exact counts, check a pointer instead of itself and differentiate between live monsters and dead. --- src/thingdef/thingdef_codeptr.cpp | 99 +++++++++++++++++++++++++++++- wadsrc/static/actors/actor.txt | 1 + wadsrc/static/actors/constants.txt | 11 ++++ 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 14440d884..c01ff687e 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5886,6 +5886,103 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMax) self->RipLevelMax = max; } +//========================================================================== +// +// A_CheckProximity(jump, classname, distance, count, flags, ptr) +// +// Checks to see if a certain actor class is close to the +// actor/pointer within distance, in numbers. +//========================================================================== +enum CPXFflags +{ + CPXF_ANCESTOR = 1, + CPXF_LESSOREQUAL = 1 << 1, + CPXF_NOZ = 1 << 2, + CPXF_COUNTDEAD = 1 << 3, + CPXF_DEADONLY = 1 << 4, + CPXF_EXACT = 1 << 5, +}; +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckProximity) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_STATE(jump, 0); + ACTION_PARAM_CLASS(classname, 1); + ACTION_PARAM_FIXED(distance, 2); + ACTION_PARAM_INT(count, 3); + ACTION_PARAM_INT(flags, 4); + ACTION_PARAM_INT(ptr, 5); + + ACTION_SET_RESULT(false); //No inventory chain results please. + AActor *ref = COPY_AAPTR(self, ptr); + + //We need these to check out. + if (!ref || !jump || !classname || distance <= 0) + return; + + int counter = 0; + bool result = false; + + TThinkerIterator it; + AActor * mo; + + //[MC] Process of elimination, I think, will get through this as quickly and + //efficiently as possible. + while ((mo = it.Next())) + { + if (mo == ref) //Don't count self. + continue; + + //Check inheritance for the classname. Taken partly from CheckClass DECORATE function. + if (flags & CPXF_ANCESTOR) + { + if (!(mo->GetClass()->IsAncestorOf(classname))) + continue; + } + //Otherwise, just check for the regular class name. + else if (classname != mo->GetClass()) + continue; + + //Make sure it's in range and respect the desire for Z or not. + if (P_AproxDistance(ref->x - mo->x, ref->y - mo->y) < distance && + ((flags & CPXF_NOZ) || + ((ref->z > mo->z && ref->z - (mo->z + mo->height) < distance) || + (ref->z <= mo->z && mo->z - (ref->z + ref->height) < distance)))) + { + if (mo->flags6 & MF6_KILLED) + { + if (!(flags & (CPXF_COUNTDEAD | CPXF_DEADONLY))) + continue; + counter++; + } + else + { + if (flags & CPXF_DEADONLY) + continue; + counter++; + } + + //Abort if the number of matching classes nearby is greater, we have obviously succeeded in our goal. + if (counter > count) + { + result = (flags & (CPXF_LESSOREQUAL | CPXF_EXACT)) ? false : true; + break; + } + } + } + + if (counter == count) + result = true; + else if (counter < count) + result = !!((flags & CPXF_LESSOREQUAL) && !(flags & CPXF_EXACT)); + + + + if (result) + { + ACTION_JUMP(jump); + } +} + /*=========================================================================== A_CheckBlock (state block, int flags, int ptr) @@ -5944,4 +6041,4 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock) { ACTION_JUMP(block); } -} \ No newline at end of file +} diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index be4b8b416..5158aa7b5 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -337,6 +337,7 @@ ACTOR Actor native //: Thinker action native A_SetRipperLevel(int level); action native A_SetRipMin(int min); action native A_SetRipMax(int max); + action native A_CheckProximity(state jump, class classname, float distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_CheckBlock(state block, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_CheckSightOrRange(float distance, state label, bool two_dimension = false); action native A_CheckRange(float distance, state label, bool two_dimension = false); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 7f8fbc09e..956ed119f 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -484,6 +484,17 @@ enum QF_WAVE = 1 << 5, }; +// A_CheckProximity flags +enum +{ + CPXF_ANCESTOR = 1, + CPXF_LESSOREQUAL = 1 << 1, + CPXF_NOZ = 1 << 2, + CPXF_COUNTDEAD = 1 << 3, + CPXF_DEADONLY = 1 << 4, + CPXF_EXACT = 1 << 5, +}; + // Flags for A_CheckBlock // These flags only affect the calling actor('s pointer), not the ones being searched. enum From a03b9477295743b038e9af141525e818eda6943c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 28 Nov 2015 20:58:14 +0100 Subject: [PATCH 06/75] - 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 07/75] - 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 08/75] - 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 81f521fe562bd5e562ce08bf2dce33007dd6d9c6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 2 Dec 2015 22:31:27 +0100 Subject: [PATCH 09/75] - fixed: Texture precaching from MAPINFO was broken The code assumed that it had access to the texture manager but that gets initialized after MAPINFO, which means that MAPINFO can only store the texture names and let the precaching code resolve the actual textures. --- src/g_level.h | 2 +- src/g_mapinfo.cpp | 11 ++--------- src/textures/texturemanager.cpp | 3 ++- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/g_level.h b/src/g_level.h index 018737f09..2e8e8e4c5 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -339,7 +339,7 @@ struct level_info_t TArray specialactions; TArray PrecacheSounds; - TArray PrecacheTextures; + TArray PrecacheTextures; level_info_t() { diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index ac938797d..9dace3e23 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1077,15 +1077,8 @@ DEFINE_MAP_OPTION(PrecacheTextures, true) do { parse.sc.MustGetString(); - FTextureID tex = TexMan.CheckForTexture(parse.sc.String, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable|FTextureManager::TEXMAN_TryAny|FTextureManager::TEXMAN_ReturnFirst); - if (!tex.isValid()) - { - parse.sc.ScriptMessage("Unknown texture \"%s\"", parse.sc.String); - } - else - { - info->PrecacheTextures.Push(tex); - } + //the texture manager is not initialized here so all we can do is store the texture's name. + info->PrecacheTextures.Push(parse.sc.String); } while (parse.sc.CheckString(",")); } diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 2944e4200..4fc82ac0d 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -1246,7 +1246,8 @@ void FTextureManager::PrecacheLevel (void) for (unsigned i = 0; i < level.info->PrecacheTextures.Size(); i++) { - hitlist[level.info->PrecacheTextures[i].GetIndex()] |= FTextureManager::HIT_Wall; + FTextureID tex = TexMan.CheckForTexture(level.info->PrecacheTextures[i], FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable|FTextureManager::TEXMAN_TryAny|FTextureManager::TEXMAN_ReturnFirst); + if (tex.Exists()) hitlist[tex.GetIndex()] |= FTextureManager::HIT_Wall; } for (int i = cnt - 1; i >= 0; i--) From f90ce1308e0764ea8c8b3c9f3ca700942b592062 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Thu, 3 Dec 2015 16:40:47 +1300 Subject: [PATCH 10/75] Fix lost focus loosing network data - Prevented focus loss from dropping network data during level transitions - Fixed delay counter underflows --- src/d_net.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index e6b5713f2..780b89382 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -973,7 +973,7 @@ void NetUpdate (void) { I_StartTic (); D_ProcessEvents (); - if ((maketic - gametic) / ticdup >= BACKUPTICS/2-1) + if (pauseext || (maketic - gametic) / ticdup >= BACKUPTICS/2-1) break; // can't hold any more //Printf ("mk:%i ",maketic); @@ -1204,7 +1204,7 @@ void NetUpdate (void) // Send current network delay // The number of tics we just made should be removed from the count. - netbuffer[k++] = ((maketic - newtics - gametic) / ticdup); + netbuffer[k++] = ((maketic - numtics - gametic) / ticdup); if (numtics > 0) { @@ -1810,7 +1810,8 @@ void TryRunTics (void) // If paused, do not eat more CPU time than we need, because it // will all be wasted anyway. - if (pauseext) r_NoInterpolate = true; + if (pauseext) + r_NoInterpolate = true; bool doWait = cl_capfps || r_NoInterpolate /*|| netgame*/; // get real tics @@ -1828,6 +1829,9 @@ void TryRunTics (void) // get available tics NetUpdate (); + if (pauseext) + return; + lowtic = INT_MAX; numplaying = 0; for (i = 0; i < doomcom.numnodes; i++) @@ -1935,7 +1939,7 @@ void TryRunTics (void) C_Ticker (); M_Ticker (); I_GetTime (true); - if (!pauseext) G_Ticker(); + G_Ticker(); gametic++; NetUpdate (); // check for new console commands From 542a1089145f426ac6a5af74961ec3d98ec036dc Mon Sep 17 00:00:00 2001 From: Gaerzi Date: Sat, 5 Dec 2015 00:26:39 +0100 Subject: [PATCH 11/75] 3D floor support for check switch range --- src/p_switch.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/p_switch.cpp b/src/p_switch.cpp index 1ca4654b5..984794c8a 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -177,10 +177,47 @@ bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) if ((TexMan.FindSwitch(side->GetTexture(side_t::top))) != NULL) { + + // Check 3D floors on back side + { + sector_t * back = line->sidedef[1 - sideno]->sector; + for (unsigned i = 0; i < back->e->XFloor.ffloors.Size(); i++) + { + F3DFloor *rover = back->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + if (!(rover->flags & FF_UPPERTEXTURE)) continue; + + if (user->z > rover->top.plane->ZatPoint(checkx, checky) || + user->z + user->height < rover->bottom.plane->ZatPoint(checkx, checky)) + continue; + + // This 3D floor depicts a switch texture in front of the player's eyes + return true; + } + } + return (user->z + user->height > open.top); } else if ((TexMan.FindSwitch(side->GetTexture(side_t::bottom))) != NULL) { + // Check 3D floors on back side + { + sector_t * back = line->sidedef[1 - sideno]->sector; + for (unsigned i = 0; i < back->e->XFloor.ffloors.Size(); i++) + { + F3DFloor *rover = back->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + if (!(rover->flags & FF_LOWERTEXTURE)) continue; + + if (user->z > rover->top.plane->ZatPoint(checkx, checky) || + user->z + user->height < rover->bottom.plane->ZatPoint(checkx, checky)) + continue; + + // This 3D floor depicts a switch texture in front of the player's eyes + return true; + } + } + return (user->z < open.bottom); } else if ((flags & ML_3DMIDTEX) || (TexMan.FindSwitch(side->GetTexture(side_t::mid))) != NULL) From ad0e71942d15dc79f81e9c3d48ea78a9d8e89534 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 6 Dec 2015 09:59:02 +0100 Subject: [PATCH 12/75] - added GetAspectRatio function to ACS. - added a sixth parameter for SetHUDClipRect so that the forced aspect ratio fudging this function performs can be disabled. --- src/g_shared/hudmessages.cpp | 11 ++++++++++- src/g_shared/sbar.h | 4 +++- src/p_acs.cpp | 9 ++++++++- src/p_acs.h | 1 + src/version.h | 2 +- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/g_shared/hudmessages.cpp b/src/g_shared/hudmessages.cpp index f60c8a84e..997021c5f 100644 --- a/src/g_shared/hudmessages.cpp +++ b/src/g_shared/hudmessages.cpp @@ -134,6 +134,7 @@ DHUDMessage::DHUDMessage (FFont *font, const char *text, float x, float y, int h NoWrap = false; ClipX = ClipY = ClipWidth = ClipHeight = 0; WrapWidth = 0; + HandleAspect = true; Top = y; Next = NULL; Lines = NULL; @@ -196,6 +197,14 @@ void DHUDMessage::Serialize (FArchive &arc) NoWrap = false; ClipX = ClipY = ClipWidth = ClipHeight = WrapWidth = 0; } + if (SaveVersion >= 4525) + { + arc << HandleAspect; + } + else + { + HandleAspect = true; + } if (arc.IsLoading ()) { Lines = NULL; @@ -257,7 +266,7 @@ void DHUDMessage::CalcClipCoords(int hudheight) else { screen->VirtualToRealCoordsInt(x, y, w, h, - HUDWidth, hudheight, false, true); + HUDWidth, hudheight, false, HandleAspect); ClipLeft = x; ClipTop = y; ClipRight = x + w; diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h index b8dde5850..b8416dd5d 100644 --- a/src/g_shared/sbar.h +++ b/src/g_shared/sbar.h @@ -94,12 +94,13 @@ public: NoWrap = nowrap; ResetText(SourceText); } - void SetClipRect(int x, int y, int width, int height) + void SetClipRect(int x, int y, int width, int height, bool aspect) { ClipX = x; ClipY = y; ClipWidth = width; ClipHeight = height; + HandleAspect = aspect; } void SetWrapWidth(int wrap) { @@ -119,6 +120,7 @@ protected: int HUDWidth, HUDHeight; int ClipX, ClipY, ClipWidth, ClipHeight, WrapWidth; // in HUD coords int ClipLeft, ClipTop, ClipRight, ClipBot; // in screen coords + bool HandleAspect; EColorRange TextColor; FFont *Font; FRenderStyle Style; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 4f8a0c6c3..4f9e8fd2e 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4442,6 +4442,7 @@ enum EACSFunctions ACSF_GetActorRoll, ACSF_QuakeEx, ACSF_Warp, // 92 + ACSF_GetAspectRatio, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -5315,6 +5316,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args, const ClipRectWidth = argCount > 2 ? args[2] : 0; ClipRectHeight = argCount > 3 ? args[3] : 0; WrapWidth = argCount > 4 ? args[4] : 0; + HandleAspect = argCount > 5 ? !!args[5] : true; break; case ACSF_SetHUDWrapWidth: @@ -5915,10 +5917,14 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return false; } + case ACSF_GetAspectRatio: + return CheckRatio(screen->GetWidth(), screen->GetHeight()); + default: break; } + return 0; } @@ -7854,7 +7860,7 @@ scriptwait: } break; } - msg->SetClipRect(ClipRectLeft, ClipRectTop, ClipRectWidth, ClipRectHeight); + msg->SetClipRect(ClipRectLeft, ClipRectTop, ClipRectWidth, ClipRectHeight, HandleAspect); if (WrapWidth != 0) { msg->SetWrapWidth(WrapWidth); @@ -9466,6 +9472,7 @@ DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr activefont = SmallFont; hudwidth = hudheight = 0; ClipRectLeft = ClipRectTop = ClipRectWidth = ClipRectHeight = WrapWidth = 0; + HandleAspect = true; state = SCRIPT_Running; // Hexen waited one second before executing any open scripts. I didn't realize diff --git a/src/p_acs.h b/src/p_acs.h index d5971e349..3188e46aa 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -891,6 +891,7 @@ protected: int hudwidth, hudheight; int ClipRectLeft, ClipRectTop, ClipRectWidth, ClipRectHeight; int WrapWidth; + bool HandleAspect; FBehavior *activeBehavior; int InModuleScriptNumber; diff --git a/src/version.h b/src/version.h index 913c9bd18..168cb7519 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 4524 +#define SAVEVER 4525 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From 0cb64dd4647603defa71b1e890eb686c662cb09f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 6 Dec 2015 20:55:05 +0100 Subject: [PATCH 13/75] - made character encoding for UFMF/ZDoom namespaces explicit. --- specs/udmf_zdoom.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index d501cb884..e79ae196b 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -27,7 +27,8 @@ II. Implementation Semantics II.A : Storage and Retrieval of Data ------------------------------------ -No changes. +Any TEXTMAP lump in the described namespaces must be encoded in ISO 8859-1 which +as of this writing is the only character encoding supported by ZDoom. ----------------------------------- II.B : Storage Within Archive Files From 72d4c3345302a1a02459005c7f510c5aa1edec55 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 7 Dec 2015 01:18:56 -0500 Subject: [PATCH 14/75] - Removed GetAspectRatio as the implementation was highly fragile. Even if converted to giving the ratio, I have strong concerns about having this function built in without ZDoom supporting arbitrary aspect ratios as the odds of people checking against the hard coded constants seems high. The existing ACS version of this function returns fixed point ratios (because why not) and I fully expected people to use a switch statement when writing it. --- src/p_acs.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 4f9e8fd2e..30733eac8 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4442,7 +4442,6 @@ enum EACSFunctions ACSF_GetActorRoll, ACSF_QuakeEx, ACSF_Warp, // 92 - ACSF_GetAspectRatio, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -5917,9 +5916,6 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return false; } - case ACSF_GetAspectRatio: - return CheckRatio(screen->GetWidth(), screen->GetHeight()); - default: break; } From 964ee6bb23b5b2cb722275cfdee9dd1a625f15cd Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 7 Dec 2015 04:49:40 -0500 Subject: [PATCH 15/75] - Worked around issue where stat doesn't work in v140_xp. Even though the bug was supposedly fixed for awhile now it didn't make it into Update 1. --- src/CMakeLists.txt | 5 +++++ src/win32/i_system.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 782e4bac0..bd353a2ed 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -496,6 +496,11 @@ if( NOT MSVC ) add_definitions( -D__forceinline=inline ) endif( NOT MSVC ) +# Fix stat in v140_xp (broken in RTM and Update 1 so far) +if( MSVC AND MSVC_VERSION EQUAL 1900 AND CMAKE_GENERATOR_TOOLSET STREQUAL "v140_xp" ) + add_definitions( -D_stat64i32=VS14Stat ) +endif( MSVC AND MSVC_VERSION EQUAL 1900 AND CMAKE_GENERATOR_TOOLSET STREQUAL "v140_xp" ) + if( UNIX ) CHECK_LIBRARY_EXISTS( rt clock_gettime "" CLOCK_GETTIME_IN_RT ) if( NOT CLOCK_GETTIME_IN_RT ) diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index ef56c7050..133337af5 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -1726,3 +1726,36 @@ FString I_GetLongPathName(FString shortpath) delete[] buff; return longpath; } + +#if _MSC_VER == 1900 && defined(_USING_V110_SDK71_) +//========================================================================== +// +// VS14Stat +// +// Work around an issue where stat doesn't work with v140_xp. This was +// supposedly fixed, but as of Update 1 continues to not function on XP. +// +//========================================================================== + +#include + +int VS14Stat(const char *path, struct _stat64i32 *buffer) +{ + WIN32_FILE_ATTRIBUTE_DATA data; + if(!GetFileAttributesEx(path, GetFileExInfoStandard, &data)) + return -1; + + buffer->st_ino = 0; + buffer->st_mode = ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR : S_IFREG)| + ((data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD|S_IWRITE); + buffer->st_dev = buffer->st_rdev = 0; + buffer->st_nlink = 1; + buffer->st_uid = 0; + buffer->st_gid = 0; + buffer->st_size = data.nFileSizeLow; + buffer->st_atime = (*(QWORD*)&data.ftLastAccessTime) / 10000000 - 11644473600LL; + buffer->st_mtime = (*(QWORD*)&data.ftLastWriteTime) / 10000000 - 11644473600LL; + buffer->st_ctime = (*(QWORD*)&data.ftCreationTime) / 10000000 - 11644473600LL; + return 0; +} +#endif From 18de376edf8bb4e1d90f8b74c70f4f1080157669 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Mon, 7 Dec 2015 11:19:42 +0100 Subject: [PATCH 16/75] - Fixed lemon trying to free non-allocated memory. This is a regression from commit 24a096fb27eb0959366c605499c7819352cc501c . It happened only the input files were present in the same directory as the executable. --- tools/lemon/lemon.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/lemon/lemon.c b/tools/lemon/lemon.c index 5e8f03a8b..651e43f20 100644 --- a/tools/lemon/lemon.c +++ b/tools/lemon/lemon.c @@ -3061,6 +3061,7 @@ struct lemon *lemp; FILE *in; char *tpltname; char *cp; + Boolean tpltnameinbuf; cp = strrchr(lemp->filename,'.'); if( cp ){ @@ -3070,10 +3071,13 @@ struct lemon *lemp; } if( access(buf,004)==0 ){ tpltname = buf; + tpltnameinbuf = LEMON_TRUE; }else if( access(templatename,004)==0 ){ tpltname = templatename; + tpltnameinbuf = LEMON_TRUE; }else{ tpltname = pathsearch(lemp->argv0,templatename,0); + tpltnameinbuf = LEMON_FALSE; } if( tpltname==0 ){ fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", @@ -3084,11 +3088,11 @@ struct lemon *lemp; in = fopen(tpltname,"rb"); if( in==0 ){ fprintf(stderr,"Can't open the template file \"%s\".\n",templatename); - free(tpltname); + if (tpltnameinbuf == LEMON_FALSE) free(tpltname); lemp->errorcnt++; return 0; } - free(tpltname); + if (tpltnameinbuf == LEMON_FALSE) free(tpltname); return in; } From 7c6237e1343915f26c84010bf668e1133e7a8395 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Thu, 10 Dec 2015 21:24:37 -0600 Subject: [PATCH 17/75] 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 af2ce6ef427d2d175bd5c19403f7bc26dcb5ad02 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 14 Dec 2015 09:06:13 +0100 Subject: [PATCH 18/75] - 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 19/75] 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 20/75] 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 21/75] 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 22/75] 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 23/75] 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 24/75] 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 25/75] 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 26/75] 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 27/75] 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 28/75] 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 29/75] - 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 30/75] - 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 31/75] 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 32/75] - 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 400038643cab557e828881c716d0f08820accd69 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 26 Dec 2015 15:31:59 +0100 Subject: [PATCH 33/75] - 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 34/75] - 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 35/75] 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 36/75] 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 37/75] - 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 38/75] 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 39/75] 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 40/75] 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 41/75] 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 42/75] 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 43/75] - 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 44/75] - 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 45/75] - 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 46/75] - 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 47/75] - 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 48/75] - 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 49/75] - 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 50/75] - 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 51/75] 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 52/75] 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 53/75] 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 54/75] 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 55/75] 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 56/75] 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 57/75] 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 58/75] - 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 59/75] - 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 60/75] - 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(); From be6daf5d787de17ce5fabf36ac2e9c5488d79f1b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 30 Dec 2015 19:13:28 +0100 Subject: [PATCH 61/75] - changed instrument lookup in MUS files. Based on evidence from several songs in Eternal Doom the description in all known documents is wrong. The instruments are not stored in a 16-bit word but in an 8-bit byte, followed by some variable size data. Known variations are: * second byte is 0 - no additional data follows * second byte is 1 - a third byte for the 'bank' value follows. --- src/sound/music_mus_midiout.cpp | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index 5f7d2d503..5f5d39d18 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -43,6 +43,7 @@ #include "doomdef.h" #include "m_swap.h" #include "files.h" +#include "s_sound.h" // MACROS ------------------------------------------------------------------ @@ -211,20 +212,34 @@ bool MUSSong2::CheckDone() void MUSSong2::Precache() { WORD *work = (WORD *)alloca(MusHeader->NumInstruments * sizeof(WORD)); - const WORD *used = (WORD *)MusHeader + sizeof(MUSHeader) / sizeof(WORD); - int i, j; + const BYTE *used = (BYTE *)MusHeader + sizeof(MUSHeader) / sizeof(BYTE); + int i, j, k; - for (i = j = 0; i < MusHeader->NumInstruments; ++i) + for (i = j = k = 0; i < MusHeader->NumInstruments; ++i) { - WORD instr = LittleShort(used[i]); + BYTE instr = used[k++]; + WORD val; if (instr < 128) { - work[j++] = instr; + val = instr; } - else if (used[i] >= 135 && used[i] <= 181) + else if (instr >= 135 && instr <= 181) { // Percussions are 100-based, not 128-based, eh? - work[j++] = instr - 100 + (1 << 14); + val = instr - 100 + (1 << 14); } + + BYTE moreparam = used[k++]; + if (moreparam == 1) + { + BYTE bank = used[k++]; + val |= (bank << 7); + } + else if (moreparam > 0) + { + // No information if this is even valid. Print a message so it can be investigated later + Printf("Unknown instrument data found in music\n"); + } + work[j++] = val; } MIDI->PrecacheInstruments(&work[0], j); } From 3c40d71c20805d96d36089559b1112c9ca682a9e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 30 Dec 2015 20:32:19 +0100 Subject: [PATCH 62/75] - hopefully fixed the MUS precaching for good. According to blzut3, it looks like it is a byte followed by a variable length field. It can be any value 0-15 and will be followed by that many bytes one for each bank used. If the bank count is 0 then it is shorthand for using one bank (bank 0). --- src/sound/music_mus_midiout.cpp | 16 ++++++++-------- src/wildmidi/wildmidi_lib.cpp | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index 5f5d39d18..e81adc07f 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -228,18 +228,18 @@ void MUSSong2::Precache() val = instr - 100 + (1 << 14); } - BYTE moreparam = used[k++]; - if (moreparam == 1) + int numbanks = used[k++]; + if (numbanks > 0) { - BYTE bank = used[k++]; - val |= (bank << 7); + for (int b = 0; b < numbanks; b++) + { + work[j++] = val | used[k++]; + } } - else if (moreparam > 0) + else { - // No information if this is even valid. Print a message so it can be investigated later - Printf("Unknown instrument data found in music\n"); + work[j++] = val; } - work[j++] = val; } MIDI->PrecacheInstruments(&work[0], j); } diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 94a446e17..0a527ec2a 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -2719,14 +2719,14 @@ midi *WildMidi_NewMidi() { } } - 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); + 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; } From c88ed426a805e191742067ad2654871a40bd15e0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 30 Dec 2015 20:39:38 +0100 Subject: [PATCH 63/75] - oops, this somehow lost the shift operator... --- src/sound/music_mus_midiout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index e81adc07f..1ebaa2eb4 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -233,7 +233,7 @@ void MUSSong2::Precache() { for (int b = 0; b < numbanks; b++) { - work[j++] = val | used[k++]; + work[j++] = val | (used[k++] << 7); } } else From 1316120fe4ebf2ce47e6ebe595d20cd10901602d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 31 Dec 2015 14:35:34 +0100 Subject: [PATCH 64/75] - fixed: The MUS precacher did not handle invalid patches well. - increased the valid range of patch values for MUS. According to the original MIDI2MUS code it can handle numbers up to 188, not 181, and at least one track from Eternal Doom uses #183. --- src/sound/music_mus_midiout.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index 1ebaa2eb4..eaf7e4afb 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -223,10 +223,17 @@ void MUSSong2::Precache() { val = instr; } - else if (instr >= 135 && instr <= 181) + else if (instr >= 135 && instr <= 188) { // Percussions are 100-based, not 128-based, eh? val = instr - 100 + (1 << 14); } + else + { + // skip it. + val = used[k++]; + k += val; + continue; + } int numbanks = used[k++]; if (numbanks > 0) From d8af2e558fde7e494bd1f5b636f8989efb8391fb Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 31 Dec 2015 15:28:18 -0600 Subject: [PATCH 65/75] Fix potentiol buffer overrun in MUSSong2::Precache() --- src/sound/music_mus_midiout.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index eaf7e4afb..cc9bc1b69 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -211,11 +211,11 @@ bool MUSSong2::CheckDone() void MUSSong2::Precache() { - WORD *work = (WORD *)alloca(MusHeader->NumInstruments * sizeof(WORD)); + TArray work(MusHeader->NumInstruments); const BYTE *used = (BYTE *)MusHeader + sizeof(MUSHeader) / sizeof(BYTE); - int i, j, k; + int i, k; - for (i = j = k = 0; i < MusHeader->NumInstruments; ++i) + for (i = k = 0; i < MusHeader->NumInstruments; ++i) { BYTE instr = used[k++]; WORD val; @@ -240,15 +240,15 @@ void MUSSong2::Precache() { for (int b = 0; b < numbanks; b++) { - work[j++] = val | (used[k++] << 7); + work.Push(val | (used[k++] << 7)); } } else { - work[j++] = val; + work.Push(val); } } - MIDI->PrecacheInstruments(&work[0], j); + MIDI->PrecacheInstruments(&work[0], work.Size()); } //========================================================================== From 5e975ac9f66a955cf19255555b725ce9170d33da Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 31 Dec 2015 23:03:53 +0100 Subject: [PATCH 66/75] - extended $mididevice to add an optional parameter, which has the following meaning for the different MIDI devices: * OPL: specify the core to use for playing this song * FluidSynth: specify a soundfont that should be used for playing the song. * WildMidi: specify a config file that should be used for playing the song. * Timidity++: specify an executable that should be used for playing the song. At least under Windows this allows using Timidity++ with different configs if the executable and each single config are placed in different directories. * GUS: currently not operational, but should later also specify the config. This will need some work, because right now this is initialized only when the sound system is initialized. * all other: no function. These options should mainly be for end users who want to fine-tune how to play the music. --- src/oplsynth/mlopl_io.cpp | 5 +-- src/oplsynth/music_opl_mididevice.cpp | 4 ++- src/oplsynth/music_opldumper_mididevice.cpp | 1 + src/s_advsound.cpp | 36 ++++++++++++++++----- src/s_sound.cpp | 7 ++-- src/s_sound.h | 13 +++++++- src/sound/i_music.cpp | 31 +++++++++--------- src/sound/i_music.h | 3 +- src/sound/i_musicinterns.h | 23 ++++++------- src/sound/music_fluidsynth_mididevice.cpp | 12 +++++-- src/sound/music_hmi_midiout.cpp | 4 +-- src/sound/music_midi_timidity.cpp | 14 ++++---- src/sound/music_midistream.cpp | 14 ++++---- src/sound/music_mus_midiout.cpp | 4 +-- src/sound/music_mus_opl.cpp | 15 +++++++-- src/sound/music_smf_midiout.cpp | 4 +-- src/sound/music_timidity_mididevice.cpp | 5 +-- src/sound/music_wildmidi_mididevice.cpp | 10 +++--- src/sound/music_xmi_midiout.cpp | 4 +-- src/timidity/timidity.cpp | 3 +- src/timidity/timidity.h | 2 +- 21 files changed, 135 insertions(+), 79 deletions(-) diff --git a/src/oplsynth/mlopl_io.cpp b/src/oplsynth/mlopl_io.cpp index 691463470..4ec41d6c9 100644 --- a/src/oplsynth/mlopl_io.cpp +++ b/src/oplsynth/mlopl_io.cpp @@ -48,6 +48,7 @@ #define HALF_PI (PI*0.5) EXTERN_CVAR(Int, opl_core) +extern int current_opl_core; OPLio::~OPLio() { @@ -323,7 +324,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3) { assert(numchips >= 1 && numchips <= countof(chips)); uint i; - IsOPL3 = (opl_core == 1 || opl_core == 2 || opl_core == 3); + IsOPL3 = (current_opl_core == 1 || current_opl_core == 2 || current_opl_core == 3); memset(chips, 0, sizeof(chips)); if (IsOPL3) @@ -332,7 +333,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3) } for (i = 0; i < numchips; ++i) { - OPLEmul *chip = IsOPL3 ? (opl_core == 1 ? DBOPLCreate(stereo) : (opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo); + OPLEmul *chip = IsOPL3 ? (current_opl_core == 1 ? DBOPLCreate(stereo) : (current_opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo); if (chip == NULL) { break; diff --git a/src/oplsynth/music_opl_mididevice.cpp b/src/oplsynth/music_opl_mididevice.cpp index 79bb10226..aa6cda7ee 100644 --- a/src/oplsynth/music_opl_mididevice.cpp +++ b/src/oplsynth/music_opl_mididevice.cpp @@ -53,6 +53,7 @@ #endif // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- +void OPL_SetCore(const char *args); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -76,8 +77,9 @@ CVAR(Bool, opl_fullpan, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); // //========================================================================== -OPLMIDIDevice::OPLMIDIDevice() +OPLMIDIDevice::OPLMIDIDevice(const char *args) { + OPL_SetCore(args); FullPan = opl_fullpan; FWadLump data = Wads.OpenLumpName("GENMIDI"); OPLloadBank(data); diff --git a/src/oplsynth/music_opldumper_mididevice.cpp b/src/oplsynth/music_opldumper_mididevice.cpp index 46dcdfef2..e6684e1a6 100644 --- a/src/oplsynth/music_opldumper_mididevice.cpp +++ b/src/oplsynth/music_opldumper_mididevice.cpp @@ -262,6 +262,7 @@ protected: //========================================================================== OPLDumperMIDIDevice::OPLDumperMIDIDevice(const char *filename) + : OPLMIDIDevice(NULL) { // Replace the standard OPL device with a disk writer. delete io; diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index cb0f26ae6..8170c1a30 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -1364,16 +1364,36 @@ static void S_AddSNDINFO (int lump) case SI_MidiDevice: { sc.MustGetString(); FName nm = sc.String; + FScanner::SavedPos save = sc.SavePos(); + + sc.SetCMode(true); sc.MustGetString(); - if (sc.Compare("timidity")) MidiDevices[nm] = MDEV_TIMIDITY; - else if (sc.Compare("fmod") || sc.Compare("sndsys")) MidiDevices[nm] = MDEV_SNDSYS; - else if (sc.Compare("standard")) MidiDevices[nm] = MDEV_MMAPI; - else if (sc.Compare("opl")) MidiDevices[nm] = MDEV_OPL; - 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; + MidiDeviceSetting devset; + if (sc.Compare("timidity")) devset.device = MDEV_TIMIDITY; + else if (sc.Compare("fmod") || sc.Compare("sndsys")) devset.device = MDEV_SNDSYS; + else if (sc.Compare("standard")) devset.device = MDEV_MMAPI; + else if (sc.Compare("opl")) devset.device = MDEV_OPL; + else if (sc.Compare("default")) devset.device = MDEV_DEFAULT; + else if (sc.Compare("fluidsynth")) devset.device = MDEV_FLUIDSYNTH; + else if (sc.Compare("gus")) devset.device = MDEV_GUS; + else if (sc.Compare("wildmidi")) devset.device = MDEV_WILDMIDI; else sc.ScriptError("Unknown MIDI device %s\n", sc.String); + + if (sc.CheckString(",")) + { + sc.SetCMode(false); + sc.MustGetString(); + devset.args = sc.String; + } + else + { + // This does not really do what one might expect, because the next token has already been parsed and can be a '$'. + // So in order to continue parsing without C-Mode, we need to reset and parse the last token again. + sc.SetCMode(false); + sc.RestorePos(save); + sc.MustGetString(); + } + MidiDevices[nm] = devset; } break; diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 7b38e816c..f388246a9 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2421,11 +2421,8 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) { int lumpnum = -1; int length = 0; - int device = MDEV_DEFAULT; MusInfo *handle = NULL; - - int *devp = MidiDevices.CheckKey(musicname); - if (devp != NULL) device = *devp; + MidiDeviceSetting *devp = MidiDevices.CheckKey(musicname); // Strip off any leading file:// component. if (strncmp(musicname, "file://", 7) == 0) @@ -2495,7 +2492,7 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) } else { - mus_playing.handle = I_RegisterSong (reader, device); + mus_playing.handle = I_RegisterSong (reader, devp); } } diff --git a/src/s_sound.h b/src/s_sound.h index 16aa8b333..a72f3be0c 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -397,8 +397,19 @@ enum EMidiDevice MDEV_WILDMIDI = 6, }; +struct MidiDeviceSetting +{ + int device; + FString args; + + MidiDeviceSetting() + { + device = MDEV_DEFAULT; + } +}; + typedef TMap MusicAliasMap; -typedef TMap MidiDeviceMap; +typedef TMap MidiDeviceMap; extern MusicAliasMap MusicAliases; extern MidiDeviceMap MidiDevices; diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index a46401f3e..5723a18dc 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -311,21 +311,21 @@ MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate) // //========================================================================== -static MIDIStreamer *CreateMIDIStreamer(FileReader &reader, EMidiDevice devtype, EMIDIType miditype) +static MIDIStreamer *CreateMIDIStreamer(FileReader &reader, EMidiDevice devtype, EMIDIType miditype, const char *args) { switch (miditype) { case MIDI_MUS: - return new MUSSong2(reader, devtype); + return new MUSSong2(reader, devtype, args); case MIDI_MIDI: - return new MIDISong2(reader, devtype); + return new MIDISong2(reader, devtype, args); case MIDI_HMI: - return new HMISong(reader, devtype); + return new HMISong(reader, devtype, args); case MIDI_XMI: - return new XMISong(reader, devtype); + return new XMISong(reader, devtype, args); default: return NULL; @@ -387,7 +387,7 @@ static EMIDIType IdentifyMIDIType(DWORD *id, int size) // //========================================================================== -MusInfo *I_RegisterSong (FileReader *reader, int device) +MusInfo *I_RegisterSong (FileReader *reader, MidiDeviceSetting *device) { MusInfo *info = NULL; const char *fmt; @@ -405,12 +405,6 @@ MusInfo *I_RegisterSong (FileReader *reader, int device) return 0; } -#ifndef _WIN32 - // non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS - if (device == MDEV_MMAPI) - device = MDEV_SNDSYS; -#endif - // Check for gzip compression. Some formats are expected to have players // that can handle it, so it simplifies things if we make all songs // gzippable. @@ -447,10 +441,15 @@ MusInfo *I_RegisterSong (FileReader *reader, int device) EMIDIType miditype = IdentifyMIDIType(id, sizeof(id)); if (miditype != MIDI_NOTMIDI) { - EMidiDevice devtype = (EMidiDevice)device; + EMidiDevice devtype = device == NULL? MDEV_DEFAULT : (EMidiDevice)device->device; +#ifndef _WIN32 + // non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS + if (devtype == MDEV_MMAPI) + devtype = MDEV_SNDSYS; +#endif retry_as_sndsys: - info = CreateMIDIStreamer(*reader, devtype, miditype); + info = CreateMIDIStreamer(*reader, devtype, miditype, device != NULL? device->args.GetChars() : ""); if (info != NULL && !info->IsValid()) { delete info; @@ -464,7 +463,7 @@ retry_as_sndsys: #ifdef _WIN32 if (info == NULL && devtype != MDEV_MMAPI && snd_mididevice >= 0) { - info = CreateMIDIStreamer(*reader, MDEV_MMAPI, miditype); + info = CreateMIDIStreamer(*reader, MDEV_MMAPI, miditype, ""); } #endif } @@ -475,7 +474,7 @@ retry_as_sndsys: (id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL (id[0] == MAKE_ID('A','D','L','I') && *((BYTE *)id + 4) == 'B')) // Martin Fernandez's modified IMF { - info = new OPLMUSSong (*reader); + info = new OPLMUSSong (*reader, device != NULL? device->args.GetChars() : ""); } // Check for game music else if ((fmt = GME_CheckFormat(id[0])) != NULL && fmt[0] != '\0') diff --git a/src/sound/i_music.h b/src/sound/i_music.h index 03e0a3212..514400e5d 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -53,7 +53,8 @@ void I_SetMusicVolume (float volume); // Registers a song handle to song data. class MusInfo; -MusInfo *I_RegisterSong (FileReader *reader, int device); +struct MidiDeviceSetting; +MusInfo *I_RegisterSong (FileReader *reader, MidiDeviceSetting *device); MusInfo *I_RegisterCDSong (int track, int cdid = 0); MusInfo *I_RegisterURLSong (const char *url); diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 275253d84..52364ab58 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -185,7 +185,7 @@ public: class TimidityPPMIDIDevice : public PseudoMIDIDevice { public: - TimidityPPMIDIDevice(); + TimidityPPMIDIDevice(const char *args); ~TimidityPPMIDIDevice(); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -270,7 +270,7 @@ protected: class OPLMIDIDevice : public SoftSynthMIDIDevice, protected OPLmusicBlock { public: - OPLMIDIDevice(); + OPLMIDIDevice(const char *args); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); void Close(); int GetTechnology() const; @@ -303,7 +303,7 @@ namespace Timidity { struct Renderer; } class TimidityMIDIDevice : public SoftSynthMIDIDevice { public: - TimidityMIDIDevice(); + TimidityMIDIDevice(const char *args); ~TimidityMIDIDevice(); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -337,7 +337,7 @@ protected: class WildMIDIDevice : public SoftSynthMIDIDevice { public: - WildMIDIDevice(); + WildMIDIDevice(const char *args); ~WildMIDIDevice(); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -366,7 +366,7 @@ struct fluid_synth_t; class FluidSynthMIDIDevice : public SoftSynthMIDIDevice { public: - FluidSynthMIDIDevice(); + FluidSynthMIDIDevice(const char *args); ~FluidSynthMIDIDevice(); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -431,7 +431,7 @@ protected: class MIDIStreamer : public MusInfo { public: - MIDIStreamer(EMidiDevice type); + MIDIStreamer(EMidiDevice type, const char *args); ~MIDIStreamer(); void MusicVolumeChanged(); @@ -517,6 +517,7 @@ protected: bool CallbackIsThreaded; int LoopLimit; FString DumpFilename; + FString Args; }; // MUS file played with a MIDI stream --------------------------------------- @@ -524,7 +525,7 @@ protected: class MUSSong2 : public MIDIStreamer { public: - MUSSong2(FileReader &reader, EMidiDevice type); + MUSSong2(FileReader &reader, EMidiDevice type, const char *args); ~MUSSong2(); MusInfo *GetOPLDumper(const char *filename); @@ -550,7 +551,7 @@ protected: class MIDISong2 : public MIDIStreamer { public: - MIDISong2(FileReader &reader, EMidiDevice type); + MIDISong2(FileReader &reader, EMidiDevice type, const char *args); ~MIDISong2(); MusInfo *GetOPLDumper(const char *filename); @@ -607,7 +608,7 @@ protected: class HMISong : public MIDIStreamer { public: - HMISong(FileReader &reader, EMidiDevice type); + HMISong(FileReader &reader, EMidiDevice type, const char *args); ~HMISong(); MusInfo *GetOPLDumper(const char *filename); @@ -650,7 +651,7 @@ protected: class XMISong : public MIDIStreamer { public: - XMISong(FileReader &reader, EMidiDevice type); + XMISong(FileReader &reader, EMidiDevice type, const char *args); ~XMISong(); MusInfo *GetOPLDumper(const char *filename); @@ -713,7 +714,7 @@ protected: class OPLMUSSong : public StreamSong { public: - OPLMUSSong (FileReader &reader); + OPLMUSSong (FileReader &reader, const char *args); ~OPLMUSSong (); void Play (bool looping, int subsong); bool IsPlaying (); diff --git a/src/sound/music_fluidsynth_mididevice.cpp b/src/sound/music_fluidsynth_mididevice.cpp index af8fe6667..3be4de56b 100644 --- a/src/sound/music_fluidsynth_mididevice.cpp +++ b/src/sound/music_fluidsynth_mididevice.cpp @@ -255,7 +255,7 @@ CUSTOM_CVAR(Int, fluid_chorus_type, FLUID_CHORUS_DEFAULT_TYPE, CVAR_ARCHIVE|CVAR // //========================================================================== -FluidSynthMIDIDevice::FluidSynthMIDIDevice() +FluidSynthMIDIDevice::FluidSynthMIDIDevice(const char *args) { FluidSynth = NULL; FluidSettings = NULL; @@ -293,7 +293,15 @@ FluidSynthMIDIDevice::FluidSynthMIDIDevice() fluid_reverb_width, fluid_reverb_level); fluid_synth_set_chorus(FluidSynth, fluid_chorus_voices, fluid_chorus_level, fluid_chorus_speed, fluid_chorus_depth, fluid_chorus_type); - if (0 == LoadPatchSets(fluid_patchset)) + + // try loading a patch set that got specified with $mididevice. + int res = 0; + if (args != NULL && *args != 0) + { + res = LoadPatchSets(args); + } + + if (res == 0 && 0 == LoadPatchSets(fluid_patchset)) { #ifdef __unix__ // This is the standard location on Ubuntu. diff --git a/src/sound/music_hmi_midiout.cpp b/src/sound/music_hmi_midiout.cpp index 5fef706dd..ca3ae0905 100644 --- a/src/sound/music_hmi_midiout.cpp +++ b/src/sound/music_hmi_midiout.cpp @@ -128,8 +128,8 @@ extern char MIDI_CommonLengths[15]; // //========================================================================== -HMISong::HMISong (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), Tracks(0) +HMISong::HMISong (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), Tracks(0) { #ifdef _WIN32 if (ExitEvent == NULL) diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index dbc56cc56..9f50b4155 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -72,7 +72,7 @@ CUSTOM_CVAR (Int, timidity_frequency, 22050, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // //========================================================================== -TimidityPPMIDIDevice::TimidityPPMIDIDevice() +TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args) : DiskName("zmid"), #ifdef _WIN32 ReadWavePipe(INVALID_HANDLE_VALUE), WriteWavePipe(INVALID_HANDLE_VALUE), @@ -85,7 +85,13 @@ TimidityPPMIDIDevice::TimidityPPMIDIDevice() #ifndef _WIN32 WavePipe[0] = WavePipe[1] = -1; #endif - + + if (args == NULL || *args == 0) args = timidity_exe; + + CommandLine.Format("%s %s -EFchorus=%s -EFreverb=%s -s%d ", + args, *timidity_extargs, + *timidity_chorus, *timidity_reverb, *timidity_frequency); + if (DiskName == NULL) { Printf(PRINT_BOLD, "Could not create temp music file\n"); @@ -187,10 +193,6 @@ int TimidityPPMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWO Validated = true; #endif // WIN32 - CommandLine.Format("%s %s -EFchorus=%s -EFreverb=%s -s%d ", - *timidity_exe, *timidity_extargs, - *timidity_chorus, *timidity_reverb, *timidity_frequency); - pipeSize = (timidity_pipe * timidity_frequency / 1000) << (timidity_stereo + !timidity_8bit); diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index a6fb5f4d8..579fd7851 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -89,12 +89,12 @@ static const BYTE StaticMIDIhead[] = // //========================================================================== -MIDIStreamer::MIDIStreamer(EMidiDevice type) +MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args) : #ifdef _WIN32 PlayerThread(0), ExitEvent(0), BufferDoneEvent(0), #endif - MIDI(0), Division(0), InitialTempo(500000), DeviceType(type) + MIDI(0), Division(0), InitialTempo(500000), DeviceType(type), Args(args) { #ifdef _WIN32 BufferDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -269,19 +269,19 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const #ifdef HAVE_FLUIDSYNTH case MDEV_FLUIDSYNTH: - return new FluidSynthMIDIDevice; + return new FluidSynthMIDIDevice(Args); #endif case MDEV_SNDSYS: return new SndSysMIDIDevice; case MDEV_GUS: - return new TimidityMIDIDevice; + return new TimidityMIDIDevice(Args); case MDEV_OPL: try { - return new OPLMIDIDevice; + return new OPLMIDIDevice(Args); } catch (CRecoverableError &err) { @@ -291,10 +291,10 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const } case MDEV_TIMIDITY: - return new TimidityPPMIDIDevice; + return new TimidityPPMIDIDevice(Args); case MDEV_WILDMIDI: - return new WildMIDIDevice; + return new WildMIDIDevice(Args); default: return NULL; diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index eaf7e4afb..fd1601ee4 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -93,8 +93,8 @@ static const BYTE CtrlTranslate[15] = // //========================================================================== -MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), MusBuffer(0) +MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), MusBuffer(0) { #ifdef _WIN32 if (ExitEvent == NULL) diff --git a/src/sound/music_mus_opl.cpp b/src/sound/music_mus_opl.cpp index 3b5012ad9..562330ef9 100644 --- a/src/sound/music_mus_opl.cpp +++ b/src/sound/music_mus_opl.cpp @@ -20,16 +20,25 @@ CUSTOM_CVAR (Int, opl_numchips, 2, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } -CVAR(Int, opl_core, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Int, opl_core, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +int current_opl_core; -OPLMUSSong::OPLMUSSong (FileReader &reader) +// Get OPL core override from $mididevice +void OPL_SetCore(const char *args) +{ + current_opl_core = opl_core; + if (args != NULL && *args >= '0' && *args < '4') current_opl_core = *args - '0'; +} + +OPLMUSSong::OPLMUSSong (FileReader &reader, const char *args) { int samples = int(OPL_SAMPLE_RATE / 14); + OPL_SetCore(args); Music = new OPLmusicFile (&reader); m_Stream = GSnd->CreateStream (FillStream, samples*4, - (opl_core == 0 ? SoundStream::Mono : 0) | SoundStream::Float, int(OPL_SAMPLE_RATE), this); + (current_opl_core == 0 ? SoundStream::Mono : 0) | SoundStream::Float, int(OPL_SAMPLE_RATE), this); if (m_Stream == NULL) { Printf (PRINT_BOLD, "Could not create music stream.\n"); diff --git a/src/sound/music_smf_midiout.cpp b/src/sound/music_smf_midiout.cpp index 49fd12502..43755eb08 100644 --- a/src/sound/music_smf_midiout.cpp +++ b/src/sound/music_smf_midiout.cpp @@ -102,8 +102,8 @@ char MIDI_CommonLengths[15] = { 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // //========================================================================== -MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), Tracks(0) +MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), Tracks(0) { int p; int i; diff --git a/src/sound/music_timidity_mididevice.cpp b/src/sound/music_timidity_mididevice.cpp index 9e5be625b..a63e1d723 100644 --- a/src/sound/music_timidity_mididevice.cpp +++ b/src/sound/music_timidity_mididevice.cpp @@ -86,10 +86,10 @@ struct FmtChunk // //========================================================================== -TimidityMIDIDevice::TimidityMIDIDevice() +TimidityMIDIDevice::TimidityMIDIDevice(const char *args) { Renderer = NULL; - Renderer = new Timidity::Renderer((float)SampleRate); + Renderer = new Timidity::Renderer((float)SampleRate, args); } //========================================================================== @@ -245,6 +245,7 @@ FString TimidityMIDIDevice::GetStats() //========================================================================== TimidityWaveWriterMIDIDevice::TimidityWaveWriterMIDIDevice(const char *filename, int rate) + :TimidityMIDIDevice(NULL) { File = fopen(filename, "wb"); if (File != NULL) diff --git a/src/sound/music_wildmidi_mididevice.cpp b/src/sound/music_wildmidi_mididevice.cpp index c3fd674c9..88550226a 100644 --- a/src/sound/music_wildmidi_mididevice.cpp +++ b/src/sound/music_wildmidi_mididevice.cpp @@ -81,7 +81,7 @@ CUSTOM_CVAR(Bool, wildmidi_enhanced_resampling, true, CVAR_ARCHIVE | CVAR_GLOBAL // //========================================================================== -WildMIDIDevice::WildMIDIDevice() +WildMIDIDevice::WildMIDIDevice(const char *args) { Renderer = NULL; @@ -94,16 +94,18 @@ WildMIDIDevice::WildMIDIDevice() SampleRate = clamp(SampleRate, 11025, 65535); } - if (CurrentConfig.CompareNoCase(wildmidi_config) != 0 || SampleRate != WildMidi_GetSampleRate()) + if (args == NULL || *args == 0) args = wildmidi_config; + + if (CurrentConfig.CompareNoCase(args) != 0 || SampleRate != WildMidi_GetSampleRate()) { if (CurrentConfig.IsNotEmpty()) { WildMidi_Shutdown(); CurrentConfig = ""; } - if (!WildMidi_Init(wildmidi_config, SampleRate, 0)) + if (!WildMidi_Init(args, SampleRate, 0)) { - CurrentConfig = wildmidi_config; + CurrentConfig = args; } } if (CurrentConfig.IsNotEmpty()) diff --git a/src/sound/music_xmi_midiout.cpp b/src/sound/music_xmi_midiout.cpp index 6c179a16a..71246f518 100644 --- a/src/sound/music_xmi_midiout.cpp +++ b/src/sound/music_xmi_midiout.cpp @@ -108,8 +108,8 @@ extern char MIDI_CommonLengths[15]; // //========================================================================== -XMISong::XMISong (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), Songs(0) +XMISong::XMISong (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), Songs(0) { #ifdef _WIN32 if (ExitEvent == NULL) diff --git a/src/timidity/timidity.cpp b/src/timidity/timidity.cpp index 09b5ae7b5..807bc07b7 100644 --- a/src/timidity/timidity.cpp +++ b/src/timidity/timidity.cpp @@ -678,8 +678,9 @@ int LoadDMXGUS() return 0; } -Renderer::Renderer(float sample_rate) +Renderer::Renderer(float sample_rate, const char *args) { + // 'args' should be used to load a custom config or DMXGUS, but since setup currently requires a snd_reset call, this will need some refactoring first rate = sample_rate; patches = NULL; resample_buffer_size = 0; diff --git a/src/timidity/timidity.h b/src/timidity/timidity.h index 59ba5f8ad..45e23a9f9 100644 --- a/src/timidity/timidity.h +++ b/src/timidity/timidity.h @@ -630,7 +630,7 @@ struct Renderer int voices; int lost_notes, cut_notes; - Renderer(float sample_rate); + Renderer(float sample_rate, const char *args); ~Renderer(); void HandleEvent(int status, int parm1, int parm2); From eed6680a6722994c2936cc4c5730e0e4e43dc637 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 13 Dec 2015 17:01:33 -0600 Subject: [PATCH 67/75] Added support for weapon states User#. - Added keybinds for the user state triggering. - Added WRF_USER# flags which must be specified in order to use. - # can be 1-4. --- src/d_player.h | 4 ++ src/g_shared/a_pickups.h | 1 + src/g_shared/a_weapons.cpp | 24 +++++++ src/namedef.h | 4 ++ src/p_pspr.cpp | 108 ++++++++++++++++++++++++++--- wadsrc/static/actors/constants.txt | 5 ++ wadsrc/static/menudef.txt | 8 ++- 7 files changed, 143 insertions(+), 11 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index e27bf1087..1e7eef61e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -221,6 +221,10 @@ enum WF_WEAPONRELOADOK = 1 << 5, // [XA] Okay to reload this weapon. WF_WEAPONZOOMOK = 1 << 6, // [XA] Okay to use weapon zoom function. WF_REFIRESWITCHOK = 1 << 7, // Mirror WF_WEAPONSWITCHOK for A_ReFire + WF_USER1OK = 1 << 8, // [MC] Allow pushing of custom state buttons 1-4 + WF_USER2OK = 1 << 9, + WF_USER3OK = 1 << 10, + WF_USER4OK = 1 << 11, }; #define WPIECE1 1 diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 74b10206b..80e1744e6 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -307,6 +307,7 @@ public: virtual FState *GetAltAtkState (bool hold); virtual FState *GetRelState (); virtual FState *GetZoomState (); + virtual FState *GetUserState(int state); virtual void PostMorphWeapon (); virtual void EndPowerup (); diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index fae232557..79546737e 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -703,6 +703,30 @@ FState *AWeapon::GetZoomState () return FindState(NAME_Zoom); } +//=========================================================================== +// +// AWeapon :: GetUserState +// +//=========================================================================== + +FState *AWeapon::GetUserState(int state) +{ + switch (state) + { + case 4: + return FindState(NAME_User4); + case 3: + return FindState(NAME_User3); + case 2: + return FindState(NAME_User2); + case 1: + return FindState(NAME_User1); + default: + return NULL; + } +} + + /* Weapon giver ***********************************************************/ IMPLEMENT_CLASS(AWeaponGiver) diff --git a/src/namedef.h b/src/namedef.h index 22dbe1b51..f9d436f28 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -221,6 +221,10 @@ xx(Flash) xx(AltFlash) xx(Reload) xx(Zoom) +xx(User1) +xx(User2) +xx(User3) +xx(User4) // State names used by ASwitchableDecoration xx(Active) diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 63f3bc648..df05b9611 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -95,7 +95,8 @@ void P_SetPsprite (player_t *player, int position, FState *state, bool nofunctio if (position == ps_weapon && !nofunction) { // A_WeaponReady will re-set these as needed - player->WeaponState &= ~(WF_WEAPONREADY | WF_WEAPONREADYALT | WF_WEAPONBOBBING | WF_WEAPONSWITCHOK | WF_WEAPONRELOADOK | WF_WEAPONZOOMOK); + player->WeaponState &= ~(WF_WEAPONREADY | WF_WEAPONREADYALT | WF_WEAPONBOBBING | WF_WEAPONSWITCHOK | WF_WEAPONRELOADOK | WF_WEAPONZOOMOK | + WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK); } psp = &player->psprites[position]; @@ -349,6 +350,34 @@ void P_ZoomWeapon (player_t *player, FState *state) P_SetPsprite (player, ps_weapon, state); } +//-------------------------------------------------------------------------- - +// +// PROC P_UserStateWeapon +// +//--------------------------------------------------------------------------- + +void P_UserStateWeapon(player_t *player, FState *state, int userstate) +{ + if (!userstate) + return; + + AWeapon *weapon; + if (player->Bot == NULL && bot_observer) + return; + + weapon = player->ReadyWeapon; + if (weapon == NULL) + return; + + if (state == NULL) + { + state = weapon->GetUserState(userstate); + } + if (state != NULL) + P_SetPsprite(player, ps_weapon, state); +} + + //--------------------------------------------------------------------------- // // PROC P_DropWeapon @@ -573,13 +602,24 @@ void DoReadyWeaponToReload (AActor *self) void DoReadyWeaponToZoom (AActor *self) { - // Prepare for reload action. + // Prepare for zoom action. player_t *player; if (self && (player = self->player)) player->WeaponState |= WF_WEAPONZOOMOK; return; } +void DoReadyWeaponToUser(AActor *self, int userStates) +{ + // Prepare for user state action. + player_t *player; + if (self && (player = self->player) && userStates) + { + player->WeaponState |= userStates; + } + return; +} + // This function replaces calls to A_WeaponReady in other codepointers. void DoReadyWeapon(AActor *self) { @@ -588,18 +628,23 @@ void DoReadyWeapon(AActor *self) DoReadyWeaponToSwitch(self); DoReadyWeaponToReload(self); DoReadyWeaponToZoom(self); + DoReadyWeaponToUser(self, (WF_USER1OK + WF_USER2OK + WF_USER3OK + WF_USER4OK)); } enum EWRF_Options { - WRF_NoBob = 1, - WRF_NoSwitch = 2, - WRF_NoPrimary = 4, - WRF_NoSecondary = 8, + WRF_NoBob = 1, + WRF_NoSwitch = 1 << 1, + WRF_NoPrimary = 1 << 2, + WRF_NoSecondary = 1 << 3, WRF_NoFire = WRF_NoPrimary + WRF_NoSecondary, - WRF_AllowReload = 16, - WRF_AllowZoom = 32, - WRF_DisableSwitch = 64, + WRF_AllowReload = 1 << 4, + WRF_AllowZoom = 1 << 5, + WRF_DisableSwitch = 1 << 6, + WRF_User1 = 1 << 7, + WRF_User2 = 1 << 8, + WRF_User3 = 1 << 9, + WRF_User4 = 1 << 10, }; DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady) @@ -613,6 +658,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady) if ((paramflags & WRF_AllowReload)) DoReadyWeaponToReload(self); if ((paramflags & WRF_AllowZoom)) DoReadyWeaponToZoom(self); + int userStates = 0; + if (paramflags & WRF_User1) userStates |= WF_USER1OK; + if (paramflags & WRF_User2) userStates |= WF_USER2OK; + if (paramflags & WRF_User3) userStates |= WF_USER3OK; + if (paramflags & WRF_User4) userStates |= WF_USER4OK; + if (userStates) DoReadyWeaponToUser(self, userStates); + DoReadyWeaponDisableSwitch(self, paramflags & WRF_DisableSwitch); } @@ -732,6 +784,40 @@ void P_CheckWeaponZoom (player_t *player) } } +//--------------------------------------------------------------------------- +// +// PROC P_CheckWeaponUserState +// +// The player can use the weapon's user state functionalities. +// +//--------------------------------------------------------------------------- + +void P_CheckWeaponUserState(player_t *player) +{ + AWeapon *weapon = player->ReadyWeapon; + + if (weapon == NULL) + return; + + // Check for user state(s). + if ((player->WeaponState & WF_USER1OK) && (player->cmd.ucmd.buttons & BT_USER1)) + { + P_UserStateWeapon(player, NULL, 1); + } + else if ((player->WeaponState & WF_USER2OK) && (player->cmd.ucmd.buttons & BT_USER2)) + { + P_UserStateWeapon(player, NULL, 2); + } + else if ((player->WeaponState & WF_USER3OK) && (player->cmd.ucmd.buttons & BT_USER3)) + { + P_UserStateWeapon(player, NULL, 3); + } + else if ((player->WeaponState & WF_USER4OK) && (player->cmd.ucmd.buttons & BT_USER4)) + { + P_UserStateWeapon(player, NULL, 4); + } +} + //--------------------------------------------------------------------------- // // PROC A_ReFire @@ -1103,6 +1189,10 @@ void P_MovePsprites (player_t *player) { P_CheckWeaponZoom (player); } + if (player->WeaponState & (WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK)) + { + P_CheckWeaponUserState(player); + } } } diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 956ed119f..32a45e54c 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -129,6 +129,11 @@ const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; const int WRF_ALLOWRELOAD = 16; const int WRF_ALLOWZOOM = 32; const int WRF_DISABLESWITCH = 64; +const int WRF_USER1 = 128; +const int WRF_USER2 = 256; +const int WRF_USER3 = 512; +const int WRF_USER4 = 1024; +const int WRF_ALLUSER = WRF_USER1 | WRF_USER2 | WRF_USER3 | WRF_USER4; // Morph constants const int MRF_ADDSTAMINA = 1; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 244b14691..087c067d1 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -435,8 +435,12 @@ OptionMenu "CustomizeControls" StaticText "Controls", 1 Control "Fire", "+attack" Control "Secondary Fire", "+altattack" - Control "Weapon Reload", "+reload" - Control "Weapon Zoom", "+zoom" + Control "Weapon Reload", "+reload" + Control "Weapon Zoom", "+zoom" + Control "Weapon State 1", "+user1" + Control "Weapon State 2", "+user2" + Control "Weapon State 3", "+user3" + Control "Weapon State 4", "+user4" Control "Use / Open", "+use" Control "Move forward", "+forward" Control "Move backward", "+back" From b09a81126fc5fb2979f7ef11c5f8bb93b3bfdb24 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Mon, 14 Dec 2015 23:50:58 -0600 Subject: [PATCH 68/75] - Changed WeaponState from 8-bit to 16-bit integer. - Because the flags for WF_USER#OK are 256 on up, this is required in order to work. --- src/d_player.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_player.h b/src/d_player.h index 1e7eef61e..4e29da8f0 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -409,7 +409,7 @@ public: int lastkilltime; // [RH] For multikills BYTE multicount; BYTE spreecount; // [RH] Keep track of killing sprees - BYTE WeaponState; + WORD WeaponState; AWeapon *ReadyWeapon; AWeapon *PendingWeapon; // WP_NOCHANGE if not changing From 6478b98eeaa06df4bcac4968f36221f18470c3df Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 15 Dec 2015 07:19:41 -0600 Subject: [PATCH 69/75] Update serialization to BYTE from WORD for older save games. --- src/p_user.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/p_user.cpp b/src/p_user.cpp index 331a978b7..15c5055c9 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -3043,6 +3043,12 @@ void player_t::Serialize (FArchive &arc) WeaponState = ((cheats >> 14) & 1) | ((cheats & (0x37 << 24)) >> (24 - 1)); cheats &= ~((1 << 14) | (0x37 << 24)); } + if (SaveVersion < 4526) + { + BYTE oldWeaponState; + arc << oldWeaponState; + WeaponState = oldWeaponState; + } else { arc << WeaponState; From 3566d3157a6bb4f56f149a83fb475acb51adea2d Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 15 Dec 2015 09:04:52 -0600 Subject: [PATCH 70/75] Use | instead of +. --- src/p_pspr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index df05b9611..9f3a24bad 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -628,7 +628,7 @@ void DoReadyWeapon(AActor *self) DoReadyWeaponToSwitch(self); DoReadyWeaponToReload(self); DoReadyWeaponToZoom(self); - DoReadyWeaponToUser(self, (WF_USER1OK + WF_USER2OK + WF_USER3OK + WF_USER4OK)); + DoReadyWeaponToUser(self, (WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK)); } enum EWRF_Options From 4931c90839577c101593c3bdd62b9c76703b3875 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 31 Dec 2015 15:44:52 -0600 Subject: [PATCH 71/75] Bump save version for bigger WeaponState property --- src/p_user.cpp | 2 +- src/version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index 15c5055c9..1eb838176 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -3043,7 +3043,7 @@ void player_t::Serialize (FArchive &arc) WeaponState = ((cheats >> 14) & 1) | ((cheats & (0x37 << 24)) >> (24 - 1)); cheats &= ~((1 << 14) | (0x37 << 24)); } - if (SaveVersion < 4526) + if (SaveVersion < 4527) { BYTE oldWeaponState; arc << oldWeaponState; diff --git a/src/version.h b/src/version.h index c1288f02f..682c5165e 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 4526 +#define SAVEVER 4527 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From afbf88cc639a3c29d98ed458fa7a1f48486f41bf Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 31 Dec 2015 16:03:38 -0600 Subject: [PATCH 72/75] Remove WRF_ALLUSER. --- wadsrc/static/actors/constants.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 32a45e54c..1e4db4092 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -133,7 +133,6 @@ const int WRF_USER1 = 128; const int WRF_USER2 = 256; const int WRF_USER3 = 512; const int WRF_USER4 = 1024; -const int WRF_ALLUSER = WRF_USER1 | WRF_USER2 | WRF_USER3 | WRF_USER4; // Morph constants const int MRF_ADDSTAMINA = 1; From 1d759283c07e99ec8784ba01e6e286fbe03703fd Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 31 Dec 2015 16:41:09 -0600 Subject: [PATCH 73/75] Cleanup the zoom/reload/userX handling for A_WeaponReady - There was lots of code duplication. Consolidated it. - Renamed WRF_UserX to WRF_AllowUserX for consistancy. --- src/d_player.h | 2 +- src/g_shared/a_pickups.h | 4 +- src/g_shared/a_weapons.cpp | 40 +--- src/p_pspr.cpp | 287 ++++++++--------------------- wadsrc/static/actors/constants.txt | 8 +- 5 files changed, 82 insertions(+), 259 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 4e29da8f0..11611cded 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -225,7 +225,7 @@ enum WF_USER2OK = 1 << 9, WF_USER3OK = 1 << 10, WF_USER4OK = 1 << 11, -}; +}; #define WPIECE1 1 #define WPIECE2 2 diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 80e1744e6..413b01db3 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -305,9 +305,7 @@ public: virtual FState *GetReadyState (); virtual FState *GetAtkState (bool hold); virtual FState *GetAltAtkState (bool hold); - virtual FState *GetRelState (); - virtual FState *GetZoomState (); - virtual FState *GetUserState(int state); + virtual FState *GetStateForButtonName (FName button); virtual void PostMorphWeapon (); virtual void EndPowerup (); diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index 79546737e..9c71ab0ea 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -683,47 +683,13 @@ FState *AWeapon::GetAltAtkState (bool hold) //=========================================================================== // -// AWeapon :: GetRelState +// AWeapon :: GetStateForButtonName // //=========================================================================== -FState *AWeapon::GetRelState () +FState *AWeapon::GetStateForButtonName (FName button) { - return FindState(NAME_Reload); -} - -//=========================================================================== -// -// AWeapon :: GetZoomState -// -//=========================================================================== - -FState *AWeapon::GetZoomState () -{ - return FindState(NAME_Zoom); -} - -//=========================================================================== -// -// AWeapon :: GetUserState -// -//=========================================================================== - -FState *AWeapon::GetUserState(int state) -{ - switch (state) - { - case 4: - return FindState(NAME_User4); - case 3: - return FindState(NAME_User3); - case 2: - return FindState(NAME_User2); - case 1: - return FindState(NAME_User1); - default: - return NULL; - } + return FindState(button); } diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 9f3a24bad..ed7d675d3 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -38,6 +38,30 @@ // TYPES ------------------------------------------------------------------- +struct FGenericButtons +{ + int ReadyFlag; // Flag passed to A_WeaponReady + int StateFlag; // Flag set in WeaponState + int ButtonFlag; // Button to press + ENamedName StateName; // Name of the button/state +}; + +enum EWRF_Options +{ + WRF_NoBob = 1, + WRF_NoSwitch = 1 << 1, + WRF_NoPrimary = 1 << 2, + WRF_NoSecondary = 1 << 3, + WRF_NoFire = WRF_NoPrimary | WRF_NoSecondary, + WRF_AllowReload = 1 << 4, + WRF_AllowZoom = 1 << 5, + WRF_DisableSwitch = 1 << 6, + WRF_AllowUser1 = 1 << 7, + WRF_AllowUser2 = 1 << 8, + WRF_AllowUser3 = 1 << 9, + WRF_AllowUser4 = 1 << 10, +}; + // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -57,6 +81,16 @@ CVAR(Int, sv_fastweapons, false, CVAR_SERVERINFO); static FRandom pr_wpnreadysnd ("WpnReadySnd"); static FRandom pr_gunshot ("GunShot"); +static const FGenericButtons ButtonChecks[] = +{ + { WRF_AllowReload, WF_WEAPONZOOMOK, BT_ZOOM, NAME_Zoom }, + { WRF_AllowZoom, WF_WEAPONRELOADOK, BT_RELOAD, NAME_Reload }, + { WRF_AllowUser1, WF_USER1OK, BT_USER1, NAME_User1 }, + { WRF_AllowUser2, WF_USER2OK, BT_USER2, NAME_User2 }, + { WRF_AllowUser3, WF_USER3OK, BT_USER3, NAME_User3 }, + { WRF_AllowUser4, WF_USER4OK, BT_USER4, NAME_User4 }, +}; + // CODE -------------------------------------------------------------------- //--------------------------------------------------------------------------- @@ -290,94 +324,6 @@ void P_FireWeaponAlt (player_t *player, FState *state) } } -//--------------------------------------------------------------------------- -// -// PROC P_ReloadWeapon -// -//--------------------------------------------------------------------------- - -void P_ReloadWeapon (player_t *player, FState *state) -{ - AWeapon *weapon; - if (player->Bot == NULL && bot_observer) - { - return; - } - - weapon = player->ReadyWeapon; - if (weapon == NULL) - { - return; - } - - if (state == NULL) - { - state = weapon->GetRelState(); - } - // [XA] don't change state if still null, so if the modder sets - // WRF_RELOAD to true but forgets to define the Reload state, the weapon - // won't disappear. ;) - if (state != NULL) - P_SetPsprite (player, ps_weapon, state); -} - -//--------------------------------------------------------------------------- -// -// PROC P_ZoomWeapon -// -//--------------------------------------------------------------------------- - -void P_ZoomWeapon (player_t *player, FState *state) -{ - AWeapon *weapon; - if (player->Bot == NULL && bot_observer) - { - return; - } - - weapon = player->ReadyWeapon; - if (weapon == NULL) - { - return; - } - - if (state == NULL) - { - state = weapon->GetZoomState(); - } - // [XA] don't change state if still null. Same reasons as above. - if (state != NULL) - P_SetPsprite (player, ps_weapon, state); -} - -//-------------------------------------------------------------------------- - -// -// PROC P_UserStateWeapon -// -//--------------------------------------------------------------------------- - -void P_UserStateWeapon(player_t *player, FState *state, int userstate) -{ - if (!userstate) - return; - - AWeapon *weapon; - if (player->Bot == NULL && bot_observer) - return; - - weapon = player->ReadyWeapon; - if (weapon == NULL) - return; - - if (state == NULL) - { - state = weapon->GetUserState(userstate); - } - if (state != NULL) - P_SetPsprite(player, ps_weapon, state); -} - - //--------------------------------------------------------------------------- // // PROC P_DropWeapon @@ -591,33 +537,21 @@ void DoReadyWeaponToBob (AActor *self) } } -void DoReadyWeaponToReload (AActor *self) +void DoReadyWeaponToGeneric(AActor *self, int paramflags) { - // Prepare for reload action. - player_t *player; - if (self && (player = self->player)) - player->WeaponState |= WF_WEAPONRELOADOK; - return; -} + int flags = 0; -void DoReadyWeaponToZoom (AActor *self) -{ - // Prepare for zoom action. - player_t *player; - if (self && (player = self->player)) - player->WeaponState |= WF_WEAPONZOOMOK; - return; -} - -void DoReadyWeaponToUser(AActor *self, int userStates) -{ - // Prepare for user state action. - player_t *player; - if (self && (player = self->player) && userStates) + for (size_t i = 0; i < countof(ButtonChecks); ++i) { - player->WeaponState |= userStates; + if (paramflags & ButtonChecks[i].ReadyFlag) + { + flags |= ButtonChecks[i].StateFlag; + } + } + if (self != NULL && self->player != NULL) + { + self->player->WeaponState |= flags; } - return; } // This function replaces calls to A_WeaponReady in other codepointers. @@ -626,27 +560,9 @@ void DoReadyWeapon(AActor *self) DoReadyWeaponToBob(self); DoReadyWeaponToFire(self); DoReadyWeaponToSwitch(self); - DoReadyWeaponToReload(self); - DoReadyWeaponToZoom(self); - DoReadyWeaponToUser(self, (WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK)); + DoReadyWeaponToGeneric(self, ~0); } -enum EWRF_Options -{ - WRF_NoBob = 1, - WRF_NoSwitch = 1 << 1, - WRF_NoPrimary = 1 << 2, - WRF_NoSecondary = 1 << 3, - WRF_NoFire = WRF_NoPrimary + WRF_NoSecondary, - WRF_AllowReload = 1 << 4, - WRF_AllowZoom = 1 << 5, - WRF_DisableSwitch = 1 << 6, - WRF_User1 = 1 << 7, - WRF_User2 = 1 << 8, - WRF_User3 = 1 << 9, - WRF_User4 = 1 << 10, -}; - DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady) { ACTION_PARAM_START(1); @@ -655,17 +571,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady) DoReadyWeaponToSwitch(self, !(paramflags & WRF_NoSwitch)); if ((paramflags & WRF_NoFire) != WRF_NoFire) DoReadyWeaponToFire(self, !(paramflags & WRF_NoPrimary), !(paramflags & WRF_NoSecondary)); if (!(paramflags & WRF_NoBob)) DoReadyWeaponToBob(self); - if ((paramflags & WRF_AllowReload)) DoReadyWeaponToReload(self); - if ((paramflags & WRF_AllowZoom)) DoReadyWeaponToZoom(self); - - int userStates = 0; - if (paramflags & WRF_User1) userStates |= WF_USER1OK; - if (paramflags & WRF_User2) userStates |= WF_USER2OK; - if (paramflags & WRF_User3) userStates |= WF_USER3OK; - if (paramflags & WRF_User4) userStates |= WF_USER4OK; - if (userStates) DoReadyWeaponToUser(self, userStates); - - DoReadyWeaponDisableSwitch(self, paramflags & WRF_DisableSwitch); + DoReadyWeaponToGeneric(self, paramflags); + DoReadyWeaponDisableSwitch(self, paramflags & WRF_DisableSwitch); } //--------------------------------------------------------------------------- @@ -742,79 +649,40 @@ void P_CheckWeaponSwitch (player_t *player) //--------------------------------------------------------------------------- // -// PROC P_CheckWeaponReload +// PROC P_CheckWeaponButtons // -// The player can reload the weapon. +// Check extra button presses for weapons. // //--------------------------------------------------------------------------- -void P_CheckWeaponReload (player_t *player) +static void P_CheckWeaponButtons (player_t *player) { - AWeapon *weapon = player->ReadyWeapon; - - if (weapon == NULL) + if (player->Bot == NULL && bot_observer) + { return; - - // Check for reload. - if ((player->WeaponState & WF_WEAPONRELOADOK) && (player->cmd.ucmd.buttons & BT_RELOAD)) - { - P_ReloadWeapon (player, NULL); } -} - -//--------------------------------------------------------------------------- -// -// PROC P_CheckWeaponZoom -// -// The player can use the weapon's zoom function. -// -//--------------------------------------------------------------------------- - -void P_CheckWeaponZoom (player_t *player) -{ AWeapon *weapon = player->ReadyWeapon; - if (weapon == NULL) + { return; - - // Check for zoom. - if ((player->WeaponState & WF_WEAPONZOOMOK) && (player->cmd.ucmd.buttons & BT_ZOOM)) - { - P_ZoomWeapon (player, NULL); } -} - -//--------------------------------------------------------------------------- -// -// PROC P_CheckWeaponUserState -// -// The player can use the weapon's user state functionalities. -// -//--------------------------------------------------------------------------- - -void P_CheckWeaponUserState(player_t *player) -{ - AWeapon *weapon = player->ReadyWeapon; - - if (weapon == NULL) - return; - - // Check for user state(s). - if ((player->WeaponState & WF_USER1OK) && (player->cmd.ucmd.buttons & BT_USER1)) + // The button checks are ordered by precedence. The first one to match a + // button press and affect a state change wins. + for (size_t i = 0; i < countof(ButtonChecks); ++i) { - P_UserStateWeapon(player, NULL, 1); - } - else if ((player->WeaponState & WF_USER2OK) && (player->cmd.ucmd.buttons & BT_USER2)) - { - P_UserStateWeapon(player, NULL, 2); - } - else if ((player->WeaponState & WF_USER3OK) && (player->cmd.ucmd.buttons & BT_USER3)) - { - P_UserStateWeapon(player, NULL, 3); - } - else if ((player->WeaponState & WF_USER4OK) && (player->cmd.ucmd.buttons & BT_USER4)) - { - P_UserStateWeapon(player, NULL, 4); + if ((player->WeaponState & ButtonChecks[i].StateFlag) && + (player->cmd.ucmd.buttons & ButtonChecks[i].ButtonFlag)) + { + FState *state = weapon->GetStateForButtonName(ButtonChecks[i].StateName); + // [XA] don't change state if still null, so if the modder + // sets WRF_xxx to true but forgets to define the corresponding + // state, the weapon won't disappear. ;) + if (state != NULL) + { + P_SetPsprite(player, ps_weapon, state); + return; + } + } } } @@ -1181,18 +1049,9 @@ void P_MovePsprites (player_t *player) { P_CheckWeaponFire (player); } - if (player->WeaponState & WF_WEAPONRELOADOK) - { - P_CheckWeaponReload (player); - } - if (player->WeaponState & WF_WEAPONZOOMOK) - { - P_CheckWeaponZoom (player); - } - if (player->WeaponState & (WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK)) - { - P_CheckWeaponUserState(player); - } + + // Check custom buttons + P_CheckWeaponButtons(player); } } diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 1e4db4092..213e2109d 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -129,10 +129,10 @@ const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; const int WRF_ALLOWRELOAD = 16; const int WRF_ALLOWZOOM = 32; const int WRF_DISABLESWITCH = 64; -const int WRF_USER1 = 128; -const int WRF_USER2 = 256; -const int WRF_USER3 = 512; -const int WRF_USER4 = 1024; +const int WRF_ALLOWUSER1 = 128; +const int WRF_ALLOWUSER2 = 256; +const int WRF_ALLOWUSER3 = 512; +const int WRF_ALLOWUSER4 = 1024; // Morph constants const int MRF_ADDSTAMINA = 1; From 77013877d1bb213a8c803fdd28f6924f4c791ce5 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 2 Jan 2016 11:27:02 -0600 Subject: [PATCH 74/75] A_WeaponReady Fix - Zoom and Reload keys triggered the other states instead of themselves (i.e. zoom keybind triggered reload states instead of zoom) --- src/p_pspr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index ed7d675d3..7cdef059c 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -83,8 +83,8 @@ static FRandom pr_gunshot ("GunShot"); static const FGenericButtons ButtonChecks[] = { - { WRF_AllowReload, WF_WEAPONZOOMOK, BT_ZOOM, NAME_Zoom }, - { WRF_AllowZoom, WF_WEAPONRELOADOK, BT_RELOAD, NAME_Reload }, + { WRF_AllowZoom, WF_WEAPONZOOMOK, BT_ZOOM, NAME_Zoom }, + { WRF_AllowReload, WF_WEAPONRELOADOK, BT_RELOAD, NAME_Reload }, { WRF_AllowUser1, WF_USER1OK, BT_USER1, NAME_User1 }, { WRF_AllowUser2, WF_USER2OK, BT_USER2, NAME_User2 }, { WRF_AllowUser3, WF_USER3OK, BT_USER3, NAME_User3 }, From a75e65a3d8df1e29f83d098ae5bf061159053c11 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 3 Jan 2016 11:32:23 +0100 Subject: [PATCH 75/75] - added BLOCKF_SOUND to flag list supported by Line_SetBlocking, --- src/p_lnspec.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 9c93ca15c..9b38e149b 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -2587,6 +2587,7 @@ FUNC(LS_Line_SetBlocking) ML_BLOCKUSE, ML_BLOCKSIGHT, ML_BLOCKHITSCAN, + ML_SOUNDBLOCK, -1 };