From 576932c47f0ed0d7b1d73708c366d3c4f69c94c7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Feb 2018 22:14:22 +0100 Subject: [PATCH] - Timidity++ sources added. This compiles but isn't hooked into the engine yet. This source has been heavily edited to remove the deep integration with the provided UI modules and to eliminate use of global variables and puts everything into a small number of C++ classes. --- src/CMakeLists.txt | 28 + src/sound/timidity++/aq.cpp | 329 ++ src/sound/timidity++/aq.h | 97 + src/sound/timidity++/common.cpp | 275 ++ src/sound/timidity++/common.h | 90 + src/sound/timidity++/configfile.cpp | 1614 +++++++ src/sound/timidity++/controls.h | 79 + src/sound/timidity++/effect.cpp | 225 + src/sound/timidity++/effect.h | 39 + src/sound/timidity++/fft4g.cpp | 1351 ++++++ src/sound/timidity++/fft4g.h | 16 + src/sound/timidity++/filter.cpp | 200 + src/sound/timidity++/filter.h | 46 + src/sound/timidity++/freq.cpp | 709 +++ src/sound/timidity++/freq.h | 67 + src/sound/timidity++/instrum.cpp | 2049 +++++++++ src/sound/timidity++/instrum.h | 560 +++ src/sound/timidity++/mblock.cpp | 169 + src/sound/timidity++/mblock.h | 78 + src/sound/timidity++/mix.cpp | 1632 +++++++ src/sound/timidity++/mix.h | 76 + src/sound/timidity++/optcode.h | 65 + src/sound/timidity++/output.cpp | 92 + src/sound/timidity++/output.h | 94 + src/sound/timidity++/playmidi.cpp | 6168 +++++++++++++++++++++++++++ src/sound/timidity++/playmidi.h | 759 ++++ src/sound/timidity++/quantity.cpp | 340 ++ src/sound/timidity++/quantity.h | 72 + src/sound/timidity++/readmidi.cpp | 1611 +++++++ src/sound/timidity++/readmidic.cpp | 2732 ++++++++++++ src/sound/timidity++/recache.cpp | 440 ++ src/sound/timidity++/recache.h | 106 + src/sound/timidity++/resample.cpp | 979 +++++ src/sound/timidity++/resample.h | 88 + src/sound/timidity++/reverb.cpp | 4437 +++++++++++++++++++ src/sound/timidity++/reverb.h | 814 ++++ src/sound/timidity++/sbkconv.cpp | 210 + src/sound/timidity++/sffile.cpp | 736 ++++ src/sound/timidity++/sffile.h | 144 + src/sound/timidity++/sfitem.cpp | 98 + src/sound/timidity++/sfitem.h | 94 + src/sound/timidity++/sflayer.h | 107 + src/sound/timidity++/smplfile.cpp | 1228 ++++++ src/sound/timidity++/sndfont.cpp | 1514 +++++++ src/sound/timidity++/sysdep.h | 102 + src/sound/timidity++/tables.cpp | 1012 +++++ src/sound/timidity++/tables.h | 96 + src/sound/timidity++/timidity.cpp | 149 + src/sound/timidity++/timidity.h | 178 + src/sound/timidity++/w32_a.cpp | 666 +++ 50 files changed, 34860 insertions(+) create mode 100644 src/sound/timidity++/aq.cpp create mode 100644 src/sound/timidity++/aq.h create mode 100644 src/sound/timidity++/common.cpp create mode 100644 src/sound/timidity++/common.h create mode 100644 src/sound/timidity++/configfile.cpp create mode 100644 src/sound/timidity++/controls.h create mode 100644 src/sound/timidity++/effect.cpp create mode 100644 src/sound/timidity++/effect.h create mode 100644 src/sound/timidity++/fft4g.cpp create mode 100644 src/sound/timidity++/fft4g.h create mode 100644 src/sound/timidity++/filter.cpp create mode 100644 src/sound/timidity++/filter.h create mode 100644 src/sound/timidity++/freq.cpp create mode 100644 src/sound/timidity++/freq.h create mode 100644 src/sound/timidity++/instrum.cpp create mode 100644 src/sound/timidity++/instrum.h create mode 100644 src/sound/timidity++/mblock.cpp create mode 100644 src/sound/timidity++/mblock.h create mode 100644 src/sound/timidity++/mix.cpp create mode 100644 src/sound/timidity++/mix.h create mode 100644 src/sound/timidity++/optcode.h create mode 100644 src/sound/timidity++/output.cpp create mode 100644 src/sound/timidity++/output.h create mode 100644 src/sound/timidity++/playmidi.cpp create mode 100644 src/sound/timidity++/playmidi.h create mode 100644 src/sound/timidity++/quantity.cpp create mode 100644 src/sound/timidity++/quantity.h create mode 100644 src/sound/timidity++/readmidi.cpp create mode 100644 src/sound/timidity++/readmidic.cpp create mode 100644 src/sound/timidity++/recache.cpp create mode 100644 src/sound/timidity++/recache.h create mode 100644 src/sound/timidity++/resample.cpp create mode 100644 src/sound/timidity++/resample.h create mode 100644 src/sound/timidity++/reverb.cpp create mode 100644 src/sound/timidity++/reverb.h create mode 100644 src/sound/timidity++/sbkconv.cpp create mode 100644 src/sound/timidity++/sffile.cpp create mode 100644 src/sound/timidity++/sffile.h create mode 100644 src/sound/timidity++/sfitem.cpp create mode 100644 src/sound/timidity++/sfitem.h create mode 100644 src/sound/timidity++/sflayer.h create mode 100644 src/sound/timidity++/smplfile.cpp create mode 100644 src/sound/timidity++/sndfont.cpp create mode 100644 src/sound/timidity++/sysdep.h create mode 100644 src/sound/timidity++/tables.cpp create mode 100644 src/sound/timidity++/tables.h create mode 100644 src/sound/timidity++/timidity.cpp create mode 100644 src/sound/timidity++/timidity.h create mode 100644 src/sound/timidity++/w32_a.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6195ac57b1..f0c995ba01 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -802,6 +802,8 @@ set( FASTMATH_SOURCES sound/oalsound.cpp sound/sndfile_decoder.cpp sound/mididevices/music_timiditypp_mididevice.cpp + sound/timidity++/fft4g.cpp + sound/timidity++/reverb.cpp gl/utility/gl_clock.cpp gl/renderer/gl_2ddrawer.cpp gl/hqnx/init.cpp @@ -832,6 +834,10 @@ set( FASTMATH_SOURCES gl/models/gl_models.cpp r_data/models/models.cpp r_data/matrix.cpp +# These will be removed later. + sound/timidity++/timidity.cpp + sound/timidity++/w32_a.cpp + ) set (PCH_SOURCES @@ -1166,6 +1172,28 @@ set (PCH_SOURCES sound/timidity/playmidi.cpp sound/timidity/resample.cpp sound/timidity/timidity.cpp + sound/timidity++/aq.cpp + sound/timidity++/common.cpp + sound/timidity++/configfile.cpp + sound/timidity++/effect.cpp + sound/timidity++/filter.cpp + sound/timidity++/freq.cpp + sound/timidity++/instrum.cpp + sound/timidity++/mblock.cpp + sound/timidity++/mix.cpp + sound/timidity++/output.cpp + sound/timidity++/playmidi.cpp + sound/timidity++/quantity.cpp + sound/timidity++/readmidi.cpp + sound/timidity++/readmidic.cpp + sound/timidity++/recache.cpp + sound/timidity++/resample.cpp + sound/timidity++/sbkconv.cpp + sound/timidity++/sffile.cpp + sound/timidity++/sfitem.cpp + sound/timidity++/smplfile.cpp + sound/timidity++/sndfont.cpp + sound/timidity++/tables.cpp sound/wildmidi/file_io.cpp sound/wildmidi/gus_pat.cpp sound/wildmidi/reverb.cpp diff --git a/src/sound/timidity++/aq.cpp b/src/sound/timidity++/aq.cpp new file mode 100644 index 0000000000..c95d184cb2 --- /dev/null +++ b/src/sound/timidity++/aq.cpp @@ -0,0 +1,329 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + aq.c - Audio queue. + Written by Masanao Izumo +*/ + +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "aq.h" +#include "instrum.h" +#include "playmidi.h" +#include "effect.h" + + +namespace TimidityPlus +{ + + +#define TEST_SPARE_RATE 0.9 +#define MAX_BUCKET_TIME 0.2 +#define MAX_FILLED_TIME 2.0 + + +AudioQueue::AudioQueue(PlayMode *pm, int bufsize, Reverb *reverb) +{ + playMode = pm; + buffer_size = bufsize; + effect = new Effect(reverb); +} + +AudioQueue::~AudioQueue() +{ + freeSoftQueue(); + delete effect; +} + +void AudioQueue::setup(void) +{ + int ch = 2; // stereo only + + /* Initialize Bps, bucket_size, device_qsize, and bucket_time */ + + if (playMode->encoding & PE_24BIT) + Bps = 3 * ch; + else + Bps = 2 * ch; + + bucket_size = buffer_size * Bps; + bucket_time = (double)bucket_size / Bps / playMode->rate; + + device_qsize = 0; + freeSoftQueue(); + nbuckets = 0; + + effect->init_effect(); + aq_add_count = 0; +} + +void AudioQueue::setSoftQueue(double soft_buff_time, double fill_start_time) +{ + int nb; + + /* for re-initialize */ + if (soft_buff_time < 0) + soft_buff_time = last_soft_buff_time; + if (fill_start_time < 0) + fill_start_time = last_fill_start_time; + + nb = (int)(soft_buff_time / bucket_time); + if (nb == 0) + aq_start_count = 0; + else + aq_start_count = (int32_t)(fill_start_time * playMode->rate); + aq_fill_buffer_flag = (aq_start_count > 0); + + if (nbuckets != nb) + { + nbuckets = nb; + allocSoftQueue(); + } + + last_soft_buff_time = soft_buff_time; + last_fill_start_time = fill_start_time; +} + +/* Send audio data to playMode->output_data() */ +int AudioQueue::outputData(char *buff, int nbytes) +{ + int i; + + play_counter += nbytes / Bps; + + while (nbytes > 0) + { + i = nbytes; + if (i > bucket_size) + i = bucket_size; + if (playMode->output_data(buff, i) == -1) + return -1; + nbytes -= i; + buff += i; + } + + return 0; +} + +int AudioQueue::add(int32_t *samples, int32_t count) +{ + int32_t nbytes, i; + char *buff; + + if (!count) + { + return 0; + } + + aq_add_count += count; + effect->do_effect(samples, count); + nbytes = general_output_convert(samples, count); + buff = (char *)samples; + + if (device_qsize == 0) + return playMode->output_data(buff, nbytes); + + aq_fill_buffer_flag = (aq_add_count <= aq_start_count); + + while ((i = addPlayBucket(buff, nbytes)) < nbytes) + { + buff += i; + nbytes -= i; + if (head && head->len == bucket_size) + { + if (fillOne() == -1) + return -1; + } + aq_fill_buffer_flag = 0; + } + return 0; +} + +/* alloc_soft_queue() (re-)initializes audio buckets. */ +void AudioQueue::allocSoftQueue(void) +{ + int i; + char *base; + + freeSoftQueue(); + + base_buckets = new AudioBucket[nbuckets]; + base = new char[nbuckets * bucket_size]; + for (i = 0; i < nbuckets; i++) + base_buckets[i].data = base + i * bucket_size; + flushBuckets(); +} + +void AudioQueue::freeSoftQueue(void) +{ + if (base_buckets) + { + delete[] base_buckets[0].data; + delete[] base_buckets; + base_buckets = NULL; + } +} + +/* aq_fill_one() transfers one audio bucket to device. */ +int AudioQueue::fillOne(void) +{ + AudioBucket *tmp; + + if (head == NULL) + return 0; + if (outputData(head->data, bucket_size) == -1) + return -1; + tmp = head; + head = head->next; + reuseAudioBucket(tmp); + return 0; +} + +int32_t AudioQueue::softFilled(void) +{ + int32_t bytes; + AudioBucket *cur; + + bytes = 0; + for (cur = head; cur != NULL; cur = cur->next) + bytes += cur->len; + return bytes / Bps; +} + +int AudioQueue::softFlush(void) +{ + while (head) + { + if (head->len < bucket_size) + { + /* Add silence code */ + memset(head->data + head->len, 0, bucket_size - head->len); + head->len = bucket_size; + } + if (fillOne() == -1) + return RC_ERROR; + } + return RC_OK; +} + +int AudioQueue::flush(int discard) +{ + aq_add_count = 0; + effect->init_effect(); + + if (discard) + { + if (playMode->acntl(PM_REQ_DISCARD, NULL) != -1) + { + flushBuckets(); + return RC_OK; + } + } + + playMode->acntl(PM_REQ_FLUSH, NULL); + flushBuckets(); + return RC_OK; +} + +/* add_play_bucket() attempts to add buf to audio bucket. + * It returns actually added bytes. + */ +int AudioQueue::addPlayBucket(const char *buf, int n) +{ + int total; + + if (n == 0) + return 0; + + if (!nbuckets) { + playMode->output_data((char *)buf, n); + return n; + } + + if (head == NULL) + head = tail = nextAllocatedBucket(); + + total = 0; + while (n > 0) + { + int i; + + if (tail->len == bucket_size) + { + AudioBucket *b; + if ((b = nextAllocatedBucket()) == NULL) + break; + if (head == NULL) + head = tail = b; + else + tail = tail->next = b; + } + + i = bucket_size - tail->len; + if (i > n) + i = n; + memcpy(tail->data + tail->len, buf + total, i); + total += i; + n -= i; + tail->len += i; + } + + return total; +} + +/* Flush and clear audio bucket */ +void AudioQueue::flushBuckets(void) +{ + int i; + + allocated_bucket_list = NULL; + for (i = 0; i < nbuckets; i++) + reuseAudioBucket(&base_buckets[i]); + head = tail = NULL; + aq_fill_buffer_flag = (aq_start_count > 0); + play_counter = play_offset_counter = 0; +} + +/* next_allocated_bucket() gets free bucket. If all buckets is used, it + * returns NULL. + */ +AudioQueue::AudioBucket *AudioQueue::nextAllocatedBucket(void) +{ + AudioBucket *b; + + if (allocated_bucket_list == NULL) + return NULL; + b = allocated_bucket_list; + allocated_bucket_list = allocated_bucket_list->next; + b->len = 0; + b->next = NULL; + return b; +} + +/* Reuse specified bucket */ +void AudioQueue::reuseAudioBucket(AudioBucket *bucket) +{ + bucket->next = allocated_bucket_list; + allocated_bucket_list = bucket; +} +} \ No newline at end of file diff --git a/src/sound/timidity++/aq.h b/src/sound/timidity++/aq.h new file mode 100644 index 0000000000..349807e203 --- /dev/null +++ b/src/sound/timidity++/aq.h @@ -0,0 +1,97 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + aq.h - Audio queue. + Written by Masanao Izumo +*/ + +#ifndef ___AQ_H_ +#define ___AQ_H_ + +#include + +namespace TimidityPlus +{ + +/* interfaces */ + +class Effect; +class Reverb; +struct PlayMode; + +class AudioQueue +{ + struct AudioBucket + { + char *data; + int len; + AudioBucket *next; + }; + + Effect *effect; + PlayMode *playMode; + + int buffer_size = 0; + int32_t device_qsize = 0; + int Bps = 0; /* Bytes per sample frame */ + int bucket_size = 0; + int nbuckets = 0; + double bucket_time = 0; + int aq_fill_buffer_flag = 0; + int32_t aq_start_count = 0; + int32_t aq_add_count = 0; + + int32_t play_counter = 0, play_offset_counter = 0; + double play_start_time = 0; + + AudioBucket *base_buckets = NULL; + AudioBucket *allocated_bucket_list = NULL; + AudioBucket *head = NULL; + AudioBucket *tail = NULL; + + void allocSoftQueue(void); + int addPlayBucket(const char *buf, int n); + void reuseAudioBucket(AudioBucket *bucket); + AudioBucket *nextAllocatedBucket(void); + void flushBuckets(void); + int fillOne(void); + int outputData(char *buff, int nbytes); + + double last_soft_buff_time, last_fill_start_time; + +public: + AudioQueue(PlayMode *pm, int buffersize, Reverb *reverb); // play_mode, audio_buffer_size + ~AudioQueue(); + + void setup(); // allocates the buffer for software queue, and estimate maxmum queue size of audio device. + void setSoftQueue(double soft_buff_time, double fill_start_time); // makes software audio queue. If fill_start_time is positive, TiMidity doesn't start playing immidiately until the autio buffer is filled. + int add(int32_t *samples, int32_t count); // adds new samples to software queue. If samples is NULL, only updates internal software queue buffer. + int32_t softFilled(void); // returns filled queue length of software buffer. + int flush(int discard); // If discard is true, discards all audio queue and returns immediately, otherwise waits until play all out. + int softFlush(void); // transfers all buffer to device + void freeSoftQueue(void); // free soft_que memory + + int fillBufferFlag() // non-zero if aq->add() is in filling mode + { + return aq_fill_buffer_flag; + } +}; + +} +#endif /* ___AQ_H_ */ diff --git a/src/sound/timidity++/common.cpp b/src/sound/timidity++/common.cpp new file mode 100644 index 0000000000..1995a1f295 --- /dev/null +++ b/src/sound/timidity++/common.cpp @@ -0,0 +1,275 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + common.c + + */ + +#include +#include +#include +#include + +#include +#include +#include "m_random.h" +#include "common.h" + +namespace TimidityPlus +{ + + +/* This'll allocate memory or die. */ +void *safe_malloc(size_t count) +{ + auto p = malloc(count); + if (p == nullptr) + { + // I_FatalError("Out of memory"); + } + return p; +} + +void *safe_large_malloc(size_t count) +{ + return safe_malloc(count); +} + +void *safe_realloc(void *ptr, size_t count) +{ + auto p = realloc(ptr, count); + if (p == nullptr) + { + // I_FatalError("Out of memory"); + } + return p; +} + +char *safe_strdup(const char *s) +{ + if (s == nullptr) s = ""; + auto p = strdup(s); + if (p == nullptr) + { + // I_FatalError("Out of memory"); + } + return p; +} + +/* free ((void **)ptr_list)[0..count-1] and ptr_list itself */ +void free_ptr_list(void *ptr_list, int count) +{ + int i; + for (i = 0; i < count; i++) + free(((void **)ptr_list)[i]); + free(ptr_list); +} + +static int atoi_limited(const char *string, int v_min, int v_max) +{ + int value = atoi(string); + + if (value <= v_min) + value = v_min; + else if (value > v_max) + value = v_max; + return value; +} + +int string_to_7bit_range(const char *string_, int *start, int *end) +{ + const char *string = string_; + + if (isdigit(*string)) + { + *start = atoi_limited(string, 0, 127); + while (isdigit(*++string)); + } + else + *start = 0; + if (*string == '-') + { + string++; + *end = isdigit(*string) ? atoi_limited(string, 0, 127) : 127; + if (*start > *end) + *end = *start; + } + else + *end = *start; + return string != string_; +} + + +static FRandom pr_rnd; + +int int_rand(int n) +{ + return (int)pr_rnd.GenRand_Real1() * n; +} + +double flt_rand() +{ + return (int)pr_rnd.GenRand_Real1(); +} + +PathList::PathList() +{ + paths.push_back("./"); +} + +/* This adds a directory to the path list */ +void PathList::addPath(const char *str) +{ + if (*str == 0) return; + for (size_t i = 0; i < paths.size(); i++) + { + if (pathcmp(paths[i].c_str(), str, 0) == 0) + { + // move string to the back. + std::string ss = paths[i]; + paths.erase(paths.begin() + i); + paths.push_back(ss); + return; + } + } + paths.push_back(str); +} + +int PathList::pathcmp(const char *p1, const char *p2, int ignore_case) +{ + int c1, c2; + +#ifdef _WIN32 + ignore_case = 1; /* Always ignore the case */ +#endif + + do { + c1 = *p1++ & 0xff; + c2 = *p2++ & 0xff; + if (ignore_case) + { + c1 = tolower(c1); + c2 = tolower(c2); + } + if (c1 == '/') c1 = *p1 ? 0x100 : 0; + if (c1 == '/') c2 = *p2 ? 0x100 : 0; + } while (c1 == c2 && c1 /* && c2 */); + + return c1 - c2; +} + +std::pair PathList::openFile(const char *name) +{ + + if (name && *name) + { + /* First try the given name */ + FileReader *fr = new FileReader; + if (fr->Open(name)) + { + return std::make_pair(fr, std::string(name)); + } + + if (!isAbsPath(name)) + { + for (int i = (int)paths.size() - 1; i >= 0; i--) + { + std::string s = paths[i]; + auto c = s.at(s.length() - 1); + if (c != '/' && c != '#' && name[0] != '#') + { + s += '/'; + } + s += name; + if (fr->Open(s.c_str())) return std::make_pair(fr, s); + } + } + delete fr; + } + return std::make_pair(nullptr, std::string()); +} + +int PathList::isAbsPath(const char *name) +{ + if (name[0] == '/') + return 1; + +#ifdef _WIN32 + /* [A-Za-z]: (for Windows) */ + if (isalpha(name[0]) && name[1] == ':') + return 1; +#endif /* _WIN32 */ + return 0; +} + +struct timidity_file *open_file(const char *name, int decompress, int noise_mode, PathList &pathList) +{ + auto file = pathList.openFile(name); + if (!file.first) return nullptr; + auto tf = new timidity_file; + tf->url = file.first; + tf->filename = file.second; + return tf; +} + +/* This closes files opened with open_file */ +void close_file(struct timidity_file *tf) +{ + if (tf->url != NULL) + { + tf->url->Close(); + } + delete tf; +} + +/* This is meant for skipping a few bytes. */ +void skip(struct timidity_file *tf, size_t len) +{ + tf->url->Seek((long)len, SEEK_CUR); +} + +char *tf_gets(char *buff, int n, struct timidity_file *tf) +{ + return tf->url->Gets(buff, n); +} + +int tf_getc(struct timidity_file *tf) +{ + unsigned char c; + auto read = tf->url->Read(&c, 1); + return read == 0 ? EOF : c; +} + +long tf_read(void *buff, int32_t size, int32_t nitems, struct timidity_file *tf) +{ + return tf->url->Read(buff, size * nitems) / size; +} + +long tf_seek(struct timidity_file *tf, long offset, int whence) +{ + + return tf->url->Seek(offset, whence); +} + +long tf_tell(struct timidity_file *tf) +{ + return tf->url->Tell(); +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/common.h b/src/sound/timidity++/common.h new file mode 100644 index 0000000000..bef061a00d --- /dev/null +++ b/src/sound/timidity++/common.h @@ -0,0 +1,90 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + common.h +*/ + +#ifndef ___COMMON_H_ +#define ___COMMON_H_ + +#include +#include +#include +#include "files.h" + +namespace TimidityPlus +{ + +class PathList +{ + std::vector paths; + + int isAbsPath(const char *name); + +public: + PathList(); + + void addPath(const char *str); + void clear() + { + paths.resize(1); + } + static int pathcmp(const char *p1, const char *p2, int ignore_case); + std::pair openFile(const char *name); + +}; + +struct timidity_file +{ + FileReader *url; + std::string filename; +}; + +/* Noise modes for open_file */ +enum +{ + OF_SILENT = 0, + OF_NORMAL = 1, + OF_VERBOSE = 2, +}; + + +extern struct timidity_file *open_file(const char *name, int decompress, int noise_mode, PathList &); +extern void close_file(struct timidity_file *tf); +extern void skip(struct timidity_file *tf, size_t len); +extern char *tf_gets(char *buff, int n, struct timidity_file *tf); +int tf_getc(struct timidity_file *tf); +extern long tf_read(void *buff, int32_t size, int32_t nitems, struct timidity_file *tf); +extern long tf_seek(struct timidity_file *tf, long offset, int whence); +extern long tf_tell(struct timidity_file *tf); +extern int int_rand(int n); /* random [0..n-1] */ +double flt_rand(); +extern int check_file_extension(char *filename, char *ext, int decompress); + +extern void *safe_malloc(size_t count); +extern void *safe_realloc(void *old_ptr, size_t new_size); +extern void *safe_large_malloc(size_t count); +extern char *safe_strdup(const char *s); +extern void free_ptr_list(void *ptr_list, int count); +extern int string_to_7bit_range(const char *s, int *start, int *end); +extern int load_table(char *file); + +} +#endif /* ___COMMON_H_ */ diff --git a/src/sound/timidity++/configfile.cpp b/src/sound/timidity++/configfile.cpp new file mode 100644 index 0000000000..9e96deb0f8 --- /dev/null +++ b/src/sound/timidity++/configfile.cpp @@ -0,0 +1,1614 @@ +/* +TiMidity++ -- MIDI to WAVE converter and player +Copyright (C) 1999-2008 Masanao Izumo +Copyright (C) 1995 Tuukka Toivonen + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include + +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "quantity.h" + + + +namespace TimidityPlus +{ + +#define MAXWORDS 130 +#define CHECKERRLIMIT \ + if(++errcnt >= 10) { \ + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, \ + "Too many errors... Give up read %s", name); \ + reuse_mblock(&varbuf); \ + close_file(tf); return 1; } + + +typedef struct { + const char *name; + int mapid, isdrum; +} MapNameEntry; + +static int mapnamecompare(const void *name, const void *entry) +{ + return strcmp((const char *)name, ((const MapNameEntry *)entry)->name); +} + +static int mapname2id(char *name, int *isdrum) +{ + static const MapNameEntry data[] = { + /* sorted in alphabetical order */ + { "gm2", GM2_TONE_MAP, 0 }, + { "gm2drum", GM2_DRUM_MAP, 1 }, + { "sc55", SC_55_TONE_MAP, 0 }, + { "sc55drum", SC_55_DRUM_MAP, 1 }, + { "sc88", SC_88_TONE_MAP, 0 }, + { "sc8850", SC_8850_TONE_MAP, 0 }, + { "sc8850drum", SC_8850_DRUM_MAP, 1 }, + { "sc88drum", SC_88_DRUM_MAP, 1 }, + { "sc88pro", SC_88PRO_TONE_MAP, 0 }, + { "sc88prodrum", SC_88PRO_DRUM_MAP, 1 }, + { "xg", XG_NORMAL_MAP, 0 }, + { "xgdrum", XG_DRUM_MAP, 1 }, + { "xgsfx126", XG_SFX126_MAP, 1 }, + { "xgsfx64", XG_SFX64_MAP, 0 } + }; + const MapNameEntry *found; + + found = (MapNameEntry *)bsearch(name, data, sizeof data / sizeof data[0], sizeof data[0], mapnamecompare); + if (found != NULL) + { + *isdrum = found->isdrum; + return found->mapid; + } + return -1; +} + +static float *config_parse_tune(const char *cp, int *num) +{ + const char *p; + float *tune_list; + int i; + + /* count num */ + *num = 1, p = cp; + while ((p = strchr(p, ',')) != NULL) + (*num)++, p++; + /* alloc */ + tune_list = (float *)safe_malloc((*num) * sizeof(float)); + /* regist */ + for (i = 0, p = cp; i < *num; i++, p++) { + tune_list[i] = atof(p); + if (!(p = strchr(p, ','))) + break; + } + return tune_list; +} + +static int16_t *config_parse_int16(const char *cp, int *num) +{ + const char *p; + int16_t *list; + int i; + + /* count num */ + *num = 1, p = cp; + while ((p = strchr(p, ',')) != NULL) + (*num)++, p++; + /* alloc */ + list = (int16_t *)safe_malloc((*num) * sizeof(int16_t)); + /* regist */ + for (i = 0, p = cp; i < *num; i++, p++) { + list[i] = atoi(p); + if (!(p = strchr(p, ','))) + break; + } + return list; +} + +static int **config_parse_envelope(const char *cp, int *num) +{ + const char *p, *px; + int **env_list; + int i, j; + + /* count num */ + *num = 1, p = cp; + while ((p = strchr(p, ',')) != NULL) + (*num)++, p++; + /* alloc */ + env_list = (int **)safe_malloc((*num) * sizeof(int *)); + for (i = 0; i < *num; i++) + env_list[i] = (int *)safe_malloc(6 * sizeof(int)); + /* init */ + for (i = 0; i < *num; i++) + for (j = 0; j < 6; j++) + env_list[i][j] = -1; + /* regist */ + for (i = 0, p = cp; i < *num; i++, p++) { + px = strchr(p, ','); + for (j = 0; j < 6; j++, p++) { + if (*p == ':') + continue; + env_list[i][j] = atoi(p); + if (!(p = strchr(p, ':'))) + break; + if (px && p > px) + break; + } + if (!(p = px)) + break; + } + return env_list; +} + +static Quantity **config_parse_modulation(const char *name, int line, const char *cp, int *num, int mod_type) +{ + const char *p, *px, *err; + char buf[128], *delim; + Quantity **mod_list; + int i, j; + static const char * qtypestr[] = { "tremolo", "vibrato" }; + static const uint16_t qtypes[] = { + QUANTITY_UNIT_TYPE(TREMOLO_SWEEP), QUANTITY_UNIT_TYPE(TREMOLO_RATE), QUANTITY_UNIT_TYPE(DIRECT_INT), + QUANTITY_UNIT_TYPE(VIBRATO_SWEEP), QUANTITY_UNIT_TYPE(VIBRATO_RATE), QUANTITY_UNIT_TYPE(DIRECT_INT) + }; + + /* count num */ + *num = 1, p = cp; + while ((p = strchr(p, ',')) != NULL) + (*num)++, p++; + /* alloc */ + mod_list = (Quantity **)safe_malloc((*num) * sizeof(Quantity *)); + for (i = 0; i < *num; i++) + mod_list[i] = (Quantity *)safe_malloc(3 * sizeof(Quantity)); + /* init */ + for (i = 0; i < *num; i++) + for (j = 0; j < 3; j++) + INIT_QUANTITY(mod_list[i][j]); + buf[sizeof buf - 1] = '\0'; + /* regist */ + for (i = 0, p = cp; i < *num; i++, p++) { + px = strchr(p, ','); + for (j = 0; j < 3; j++, p++) { + if (*p == ':') + continue; + if ((delim = strpbrk(strncpy(buf, p, sizeof buf - 1), ":,")) != NULL) + *delim = '\0'; + if (*buf != '\0' && (err = string_to_quantity(buf, &mod_list[i][j], qtypes[mod_type * 3 + j])) != NULL) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: %s: parameter %d of item %d: %s (%s)", + name, line, qtypestr[mod_type], j + 1, i + 1, err, buf); + free_ptr_list(mod_list, *num); + mod_list = NULL; + *num = 0; + return NULL; + } + if (!(p = strchr(p, ':'))) + break; + if (px && p > px) + break; + } + if (!(p = px)) + break; + } + return mod_list; +} + + +/*! copy bank and, if necessary, map appropriately */ +void Instruments::copybank(ToneBank *to, ToneBank *from, int mapid, int bankmapfrom, int bankno) +{ + ToneBankElement *toelm, *fromelm; + int i; + + if (from == NULL) + return; + for (i = 0; i < 128; i++) + { + toelm = &to->tone[i]; + fromelm = &from->tone[i]; + if (fromelm->name == NULL) + continue; + copy_tone_bank_element(toelm, fromelm); + toelm->instrument = NULL; + if (mapid != INST_NO_MAP) + set_instrument_map(mapid, bankmapfrom, i, bankno, i); + } +} + +/*! copy the whole mapped bank. returns 0 if no error. */ +int Instruments::copymap(int mapto, int mapfrom, int isdrum) +{ + ToneBank **tb = isdrum ? drumset : tonebank; + int i, bankfrom, bankto; + + for (i = 0; i < 128; i++) + { + bankfrom = find_instrument_map_bank(isdrum, mapfrom, i); + if (bankfrom <= 0) /* not mapped */ + continue; + bankto = alloc_instrument_map_bank(isdrum, mapto, i); + if (bankto == -1) /* failed */ + return 1; + copybank(tb[bankto], tb[bankfrom], mapto, i, bankto); + } + return 0; +} + +int Instruments::set_gus_patchconf_opts(const char *name, int line, char *opts, ToneBankElement *tone) +{ + char *cp; + int k; + + if (!(cp = strchr(opts, '='))) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: bad patch option %s", name, line, opts); + return 1; + } + *cp++ = 0; + if (!strcmp(opts, "amp")) { + k = atoi(cp); + if ((k < 0 || k > MAX_AMPLIFICATION) || (*cp < '0' || *cp > '9')) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: amplification must be between 0 and %d", + name, line, MAX_AMPLIFICATION); + return 1; + } + tone->amp = k; + } + else if (!strcmp(opts, "note")) { + k = atoi(cp); + if ((k < 0 || k > 127) || (*cp < '0' || *cp > '9')) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: note must be between 0 and 127", + name, line); + return 1; + } + tone->note = k; + tone->scltune = config_parse_int16("100", &tone->scltunenum); + } + else if (!strcmp(opts, "pan")) { + if (!strcmp(cp, "center")) + k = 64; + else if (!strcmp(cp, "left")) + k = 0; + else if (!strcmp(cp, "right")) + k = 127; + else { + k = ((atoi(cp) + 100) * 100) / 157; + if ((k < 0 || k > 127) + || (k == 0 && *cp != '-' && (*cp < '0' || *cp > '9'))) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: panning must be left, right, " + "center, or between -100 and 100", + name, line); + return 1; + } + } + tone->pan = k; + } + else if (!strcmp(opts, "tune")) + tone->tune = config_parse_tune(cp, &tone->tunenum); + else if (!strcmp(opts, "rate")) + tone->envrate = config_parse_envelope(cp, &tone->envratenum); + else if (!strcmp(opts, "offset")) + tone->envofs = config_parse_envelope(cp, &tone->envofsnum); + else if (!strcmp(opts, "keep")) { + if (!strcmp(cp, "env")) + tone->strip_envelope = 0; + else if (!strcmp(cp, "loop")) + tone->strip_loop = 0; + else { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: keep must be env or loop", name, line); + return 1; + } + } + else if (!strcmp(opts, "strip")) { + if (!strcmp(cp, "env")) + tone->strip_envelope = 1; + else if (!strcmp(cp, "loop")) + tone->strip_loop = 1; + else if (!strcmp(cp, "tail")) + tone->strip_tail = 1; + else { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: strip must be env, loop, or tail", + name, line); + return 1; + } + } + else if (!strcmp(opts, "tremolo")) { + if ((tone->trem = config_parse_modulation(name, + line, cp, &tone->tremnum, 0)) == NULL) + return 1; + } + else if (!strcmp(opts, "vibrato")) { + if ((tone->vib = config_parse_modulation(name, + line, cp, &tone->vibnum, 1)) == NULL) + return 1; + } + else if (!strcmp(opts, "sclnote")) + tone->sclnote = config_parse_int16(cp, &tone->sclnotenum); + else if (!strcmp(opts, "scltune")) + tone->scltune = config_parse_int16(cp, &tone->scltunenum); + else if (!strcmp(opts, "comm")) { + char *p; + + if (tone->comment) + free(tone->comment); + p = tone->comment = safe_strdup(cp); + while (*p) { + if (*p == ',') + *p = ' '; + p++; + } + } + else if (!strcmp(opts, "modrate")) + tone->modenvrate = config_parse_envelope(cp, &tone->modenvratenum); + else if (!strcmp(opts, "modoffset")) + tone->modenvofs = config_parse_envelope(cp, &tone->modenvofsnum); + else if (!strcmp(opts, "envkeyf")) + tone->envkeyf = config_parse_envelope(cp, &tone->envkeyfnum); + else if (!strcmp(opts, "envvelf")) + tone->envvelf = config_parse_envelope(cp, &tone->envvelfnum); + else if (!strcmp(opts, "modkeyf")) + tone->modenvkeyf = config_parse_envelope(cp, &tone->modenvkeyfnum); + else if (!strcmp(opts, "modvelf")) + tone->modenvvelf = config_parse_envelope(cp, &tone->modenvvelfnum); + else if (!strcmp(opts, "trempitch")) + tone->trempitch = config_parse_int16(cp, &tone->trempitchnum); + else if (!strcmp(opts, "tremfc")) + tone->tremfc = config_parse_int16(cp, &tone->tremfcnum); + else if (!strcmp(opts, "modpitch")) + tone->modpitch = config_parse_int16(cp, &tone->modpitchnum); + else if (!strcmp(opts, "modfc")) + tone->modfc = config_parse_int16(cp, &tone->modfcnum); + else if (!strcmp(opts, "fc")) + tone->fc = config_parse_int16(cp, &tone->fcnum); + else if (!strcmp(opts, "q")) + tone->reso = config_parse_int16(cp, &tone->resonum); + else if (!strcmp(opts, "fckeyf")) /* filter key-follow */ + tone->key_to_fc = atoi(cp); + else if (!strcmp(opts, "fcvelf")) /* filter velocity-follow */ + tone->vel_to_fc = atoi(cp); + else if (!strcmp(opts, "qvelf")) /* resonance velocity-follow */ + tone->vel_to_resonance = atoi(cp); + else { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: bad patch option %s", + name, line, opts); + return 1; + } + return 0; +} + + +void Instruments::reinit_tone_bank_element(ToneBankElement *tone) +{ + free_tone_bank_element(tone); + tone->note = tone->pan = -1; + tone->strip_loop = tone->strip_envelope = tone->strip_tail = -1; + tone->amp = -1; + tone->rnddelay = 0; + tone->loop_timeout = 0; + tone->legato = tone->damper_mode = tone->key_to_fc = tone->vel_to_fc = 0; + tone->reverb_send = tone->chorus_send = tone->delay_send = -1; + tone->tva_level = -1; + tone->play_note = -1; +} + + +int Instruments::set_gus_patchconf(const char *name, int line, ToneBankElement *tone, char *pat, char **opts) +{ + int j; + reinit_tone_bank_element(tone); + + if (strcmp(pat, "%font") == 0) /* Font extention */ + { + /* %font filename bank prog [note-to-use] + * %font filename 128 bank key + */ + + if (opts[0] == NULL || opts[1] == NULL || opts[2] == NULL || + (atoi(opts[1]) == 128 && opts[3] == NULL)) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Syntax error", name, line); + return 1; + } + tone->name = safe_strdup(opts[0]); + tone->instype = 1; + if (atoi(opts[1]) == 128) /* drum */ + { + tone->font_bank = 128; + tone->font_preset = atoi(opts[2]); + tone->font_keynote = atoi(opts[3]); + opts += 4; + } + else + { + tone->font_bank = atoi(opts[1]); + tone->font_preset = atoi(opts[2]); + + if (opts[3] && isdigit(opts[3][0])) + { + tone->font_keynote = atoi(opts[3]); + opts += 4; + } + else + { + tone->font_keynote = -1; + opts += 3; + } + } + } + else if (strcmp(pat, "%sample") == 0) /* Sample extention */ + { + /* %sample filename */ + + if (opts[0] == NULL) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Syntax error", name, line); + return 1; + } + tone->name = safe_strdup(opts[0]); + tone->instype = 2; + opts++; + } + else + { + tone->instype = 0; + tone->name = safe_strdup(pat); + } + + for (j = 0; opts[j] != NULL; j++) + { + int err; + if ((err = set_gus_patchconf_opts(name, line, opts[j], tone)) != 0) + return err; + } + if (tone->comment == NULL) + tone->comment = safe_strdup(tone->name); + return 0; +} + + + +int Instruments::set_patchconf(const char *name, int line, ToneBank *bank, char *w[], int dr, int mapid, int bankmapfrom, int bankno) +{ + int i; + + i = atoi(w[0]); + if (!dr) + i -= progbase; + if (i < 0 || i > 127) + { + if (dr) + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Drum number must be between " + "0 and 127", + name, line); + else + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Program must be between " + "%d and %d", + name, line, progbase, 127 + progbase); + return 1; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + return 1; + } + + if (set_gus_patchconf(name, line, &bank->tone[i], w[1], w + 2)) + return 1; + if (mapid != INST_NO_MAP) + set_instrument_map(mapid, bankmapfrom, i, bankno, i); + return 0; +} + + + +/* string[0] should not be '#' */ +int Instruments::strip_trailing_comment(char *string, int next_token_index) +{ + if (string[next_token_index - 1] == '#' /* strip \1 in /^\S+(#*[ \t].*)/ */ + && (string[next_token_index] == ' ' || string[next_token_index] == '\t')) + { + string[next_token_index] = '\0'; /* new c-string terminator */ + while (string[--next_token_index - 1] == '#') + ; + } + return next_token_index; +} + +char *Instruments::expand_variables(char *string, MBlockList *varbuf, const char *basedir) +{ + char *p, *expstr; + const char *copystr; + int limlen, copylen, explen, varlen, braced; + + if ((p = strchr(string, '$')) == NULL) + return string; + varlen = (int)strlen(basedir); + explen = limlen = 0; + expstr = NULL; + copystr = string; + copylen = p - string; + string = p; + for (;;) + { + if (explen + copylen + 1 > limlen) + { + limlen += copylen + 128; + expstr = (char*)memcpy(new_segment(varbuf, limlen), expstr, explen); + } + memcpy(&expstr[explen], copystr, copylen); + explen += copylen; + if (*string == '\0') + break; + else if (*string == '$') + { + braced = *++string == '{'; + if (braced) + { + if ((p = strchr(string + 1, '}')) == NULL) + p = string; /* no closing brace */ + else + string++; + } + else + for (p = string; isalnum(*p) || *p == '_'; p++); + if (p == string) /* empty */ + { + copystr = "${"; + copylen = 1 + braced; + } + else + { + if (p - string == 7 && memcmp(string, "basedir", 7) == 0) + { + copystr = basedir; + copylen = varlen; + } + else /* undefined variable */ + copylen = 0; + string = p + braced; + } + } + else /* search next */ + { + p = strchr(string, '$'); + if (p == NULL) + copylen = (int)strlen(string); + else + copylen = int(p - string); + copystr = string; + string += copylen; + } + } + expstr[explen] = '\0'; + return expstr; +} + + +int Instruments::read_config_file(const char *name, int self, int allow_missing_file) +{ + struct timidity_file *tf; + char buf[1024], *tmp, *w[MAXWORDS + 1], *cp; + ToneBank *bank = NULL; + int i, j, k, line = 0, words, errcnt = 0; + static int rcf_count = 0; + int dr = 0, bankno = 0, mapid = INST_NO_MAP, origbankno = 0x7FFFFFFF; + int extension_flag, param_parse_err; + MBlockList varbuf; + const char *basedir; + char *sep; + + if (rcf_count > 50) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "Probable source loop in configuration files"); + return READ_CONFIG_RECURSION; + } + + tf = open_file(name, 1, allow_missing_file ? OF_NORMAL : OF_VERBOSE, pathlist); + if (tf == NULL) + return allow_missing_file ? READ_CONFIG_FILE_NOT_FOUND : + READ_CONFIG_ERROR; + + init_mblock(&varbuf); + if (!self) + { + basedir = strdup_mblock(&varbuf, tf->filename.c_str()); + sep = (char*)strrchr(basedir, '/'); + } + else + sep = NULL; + if (sep == NULL) + { + basedir = "."; + } + else + { + if ((cp = (char*)strchr(sep, '#')) != NULL) + sep = cp + 1; /* inclusive of '#' */ + *sep = '\0'; + } + + while (tf_gets(buf, sizeof(buf), tf)) + { + line++; + if (strncmp(buf, "#extension", 10) == 0) { + extension_flag = 1; + i = 10; + } + else + { + extension_flag = 0; + i = 0; + } + + while (isspace(buf[i])) /* skip /^\s*(?#)/ */ + i++; + if (buf[i] == '#' || buf[i] == '\0') /* /^#|^$/ */ + continue; + tmp = expand_variables(buf, &varbuf, basedir); + j = (int)strcspn(tmp + i, " \t\r\n\240"); + if (j == 0) + j = (int)strlen(tmp + i); + j = strip_trailing_comment(tmp + i, j); + tmp[i + j] = '\0'; /* terminate the first token */ + w[0] = tmp + i; + i += j + 1; + words = param_parse_err = 0; + while (words < MAXWORDS - 1) /* -1 : next arg */ + { + char *terminator; + + while (isspace(tmp[i])) /* skip /^\s*(?#)/ */ + i++; + if (tmp[i] == '\0' + || tmp[i] == '#') /* /\s#/ */ + break; + if ((tmp[i] == '"' || tmp[i] == '\'') + && (terminator = strchr(tmp + i + 1, tmp[i])) != NULL) + { + if (!isspace(terminator[1]) && terminator[1] != '\0') + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: there must be at least one whitespace between " + "string terminator (%c) and the next parameter", name, line, tmp[i]); + CHECKERRLIMIT; + param_parse_err = 1; + break; + } + w[++words] = tmp + i + 1; + i = terminator - tmp + 1; + *terminator = '\0'; + } + else /* not terminated */ + { + j = (int)strcspn(tmp + i, " \t\r\n\240"); + if (j > 0) + j = strip_trailing_comment(tmp + i, j); + w[++words] = tmp + i; + i += j; + if (tmp[i] != '\0') /* unless at the end-of-string (i.e. EOF) */ + tmp[i++] = '\0'; /* terminate the token */ + } + } + if (param_parse_err) + continue; + w[++words] = NULL; + + /* + * #extension [something...] + */ + + /* #extension timeout program sec */ + if (strcmp(w[0], "timeout") == 0) + { + if (words != 3) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension timeout " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + bank->tone[i].loop_timeout = atoi(w[2]); + } + /* #extension copydrumset drumset */ + else if (strcmp(w[0], "copydrumset") == 0) + { + if (words < 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No copydrumset number given", + name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension copydrumset " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or " + "drum set before assignment", name, line); + CHECKERRLIMIT; + continue; + } + copybank(bank, drumset[i], mapid, origbankno, bankno); + } + /* #extension copybank bank */ + else if (strcmp(w[0], "copybank") == 0) + { + if (words < 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No copybank number given", + name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension copybank " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or " + "drum set before assignment", name, line); + CHECKERRLIMIT; + continue; + } + copybank(bank, tonebank[i], mapid, origbankno, bankno); + } + /* #extension copymap tomapid frommapid */ + else if (strcmp(w[0], "copymap") == 0) + { + int mapto, mapfrom; + int toisdrum, fromisdrum; + + if (words != 3) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if ((mapto = mapname2id(w[1], &toisdrum)) == -1) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid map name: %s", name, line, w[1]); + CHECKERRLIMIT; + continue; + } + if ((mapfrom = mapname2id(w[2], &fromisdrum)) == -1) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid map name: %s", name, line, w[2]); + CHECKERRLIMIT; + continue; + } + if (toisdrum != fromisdrum) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Map type should be matched", name, line); + CHECKERRLIMIT; + continue; + } + if (copymap(mapto, mapfrom, toisdrum)) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No free %s available to map", + name, line, toisdrum ? "drum set" : "tone bank"); + CHECKERRLIMIT; + continue; + } + } + /* #extension undef program */ + else if (strcmp(w[0], "undef") == 0) + { + if (words < 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No undef number given", + name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension undef " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or " + "drum set before assignment", name, line); + CHECKERRLIMIT; + continue; + } + free_tone_bank_element(&bank->tone[i]); + } + /* #extension altassign numbers... */ + else if (strcmp(w[0], "altassign") == 0) + { + ToneBank *bk; + + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before altassign", name, line); + CHECKERRLIMIT; + continue; + } + if (words < 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No alternate assignment", name, line); + CHECKERRLIMIT; + continue; + } + + if (!dr) { + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, + "%s: line %d: Warning: Not a drumset altassign" + " (ignored)", + name, line); + continue; + } + + bk = drumset[bankno]; + bk->alt = add_altassign_string(bk->alt, w + 1, words - 1); + } /* #extension legato [program] [0 or 1] */ + else if (strcmp(w[0], "legato") == 0) + { + if (words != 3) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension legato " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + bank->tone[i].legato = atoi(w[2]); + } /* #extension damper [program] [0 or 1] */ + else if (strcmp(w[0], "damper") == 0) + { + if (words != 3) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension damper " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + bank->tone[i].damper_mode = atoi(w[2]); + } /* #extension rnddelay [program] [0 or 1] */ + else if (strcmp(w[0], "rnddelay") == 0) + { + if (words != 3) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension rnddelay " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + bank->tone[i].rnddelay = atoi(w[2]); + } /* #extension level program tva_level */ + else if (strcmp(w[0], "level") == 0) + { + if (words != 3) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[2]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension level " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + cp = w[1]; + do { + if (string_to_7bit_range(cp, &j, &k)) + { + while (j <= k) + bank->tone[j++].tva_level = i; + } + cp = strchr(cp, ','); + } while (cp++ != NULL); + } /* #extension reverbsend */ + else if (strcmp(w[0], "reverbsend") == 0) + { + if (words != 3) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[2]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension reverbsend " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + cp = w[1]; + do { + if (string_to_7bit_range(cp, &j, &k)) + { + while (j <= k) + bank->tone[j++].reverb_send = i; + } + cp = strchr(cp, ','); + } while (cp++ != NULL); + } /* #extension chorussend */ + else if (strcmp(w[0], "chorussend") == 0) + { + if (words != 3) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[2]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension chorussend " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + cp = w[1]; + do { + if (string_to_7bit_range(cp, &j, &k)) + { + while (j <= k) + bank->tone[j++].chorus_send = i; + } + cp = strchr(cp, ','); + } while (cp++ != NULL); + } /* #extension delaysend */ + else if (strcmp(w[0], "delaysend") == 0) + { + if (words != 3) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[2]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension delaysend " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + cp = w[1]; + do { + if (string_to_7bit_range(cp, &j, &k)) + { + while (j <= k) + bank->tone[j++].delay_send = i; + } + cp = strchr(cp, ','); + } while (cp++ != NULL); + } /* #extension playnote */ + else if (strcmp(w[0], "playnote") == 0) + { + if (words != 3) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[2]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension playnote" + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + cp = w[1]; + do { + if (string_to_7bit_range(cp, &j, &k)) + { + while (j <= k) + bank->tone[j++].play_note = i; + } + cp = strchr(cp, ','); + } while (cp++ != NULL); + } + else if (!strcmp(w[0], "soundfont")) + { + int order, cutoff, isremove, reso, amp; + char *sf_file; + + if (words < 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No soundfont file given", + name, line); + CHECKERRLIMIT; + continue; + } + + sf_file = w[1]; + order = cutoff = reso = amp = -1; + isremove = 0; + for (j = 2; j < words; j++) + { + if (strcmp(w[j], "remove") == 0) + { + isremove = 1; + break; + } + if (!(cp = strchr(w[j], '='))) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: bad patch option %s", + name, line, w[j]); + CHECKERRLIMIT; + break; + } + *cp++ = 0; + k = atoi(cp); + if (!strcmp(w[j], "order")) + { + if (k < 0 || (*cp < '0' || *cp > '9')) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: order must be a digit", + name, line); + CHECKERRLIMIT; + break; + } + order = k; + } + else if (!strcmp(w[j], "cutoff")) + { + if (k < 0 || (*cp < '0' || *cp > '9')) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: cutoff must be a digit", + name, line); + CHECKERRLIMIT; + break; + } + cutoff = k; + } + else if (!strcmp(w[j], "reso")) + { + if (k < 0 || (*cp < '0' || *cp > '9')) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: reso must be a digit", + name, line); + CHECKERRLIMIT; + break; + } + reso = k; + } + else if (!strcmp(w[j], "amp")) + { + amp = k; + } + } + if (isremove) + remove_soundfont(sf_file); + else + add_soundfont(sf_file, order, cutoff, reso, amp); + } + else if (!strcmp(w[0], "font")) + { + int bank, preset, keynote; + if (words < 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: no font command", name, line); + CHECKERRLIMIT; + continue; + } + if (!strcmp(w[1], "exclude")) + { + if (words < 3) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No bank/preset/key is given", + name, line); + CHECKERRLIMIT; + continue; + } + bank = atoi(w[2]); + if (words >= 4) + preset = atoi(w[3]) - progbase; + else + preset = -1; + if (words >= 5) + keynote = atoi(w[4]); + else + keynote = -1; + if (exclude_soundfont(bank, preset, keynote)) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No soundfont is given", + name, line); + CHECKERRLIMIT; + } + } + else if (!strcmp(w[1], "order")) + { + int order; + if (words < 4) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No order/bank is given", + name, line); + CHECKERRLIMIT; + continue; + } + order = atoi(w[2]); + bank = atoi(w[3]); + if (words >= 5) + preset = atoi(w[4]) - progbase; + else + preset = -1; + if (words >= 6) + keynote = atoi(w[5]); + else + keynote = -1; + if (order_soundfont(bank, preset, keynote, order)) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No soundfont is given", + name, line); + CHECKERRLIMIT; + } + } + } + else if (!strcmp(w[0], "progbase")) + { + if (words < 2 || *w[1] < '0' || *w[1] > '9') + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + progbase = atoi(w[1]); + } + else if (!strcmp(w[0], "map")) /* map set1 elem1 set2 elem2 */ + { + int arg[5], isdrum; + + if (words != 6) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if ((arg[0] = mapname2id(w[1], &isdrum)) == -1) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid map name: %s", name, line, w[1]); + CHECKERRLIMIT; + continue; + } + for (i = 2; i < 6; i++) + arg[i - 1] = atoi(w[i]); + if (isdrum) + { + arg[1] -= progbase; + arg[3] -= progbase; + } + else + { + arg[2] -= progbase; + arg[4] -= progbase; + } + + for (i = 1; i < 5; i++) + if (arg[i] < 0 || arg[i] > 127) + break; + if (i != 5) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid parameter", name, line); + CHECKERRLIMIT; + continue; + } + set_instrument_map(arg[0], arg[1], arg[2], arg[3], arg[4]); + } + + /* + * Standard configurations + */ + else if (!strcmp(w[0], "dir")) + { + if (words < 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No directory given", name, line); + CHECKERRLIMIT; + continue; + } + for (i = 1; i < words; i++) + pathlist.addPath(w[i]); + } + else if (!strcmp(w[0], "source") || !strcmp(w[0], "trysource")) + { + if (words < 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No file name given", name, line); + CHECKERRLIMIT; + continue; + } + for (i = 1; i < words; i++) + { + int status; + rcf_count++; + status = read_config_file(w[i], 0, !strcmp(w[0], "trysource")); + rcf_count--; + switch (status) { + case READ_CONFIG_SUCCESS: + break; + case READ_CONFIG_ERROR: + CHECKERRLIMIT; + continue; + case READ_CONFIG_RECURSION: + reuse_mblock(&varbuf); + close_file(tf); + return READ_CONFIG_RECURSION; + case READ_CONFIG_FILE_NOT_FOUND: + break; + } + } + } + else if (!strcmp(w[0], "default")) + { + if (words != 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify exactly one patch name", + name, line); + CHECKERRLIMIT; + continue; + } + strncpy(def_instr_name, w[1], 255); + def_instr_name[255] = '\0'; + default_instrument_name = def_instr_name; + } + /* drumset [mapid] num */ + else if (!strcmp(w[0], "drumset")) + { + int newmapid, isdrum, newbankno; + + if (words < 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No drum set number given", name, line); + CHECKERRLIMIT; + continue; + } + if (words != 2 && !isdigit(*w[1])) + { + if ((newmapid = mapname2id(w[1], &isdrum)) == -1 || !isdrum) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid drum set map name: %s", name, line, w[1]); + CHECKERRLIMIT; + continue; + } + words--; + memmove(&w[1], &w[2], sizeof w[0] * words); + } + else + newmapid = INST_NO_MAP; + i = atoi(w[1]) - progbase; + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Drum set must be between %d and %d", + name, line, + progbase, progbase + 127); + CHECKERRLIMIT; + continue; + } + + newbankno = i; + i = alloc_instrument_map_bank(1, newmapid, i); + if (i == -1) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No free drum set available to map", + name, line); + CHECKERRLIMIT; + continue; + } + + if (words == 2) + { + bank = drumset[i]; + bankno = i; + mapid = newmapid; + origbankno = newbankno; + dr = 1; + } + else + { + if (words < 4 || *w[2] < '0' || *w[2] > '9') + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (set_patchconf(name, line, drumset[i], &w[2], 1, newmapid, newbankno, i)) + { + CHECKERRLIMIT; + continue; + } + } + } + /* bank [mapid] num */ + else if (!strcmp(w[0], "bank")) + { + int newmapid, isdrum, newbankno; + + if (words < 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No bank number given", name, line); + CHECKERRLIMIT; + continue; + } + if (words != 2 && !isdigit(*w[1])) + { + if ((newmapid = mapname2id(w[1], &isdrum)) == -1 || isdrum) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid bank map name: %s", name, line, w[1]); + CHECKERRLIMIT; + continue; + } + words--; + memmove(&w[1], &w[2], sizeof w[0] * words); + } + else + newmapid = INST_NO_MAP; + i = atoi(w[1]); + if (i < 0 || i > 127) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Tone bank must be between 0 and 127", + name, line); + CHECKERRLIMIT; + continue; + } + + newbankno = i; + i = alloc_instrument_map_bank(0, newmapid, i); + if (i == -1) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No free tone bank available to map", + name, line); + CHECKERRLIMIT; + continue; + } + + if (words == 2) + { + bank = tonebank[i]; + bankno = i; + mapid = newmapid; + origbankno = newbankno; + dr = 0; + } + else + { + if (words < 4 || *w[2] < '0' || *w[2] > '9') + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (set_patchconf(name, line, tonebank[i], &w[2], 0, newmapid, newbankno, i)) + { + CHECKERRLIMIT; + continue; + } + } + } + else + { + if (words < 2 || *w[0] < '0' || *w[0] > '9') + { + if (extension_flag) + continue; + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (set_patchconf(name, line, bank, w, dr, mapid, origbankno, bankno)) + { + CHECKERRLIMIT; + continue; + } + } + } + reuse_mblock(&varbuf); + close_file(tf); + return (errcnt == 0) ? READ_CONFIG_SUCCESS : READ_CONFIG_ERROR; +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/controls.h b/src/sound/timidity++/controls.h new file mode 100644 index 0000000000..180d924f90 --- /dev/null +++ b/src/sound/timidity++/controls.h @@ -0,0 +1,79 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + controls.h +*/ + +#ifndef ___CONTROLS_H_ +#define ___CONTROLS_H_ + + +namespace TimidityPlus +{ + +enum +{ + /* Return values for ControlMode.read */ + RC_ERROR = -1, + RC_OK = 0, + RC_QUIT = 1, + RC_TUNE_END = 3, + RC_STOP = 4, /* Stop to play */ + + CMSG_INFO = 0, + CMSG_WARNING = 1, + CMSG_ERROR = 2, + + VERB_NORMAL = 0, + VERB_VERBOSE = 1, + VERB_NOISY = 2, + VERB_DEBUG = 3, +}; + +inline bool RC_IS_SKIP_FILE(int rc) +{ + return ((rc) == RC_QUIT || (rc) == RC_ERROR || (rc) == RC_STOP || (rc) == RC_TUNE_END); +} + +inline void ctl_cmsg(int type, int verbosity_level, const char *fmt, ...) +{ + /* todo: + CMSG_ERROR: I_Error(...) + CMSG_WARNING, VERB_NORMAL: Printf(TEXTCOLOR_YELLOW + CMSG_WARNING, VERB_VERBOSE: DPrintf(DMSG_SPAMMY + + + CMSG_INFO, VERB_DEBUG/VERB_NOISY: ignore + +/* +#ifdef _WIN32 + char buf[1024]; + va_list args; + va_start(args, fmt); + vsprintf(buf, fmt, args); + va_end(args); + I_DebugPrint(buf); +#endif +*/ +} + + +} + +#endif /* ___CONTROLS_H_ */ diff --git a/src/sound/timidity++/effect.cpp b/src/sound/timidity++/effect.cpp new file mode 100644 index 0000000000..6eb8f2260a --- /dev/null +++ b/src/sound/timidity++/effect.cpp @@ -0,0 +1,225 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + effect.c - To apply sound effects. + Mainly written by Masanao Izumo + + Interfaces: + void init_effect(void); + do_effect(int32_t* buf, int32_t count); +*/ + +#include +#include + +#include "effect.h" +#include "instrum.h" +#include "playmidi.h" +#include "reverb.h" + + +namespace TimidityPlus +{ + + + +#define SIDE_CONTI_SEC 10 +#define CHANGE_SEC 2.0 + +void Effect::init_effect() +{ + effect_left_right_delay(NULL, 0); + reverb->init_for_effect(); +} + +/* + * Left & Right Delay Effect + */ +void Effect::effect_left_right_delay(int32_t *buff, int32_t count) +{ + int32_t save[AUDIO_BUFFER_SIZE * 2]; + int32_t pi, i, j, k, v, backoff; + int b; + int32_t *p; + + if (buff == NULL) + { + memset(prev, 0, sizeof(prev)); + return; + } + if (effect_lr_mode == 0 || effect_lr_mode == 1 || effect_lr_mode == 2) + b = effect_lr_mode; + else + return; + count *= 2; + backoff = 2 * (int)(playback_rate * effect_lr_delay_msec / 1000.0); + if (backoff == 0) + return; + if (backoff > count) + backoff = count; + if (count < audio_buffer_size * 2) + { + memset(buff + count, 0, 4 * (audio_buffer_size * 2 - count)); + count = audio_buffer_size * 2; + } + memcpy(save, buff, 4 * count); + pi = count - backoff; + if (b == 2) + { + if (turn_counter == 0) + { + turn_counter = SIDE_CONTI_SEC * playback_rate; + /* status: 0 -> 2 -> 3 -> 1 -> 4 -> 5 -> 0 -> ... + * status left right + * 0 - + (right) + * 1 + - (left) + * 2 -> + + (right -> center) + * 3 + -> - (center -> left) + * 4 -> - - (left -> center) + * 5 - -> + (center -> right) + */ + status = 0; + tc = 0; + } + p = prev; + for (i = 0; i < count; i += 2, pi += 2) + { + if (i < backoff) + p = prev; + else if (p == prev) + { + pi = 0; + p = save; + } + if (status < 2) + buff[i + status] = p[pi + status]; + else if (status < 4) + { + j = (status & 1); + v = (int32_t)(rate0 * buff[i + j] + rate1 * p[pi + j]); + buff[i + j] = v; + rate0 += dr, rate1 -= dr; + } + else + { + j = (status & 1); + k = !j; + v = (int32_t)(rate0 * buff[i + j] + rate1 * p[pi + j]); + buff[i + j] = v; + buff[i + k] = p[pi + k]; + rate0 += dr, rate1 -= dr; + } + tc++; + if (tc == turn_counter) + { + tc = 0; + switch (status) + { + case 0: + status = 2; + turn_counter = (CHANGE_SEC / 2.0) * playback_rate; + rate0 = 0.0; + rate1 = 1.0; + dr = 1.0 / turn_counter; + break; + case 2: + status = 3; + rate0 = 1.0; + rate1 = 0.0; + dr = -1.0 / turn_counter; + break; + case 3: + status = 1; + turn_counter = SIDE_CONTI_SEC * playback_rate; + break; + case 1: + status = 4; + turn_counter = (CHANGE_SEC / 2.0) * playback_rate; + rate0 = 1.0; + rate1 = 0.0; + dr = -1.0 / turn_counter; + break; + case 4: + status = 5; + turn_counter = (CHANGE_SEC / 2.0) * playback_rate; + rate0 = 0.0; + rate1 = 1.0; + dr = 1.0 / turn_counter; + break; + case 5: + status = 0; + turn_counter = SIDE_CONTI_SEC * playback_rate; + break; + } + } + } + } + else + { + for (i = 0; i < backoff; i += 2, pi += 2) + buff[b + i] = prev[b + pi]; + for (pi = 0; i < count; i += 2, pi += 2) + buff[b + i] = save[b + pi]; + } + memcpy(prev + count - backoff, save + count - backoff, 4 * backoff); +} + +void Effect::do_effect(int32_t *buf, int32_t count) +{ + int32_t nsamples = count * 2; + int reverb_level = (opt_reverb_control < 0) + ? -opt_reverb_control & 0x7f : DEFAULT_REVERB_SEND_LEVEL; + + /* for static reverb / chorus level */ + if (opt_reverb_control == 2 || opt_reverb_control == 4 + || (opt_reverb_control < 0 && !(opt_reverb_control & 0x80)) + || opt_chorus_control < 0) + { + reverb->set_dry_signal(buf, nsamples); + /* chorus sounds horrible + * if applied globally on top of channel chorus + */ + if (opt_reverb_control == 2 || opt_reverb_control == 4 + || (opt_reverb_control < 0 && !(opt_reverb_control & 0x80))) + reverb->set_ch_reverb(buf, nsamples, reverb_level); + reverb->mix_dry_signal(buf, nsamples); + /* chorus sounds horrible + * if applied globally on top of channel chorus + */ + if (opt_reverb_control == 2 || opt_reverb_control == 4 + || (opt_reverb_control < 0 && !(opt_reverb_control & 0x80))) + reverb->do_ch_reverb(buf, nsamples); + } + /* L/R Delay */ + effect_left_right_delay(buf, count); +} + +uint32_t Effect::frand(void) +{ + return rng.GenRand32(); +} + +int32_t Effect::my_mod(int32_t x, int32_t n) +{ + if (x >= n) + x -= n; + return x; +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/effect.h b/src/sound/timidity++/effect.h new file mode 100644 index 0000000000..77bb52dc12 --- /dev/null +++ b/src/sound/timidity++/effect.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include "m_random.h" + +#include "timidity.h" + +namespace TimidityPlus +{ + +class Reverb; + +class Effect +{ + void effect_left_right_delay(int32_t *, int32_t); + void init_mtrand(void); + uint32_t frand(void); + int32_t my_mod(int32_t, int32_t); + + int turn_counter = 0, tc = 0; + int status = 0; + double rate0 = 0, rate1 = 0, dr = 0; + int32_t prev[AUDIO_BUFFER_SIZE * 2] = { 0 }; + + FRandom rng; + Reverb *reverb; + +public: + Effect(Reverb *_reverb) + { + reverb = _reverb; + } + + void init_effect(); + void do_effect(int32_t *buf, int32_t count); + +}; + +} \ No newline at end of file diff --git a/src/sound/timidity++/fft4g.cpp b/src/sound/timidity++/fft4g.cpp new file mode 100644 index 0000000000..126d498923 --- /dev/null +++ b/src/sound/timidity++/fft4g.cpp @@ -0,0 +1,1351 @@ +/* EAW - May 11th, 2001: Changed all doubles to floats */ + +/* +Copyright: + Copyright(C) 1996-1999 Takuya OOURA + email: ooura@mmm.t.u-tokyo.ac.jp + download: http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html + You may use, copy, modify this code for any purpose and + without fee. You may distribute this ORIGINAL package. +*/ + +/* +Fast Fourier/Cosine/Sine Transform + dimension :one + data length :power of 2 + decimation :frequency + radix :4, 2 + data :inplace + table :use +functions + cdft: Complex Discrete Fourier Transform + rdft: Real Discrete Fourier Transform + ddct: Discrete Cosine Transform + ddst: Discrete Sine Transform + dfct: Cosine Transform of RDFT (Real Symmetric DFT) + dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) +function prototypes + void cdft(int, int, float *, int *, float *); + void rdft(int, int, float *, int *, float *); + void ddct(int, int, float *, int *, float *); + void ddst(int, int, float *, int *, float *); + void dfct(int, float *, float *, int *, float *); + void dfst(int, float *, float *, int *, float *); + + +-------- Complex DFT (Discrete Fourier Transform) -------- + [definition] + + X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k + X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k + ip[0] = 0; // first time only + cdft(2*n, 1, a, ip, w); + + ip[0] = 0; // first time only + cdft(2*n, -1, a, ip, w); + [parameters] + 2*n :data length (int) + n >= 1, n = power of 2 + a[0...2*n-1] :input/output data (float *) + input data + a[2*j] = Re(x[j]), + a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) + strictly, + length of ip >= + 2+(1<<(int)(log(n+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + cdft(2*n, -1, a, ip, w); + is + cdft(2*n, 1, a, ip, w); + for (j = 0; j <= 2 * n - 1; j++) { + a[j] *= 1.0 / n; + } + . + + +-------- Real DFT / Inverse of Real DFT -------- + [definition] + RDFT + R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 + I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0 IRDFT (excluding scale) + a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k + ip[0] = 0; // first time only + rdft(n, 1, a, ip, w); + + ip[0] = 0; // first time only + rdft(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + + output data + a[2*k] = R[k], 0<=k + input data + a[2*j] = R[j], 0<=j= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + rdft(n, 1, a, ip, w); + is + rdft(n, -1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- + [definition] + IDCT (excluding scale) + C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k DCT + C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k + ip[0] = 0; // first time only + ddct(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddct(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + output data + a[k] = C[k], 0<=k= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddct(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddct(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DST (Discrete Sine Transform) / Inverse of DST -------- + [definition] + IDST (excluding scale) + S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k DST + S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0 + ip[0] = 0; // first time only + ddst(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddst(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + + input data + a[j] = A[j], 0 + output data + a[k] = S[k], 0= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddst(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddst(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Cosine Transform of RDFT (Real Symmetric DFT) -------- + [definition] + C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n + [usage] + ip[0] = 0; // first time only + dfct(n, a, t, ip, w); + [parameters] + n :data length - 1 (int) + n >= 2, n = power of 2 + a[0...n] :input/output data (float *) + output data + a[k] = C[k], 0<=k<=n + t[0...n/2] :work area (float *) + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + is + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + for (j = 0; j <= n; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- + [definition] + S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + output data + a[k] = S[k], 0= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + dfst(n, a, t, ip, w); + is + dfst(n, a, t, ip, w); + for (j = 1; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +Appendix : + The cos/sin table is recalculated when the larger table required. + w[] and ip[] are compatible with all routines. +*/ + +namespace TimidityPlus +{ + void makewt(int nw, int *ip, float *w); + void bitrv2(int n, int *ip, float *a); + void bitrv2conj(int n, int *ip, float *a); + void cftfsub(int n, float *a, float *w); + void cftbsub(int n, float *a, float *w); + void makect(int nc, int *ip, float *c); + void rftfsub(int n, float *a, int nc, float *c); + void rftbsub(int n, float *a, int nc, float *c); + void dctsub(int n, float *a, int nc, float *c); + void dstsub(int n, float *a, int nc, float *c); + void cft1st(int n, float *a, float *w); + void cftmdl(int n, int l, float *a, float *w); + + + +#ifdef _MSC_VER +#pragma warning(disable:4244) // conversion from 'double' to 'float', possible loss of data +#endif + + +void cdft(int n, int isgn, float *a, int *ip, float *w) +{ + if (n > (ip[0] << 2)) { + makewt(n >> 2, ip, w); + } + if (n > 4) { + if (isgn >= 0) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + } + else { + bitrv2conj(n, ip + 2, a); + cftbsub(n, a, w); + } + } + else if (n == 4) { + cftfsub(n, a, w); + } +} + + +void rdft(int n, int isgn, float *a, int *ip, float *w) +{ + int nw, nc; + float xi; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 2)) { + nc = n >> 2; + makect(nc, ip, w + nw); + } + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } + else if (n == 4) { + cftfsub(n, a, w); + } + xi = a[0] - a[1]; + a[0] += a[1]; + a[1] = xi; + } + else { + a[1] = 0.5 * (a[0] - a[1]); + a[0] -= a[1]; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } + else if (n == 4) { + cftfsub(n, a, w); + } + } +} + + +void ddct(int n, int isgn, float *a, int *ip, float *w) +{ + int j, nw, nc; + float xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = a[j] - a[j - 1]; + a[j] += a[j - 1]; + } + a[1] = a[0] - xr; + a[0] += xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } + else if (n == 4) { + cftfsub(n, a, w); + } + } + dctsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } + else if (n == 4) { + cftfsub(n, a, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = a[j] - a[j + 1]; + a[j] += a[j + 1]; + } + a[n - 1] = xr; + } +} + + +void ddst(int n, int isgn, float *a, int *ip, float *w) +{ + int j, nw, nc; + float xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = -a[j] - a[j - 1]; + a[j] -= a[j - 1]; + } + a[1] = a[0] + xr; + a[0] -= xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } + else if (n == 4) { + cftfsub(n, a, w); + } + } + dstsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } + else if (n == 4) { + cftfsub(n, a, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = -a[j] - a[j + 1]; + a[j] -= a[j + 1]; + } + a[n - 1] = -xr; + } +} + + +void dfct(int n, float *a, float *t, int *ip, float *w) +{ + int j, k, l, m, mh, nw, nc; + float xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + m = n >> 1; + yi = a[m]; + xi = a[0] + a[n]; + a[0] -= a[n]; + t[0] = xi - yi; + t[m] = xi + yi; + if (n > 2) { + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] - a[n - j]; + xi = a[j] + a[n - j]; + yr = a[k] - a[n - k]; + yi = a[k] + a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi - yi; + t[k] = xi + yi; + } + t[mh] = a[mh] + a[n - mh]; + a[mh] -= a[n - mh]; + dctsub(m, a, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, a); + cftfsub(m, a, w); + rftfsub(m, a, nc, w + nw); + } + else if (m == 4) { + cftfsub(m, a, w); + } + a[n - 1] = a[0] - a[1]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] + a[j + 1]; + a[2 * j - 1] = a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dctsub(m, t, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, t); + cftfsub(m, t, w); + rftfsub(m, t, nc, w + nw); + } + else if (m == 4) { + cftfsub(m, t, w); + } + a[n - l] = t[0] - t[1]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = t[j] - t[j + 1]; + a[k + l] = t[j] + t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 0; j < mh; j++) { + k = m - j; + t[j] = t[m + k] - t[m + j]; + t[k] = t[m + k] + t[m + j]; + } + t[mh] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + a[n] = t[2] - t[1]; + a[0] = t[2] + t[1]; + } + else { + a[1] = a[0]; + a[2] = t[0]; + a[0] = t[1]; + } +} + + +void dfst(int n, float *a, float *t, int *ip, float *w) +{ + int j, k, l, m, mh, nw, nc; + float xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + if (n > 2) { + m = n >> 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] + a[n - j]; + xi = a[j] - a[n - j]; + yr = a[k] + a[n - k]; + yi = a[k] - a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi + yi; + t[k] = xi - yi; + } + t[0] = a[mh] - a[n - mh]; + a[mh] += a[n - mh]; + a[0] = a[m]; + dstsub(m, a, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, a); + cftfsub(m, a, w); + rftfsub(m, a, nc, w + nw); + } + else if (m == 4) { + cftfsub(m, a, w); + } + a[n - 1] = a[1] - a[0]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] - a[j + 1]; + a[2 * j - 1] = -a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dstsub(m, t, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, t); + cftfsub(m, t, w); + rftfsub(m, t, nc, w + nw); + } + else if (m == 4) { + cftfsub(m, t, w); + } + a[n - l] = t[1] - t[0]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = -t[j] - t[j + 1]; + a[k + l] = t[j] - t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + t[j] = t[m + k] + t[m + j]; + t[k] = t[m + k] - t[m + j]; + } + t[0] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + } + a[0] = 0; +} + + +/* -------- initializing routines -------- */ + + +#include + +void makewt(int nw, int *ip, float *w) +{ + void bitrv2(int n, int *ip, float *a); + int j, nwh; + float delta, x, y; + + ip[0] = nw; + ip[1] = 1; + if (nw > 2) { + nwh = nw >> 1; + delta = atan(1.0) / nwh; + w[0] = 1; + w[1] = 0; + w[nwh] = cos(delta * nwh); + w[nwh + 1] = w[nwh]; + if (nwh > 2) { + for (j = 2; j < nwh; j += 2) { + x = cos(delta * j); + y = sin(delta * j); + w[j] = x; + w[j + 1] = y; + w[nw - j] = y; + w[nw - j + 1] = x; + } + bitrv2(nw, ip + 2, w); + } + } +} + + +void makect(int nc, int *ip, float *c) +{ + int j, nch; + float delta; + + ip[1] = nc; + if (nc > 1) { + nch = nc >> 1; + delta = atan(1.0) / nch; + c[0] = cos(delta * nch); + c[nch] = 0.5 * c[0]; + for (j = 1; j < nch; j++) { + c[j] = 0.5 * cos(delta * j); + c[nc - j] = 0.5 * sin(delta * j); + } + } +} + + +/* -------- child routines -------- */ + + +void bitrv2(int n, int *ip, float *a) +{ + int j, j1, k, k1, l, m, m2; + float xr, xi, yr, yi; + + ip[0] = 0; + l = n; + m = 1; + while ((m << 3) < l) { + l >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + l; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == l) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + j1 = 2 * k + m2 + ip[k]; + k1 = j1 + m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } + else { + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } + } +} + + +void bitrv2conj(int n, int *ip, float *a) +{ + int j, j1, k, k1, l, m, m2; + float xr, xi, yr, yi; + + ip[0] = 0; + l = n; + m = 1; + while ((m << 3) < l) { + l >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + l; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == l) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 2 * k + ip[k]; + a[k1 + 1] = -a[k1 + 1]; + j1 = k1 + m2; + k1 = j1 + m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + k1 += m2; + a[k1 + 1] = -a[k1 + 1]; + } + } + else { + a[1] = -a[1]; + a[m2 + 1] = -a[m2 + 1]; + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 2 * k + ip[k]; + a[k1 + 1] = -a[k1 + 1]; + a[k1 + m2 + 1] = -a[k1 + m2 + 1]; + } + } +} + + +void cftfsub(int n, float *a, float *w) +{ + int j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + } + else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = a[j + 1] - a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] += a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + + +void cftbsub(int n, float *a, float *w) +{ + int j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = -a[j + 1] - a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = -a[j + 1] + a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i + x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i - x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i + x3r; + } + } + else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = -a[j + 1] + a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] = -a[j + 1] - a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + + +void cft1st(int n, float *a, float *w) +{ + int j, k1, k2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[2]; + x0i = a[1] + a[3]; + x1r = a[0] - a[2]; + x1i = a[1] - a[3]; + x2r = a[4] + a[6]; + x2i = a[5] + a[7]; + x3r = a[4] - a[6]; + x3i = a[5] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[2] = x1r - x3i; + a[3] = x1i + x3r; + a[6] = x1r + x3i; + a[7] = x1i - x3r; + wk1r = w[2]; + x0r = a[8] + a[10]; + x0i = a[9] + a[11]; + x1r = a[8] - a[10]; + x1i = a[9] - a[11]; + x2r = a[12] + a[14]; + x2i = a[13] + a[15]; + x3r = a[12] - a[14]; + x3i = a[13] - a[15]; + a[8] = x0r + x2r; + a[9] = x0i + x2i; + a[12] = x2i - x0i; + a[13] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[10] = wk1r * (x0r - x0i); + a[11] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[14] = wk1r * (x0i - x0r); + a[15] = wk1r * (x0i + x0r); + k1 = 0; + for (j = 16; j < n; j += 16) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + x0r = a[j] + a[j + 2]; + x0i = a[j + 1] + a[j + 3]; + x1r = a[j] - a[j + 2]; + x1i = a[j + 1] - a[j + 3]; + x2r = a[j + 4] + a[j + 6]; + x2i = a[j + 5] + a[j + 7]; + x3r = a[j + 4] - a[j + 6]; + x3i = a[j + 5] - a[j + 7]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 4] = wk2r * x0r - wk2i * x0i; + a[j + 5] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 2] = wk1r * x0r - wk1i * x0i; + a[j + 3] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 6] = wk3r * x0r - wk3i * x0i; + a[j + 7] = wk3r * x0i + wk3i * x0r; + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + x0r = a[j + 8] + a[j + 10]; + x0i = a[j + 9] + a[j + 11]; + x1r = a[j + 8] - a[j + 10]; + x1i = a[j + 9] - a[j + 11]; + x2r = a[j + 12] + a[j + 14]; + x2i = a[j + 13] + a[j + 15]; + x3r = a[j + 12] - a[j + 14]; + x3i = a[j + 13] - a[j + 15]; + a[j + 8] = x0r + x2r; + a[j + 9] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 12] = -wk2i * x0r - wk2r * x0i; + a[j + 13] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 10] = wk1r * x0r - wk1i * x0i; + a[j + 11] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 14] = wk3r * x0r - wk3i * x0i; + a[j + 15] = wk3r * x0i + wk3i * x0r; + } +} + + +void cftmdl(int n, int l, float *a, float *w) +{ + int j, j1, j2, j3, k, k1, k2, m, m2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + m = l << 2; + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + wk1r = w[2]; + for (j = m; j < l + m; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x2i - x0i; + a[j2 + 1] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * (x0r - x0i); + a[j1 + 1] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[j3] = wk1r * (x0i - x0r); + a[j3 + 1] = wk1r * (x0i + x0r); + } + k1 = 0; + m2 = 2 * m; + for (k = m2; k < n; k += m2) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + for (j = k; j < l + k; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = wk2r * x0r - wk2i * x0i; + a[j2 + 1] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + for (j = k + m; j < l + (k + m); j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = -wk2i * x0r - wk2r * x0i; + a[j2 + 1] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + } +} + + +void rftfsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + + +void rftbsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + a[1] = -a[1]; + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j] -= yr; + a[j + 1] = yi - a[j + 1]; + a[k] += yr; + a[k + 1] = yi - a[k + 1]; + } + a[m + 1] = -a[m + 1]; +} + + +void dctsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[j] - wkr * a[k]; + a[j] = wkr * a[j] + wki * a[k]; + a[k] = xr; + } + a[m] *= c[0]; +} + + +void dstsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[k] - wkr * a[j]; + a[k] = wkr * a[k] + wki * a[j]; + a[j] = xr; + } + a[m] *= c[0]; +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/fft4g.h b/src/sound/timidity++/fft4g.h new file mode 100644 index 0000000000..2465feae32 --- /dev/null +++ b/src/sound/timidity++/fft4g.h @@ -0,0 +1,16 @@ +/* + Copyright(C) 1996-1999 Takuya OOURA + email: ooura@mmm.t.u-tokyo.ac.jp + download: http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html + You may use, copy, modify this code for any purpose and + without fee. You may distribute this ORIGINAL package. +*/ +namespace TimidityPlus +{ + extern void cdft(int, int, float *, int *, float *); + extern void rdft(int, int, float *, int *, float *); + extern void ddct(int, int, float *, int *, float *); + extern void ddst(int, int, float *, int *, float *); + extern void dfct(int, float *, float *, int *, float *); + extern void dfst(int, float *, float *, int *, float *); +} \ No newline at end of file diff --git a/src/sound/timidity++/filter.cpp b/src/sound/timidity++/filter.cpp new file mode 100644 index 0000000000..7e3cb37ea9 --- /dev/null +++ b/src/sound/timidity++/filter.cpp @@ -0,0 +1,200 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + filter.c: written by Vincent Pagel ( pagel@loria.fr ) + + implements fir antialiasing filter : should help when setting sample + rates as low as 8Khz. + + April 95 + - first draft + + 22/5/95 + - modify "filter" so that it simulate leading and trailing 0 in the buffer + */ + +#include +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "filter.h" + +namespace TimidityPlus +{ + +/* bessel function */ +static double ino(double x) +{ + double y, de, e, sde; + int i; + + y = x / 2; + e = 1.0; + de = 1.0; + i = 1; + do { + de = de * y / (double)i; + sde = de * de; + e += sde; + } while (!((e * 1.0e-08 - sde > 0) || (i++ > 25))); + return(e); +} + +/* Kaiser Window (symetric) */ +static void kaiser(double *w, int n, double beta) +{ + double xind, xi; + int i; + + xind = (2 * n - 1) * (2 * n - 1); + for (i = 0; i < n; i++) + { + xi = i + 0.5; + w[i] = ino((double)(beta * sqrt((double)(1. - 4 * xi * xi / xind)))) + / ino((double)beta); + } +} + +/* + * fir coef in g, cuttoff frequency in fc + */ +static void designfir(double *g, double fc) +{ + int i; + double xi, omega, att, beta; + double w[ORDER2]; + + for (i = 0; i < ORDER2; i++) + { + xi = (double)i + 0.5; + omega = M_PI * xi; + g[i] = sin((double)omega * fc) / omega; + } + + att = 40.; /* attenuation in db */ + beta = (double)exp(log((double)0.58417 * (att - 20.96)) * 0.4) + 0.07886 + * (att - 20.96); + kaiser(w, ORDER2, beta); + + /* Matrix product */ + for (i = 0; i < ORDER2; i++) + g[i] = g[i] * w[i]; +} + +/* + * FIR filtering -> apply the filter given by coef[] to the data buffer + * Note that we simulate leading and trailing 0 at the border of the + * data buffer + */ +static void filter(int16_t *result, int16_t *data, int32_t length, double coef[]) +{ + int32_t sample, i, sample_window; + int16_t peak = 0; + double sum; + + /* Simulate leading 0 at the begining of the buffer */ + for (sample = 0; sample < ORDER2; sample++) + { + sum = 0.0; + sample_window = sample - ORDER2; + + for (i = 0; i < ORDER; i++) + sum += coef[i] * + ((sample_window < 0) ? 0.0 : data[sample_window++]); + + /* Saturation ??? */ + if (sum > 32767.) { sum = 32767.; peak++; } + if (sum < -32768.) { sum = -32768; peak++; } + result[sample] = (int16_t)sum; + } + + /* The core of the buffer */ + for (sample = ORDER2; sample < length - ORDER + ORDER2; sample++) + { + sum = 0.0; + sample_window = sample - ORDER2; + + for (i = 0; i < ORDER; i++) + sum += data[sample_window++] * coef[i]; + + /* Saturation ??? */ + if (sum > 32767.) { sum = 32767.; peak++; } + if (sum < -32768.) { sum = -32768; peak++; } + result[sample] = (int16_t)sum; + } + + /* Simulate 0 at the end of the buffer */ + for (sample = length - ORDER + ORDER2; sample < length; sample++) + { + sum = 0.0; + sample_window = sample - ORDER2; + + for (i = 0; i < ORDER; i++) + sum += coef[i] * + ((sample_window >= length) ? 0.0 : data[sample_window++]); + + /* Saturation ??? */ + if (sum > 32767.) { sum = 32767.; peak++; } + if (sum < -32768.) { sum = -32768; peak++; } + result[sample] = (int16_t)sum; + } +} + +/***********************************************************************/ +/* Prevent aliasing by filtering any freq above the output_rate */ +/* */ +/* I don't worry about looping point -> they will remain soft if they */ +/* were already */ +/***********************************************************************/ +void antialiasing(int16_t *data, int32_t data_length, + int32_t sample_rate, int32_t output_rate) +{ + int16_t *temp; + int i; + double fir_symetric[ORDER]; + double fir_coef[ORDER2]; + double freq_cut; /* cutoff frequency [0..1.0] FREQ_CUT/SAMP_FREQ*/ + + + /* No oversampling */ + if (output_rate >= sample_rate) + return; + + freq_cut = (double)output_rate / (double)sample_rate; + + designfir(fir_coef, freq_cut); + + /* Make the filter symetric */ + for (i = 0; i < ORDER2; i++) + fir_symetric[ORDER - 1 - i] = fir_symetric[i] = fir_coef[ORDER2 - 1 - i]; + + /* We apply the filter we have designed on a copy of the patch */ + temp = (int16_t *)safe_malloc(2 * data_length); + memcpy(temp, data, 2 * data_length); + + filter(data, temp, data_length, fir_symetric); + + free(temp); +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/filter.h b/src/sound/timidity++/filter.h new file mode 100644 index 0000000000..fd868a6cb7 --- /dev/null +++ b/src/sound/timidity++/filter.h @@ -0,0 +1,46 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + filter.h : written by Vincent Pagel ( pagel@loria.fr ) + + implements fir antialiasing filter : should help when setting sample + rates as low as 8Khz. + + */ + +#ifndef ___FILTER_H_ +#define ___FILTER_H_ + +#include + + +namespace TimidityPlus +{ + +/* Order of the FIR filter = 20 should be enough ! */ +enum +{ + ORDER = 20, + ORDER2 = ORDER / 2 +}; + +void antialiasing(int16_t *data, int32_t data_length, int32_t sample_rate, int32_t output_rate); + +} +#endif /* ___FILTER_H_ */ diff --git a/src/sound/timidity++/freq.cpp b/src/sound/timidity++/freq.cpp new file mode 100644 index 0000000000..4a0599b0ed --- /dev/null +++ b/src/sound/timidity++/freq.cpp @@ -0,0 +1,709 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "freq.h" +#include "fft4g.h" + + +namespace TimidityPlus +{ + + +/* middle C = pitch 60 = 261.6 Hz + freq = 13.75 * exp((pitch - 9) / 12 * log(2)) + pitch = 9 - log(13.75 / freq) * 12 / log(2) + = -36.37631656 + 17.31234049 * log(freq) +*/ +const float pitch_freq_table[129] = { + 8.17579892f, 8.66195722f, 9.17702400f, 9.72271824f, 10.3008612f, 10.9133822f, + 11.5623257f, 12.2498574f, 12.9782718f, 13.7500000f, 14.5676175f, 15.4338532f, + + 16.3515978f, 17.3239144f, 18.3540480f, 19.4454365f, 20.6017223f, 21.8267645f, + 23.1246514f, 24.4997147f, 25.9565436f, 27.5000000f, 29.1352351f, 30.8677063f, + + 32.7031957f, 34.6478289f, 36.7080960f, 38.8908730f, 41.2034446f, 43.6535289f, + 46.2493028f, 48.9994295f, 51.9130872f, 55.0000000f, 58.2704702f, 61.7354127f, + + 65.4063913f, 69.2956577f, 73.4161920f, 77.7817459f, 82.4068892f, 87.3070579f, + 92.4986057f, 97.9988590f, 103.826174f, 110.000000f, 116.540940f, 123.470825f, + + 130.812783f, 138.591315f, 146.832384f, 155.563492f, 164.813778f, 174.614116f, + 184.997211f, 195.997718f, 207.652349f, 220.000000f, 233.081881f, 246.941651f, + + 261.625565f, 277.182631f, 293.664768f, 311.126984f, 329.627557f, 349.228231f, + 369.994423f, 391.995436f, 415.304698f, 440.000000f, 466.163762f, 493.883301f, + + 523.251131f, 554.365262f, 587.329536f, 622.253967f, 659.255114f, 698.456463f, + 739.988845f, 783.990872f, 830.609395f, 880.000000f, 932.327523f, 987.766603f, + + 1046.50226f, 1108.73052f, 1174.65907f, 1244.50793f, 1318.51023f, 1396.91293f, + 1479.97769f, 1567.98174f, 1661.21879f, 1760.00000f, 1864.65505f, 1975.53321f, + + 2093.00452f, 2217.46105f, 2349.31814f, 2489.01587f, 2637.02046f, 2793.82585f, + 2959.95538f, 3135.96349f, 3322.43758f, 3520.00000f, 3729.31009f, 3951.06641f, + + 4186.00904f, 4434.92210f, 4698.63629f, 4978.03174f, 5274.04091f, 5587.65170f, + 5919.91076f, 6271.92698f, 6644.87516f, 7040.00000f, 7458.62018f, 7902.13282f, + + 8372.01809f, 8869.84419f, 9397.27257f, 9956.06348f, 10548.0818f, 11175.3034f, + 11839.8215f, 12543.8540f, 13289.7503f +}; + + + +/* center_pitch + 0.49999 */ +const float pitch_freq_ub_table[129] = { + 8.41536325f, 8.91576679f, 9.44592587f, 10.0076099f, 10.6026933f, 11.2331623f, + 11.9011208f, 12.6087983f, 13.3585565f, 14.1528976f, 14.9944727f, 15.8860904f, + 16.8307265f, 17.8315336f, 18.8918517f, 20.0152197f, 21.2053866f, 22.4663245f, + 23.8022417f, 25.2175966f, 26.7171129f, 28.3057952f, 29.9889453f, 31.7721808f, + 33.6614530f, 35.6630672f, 37.7837035f, 40.0304394f, 42.4107732f, 44.9326490f, + 47.6044834f, 50.4351932f, 53.4342259f, 56.6115903f, 59.9778907f, 63.5443616f, + 67.3229060f, 71.3261343f, 75.5674070f, 80.0608788f, 84.8215464f, 89.8652980f, + 95.2089667f, 100.870386f, 106.868452f, 113.223181f, 119.955781f, 127.088723f, + 134.645812f, 142.652269f, 151.134814f, 160.121758f, 169.643093f, 179.730596f, + 190.417933f, 201.740773f, 213.736904f, 226.446361f, 239.911563f, 254.177446f, + 269.291624f, 285.304537f, 302.269628f, 320.243515f, 339.286186f, 359.461192f, + 380.835867f, 403.481546f, 427.473807f, 452.892723f, 479.823125f, 508.354893f, + 538.583248f, 570.609074f, 604.539256f, 640.487030f, 678.572371f, 718.922384f, + 761.671734f, 806.963092f, 854.947614f, 905.785445f, 959.646250f, 1016.70979f, + 1077.16650f, 1141.21815f, 1209.07851f, 1280.97406f, 1357.14474f, 1437.84477f, + 1523.34347f, 1613.92618f, 1709.89523f, 1811.57089f, 1919.29250f, 2033.41957f, + 2154.33299f, 2282.43630f, 2418.15702f, 2561.94812f, 2714.28948f, 2875.68954f, + 3046.68693f, 3227.85237f, 3419.79046f, 3623.14178f, 3838.58500f, 4066.83914f, + 4308.66598f, 4564.87260f, 4836.31405f, 5123.89624f, 5428.57897f, 5751.37907f, + 6093.37387f, 6455.70474f, 6839.58092f, 7246.28356f, 7677.17000f, 8133.67829f, + 8617.33197f, 9129.74519f, 9672.62809f, 10247.7925f, 10857.1579f, 11502.7581f, + 12186.7477f, 12911.4095f, 13679.1618f +}; + + + +/* center_pitch - 0.49999 */ +const float pitch_freq_lb_table[129] = { + 7.94305438f, 8.41537297f, 8.91577709f, 9.44593678f, 10.0076214f, 10.6027055f, + 11.2331752f, 11.9011346f, 12.6088129f, 13.3585719f, 14.1529139f, 14.9944900f, + 15.8861088f, 16.8307459f, 17.8315542f, 18.8918736f, 20.0152428f, 21.2054111f, + 22.4663505f, 23.8022692f, 25.2176258f, 26.7171438f, 28.3058279f, 29.9889800f, + 31.7722175f, 33.6614919f, 35.6631084f, 37.7837471f, 40.0304857f, 42.4108222f, + 44.9327009f, 47.6045384f, 50.4352515f, 53.4342876f, 56.6116557f, 59.9779599f, + 63.5444350f, 67.3229838f, 71.3262167f, 75.5674943f, 80.0609713f, 84.8216444f, + 89.8654018f, 95.2090767f, 100.870503f, 106.868575f, 113.223311f, 119.955920f, + 127.088870f, 134.645968f, 142.652433f, 151.134989f, 160.121943f, 169.643289f, + 179.730804f, 190.418153f, 201.741006f, 213.737151f, 226.446623f, 239.911840f, + 254.177740f, 269.291935f, 285.304867f, 302.269977f, 320.243885f, 339.286578f, + 359.461607f, 380.836307f, 403.482012f, 427.474301f, 452.893246f, 479.823680f, + 508.355480f, 538.583870f, 570.609734f, 604.539954f, 640.487770f, 678.573155f, + 718.923215f, 761.672614f, 806.964024f, 854.948602f, 905.786491f, 959.647359f, + 1016.71096f, 1077.16774f, 1141.21947f, 1209.07991f, 1280.97554f, 1357.14631f, + 1437.84643f, 1523.34523f, 1613.92805f, 1709.89720f, 1811.57298f, 1919.29472f, + 2033.42192f, 2154.33548f, 2282.43893f, 2418.15982f, 2561.95108f, 2714.29262f, + 2875.69286f, 3046.69045f, 3227.85610f, 3419.79441f, 3623.14597f, 3838.58944f, + 4066.84384f, 4308.67096f, 4564.87787f, 4836.31963f, 5123.90216f, 5428.58524f, + 5751.38572f, 6093.38091f, 6455.71219f, 6839.58882f, 7246.29193f, 7677.17887f, + 8133.68768f, 8617.34192f, 9129.75574f, 9672.63927f, 10247.8043f, 10857.1705f, + 11502.7714f, 12186.7618f, 12911.4244f +}; + + + +/* (M)ajor, rotate back 1, rotate back 2 + (m)inor, rotate back 1, rotate back 2 + (d)iminished minor, rotate back 1, rotate back 2 + (f)ifth, rotate back 1, rotate back 2 +*/ +const int chord_table[4][3][3] = { + { { 0, 4, 7, }, { -5, 0, 4, }, { -8, -5, 0, }, }, + { { 0, 3, 7, }, { -5, 0, 3, }, { -9, -5, 0, }, }, + { { 0, 3, 6, }, { -6, 0, 3, }, { -9, -6, 0, }, }, + { { 0, 5, 7, }, { -5, 0, 5, }, { -7, -5, 0, }, }, +}; + +/* write the chord type to *chord, returns the root note of the chord */ +int Freq::assign_chord(double *pitchbins, int *chord, + int min_guesspitch, int max_guesspitch, int root_pitch) +{ + + int type, subtype; + int pitches[19] = { 0 }; + int prune_pitches[10] = { 0 }; + int i, j, k, n, n2; + double val, cutoff, max; + int root_flag; + + *chord = -1; + + if (root_pitch - 9 > min_guesspitch) + min_guesspitch = root_pitch - 9; + + if (min_guesspitch <= LOWEST_PITCH) + min_guesspitch = LOWEST_PITCH + 1; + + if (root_pitch + 9 < max_guesspitch) + max_guesspitch = root_pitch + 9; + + if (max_guesspitch >= HIGHEST_PITCH) + max_guesspitch = HIGHEST_PITCH - 1; + + /* keep only local maxima */ + for (i = min_guesspitch, n = 0; i <= max_guesspitch; i++) + { + val = pitchbins[i]; + if (val) + { + if (pitchbins[i - 1] < val && pitchbins[i + 1] < val) + pitches[n++] = i; + } + } + + if (n < 3) + return -1; + + /* find largest peak */ + max = -1; + for (i = 0; i < n; i++) + { + val = pitchbins[pitches[i]]; + if (val > max) + max = val; + } + + /* discard any peaks below cutoff */ + cutoff = 0.2 * max; + for (i = 0, n2 = 0, root_flag = 0; i < n; i++) + { + val = pitchbins[pitches[i]]; + if (val >= cutoff) + { + prune_pitches[n2++] = pitches[i]; + if (pitches[i] == root_pitch) + root_flag = 1; + } + } + + if (!root_flag || n2 < 3) + return -1; + + /* search for a chord, must contain root pitch */ + for (i = 0; i < n2; i++) + { + for (subtype = 0; subtype < 3; subtype++) + { + if (i + subtype >= n2) + continue; + + for (type = 0; type < 4; type++) + { + for (j = 0, n = 0, root_flag = 0; j < 3; j++) + { + k = i + j; + + if (k >= n2) + continue; + + if (prune_pitches[k] == root_pitch) + root_flag = 1; + + if (prune_pitches[k] - prune_pitches[i + subtype] == + chord_table[type][subtype][j]) + n++; + } + if (root_flag && n == 3) + { + *chord = 3 * type + subtype; + return prune_pitches[i + subtype]; + } + } + } + } + + return -1; +} + + + +/* initialize FFT arrays for the frequency analysis */ +int Freq::freq_initialize_fft_arrays(Sample *sp) +{ + + uint32_t i; + uint32_t length, newlength; + unsigned int rate; + sample_t *origdata; + + rate = sp->sample_rate; + length = sp->data_length >> FRACTION_BITS; + origdata = sp->data; + + /* copy the sample to a new float array */ + floatData.resize(length); + for (i = 0; i < length; i++) + floatData[i] = origdata[i]; + + /* length must be a power of 2 */ + /* set it to smallest power of 2 >= 1.4*rate */ + /* at least 1.4*rate is required for decent resolution of low notes */ + newlength = pow(2, ceil(log(1.4*rate) / log(2))); + if (length < newlength) + { + floatData.resize(newlength); + memset(&floatData[0] + length, 0, (newlength - length) * sizeof(float)); + } + length = newlength; + + /* allocate FFT arrays */ + /* calculate sin/cos and fft1_bin_to_pitch tables */ + if (length != oldfftsize) + { + float f0; + + magData.resize(length); + pruneMagData.resize(length); + ipa.resize(int(2 + sqrt(length)) * sizeof(int)); + ipa[0] = 0; + wa.resize(length >> 1); + fft1BinToPitch.resize(length >> 1); + + for (i = 1, f0 = (float)rate / length; i < (length >> 1); i++) { + fft1BinToPitch[i] = assign_pitch_to_freq(i * f0); + } + } + oldfftsize = length; + + /* zero out arrays that need it */ + memset(pitchmags, 0, 129 * sizeof(float)); + memset(pitchbins, 0, 129 * sizeof(double)); + memset(new_pitchbins, 0, 129 * sizeof(double)); + memset(&pruneMagData[0], 0, length * sizeof(float)); + + return(length); +} + + + +/* return the frequency of the sample */ +/* max of 1.4 - 2.0 seconds of audio is analyzed, depending on sample rate */ +/* samples < 1.4 seconds are padded to max length for higher fft accuracy */ +float Freq::freq_fourier(Sample *sp, int *chord) +{ + + uint32_t length, length0; + int32_t maxoffset, minoffset, minoffset1, minoffset2; + int32_t minbin, maxbin; + int32_t bin; + uint32_t i; + int32_t j, n, total; + unsigned int rate; + int pitch, bestpitch, minpitch, maxpitch, maxpitch2; + sample_t *origdata; + float f0, mag, maxmag; + int16_t amp, oldamp, maxamp; + int32_t maxpos = 0; + double sum, weightsum, maxsum; + double f0_inv; + float freq, newfreq, bestfreq, freq_inc; + float minfreq, maxfreq, minfreq2, maxfreq2; + float min_guessfreq, max_guessfreq; + + rate = sp->sample_rate; + length = length0 = sp->data_length >> FRACTION_BITS; + origdata = sp->data; + + length = freq_initialize_fft_arrays(sp); + + /* base frequency of the FFT */ + f0 = (float)rate / length; + f0_inv = 1.0 / f0; + + /* get maximum amplitude */ + maxamp = -1; + for (uint32_t i = 0; i < length0; i++) + { + amp = abs(origdata[i]); + if (amp >= maxamp) + { + maxamp = amp; + maxpos = i; + } + } + + /* go out 2 zero crossings in both directions, starting at maxpos */ + /* find the peaks after the 2nd crossing */ + minoffset1 = 0; + for (n = 0, oldamp = origdata[maxpos], i = maxpos - 1; i >= 0 && n < 2; i--) + { + amp = origdata[i]; + if ((oldamp && amp == 0) || (oldamp > 0 && amp < 0) || + (oldamp < 0 && amp > 0)) + n++; + oldamp = amp; + } + minoffset1 = i; + maxamp = labs(origdata[i]); + while (i >= 0) + { + amp = origdata[i]; + if ((oldamp && amp == 0) || (oldamp > 0 && amp < 0) || + (oldamp < 0 && amp > 0)) + { + break; + } + oldamp = amp; + + amp = labs(amp); + if (amp > maxamp) + { + maxamp = amp; + minoffset1 = i; + } + i--; + } + + minoffset2 = 0; + for (n = 0, oldamp = origdata[maxpos], i = maxpos + 1; i < length0 && n < 2; i++) + { + amp = origdata[i]; + if ((oldamp && amp == 0) || (oldamp > 0 && amp < 0) || + (oldamp < 0 && amp > 0)) + n++; + oldamp = amp; + } + minoffset2 = i; + maxamp = labs(origdata[i]); + while (i < length0) + { + amp = origdata[i]; + if ((oldamp && amp == 0) || (oldamp > 0 && amp < 0) || + (oldamp < 0 && amp > 0)) + { + break; + } + oldamp = amp; + + amp = labs(amp); + if (amp > maxamp) + { + maxamp = amp; + minoffset2 = i; + } + i++; + } + + /* upper bound on the detected frequency */ + /* distance between the two peaks is at most 2 periods */ + minoffset = (minoffset2 - minoffset1); + if (minoffset < 4) + minoffset = 4; + max_guessfreq = (float)rate / (minoffset * 0.5); + if (max_guessfreq >= (rate >> 1)) max_guessfreq = (rate >> 1) - 1; + + /* lower bound on the detected frequency */ + maxoffset = rate / pitch_freq_lb_table[LOWEST_PITCH] + 0.5; + if ((uint32_t)maxoffset > (length >> 1)) + maxoffset = (length >> 1); + min_guessfreq = (float)rate / maxoffset; + + /* perform the in place FFT */ + rdft(length, 1, &floatData[0], &ipa[0], &wa[0]); + + /* calc the magnitudes */ + for (i = 2; i < length; i++) + { + mag = floatData[i++]; + mag *= mag; + mag += floatData[i] * floatData[i]; + magData[i >> 1] = sqrt(mag); + } + + /* find max mag */ + maxmag = 0; + for (i = 1; i < (length >> 1); i++) + { + mag = magData[i]; + + pitch = fft1BinToPitch[i]; + if (pitch && mag > maxmag) + maxmag = mag; + } + + /* Apply non-linear scaling to the magnitudes + * I don't know why this improves the pitch detection, but it does + * The best choice of power seems to be between 1.64 - 1.68 + */ + for (i = 1; i < (length >> 1); i++) + magData[i] = maxmag * pow(magData[i] / maxmag, 1.66); + + /* bin the pitches */ + for (i = 1; i < (length >> 1); i++) + { + mag = magData[i]; + + pitch = fft1BinToPitch[i]; + pitchbins[pitch] += mag; + + if (mag > pitchmags[pitch]) + pitchmags[pitch] = mag; + } + + /* zero out lowest pitch, since it contains all lower frequencies too */ + pitchbins[LOWEST_PITCH] = 0; + + /* find the largest peak */ + for (i = LOWEST_PITCH + 1, maxsum = -42; i <= HIGHEST_PITCH; i++) + { + sum = pitchbins[i]; + if (sum > maxsum) + { + maxsum = sum; + } + } + + minpitch = assign_pitch_to_freq(min_guessfreq); + if (minpitch > HIGHEST_PITCH) minpitch = HIGHEST_PITCH; + + /* zero out any peak below minpitch */ + for (int i = LOWEST_PITCH + 1; i < minpitch; i++) + pitchbins[i] = 0; + + /* remove all pitches below threshold */ + for (int i = minpitch; i <= HIGHEST_PITCH; i++) + { + if (pitchbins[i] / maxsum < 0.01 && pitchmags[i] / maxmag < 0.01) + pitchbins[i] = 0; + } + + /* keep local maxima */ + for (int i = LOWEST_PITCH + 1; i < HIGHEST_PITCH; i++) + { + double temp; + + temp = pitchbins[i]; + + /* also keep significant bands to either side */ + if (temp && pitchbins[i - 1] < temp && pitchbins[i + 1] < temp) + { + new_pitchbins[i] = temp; + + temp *= 0.5; + if (pitchbins[i - 1] >= temp) + new_pitchbins[i - 1] = pitchbins[i - 1]; + if (pitchbins[i + 1] >= temp) + new_pitchbins[i + 1] = pitchbins[i - 1]; + } + } + memcpy(pitchbins, new_pitchbins, 129 * sizeof(double)); + + /* find lowest and highest pitches */ + minpitch = LOWEST_PITCH; + while (minpitch < HIGHEST_PITCH && !pitchbins[minpitch]) + minpitch++; + maxpitch = HIGHEST_PITCH; + while (maxpitch > LOWEST_PITCH && !pitchbins[maxpitch]) + maxpitch--; + + /* uh oh, no pitches left... + * best guess is middle C + * return 260 Hz, since exactly 260 Hz is never returned except on error + * this should only occur on blank/silent samples + */ + if (maxpitch < minpitch) + { + return 260.0; + } + + /* pitch assignment bounds based on zero crossings and pitches kept */ + if (pitch_freq_lb_table[minpitch] > min_guessfreq) + min_guessfreq = pitch_freq_lb_table[minpitch]; + if (pitch_freq_ub_table[maxpitch] < max_guessfreq) + max_guessfreq = pitch_freq_ub_table[maxpitch]; + + minfreq = pitch_freq_lb_table[minpitch]; + if (minfreq >= (rate >> 1)) minfreq = (rate >> 1) - 1; + + maxfreq = pitch_freq_ub_table[maxpitch]; + if (maxfreq >= (rate >> 1)) maxfreq = (rate >> 1) - 1; + + minbin = minfreq / f0; + if (!minbin) + minbin = 1; + maxbin = ceil(maxfreq / f0); + if ((uint32_t)maxbin >= (length >> 1)) + maxbin = (length >> 1) - 1; + + /* filter out all "noise" from magnitude array */ + for (int i = minbin, n = 0; i <= maxbin; i++) + { + pitch = fft1BinToPitch[i]; + if (pitchbins[pitch]) + { + pruneMagData[i] = magData[i]; + n++; + } + } + + /* whoa!, there aren't any strong peaks at all !!! bomb early + * best guess is middle C + * return 260 Hz, since exactly 260 Hz is never returned except on error + * this should only occur on blank/silent samples + */ + if (!n) + { + return 260.0; + } + + memset(new_pitchbins, 0, 129 * sizeof(double)); + + maxsum = -1; + minpitch = assign_pitch_to_freq(min_guessfreq); + maxpitch = assign_pitch_to_freq(max_guessfreq); + maxpitch2 = assign_pitch_to_freq(max_guessfreq) + 9; + if (maxpitch2 > HIGHEST_PITCH) maxpitch2 = HIGHEST_PITCH; + + /* initial guess is first local maximum */ + bestfreq = pitch_freq_table[minpitch]; + if (minpitch < HIGHEST_PITCH && + pitchbins[minpitch + 1] > pitchbins[minpitch]) + bestfreq = pitch_freq_table[minpitch + 1]; + + /* find best fundamental */ + for (int i = minpitch; i <= maxpitch2; i++) + { + if (!pitchbins[i]) + continue; + + minfreq2 = pitch_freq_lb_table[i]; + maxfreq2 = pitch_freq_ub_table[i]; + freq_inc = (maxfreq2 - minfreq2) * 0.1; + if (minfreq2 >= (rate >> 1)) minfreq2 = (rate >> 1) - 1; + if (maxfreq2 >= (rate >> 1)) maxfreq2 = (rate >> 1) - 1; + + /* look for harmonics */ + for (freq = minfreq2; freq <= maxfreq2; freq += freq_inc) + { + n = total = 0; + sum = weightsum = 0; + + for (j = 1; j <= 32 && (newfreq = j * freq) <= maxfreq; j++) + { + pitch = assign_pitch_to_freq(newfreq); + + if (pitchbins[pitch]) + { + sum += pitchbins[pitch]; + n++; + total = j; + } + } + + /* only pitches with good harmonics are assignment candidates */ + if (n > 1) + { + double ratio; + + ratio = (double)n / total; + if (ratio >= 0.333333) + { + weightsum = ratio * sum; + pitch = assign_pitch_to_freq(freq); + + /* use only these pitches for chord detection */ + if (pitch <= HIGHEST_PITCH && pitchbins[pitch]) + new_pitchbins[pitch] = weightsum; + + if (pitch > maxpitch) + continue; + + if (n < 2 || weightsum > maxsum) + { + maxsum = weightsum; + bestfreq = freq; + } + } + } + } + } + + bestpitch = assign_pitch_to_freq(bestfreq); + + /* assign chords */ + if ((pitch = assign_chord(new_pitchbins, chord, + bestpitch - 9, maxpitch2, bestpitch)) >= 0) + bestpitch = pitch; + + bestfreq = pitch_freq_table[bestpitch]; + + /* tune based on the fundamental and harmonics up to +5 octaves */ + sum = weightsum = 0; + for (i = 1; i <= 32 && (freq = i * bestfreq) <= maxfreq; i++) + { + double tune; + + minfreq2 = pitch_freq_lb_table[bestpitch]; + maxfreq2 = pitch_freq_ub_table[bestpitch]; + + minbin = minfreq2 * f0_inv; + if (!minbin) minbin = 1; + maxbin = ceil(maxfreq2 * f0_inv); + if ((uint32_t)maxbin >= (length >> 1)) + maxbin = (length >> 1) - 1; + + for (bin = minbin; bin <= maxbin; bin++) + { + tune = -36.37631656 + 17.31234049 * log(bin*f0) - bestpitch; + sum += magData[bin]; + weightsum += magData[bin] * tune; + } + } + + bestfreq = 13.75 * exp(((bestpitch + weightsum / sum) - 9) / + 12 * log(2)); + + /* Since we are using exactly 260 Hz as an error code, fudge the freq + * on the extremely unlikely chance that the detected pitch is exactly + * 260 Hz. + */ + if (bestfreq == 260.0f) + bestfreq += 1E-5f; + + return bestfreq; +} + + + +int assign_pitch_to_freq(float freq) +{ + /* round to nearest integer using: ceil(fraction - 0.5) */ + /* -0.5 is already added into the first constant below */ + int pitch = ceil(-36.87631656f + 17.31234049f * log(freq)); + + /* min and max pitches */ + if (pitch < LOWEST_PITCH) pitch = LOWEST_PITCH; + else if (pitch > HIGHEST_PITCH) pitch = HIGHEST_PITCH; + + return pitch; +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/freq.h b/src/sound/timidity++/freq.h new file mode 100644 index 0000000000..9f3bef4f63 --- /dev/null +++ b/src/sound/timidity++/freq.h @@ -0,0 +1,67 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include + +namespace TimidityPlus +{ + +extern const float pitch_freq_table[129]; +extern const float pitch_freq_ub_table[129]; +extern const float pitch_freq_lb_table[129]; +extern const int chord_table[4][3][3]; + +extern int assign_pitch_to_freq(float freq); + +enum +{ + CHORD_MAJOR = 0, + CHORD_MINOR = 3, + CHORD_DIM = 6, + CHORD_FIFTH = 9, + LOWEST_PITCH = 0, + HIGHEST_PITCH = 127 +}; + +struct Sample; + +class Freq +{ + std::vector floatData; + std::vector magData; + std::vector pruneMagData; + std::vector ipa; + std::vector wa; + std::vector fft1BinToPitch; + uint32_t oldfftsize = 0; + float pitchmags[129] = { 0 }; + double pitchbins[129] = { 0 }; + double new_pitchbins[129] = { 0 }; + + int assign_chord(double *pitchbins, int *chord, int min_guesspitch, int max_guesspitch, int root_pitch); + int freq_initialize_fft_arrays(Sample *sp); + +public: + + float freq_fourier(Sample *sp, int *chord); + +}; + +} \ No newline at end of file diff --git a/src/sound/timidity++/instrum.cpp b/src/sound/timidity++/instrum.cpp new file mode 100644 index 0000000000..3339a885d5 --- /dev/null +++ b/src/sound/timidity++/instrum.cpp @@ -0,0 +1,2049 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + instrum.c + + Code to load and unload GUS-compatible instrument patches. +*/ + +#include +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "resample.h" +#include "tables.h" +#include "filter.h" +#include "quantity.h" +#include "freq.h" + +namespace TimidityPlus +{ + + +Instruments::Instruments() +{ + // init one-time global stuff - this should go to the device class once it exists. + initialize_resampler_coeffs(); + init_tables(); + + memset(&standard_tonebank, 0, sizeof(standard_tonebank)); + memset(&standard_drumset, 0, sizeof(standard_drumset)); + memcpy(layer_items, static_layer_items, sizeof(layer_items)); +} + +bool Instruments::load(const char *config) +{ + if (read_config_file(config, 0, 0)) + { + init_load_soundfont(); + set_default_instrument(); + return true; + } + return false; +} + +Instruments::~Instruments() +{ + free_instruments(0); + free_soundfonts(); + + free_tone_bank(); + free_instrument_map(); +} + +void Instruments::free_instrument(Instrument *ip) +{ + Sample *sp; + int i; + if (!ip) return; + + for (i = 0; isamples; i++) + { + sp = &(ip->sample[i]); + if (sp->data_alloced) + free(sp->data); + } + free(ip->sample); + free(ip); +} + + + +/* calculate ramp rate in fractional unit; + * diff = 8bit, time = msec + */ +int32_t Instruments::calc_rate_i(int diff, double msec) +{ + double rate; + + if(msec < 6) + msec = 6; + if(diff == 0) + diff = 255; + diff <<= (7+15); + rate = ((double)diff / playback_rate) * control_ratio * 1000.0 / msec; + if(fast_decay) + rate *= 2; + return (int32_t)rate; +} +/*End of Pseudo Reverb*/ + + +void Instruments::clear_magic_instruments(void) +{ + int i, j; + + for (j = 0; j < 128 + map_bank_counter; j++) + { + if (tonebank[j]) + { + ToneBank *bank = tonebank[j]; + for (i = 0; i < 128; i++) + if (IS_MAGIC_INSTRUMENT(bank->tone[i].instrument)) + bank->tone[i].instrument = NULL; + } + if (drumset[j]) + { + ToneBank *bank = drumset[j]; + for (i = 0; i < 128; i++) + if (IS_MAGIC_INSTRUMENT(bank->tone[i].instrument)) + bank->tone[i].instrument = NULL; + } + } +} + + +int32_t Instruments::convert_envelope_rate(uint8_t rate) +{ + const int32_t GUS_ENVRATE_MAX = (int32_t)(0x3FFFFFFF >> 9); + + int32_t r; + + r = 3 - ((rate >> 6) & 0x3); + r *= 3; + r = (int32_t)(rate & 0x3f) << r; /* 6.9 fixed point */ + + /* 15.15 fixed point. */ + r = r * 44100 / playback_rate * control_ratio * (1 << fast_decay); + if (r > GUS_ENVRATE_MAX) { r = GUS_ENVRATE_MAX; } + return (r << 9); +} + +int32_t Instruments::convert_envelope_offset(uint8_t offset) +{ + /* This is not too good... Can anyone tell me what these values mean? + Are they GUS-style "exponential" volumes? And what does that mean? */ + + /* 15.15 fixed point */ + return offset << (7 + 15); +} + +int32_t Instruments::convert_tremolo_sweep(uint8_t sweep) +{ + if (!sweep) + return 0; + + return + ((control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (playback_rate * sweep); +} + +int32_t Instruments::convert_vibrato_sweep(uint8_t sweep, int32_t vib_control_ratio) +{ + if (!sweep) + return 0; + + return (int32_t)(TIM_FSCALE((double) (vib_control_ratio) + * SWEEP_TUNING, SWEEP_SHIFT) + / (double)(playback_rate * sweep)); + + /* this was overflowing with seashore.pat + + ((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (playback_rate * sweep); */ +} + +int32_t Instruments::convert_tremolo_rate(uint8_t rate) +{ + return + ((SINE_CYCLE_LENGTH * control_ratio * rate) << RATE_SHIFT) / + (TREMOLO_RATE_TUNING * playback_rate); +} + +int32_t Instruments::convert_vibrato_rate(uint8_t rate) +{ + /* Return a suitable vibrato_control_ratio value */ + return + (VIBRATO_RATE_TUNING * playback_rate) / + (rate * 2 * VIBRATO_SAMPLE_INCREMENTS); +} + +void Instruments::reverse_data(int16_t *sp, int32_t ls, int32_t le) +{ + int16_t s, *ep = sp + le; + int32_t i; + sp += ls; + le -= ls; + le /= 2; + for (i = 0; i < le; i++) + { + s = *sp; + *sp++ = *ep; + *ep-- = s; + } +} + +int Instruments::name_hash(char *name) +{ + unsigned int addr = 0; + + while(*name) + addr += *name++; + return addr % INSTRUMENT_HASH_SIZE; +} + +Instrument *Instruments::search_instrument_cache(char *name, int panning, int amp, int note_to_use, int strip_loop, int strip_envelope, int strip_tail) +{ + struct InstrumentCache *p; + + for (p = instrument_cache[name_hash(name)]; p != NULL; p = p->next) + { + if (strcmp(p->name, name) != 0) + return NULL; + if (p->panning == panning && + p->amp == amp && + p->note_to_use == note_to_use && + p->strip_loop == strip_loop && + p->strip_envelope == strip_envelope && + p->strip_tail == strip_tail) + return p->ip; + } + return NULL; +} + +void Instruments::store_instrument_cache(Instrument *ip, char *name, int panning, int amp, int note_to_use, int strip_loop, int strip_envelope, int strip_tail) +{ + struct InstrumentCache *p; + int addr; + + addr = name_hash(name); + p = (struct InstrumentCache *)safe_malloc(sizeof(struct InstrumentCache)); + p->next = instrument_cache[addr]; + instrument_cache[addr] = p; + p->name = name; + p->panning = panning; + p->amp = amp; + p->note_to_use = note_to_use; + p->strip_loop = strip_loop; + p->strip_envelope = strip_envelope; + p->strip_tail = strip_tail; + p->ip = ip; +} + +static int32_t adjust_tune_freq(int32_t val, float tune) +{ + if (! tune) + return val; + return val / pow(2.0, tune / 12.0); +} + +static int16_t adjust_scale_tune(int16_t val) +{ + return 1024 * (double) val / 100 + 0.5; +} + +static int16_t adjust_fc(int16_t val) +{ + if (val < 0 || val > playback_rate / 2) { + return 0; + } else { + return val; + } +} + +static int16_t adjust_reso(int16_t val) +{ + if (val < 0 || val > 960) { + return 0; + } else { + return val; + } +} + +int32_t Instruments::to_rate(int rate) +{ + return (rate) ? (int32_t) (0x200 * pow(2.0, rate / 17.0) + * 44100 / playback_rate * control_ratio) << fast_decay : 0; +} + + +void Instruments::apply_bank_parameter(Instrument *ip, ToneBankElement *tone) +{ + int i, j; + Sample *sp; + + if (tone->tunenum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->tunenum == 1) { + sp->low_freq = adjust_tune_freq(sp->low_freq, tone->tune[0]); + sp->high_freq = adjust_tune_freq(sp->high_freq, tone->tune[0]); + sp->root_freq = adjust_tune_freq(sp->root_freq, tone->tune[0]); + } else if (i < tone->tunenum) { + sp->low_freq = adjust_tune_freq(sp->low_freq, tone->tune[i]); + sp->high_freq = adjust_tune_freq(sp->high_freq, tone->tune[i]); + sp->root_freq = adjust_tune_freq(sp->root_freq, tone->tune[i]); + } + } + if (tone->envratenum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->envratenum == 1) { + for (j = 0; j < 6; j++) + if (tone->envrate[0][j] >= 0) + sp->envelope_rate[j] = to_rate(tone->envrate[0][j]); + } else if (i < tone->envratenum) { + for (j = 0; j < 6; j++) + if (tone->envrate[i][j] >= 0) + sp->envelope_rate[j] = to_rate(tone->envrate[i][j]); + } + } + if (tone->envofsnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->envofsnum == 1) { + for (j = 0; j < 6; j++) + if (tone->envofs[0][j] >= 0) + sp->envelope_offset[j] = to_offset_22(tone->envofs[0][j]); + } else if (i < tone->envofsnum) { + for (j = 0; j < 6; j++) + if (tone->envofs[i][j] >= 0) + sp->envelope_offset[j] = to_offset_22(tone->envofs[i][j]); + } + } + if (tone->tremnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->tremnum == 1) { + if (IS_QUANTITY_DEFINED(tone->trem[0][0])) + sp->tremolo_sweep_increment = + quantity_to_int(&tone->trem[0][0], 0); + if (IS_QUANTITY_DEFINED(tone->trem[0][1])) + sp->tremolo_phase_increment = + quantity_to_int(&tone->trem[0][1], 0); + if (IS_QUANTITY_DEFINED(tone->trem[0][2])) + sp->tremolo_depth = + quantity_to_int(&tone->trem[0][2], 0) << 1; + } else if (i < tone->tremnum) { + if (IS_QUANTITY_DEFINED(tone->trem[i][0])) + sp->tremolo_sweep_increment = + quantity_to_int(&tone->trem[i][0], 0); + if (IS_QUANTITY_DEFINED(tone->trem[i][1])) + sp->tremolo_phase_increment = + quantity_to_int(&tone->trem[i][1], 0); + if (IS_QUANTITY_DEFINED(tone->trem[i][2])) + sp->tremolo_depth = + quantity_to_int(&tone->trem[i][2], 0) << 1; + } + } + if (tone->vibnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->vibnum == 1) { + if (IS_QUANTITY_DEFINED(tone->vib[0][1])) + sp->vibrato_control_ratio = + quantity_to_int(&tone->vib[0][1], 0); + if (IS_QUANTITY_DEFINED(tone->vib[0][0])) + sp->vibrato_sweep_increment = + quantity_to_int(&tone->vib[0][0], + sp->vibrato_control_ratio); + if (IS_QUANTITY_DEFINED(tone->vib[0][2])) + sp->vibrato_depth = quantity_to_int(&tone->vib[0][2], 0); + } else if (i < tone->vibnum) { + if (IS_QUANTITY_DEFINED(tone->vib[i][1])) + sp->vibrato_control_ratio = + quantity_to_int(&tone->vib[i][1], 0); + if (IS_QUANTITY_DEFINED(tone->vib[i][0])) + sp->vibrato_sweep_increment = + quantity_to_int(&tone->vib[i][0], + sp->vibrato_control_ratio); + if (IS_QUANTITY_DEFINED(tone->vib[i][2])) + sp->vibrato_depth = quantity_to_int(&tone->vib[i][2], 0); + } + } + if (tone->sclnotenum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->sclnotenum == 1) + sp->scale_freq = tone->sclnote[0]; + else if (i < tone->sclnotenum) + sp->scale_freq = tone->sclnote[i]; + } + if (tone->scltunenum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->scltunenum == 1) + sp->scale_factor = adjust_scale_tune(tone->scltune[0]); + else if (i < tone->scltunenum) + sp->scale_factor = adjust_scale_tune(tone->scltune[i]); + } + if (tone->modenvratenum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modenvratenum == 1) { + for (j = 0; j < 6; j++) + if (tone->modenvrate[0][j] >= 0) + sp->modenv_rate[j] = to_rate(tone->modenvrate[0][j]); + } else if (i < tone->modenvratenum) { + for (j = 0; j < 6; j++) + if (tone->modenvrate[i][j] >= 0) + sp->modenv_rate[j] = to_rate(tone->modenvrate[i][j]); + } + } + if (tone->modenvofsnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modenvofsnum == 1) { + for (j = 0; j < 6; j++) + if (tone->modenvofs[0][j] >= 0) + sp->modenv_offset[j] = + to_offset_22(tone->modenvofs[0][j]); + } else if (i < tone->modenvofsnum) { + for (j = 0; j < 6; j++) + if (tone->modenvofs[i][j] >= 0) + sp->modenv_offset[j] = + to_offset_22(tone->modenvofs[i][j]); + } + } + if (tone->envkeyfnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->envkeyfnum == 1) { + for (j = 0; j < 6; j++) + if (tone->envkeyf[0][j] != -1) + sp->envelope_keyf[j] = tone->envkeyf[0][j]; + } else if (i < tone->envkeyfnum) { + for (j = 0; j < 6; j++) + if (tone->envkeyf[i][j] != -1) + sp->envelope_keyf[j] = tone->envkeyf[i][j]; + } + } + if (tone->envvelfnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->envvelfnum == 1) { + for (j = 0; j < 6; j++) + if (tone->envvelf[0][j] != -1) + sp->envelope_velf[j] = tone->envvelf[0][j]; + } else if (i < tone->envvelfnum) { + for (j = 0; j < 6; j++) + if (tone->envvelf[i][j] != -1) + sp->envelope_velf[j] = tone->envvelf[i][j]; + } + } + if (tone->modenvkeyfnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modenvkeyfnum == 1) { + for (j = 0; j < 6; j++) + if (tone->modenvkeyf[0][j] != -1) + sp->modenv_keyf[j] = tone->modenvkeyf[0][j]; + } else if (i < tone->modenvkeyfnum) { + for (j = 0; j < 6; j++) + if (tone->modenvkeyf[i][j] != -1) + sp->modenv_keyf[j] = tone->modenvkeyf[i][j]; + } + } + if (tone->modenvvelfnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modenvvelfnum == 1) { + for (j = 0; j < 6; j++) + if (tone->modenvvelf[0][j] != -1) + sp->modenv_velf[j] = tone->modenvvelf[0][j]; + } else if (i < tone->modenvvelfnum) { + for (j = 0; j < 6; j++) + if (tone->modenvvelf[i][j] != -1) + sp->modenv_velf[j] = tone->modenvvelf[i][j]; + } + } + if (tone->trempitchnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->trempitchnum == 1) + sp->tremolo_to_pitch = tone->trempitch[0]; + else if (i < tone->trempitchnum) + sp->tremolo_to_pitch = tone->trempitch[i]; + } + if (tone->tremfcnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->tremfcnum == 1) + sp->tremolo_to_fc = tone->tremfc[0]; + else if (i < tone->tremfcnum) + sp->tremolo_to_fc = tone->tremfc[i]; + } + if (tone->modpitchnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modpitchnum == 1) + sp->modenv_to_pitch = tone->modpitch[0]; + else if (i < tone->modpitchnum) + sp->modenv_to_pitch = tone->modpitch[i]; + } + if (tone->modfcnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modfcnum == 1) + sp->modenv_to_fc = tone->modfc[0]; + else if (i < tone->modfcnum) + sp->modenv_to_fc = tone->modfc[i]; + } + if (tone->fcnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->fcnum == 1) + sp->cutoff_freq = adjust_fc(tone->fc[0]); + else if (i < tone->fcnum) + sp->cutoff_freq = adjust_fc(tone->fc[i]); + } + if (tone->resonum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->resonum == 1) + sp->resonance = adjust_reso(tone->reso[0]); + else if (i < tone->resonum) + sp->resonance = adjust_reso(tone->reso[i]); + } +} + +#define READ_CHAR(thing) { \ + uint8_t tmpchar; \ + \ + if (tf_read(&tmpchar, 1, 1, tf) != 1) \ + goto fail; \ + thing = tmpchar; \ +} +#define READ_SHORT(thing) { \ + uint16_t tmpshort; \ + \ + if (tf_read(&tmpshort, 2, 1, tf) != 1) \ + goto fail; \ + thing = LE_SHORT(tmpshort); \ +} +#define READ_LONG(thing) { \ + int32_t tmplong; \ + \ + if (tf_read(&tmplong, 4, 1, tf) != 1) \ + goto fail; \ + thing = LE_LONG(tmplong); \ +} + +/* If panning or note_to_use != -1, it will be used for all samples, + * instead of the sample-specific values in the instrument file. + * + * For note_to_use, any value < 0 or > 127 will be forced to 0. + * + * For other parameters, 1 means yes, 0 means no, other values are + * undefined. + * + * TODO: do reverse loops right + */ +Instrument *Instruments::load_gus_instrument(char *name, ToneBank *bank, int dr, int prog) +{ + ToneBankElement *tone; + int amp, note_to_use, panning, strip_envelope, strip_loop, strip_tail; + Instrument *ip; + struct timidity_file *tf; + uint8_t tmp[1024], fractions; + Sample *sp; + int i, j, noluck = 0; + + if (!name) + return 0; + + if (bank) { + tone = &bank->tone[prog]; + amp = tone->amp; + note_to_use = (tone->note != -1) ? tone->note : ((dr) ? prog : -1); + panning = tone->pan; + strip_envelope = (tone->strip_envelope != -1) + ? tone->strip_envelope : ((dr) ? 1 : -1); + strip_loop = (tone->strip_loop != -1) + ? tone->strip_loop : ((dr) ? 1 : -1); + strip_tail = tone->strip_tail; + } + else { + tone = NULL; + amp = note_to_use = panning = -1; + strip_envelope = strip_loop = strip_tail = 0; + } + if (tone && tone->tunenum == 0 + && tone->envratenum == 0 && tone->envofsnum == 0 + && tone->tremnum == 0 && tone->vibnum == 0 + && tone->sclnotenum == 0 && tone->scltunenum == 0 + && tone->modenvratenum == 0 && tone->modenvofsnum == 0 + && tone->envkeyfnum == 0 && tone->envvelfnum == 0 + && tone->modenvkeyfnum == 0 && tone->modenvvelfnum == 0 + && tone->trempitchnum == 0 && tone->tremfcnum == 0 + && tone->modpitchnum == 0 && tone->modfcnum == 0 + && tone->fcnum == 0 && tone->resonum == 0) + if ((ip = search_instrument_cache(name, panning, amp, note_to_use, + strip_loop, strip_envelope, strip_tail)) != NULL) { + ctl_cmsg(CMSG_INFO, VERB_DEBUG, " * Cached"); + return ip; + } + /* Open patch file */ + if (!(tf = open_file(name, 2, OF_NORMAL, pathlist))) { +#ifdef PATCH_EXT_LIST + int name_len, ext_len; + static const char *patch_ext[] = PATCH_EXT_LIST; +#endif + + noluck = 1; +#ifdef PATCH_EXT_LIST + name_len = (int)strlen(name); + /* Try with various extensions */ + for (i = 0; patch_ext[i]; i++) { + ext_len = (int)strlen(patch_ext[i]); + if (name_len + ext_len < 1024) { + if (name_len >= ext_len && strcmp(name + name_len - ext_len, + patch_ext[i]) == 0) + continue; /* duplicated ext. */ + strcpy((char *)tmp, name); + strcat((char *)tmp, patch_ext[i]); + if ((tf = open_file((char *)tmp, 1, OF_NORMAL, pathlist))) { + noluck = 0; + break; + } + } + } +#endif + } + if (noluck) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "Instrument `%s' can't be found.", name); + return 0; + } + /* Read some headers and do cursory sanity checks. There are loads + * of magic offsets. This could be rewritten... + */ + tmp[0] = tf_getc(tf); + if (tmp[0] == '\0') { + /* for Mac binary */ + skip(tf, 127); + tmp[0] = tf_getc(tf); + } + if ((tf_read(tmp + 1, 1, 238, tf) != 238) + || (memcmp(tmp, "GF1PATCH110\0ID#000002", 22) + && memcmp(tmp, "GF1PATCH100\0ID#000002", 22))) { + /* don't know what the differences are */ + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "%s: not an instrument", name); + close_file(tf); + return 0; + } + /* instruments. To some patch makers, 0 means 1 */ + if (tmp[82] != 1 && tmp[82] != 0) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "Can't handle patches with %d instruments", tmp[82]); + close_file(tf); + return 0; + } + if (tmp[151] != 1 && tmp[151] != 0) { /* layers. What's a layer? */ + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "Can't handle instruments with %d layers", tmp[151]); + close_file(tf); + return 0; + } + ip = (Instrument *)safe_malloc(sizeof(Instrument)); + ip->type = INST_GUS; + ip->samples = tmp[198]; + ip->sample = (Sample *)safe_malloc(sizeof(Sample) * ip->samples); + memset(ip->sample, 0, sizeof(Sample) * ip->samples); + for (i = 0; i < ip->samples; i++) { + skip(tf, 7); /* Skip the wave name */ + if (tf_read(&fractions, 1, 1, tf) != 1) { + fail: + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "Error reading sample %d", i); + for (j = 0; j < i; j++) + free(ip->sample[j].data); + free(ip->sample); + free(ip); + close_file(tf); + return 0; + } + sp = &(ip->sample[i]); + sp->low_vel = 0; + sp->high_vel = 127; + sp->cutoff_freq = sp->resonance = 0; + sp->tremolo_to_pitch = sp->tremolo_to_fc = 0; + sp->modenv_to_pitch = sp->modenv_to_fc = 0; + sp->vel_to_fc = sp->key_to_fc = sp->vel_to_resonance = 0; + sp->envelope_velf_bpo = sp->modenv_velf_bpo = 64; + sp->vel_to_fc_threshold = 64; + sp->key_to_fc_bpo = 60; + sp->envelope_delay = sp->modenv_delay = 0; + sp->tremolo_delay = sp->vibrato_delay = 0; + sp->inst_type = INST_GUS; + sp->sample_type = SF_SAMPLETYPE_MONO; + sp->sf_sample_link = -1; + sp->sf_sample_index = 0; + memset(sp->envelope_velf, 0, sizeof(sp->envelope_velf)); + memset(sp->envelope_keyf, 0, sizeof(sp->envelope_keyf)); + memset(sp->modenv_velf, 0, sizeof(sp->modenv_velf)); + memset(sp->modenv_keyf, 0, sizeof(sp->modenv_keyf)); + memset(sp->modenv_rate, 0, sizeof(sp->modenv_rate)); + memset(sp->modenv_offset, 0, sizeof(sp->modenv_offset)); + READ_LONG(sp->data_length); + READ_LONG(sp->loop_start); + READ_LONG(sp->loop_end); + READ_SHORT(sp->sample_rate); + READ_LONG(sp->low_freq); + READ_LONG(sp->high_freq); + READ_LONG(sp->root_freq); + skip(tf, 2); /* Why have a "root frequency" and then "tuning"?? */ + READ_CHAR(tmp[0]); + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Rate/Low/Hi/Root = %d/%d/%d/%d", + sp->sample_rate, sp->low_freq, sp->high_freq, sp->root_freq); + if (panning == -1) + /* 0x07 and 0x08 are both center panning */ + sp->panning = ((tmp[0] - ((tmp[0] < 8) ? 7 : 8)) * 63) / 7 + 64; + else + sp->panning = (uint8_t)(panning & 0x7f); + /* envelope, tremolo, and vibrato */ + if (tf_read(tmp, 1, 18, tf) != 18) + goto fail; + if (!tmp[13] || !tmp[14]) { + sp->tremolo_sweep_increment = sp->tremolo_phase_increment = 0; + sp->tremolo_depth = 0; + ctl_cmsg(CMSG_INFO, VERB_DEBUG, " * no tremolo"); + } + else { + sp->tremolo_sweep_increment = convert_tremolo_sweep(tmp[12]); + sp->tremolo_phase_increment = convert_tremolo_rate(tmp[13]); + sp->tremolo_depth = tmp[14]; + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + " * tremolo: sweep %d, phase %d, depth %d", + sp->tremolo_sweep_increment, sp->tremolo_phase_increment, + sp->tremolo_depth); + } + if (!tmp[16] || !tmp[17]) { + sp->vibrato_sweep_increment = sp->vibrato_control_ratio = 0; + sp->vibrato_depth = 0; + ctl_cmsg(CMSG_INFO, VERB_DEBUG, " * no vibrato"); + } + else { + sp->vibrato_control_ratio = convert_vibrato_rate(tmp[16]); + sp->vibrato_sweep_increment = convert_vibrato_sweep(tmp[15], + sp->vibrato_control_ratio); + sp->vibrato_depth = tmp[17]; + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + " * vibrato: sweep %d, ctl %d, depth %d", + sp->vibrato_sweep_increment, sp->vibrato_control_ratio, + sp->vibrato_depth); + } + READ_CHAR(sp->modes); + ctl_cmsg(CMSG_INFO, VERB_DEBUG, " * mode: 0x%02x", sp->modes); + READ_SHORT(sp->scale_freq); + READ_SHORT(sp->scale_factor); + skip(tf, 36); /* skip reserved space */ + /* Mark this as a fixed-pitch instrument if such a deed is desired. */ + sp->note_to_use = (note_to_use != -1) ? (uint8_t)note_to_use : 0; + /* seashore.pat in the Midia patch set has no Sustain. I don't + * understand why, and fixing it by adding the Sustain flag to + * all looped patches probably breaks something else. We do it + * anyway. + */ + if (sp->modes & MODES_LOOPING) + sp->modes |= MODES_SUSTAIN; + /* Strip any loops and envelopes we're permitted to */ + if ((strip_loop == 1) && (sp->modes & (MODES_SUSTAIN | MODES_LOOPING + | MODES_PINGPONG | MODES_REVERSE))) { + sp->modes &= ~(MODES_SUSTAIN | MODES_LOOPING + | MODES_PINGPONG | MODES_REVERSE); + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + " - Removing loop and/or sustain"); + } + if (strip_envelope == 1) { + if (sp->modes & MODES_ENVELOPE) + ctl_cmsg(CMSG_INFO, VERB_DEBUG, " - Removing envelope"); + sp->modes &= ~MODES_ENVELOPE; + } + else if (strip_envelope != 0) { + /* Have to make a guess. */ + if (!(sp->modes & (MODES_LOOPING + | MODES_PINGPONG | MODES_REVERSE))) { + /* No loop? Then what's there to sustain? + * No envelope needed either... + */ + sp->modes &= ~(MODES_SUSTAIN | MODES_ENVELOPE); + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + " - No loop, removing sustain and envelope"); + } + else if (!memcmp(tmp, "??????", 6) || tmp[11] >= 100) { + /* Envelope rates all maxed out? + * Envelope end at a high "offset"? + * That's a weird envelope. Take it out. + */ + sp->modes &= ~MODES_ENVELOPE; + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + " - Weirdness, removing envelope"); + } + else if (!(sp->modes & MODES_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 &= ~MODES_ENVELOPE; + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + " - No sustain, removing envelope"); + } + } + for (j = 0; j < 6; j++) { + sp->envelope_rate[j] = convert_envelope_rate(tmp[j]); + sp->envelope_offset[j] = convert_envelope_offset(tmp[j + 6]); + } + /* this envelope seems to give reverb like effects to most patches + * use the same method as soundfont + */ + if (modify_release) { + sp->envelope_offset[3] = to_offset_22(5); + sp->envelope_rate[3] = calc_rate_i(255, modify_release); + sp->envelope_offset[4] = to_offset_22(4); + sp->envelope_rate[4] = to_offset_22(200); + sp->envelope_offset[5] = to_offset_22(4); + sp->envelope_rate[5] = to_offset_22(200); + } + /* Then read the sample data */ + sp->data = (sample_t *)safe_malloc(sp->data_length + 4); + sp->data_alloced = 1; + if ((j = tf_read(sp->data, 1, sp->data_length, tf)) + != sp->data_length) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "Too small this patch length: %d < %d", + j, sp->data_length); + goto fail; + } + if (!(sp->modes & MODES_16BIT)) { /* convert to 16-bit data */ + uint16_t *tmp; + uint8_t *cp = (uint8_t *)sp->data; + + tmp = (uint16_t *)safe_malloc(sp->data_length * 2 + 4); + for (splen_t i = 0; i < sp->data_length; i++) + tmp[i] = (uint16_t)cp[i] << 8; + sp->data = (sample_t *)tmp; + free(cp); + sp->data_length *= 2; + sp->loop_start *= 2; + sp->loop_end *= 2; + } +#ifdef _BIG_ENDIAN_ + else { /* convert to machine byte order */ + int32_t i; + int16_t *tmp = (int16_t *)sp->data, s; + + for (i = 0; i < sp->data_length / 2; i++) + s = LE_SHORT(tmp[i]), tmp[i] = s; + } +#endif + if (sp->modes & MODES_UNSIGNED) { /* convert to signed data */ + int32_t i = sp->data_length / 2; + int16_t *tmp = (int16_t *)sp->data; + + while (i--) + *tmp++ ^= 0x8000; + } + /* Reverse loops and pass them off as normal loops */ + if (sp->modes & MODES_REVERSE) { + /* The GUS apparently plays reverse loops by reversing the + * whole sample. We do the same because the GUS does not SUCK. + */ + int32_t t; + + reverse_data((int16_t *)sp->data, 0, sp->data_length / 2); + t = sp->loop_start; + sp->loop_start = sp->data_length - sp->loop_end; + sp->loop_end = sp->data_length - t; + sp->modes &= ~MODES_REVERSE; + sp->modes |= MODES_LOOPING; /* just in case */ + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, "Reverse loop in %s", name); + } + /* If necessary do some anti-aliasing filtering */ + if (antialiasing_allowed) + antialiasing((int16_t *)sp->data, sp->data_length / 2, + sp->sample_rate, playback_rate); + if (amp != -1) + sp->volume = (double) amp / 100; + 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. + */ + int32_t a, maxamp = 0; + int16_t *tmp = (int16_t *)sp->data; + + for (splen_t i = 0; i < sp->data_length / 2; i++) + if ((a = abs(tmp[i])) > maxamp) + maxamp = a; + sp->volume = 32768 / (double)maxamp; + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + " * volume comp: %f", sp->volume); + } + /* These are in bytes. Convert into samples. */ + sp->data_length /= 2; + sp->loop_start /= 2; + sp->loop_end /= 2; + /* The sample must be padded out by 2 extra sample, so that + * round off errors in the offsets used in interpolation will not + * cause a "pop" by reading random data beyond data_length + */ + sp->data[sp->data_length] = sp->data[sp->data_length + 1] = 0; + /* Remove abnormal loops which cause pop noise + * in long sustain stage + */ + if (!(sp->modes & MODES_LOOPING)) { + sp->loop_start = sp->data_length - 1; + sp->loop_end = sp->data_length; + sp->data[sp->data_length - 1] = 0; + } + /* Then fractional samples */ + sp->data_length <<= FRACTION_BITS; + sp->loop_start <<= FRACTION_BITS; + sp->loop_end <<= FRACTION_BITS; + /* Adjust for fractional loop points. This is a guess. Does anyone + * know what "fractions" really stands for? + */ + sp->loop_start |= (fractions & 0x0f) << (FRACTION_BITS - 4); + sp->loop_end |= ((fractions >> 4) & 0x0f) << (FRACTION_BITS - 4); + /* If this instrument will always be played on the same note, + * and it's not looped, we can resample it now. + */ + if (sp->note_to_use && !(sp->modes & MODES_LOOPING)) + pre_resample(sp); + + /* do pitch detection on drums if surround chorus is used */ + if (dr && opt_surround_chorus) + { + Freq freq; + sp->chord = -1; + sp->root_freq_detected = freq.freq_fourier(sp, &(sp->chord)); + sp->transpose_detected = + assign_pitch_to_freq(sp->root_freq_detected) - + assign_pitch_to_freq(sp->root_freq / 1024.0); + } + + if (strip_tail == 1) { + /* Let's not really, just say we did. */ + sp->data_length = sp->loop_end; + ctl_cmsg(CMSG_INFO, VERB_DEBUG, " - Stripping tail"); + } + } + close_file(tf); + store_instrument_cache(ip, name, panning, amp, note_to_use, + strip_loop, strip_envelope, strip_tail); + return ip; +} + + +Instrument *Instruments::load_instrument(int dr, int b, int prog) +{ + ToneBank *bank = ((dr) ? drumset[b] : tonebank[b]); + Instrument *ip; + int i, font_bank, font_preset, font_keynote; + double volume_max; + int pan, panning; + +#if 0 + // This cannot possibly work as implemented. + if (play_system_mode == GS_SYSTEM_MODE && (b == 64 || b == 65)) { + if (!dr) /* User Instrument */ + recompute_userinst(b, prog); + else { /* User Drumset */ + ip = recompute_userdrum(b, prog); + if (ip != NULL) { + return ip; + } + } + } +#endif + if (bank->tone[prog].instype == 1 || bank->tone[prog].instype == 2) { + if (bank->tone[prog].instype == 1) { /* Font extention */ + font_bank = bank->tone[prog].font_bank; + font_preset = bank->tone[prog].font_preset; + font_keynote = bank->tone[prog].font_keynote; + ip = extract_soundfont(bank->tone[prog].name, + font_bank, font_preset, font_keynote); + } + else /* Sample extension */ + ip = extract_sample_file(bank->tone[prog].name); + /* amp tuning */ + if (ip != NULL && bank->tone[prog].amp != -1) { + for (i = 0, volume_max = 0; i < ip->samples; i++) + if (volume_max < ip->sample[i].volume) + volume_max = ip->sample[i].volume; + if (volume_max != 0) + for (i = 0; i < ip->samples; i++) + ip->sample[i].volume *= bank->tone[prog].amp + / 100.0 / volume_max; + } + /* panning */ + if (ip != NULL && bank->tone[prog].pan != -1) { + pan = ((int)bank->tone[prog].pan & 0x7f) - 64; + for (i = 0; i < ip->samples; i++) { + panning = (int)ip->sample[i].panning + pan; + panning = (panning < 0) ? 0 + : ((panning > 127) ? 127 : panning); + ip->sample[i].panning = panning; + } + } + /* note to use */ + if (ip != NULL && bank->tone[prog].note != -1) + for (i = 0; i < ip->samples; i++) + ip->sample[i].root_freq = + freq_table[bank->tone[prog].note & 0x7f]; + /* filter key-follow */ + if (ip != NULL && bank->tone[prog].key_to_fc != 0) + for (i = 0; i < ip->samples; i++) + ip->sample[i].key_to_fc = bank->tone[prog].key_to_fc; + /* filter velocity-follow */ + if (ip != NULL && bank->tone[prog].vel_to_fc != 0) + for (i = 0; i < ip->samples; i++) + ip->sample[i].key_to_fc = bank->tone[prog].vel_to_fc; + /* resonance velocity-follow */ + if (ip != NULL && bank->tone[prog].vel_to_resonance != 0) + for (i = 0; i < ip->samples; i++) + ip->sample[i].vel_to_resonance = + bank->tone[prog].vel_to_resonance; + /* strip tail */ + if (ip != NULL && bank->tone[prog].strip_tail == 1) + for (i = 0; i < ip->samples; i++) + ip->sample[i].data_length = ip->sample[i].loop_end; + if (ip != NULL) { + i = (dr) ? 0 : prog; + if (bank->tone[i].comment) + free(bank->tone[i].comment); + bank->tone[i].comment = safe_strdup(ip->instname); + apply_bank_parameter(ip, &bank->tone[prog]); + } + return ip; + } + if (!dr) { + font_bank = b; + font_preset = prog; + font_keynote = -1; + } + else { + font_bank = 128; + font_preset = b; + font_keynote = prog; + } + /* preload soundfont */ + ip = load_soundfont_inst(0, font_bank, font_preset, font_keynote); + if (ip != NULL) { + if (bank->tone[prog].name == NULL) /* this should not be NULL to play the instrument */ + bank->tone[prog].name = safe_strdup(DYNAMIC_INSTRUMENT_NAME); + if (bank->tone[prog].comment) + free(bank->tone[prog].comment); + bank->tone[prog].comment = safe_strdup(ip->instname); + } + if (ip == NULL) { /* load GUS/patch file */ + ip = load_gus_instrument(bank->tone[prog].name, bank, dr, prog); + if (ip == NULL) { /* no patch; search soundfont again */ + ip = load_soundfont_inst(1, font_bank, font_preset, font_keynote); + if (ip != NULL) { + if (bank->tone[0].comment) + free(bank->tone[0].comment); + bank->tone[0].comment = safe_strdup(ip->instname); + } + } + } + if (ip != NULL) + apply_bank_parameter(ip, &bank->tone[prog]); + return ip; +} + +int Instruments::fill_bank(int dr, int b, int *rc) +{ + int i, errors = 0; + ToneBank *bank = ((dr) ? drumset[b] : tonebank[b]); + + if (rc != NULL) + *rc = RC_OK; + + for (i = 0; i < 128; i++) + { + if (bank->tone[i].instrument == MAGIC_LOAD_INSTRUMENT) + { + if (!(bank->tone[i].name)) + { + bank->tone[i].instrument = load_instrument(dr, b, i); + if (bank->tone[i].instrument == NULL) + { + ctl_cmsg(CMSG_WARNING, + (b != 0) ? VERB_VERBOSE : VERB_NORMAL, + "No instrument mapped to %s %d, program %d%s", + dr ? "drum set" : "tone bank", + dr ? b + progbase : b, + dr ? i : i + progbase, + (b != 0) ? "" : + " - this instrument will not be heard"); + if (b != 0) + { + /* Mark the corresponding instrument in the default + bank / drumset for loading (if it isn't already) */ + if (!dr) + { + if (!(standard_tonebank.tone[i].instrument)) + standard_tonebank.tone[i].instrument = + MAGIC_LOAD_INSTRUMENT; + } + else + { + if (!(standard_drumset.tone[i].instrument)) + standard_drumset.tone[i].instrument = + MAGIC_LOAD_INSTRUMENT; + } + bank->tone[i].instrument = 0; + } + else + bank->tone[i].instrument = MAGIC_ERROR_INSTRUMENT; + errors++; + } + } + else + { + if (rc != NULL) + { + *rc = RC_OK; + } + + bank->tone[i].instrument = load_instrument(dr, b, i); + if (!bank->tone[i].instrument) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "Couldn't load instrument %s " + "(%s %d, program %d)", bank->tone[i].name, + dr ? "drum set" : "tone bank", + dr ? b + progbase : b, + dr ? i : i + progbase); + errors++; + } + } + } + } + return errors; +} + +int Instruments::load_missing_instruments(int *rc) +{ + int i = 128 + map_bank_counter, errors = 0; + if (rc != NULL) + *rc = RC_OK; + while (i--) + { + if (tonebank[i]) + errors += fill_bank(0, i, rc); + if (rc != NULL && RC_IS_SKIP_FILE(*rc)) + return errors; + if (drumset[i]) + errors += fill_bank(1, i, rc); + if (rc != NULL && RC_IS_SKIP_FILE(*rc)) + return errors; + } + return errors; +} + +// The precaching code is from ZDoom's Timidity-based GUS emulation +void Instruments::MarkInstrument(int banknum, int percussion, int instr) +{ + ToneBank *bank; + + if (banknum >= 128) + { + return; + } + if (banknum != 0) + { + /* Mark the standard bank in case it's not defined by this one. */ + MarkInstrument(0, percussion, instr); + } + if (percussion) + { + bank = drumset[banknum]; + } + else + { + bank = tonebank[banknum]; + } + if (bank == NULL) + { + return; + } + if (bank->tone[instr].instrument == NULL) + { + bank->tone[instr].instrument = MAGIC_LOAD_INSTRUMENT; + } +} + +void Instruments::PrecacheInstruments(const uint16_t *instruments, int count) +{ + for (int i = 0; i < count; ++i) + { + MarkInstrument((instruments[i] >> 7) & 127, instruments[i] >> 14, instruments[i] & 127); + } + load_missing_instruments(nullptr); +} + + + +void *Instruments::safe_memdup(void *s, size_t size) +{ + return memcpy(safe_malloc(size), s, size); +} + +/*! Copy ToneBankElement src to elm. The original elm is released. */ +void Instruments::copy_tone_bank_element(ToneBankElement *elm, const ToneBankElement *src) +{ + int i; + + free_tone_bank_element(elm); + memcpy(elm, src, sizeof(ToneBankElement)); + if (elm->name) + elm->name = safe_strdup(elm->name); + if (elm->tunenum) + elm->tune = (float *)safe_memdup(elm->tune, + elm->tunenum * sizeof(float)); + if (elm->envratenum) { + elm->envrate = (int **)safe_memdup(elm->envrate, + elm->envratenum * sizeof(int *)); + for (i = 0; i < elm->envratenum; i++) + elm->envrate[i] = (int *)safe_memdup(elm->envrate[i], + 6 * sizeof(int)); + } + if (elm->envofsnum) { + elm->envofs = (int **)safe_memdup(elm->envofs, + elm->envofsnum * sizeof(int *)); + for (i = 0; i < elm->envofsnum; i++) + elm->envofs[i] = (int *)safe_memdup(elm->envofs[i], + 6 * sizeof(int)); + } + if (elm->tremnum) { + elm->trem = (Quantity **)safe_memdup(elm->trem, + elm->tremnum * sizeof(Quantity *)); + for (i = 0; i < elm->tremnum; i++) + elm->trem[i] = (Quantity *)safe_memdup(elm->trem[i], + 3 * sizeof(Quantity)); + } + if (elm->vibnum) { + elm->vib = (Quantity **)safe_memdup(elm->vib, + elm->vibnum * sizeof(Quantity *)); + for (i = 0; i < elm->vibnum; i++) + elm->vib[i] = (Quantity *)safe_memdup(elm->vib[i], + 3 * sizeof(Quantity)); + } + if (elm->sclnotenum) + elm->sclnote = (int16_t *)safe_memdup(elm->sclnote, + elm->sclnotenum * sizeof(int16_t)); + if (elm->scltunenum) + elm->scltune = (int16_t *)safe_memdup(elm->scltune, + elm->scltunenum * sizeof(int16_t)); + if (elm->comment) + elm->comment = safe_strdup(elm->comment); + if (elm->modenvratenum) { + elm->modenvrate = (int **)safe_memdup(elm->modenvrate, + elm->modenvratenum * sizeof(int *)); + for (i = 0; i < elm->modenvratenum; i++) + elm->modenvrate[i] = (int *)safe_memdup(elm->modenvrate[i], + 6 * sizeof(int)); + } + if (elm->modenvofsnum) { + elm->modenvofs = (int **)safe_memdup(elm->modenvofs, + elm->modenvofsnum * sizeof(int *)); + for (i = 0; i < elm->modenvofsnum; i++) + elm->modenvofs[i] = (int *)safe_memdup(elm->modenvofs[i], + 6 * sizeof(int)); + } + if (elm->envkeyfnum) { + elm->envkeyf = (int **)safe_memdup(elm->envkeyf, + elm->envkeyfnum * sizeof(int *)); + for (i = 0; i < elm->envkeyfnum; i++) + elm->envkeyf[i] = (int *)safe_memdup(elm->envkeyf[i], + 6 * sizeof(int)); + } + if (elm->envvelfnum) { + elm->envvelf = (int **)safe_memdup(elm->envvelf, + elm->envvelfnum * sizeof(int *)); + for (i = 0; i < elm->envvelfnum; i++) + elm->envvelf[i] = (int *)safe_memdup(elm->envvelf[i], + 6 * sizeof(int)); + } + if (elm->modenvkeyfnum) { + elm->modenvkeyf = (int **)safe_memdup(elm->modenvkeyf, + elm->modenvkeyfnum * sizeof(int *)); + for (i = 0; i < elm->modenvkeyfnum; i++) + elm->modenvkeyf[i] = (int *)safe_memdup(elm->modenvkeyf[i], + 6 * sizeof(int)); + } + if (elm->modenvvelfnum) { + elm->modenvvelf = (int **)safe_memdup(elm->modenvvelf, + elm->modenvvelfnum * sizeof(int *)); + for (i = 0; i < elm->modenvvelfnum; i++) + elm->modenvvelf[i] = (int *)safe_memdup(elm->modenvvelf[i], + 6 * sizeof(int)); + } + if (elm->trempitchnum) + elm->trempitch = (int16_t *)safe_memdup(elm->trempitch, + elm->trempitchnum * sizeof(int16_t)); + if (elm->tremfcnum) + elm->tremfc = (int16_t *)safe_memdup(elm->tremfc, + elm->tremfcnum * sizeof(int16_t)); + if (elm->modpitchnum) + elm->modpitch = (int16_t *)safe_memdup(elm->modpitch, + elm->modpitchnum * sizeof(int16_t)); + if (elm->modfcnum) + elm->modfc = (int16_t *)safe_memdup(elm->modfc, + elm->modfcnum * sizeof(int16_t)); + if (elm->fcnum) + elm->fc = (int16_t *)safe_memdup(elm->fc, + elm->fcnum * sizeof(int16_t)); + if (elm->resonum) + elm->reso = (int16_t *)safe_memdup(elm->reso, + elm->resonum * sizeof(int16_t)); + +} + +/*! Release ToneBank[128 + MAP_BANK_COUNT] */ +void Instruments::free_tone_bank_list(ToneBank *tb[]) +{ + int i, j; + ToneBank *bank; + + for (i = 0; i < 128 + map_bank_counter; i++) + { + bank = tb[i]; + if (!bank) + continue; + for (j = 0; j < 128; j++) + free_tone_bank_element(&bank->tone[j]); + if (i > 0) + { + free(bank); + tb[i] = NULL; + } + } +} + +/*! Release tonebank and drumset */ +void Instruments::free_tone_bank(void) +{ + free_tone_bank_list(tonebank); + free_tone_bank_list(drumset); +} + +/*! Release ToneBankElement. */ +void Instruments::free_tone_bank_element(ToneBankElement *elm) +{ + elm->instype = 0; + if (elm->name) + free(elm->name); + elm->name = NULL; + if (elm->tune) + free(elm->tune); + elm->tune = NULL, elm->tunenum = 0; + if (elm->envratenum) + free_ptr_list(elm->envrate, elm->envratenum); + elm->envrate = NULL, elm->envratenum = 0; + if (elm->envofsnum) + free_ptr_list(elm->envofs, elm->envofsnum); + elm->envofs = NULL, elm->envofsnum = 0; + if (elm->tremnum) + free_ptr_list(elm->trem, elm->tremnum); + elm->trem = NULL, elm->tremnum = 0; + if (elm->vibnum) + free_ptr_list(elm->vib, elm->vibnum); + elm->vib = NULL, elm->vibnum = 0; + if (elm->sclnote) + free(elm->sclnote); + elm->sclnote = NULL, elm->sclnotenum = 0; + if (elm->scltune) + free(elm->scltune); + elm->scltune = NULL, elm->scltunenum = 0; + if (elm->comment) + free(elm->comment); + elm->comment = NULL; + if (elm->modenvratenum) + free_ptr_list(elm->modenvrate, elm->modenvratenum); + elm->modenvrate = NULL, elm->modenvratenum = 0; + if (elm->modenvofsnum) + free_ptr_list(elm->modenvofs, elm->modenvofsnum); + elm->modenvofs = NULL, elm->modenvofsnum = 0; + if (elm->envkeyfnum) + free_ptr_list(elm->envkeyf, elm->envkeyfnum); + elm->envkeyf = NULL, elm->envkeyfnum = 0; + if (elm->envvelfnum) + free_ptr_list(elm->envvelf, elm->envvelfnum); + elm->envvelf = NULL, elm->envvelfnum = 0; + if (elm->modenvkeyfnum) + free_ptr_list(elm->modenvkeyf, elm->modenvkeyfnum); + elm->modenvkeyf = NULL, elm->modenvkeyfnum = 0; + if (elm->modenvvelfnum) + free_ptr_list(elm->modenvvelf, elm->modenvvelfnum); + elm->modenvvelf = NULL, elm->modenvvelfnum = 0; + if (elm->trempitch) + free(elm->trempitch); + elm->trempitch = NULL, elm->trempitchnum = 0; + if (elm->tremfc) + free(elm->tremfc); + elm->tremfc = NULL, elm->tremfcnum = 0; + if (elm->modpitch) + free(elm->modpitch); + elm->modpitch = NULL, elm->modpitchnum = 0; + if (elm->modfc) + free(elm->modfc); + elm->modfc = NULL, elm->modfcnum = 0; + if (elm->fc) + free(elm->fc); + elm->fc = NULL, elm->fcnum = 0; + if (elm->reso) + free(elm->reso); + elm->reso = NULL, elm->resonum = 0; +} + +void Instruments::free_instruments(int reload_default_inst) +{ + int i = 128 + map_bank_counter, j; + struct InstrumentCache *p; + ToneBank *bank; + Instrument *ip; + struct InstrumentCache *default_entry; + int default_entry_addr; + + clear_magic_instruments(); + + /* Free soundfont instruments */ + while (i--) + { + /* Note that bank[*]->tone[j].instrument may pointer to + bank[0]->tone[j].instrument. See play_midi_load_instrument() + at playmidi.c for the implementation */ + + if ((bank = tonebank[i]) != NULL) + for (j = 127; j >= 0; j--) + { + ip = bank->tone[j].instrument; + if (ip != NULL && ip->type == INST_SF2 && + (i == 0 || ip != tonebank[0]->tone[j].instrument)) + free_instrument(ip); + bank->tone[j].instrument = NULL; + if (bank->tone[j].name && !bank->tone[j].name[0]) /* DYNAMIC_INSTRUMENT_NAME */ + { + free(bank->tone[j].name); + bank->tone[j].name = NULL; + } + } + if ((bank = drumset[i]) != NULL) + for (j = 127; j >= 0; j--) + { + ip = bank->tone[j].instrument; + if (ip != NULL && ip->type == INST_SF2 && + (i == 0 || ip != drumset[0]->tone[j].instrument)) + free_instrument(ip); + bank->tone[j].instrument = NULL; + if (bank->tone[j].name && !bank->tone[j].name[0]) /* DYNAMIC_INSTRUMENT_NAME */ + { + free(bank->tone[j].name); + bank->tone[j].name = NULL; + } + } +#if 0 + if ((drumset[i] != NULL) && (drumset[i]->alt != NULL)) { + free(drumset[i]->alt); + drumset[i]->alt = NULL; + } +#endif + } + + /* Free GUS/patch instruments */ + default_entry = NULL; + default_entry_addr = 0; + for (i = 0; i < INSTRUMENT_HASH_SIZE; i++) + { + p = instrument_cache[i]; + while (p != NULL) + { + if (!reload_default_inst && p->ip == default_instrument) + { + default_entry = p; + default_entry_addr = i; + p = p->next; + } + else + { + struct InstrumentCache *tmp; + + tmp = p; + p = p->next; + free_instrument(tmp->ip); + free(tmp); + } + } + instrument_cache[i] = NULL; + } + + if (reload_default_inst) + set_default_instrument(NULL); + else if (default_entry) + { + default_entry->next = NULL; + instrument_cache[default_entry_addr] = default_entry; + } +} + +void Instruments::free_special_patch(int id) +{ + int i, j, start, end; + + if (id >= 0) + start = end = id; + else + { + start = 0; + end = NSPECIAL_PATCH - 1; + } + + for (i = start; i <= end; i++) + { + if (special_patch[i] != NULL) + { + Sample *sp; + int n; + + if (special_patch[i]->name != NULL) + free(special_patch[i]->name); + special_patch[i]->name = NULL; + n = special_patch[i]->samples; + sp = special_patch[i]->sample; + if (sp) + { + for (j = 0; j < n; j++) + if (sp[j].data_alloced && sp[j].data) + free(sp[j].data); + free(sp); + } + free(special_patch[i]); + special_patch[i] = NULL; + } + } +} + +int Instruments::set_default_instrument(char *name) +{ + Instrument *ip; + int i; + static char *last_name; + + if (name == NULL) + { + name = last_name; + if (name == NULL) + return 0; + } + + if (!(ip = load_gus_instrument(name, NULL, 0, 0))) + return -1; + if (default_instrument) + free_instrument(default_instrument); + default_instrument = ip; + for (i = 0; i < MAX_CHANNELS; i++) + default_program[i] = SPECIAL_PROGRAM; + last_name = name; + + return 0; +} + +/*! search mapped bank. + returns negative value indicating free bank if not found, + 0 if no free bank was available */ +int Instruments::find_instrument_map_bank(int dr, int map, int bk) +{ + struct bank_map_elem *bm; + int i; + + if (map == INST_NO_MAP) + return 0; + bm = dr ? map_drumset : map_bank; + for (i = 0; i < MAP_BANK_COUNT; i++) + { + if (!bm[i].used) + return -(128 + i); + else if (bm[i].mapid == map && bm[i].bankno == bk) + return 128 + i; + } + return 0; +} + +/*! allocate mapped bank if needed. returns -1 if allocation failed. */ +int Instruments::alloc_instrument_map_bank(int dr, int map, int bk) +{ + struct bank_map_elem *bm; + int i; + + if (map == INST_NO_MAP) + { + alloc_instrument_bank(dr, bk); + return bk; + } + i = find_instrument_map_bank(dr, map, bk); + if (i == 0) + return -1; + if (i < 0) + { + i = -i - 128; + bm = dr ? map_drumset : map_bank; + bm[i].used = 1; + bm[i].mapid = map; + bm[i].bankno = bk; + if (map_bank_counter < i + 1) + map_bank_counter = i + 1; + i += 128; + alloc_instrument_bank(dr, i); + } + return i; +} + +void Instruments::alloc_instrument_bank(int dr, int bk) +{ + ToneBank *b; + + if (dr) + { + if ((b = drumset[bk]) == NULL) + { + b = drumset[bk] = (ToneBank *)safe_malloc(sizeof(ToneBank)); + memset(b, 0, sizeof(ToneBank)); + } + } + else + { + if ((b = tonebank[bk]) == NULL) + { + b = tonebank[bk] = (ToneBank *)safe_malloc(sizeof(ToneBank)); + memset(b, 0, sizeof(ToneBank)); + } + } +} + + +/* Instrument alias map - Written by Masanao Izumo */ + +int Instruments::instrument_map(int mapID, int *set, int *elem) const +{ + int s, e; + struct inst_map_elem *p; + + if (mapID == INST_NO_MAP) + return 0; /* No map */ + + s = *set; + e = *elem; + p = inst_map_table[mapID][s]; + if (p != NULL && p[e].mapped) + { + *set = p[e].set; + *elem = p[e].elem; + return 1; + } + + if (s != 0) + { + p = inst_map_table[mapID][0]; + if (p != NULL && p[e].mapped) + { + *set = p[e].set; + *elem = p[e].elem; + } + return 2; + } + return 0; +} + +void Instruments::set_instrument_map(int mapID, + int set_from, int elem_from, + int set_to, int elem_to) +{ + struct inst_map_elem *p; + + p = inst_map_table[mapID][set_from]; + if (p == NULL) + { + p = (struct inst_map_elem *) + safe_malloc(128 * sizeof(struct inst_map_elem)); + memset(p, 0, 128 * sizeof(struct inst_map_elem)); + inst_map_table[mapID][set_from] = p; + } + p[elem_from].set = set_to; + p[elem_from].elem = elem_to; + p[elem_from].mapped = 1; +} + +void Instruments::free_instrument_map(void) +{ + int i, j; + + for (i = 0; i < map_bank_counter; i++) + map_bank[i].used = map_drumset[i].used = 0; + /* map_bank_counter = 0; never shrinks rather than assuming tonebank was already freed */ + for (i = 0; i < NUM_INST_MAP; i++) { + for (j = 0; j < 128; j++) { + struct inst_map_elem *map; + map = inst_map_table[i][j]; + if (map) { + free(map); + inst_map_table[i][j] = NULL; + } + } + } +} + +/* Alternate assign - Written by Masanao Izumo */ + +AlternateAssign *Instruments::add_altassign_string(AlternateAssign *old, char **params, int n) +{ + int i, j; + char *p; + int beg, end; + AlternateAssign *alt; + + if (n == 0) + return old; + if (!strcmp(*params, "clear")) { + while (old) { + AlternateAssign *next; + next = old->next; + free(old); + old = next; + } + params++; + n--; + if (n == 0) + return NULL; + } + + alt = (AlternateAssign *)safe_malloc(sizeof(AlternateAssign)); + memset(alt, 0, sizeof(AlternateAssign)); + for (i = 0; i < n; i++) { + p = params[i]; + if (*p == '-') { + beg = 0; + p++; + } + else + beg = atoi(p); + if ((p = strchr(p, '-')) != NULL) { + if (p[1] == '\0') + end = 127; + else + end = atoi(p + 1); + } + else + end = beg; + if (beg > end) { + int t; + t = beg; + beg = end; + end = t; + } + if (beg < 0) + beg = 0; + if (end > 127) + end = 127; + for (j = beg; j <= end; j++) + alt->bits[(j >> 5) & 0x3] |= 1 << (j & 0x1F); + } + alt->next = old; + return alt; +} + +AlternateAssign *Instruments::find_altassign(AlternateAssign *altassign, int note) +{ + AlternateAssign *p; + uint32_t mask; + int idx; + + mask = 1 << (note & 0x1F); + idx = (note >> 5) & 0x3; + for (p = altassign; p != NULL; p = p->next) + if (p->bits[idx] & mask) + return p; + return NULL; +} + +Instrument *Instruments::play_midi_load_instrument(int dr, int bk, int prog, bool *pLoad_success) +{ + ToneBank **bank = (dr) ? drumset : tonebank; + ToneBankElement *tone; + Instrument *ip; + bool load_success = false; + + if (bank[bk] == NULL) + alloc_instrument_bank(dr, bk); + + tone = &bank[bk]->tone[prog]; + /* tone->name is NULL if "soundfont" directive is used, and ip is NULL when not preloaded */ + /* dr: not sure but only drumsets are concerned at the moment */ + if (dr && !tone->name && ((ip = tone->instrument) == MAGIC_LOAD_INSTRUMENT || ip == NULL) + && (ip = load_instrument(dr, bk, prog)) != NULL) { + tone->instrument = ip; + tone->name = safe_strdup(DYNAMIC_INSTRUMENT_NAME); + load_success = 1; + } + else if (tone->name) { + /* Instrument is found. */ + if ((ip = tone->instrument) == MAGIC_LOAD_INSTRUMENT +#ifndef SUPPRESS_CHANNEL_LAYER + || ip == NULL /* see also readmidi.c: groom_list(). */ +#endif + ) { + ip = tone->instrument = load_instrument(dr, bk, prog); + } + if (ip == NULL || IS_MAGIC_INSTRUMENT(ip)) { + tone->instrument = MAGIC_ERROR_INSTRUMENT; + } + else { + load_success = true; + } + } + else { + /* Instrument is not found. + Try to load the instrument from bank 0 */ + ToneBankElement *tone0 = &bank[0]->tone[prog]; + if ((ip = tone0->instrument) == NULL + || ip == MAGIC_LOAD_INSTRUMENT) + ip = tone0->instrument = load_instrument(dr, 0, prog); + if (ip == NULL || IS_MAGIC_INSTRUMENT(ip)) { + tone0->instrument = MAGIC_ERROR_INSTRUMENT; + } + else { + copy_tone_bank_element(tone, tone0); + tone->instrument = ip; + load_success = 1; + } + } + + *pLoad_success = load_success; + + if (ip == MAGIC_ERROR_INSTRUMENT) + return NULL; + + return ip; +} + + + +//void recompute_userinst_altassign(int bank, int group); + + +/*! initialize GS user drumset. */ +void Instruments::init_userdrum() +{ + int i; + AlternateAssign *alt; + + free_userdrum(); + + for (i = 0; i<2; i++) { /* allocate alternative assign */ + alt = (AlternateAssign *)safe_malloc(sizeof(AlternateAssign)); + memset(alt, 0, sizeof(AlternateAssign)); + alloc_instrument_bank(1, 64 + i); + drumset[64 + i]->alt = alt; + } +} + + +/*! free GS user drumset. */ +void Instruments::free_userdrum() +{ + UserDrumset *p, *next; + + for (p = userdrum_first; p != NULL; p = next) { + next = p->next; + free(p); + } + userdrum_first = userdrum_last = NULL; +} + +/*! free GS user instrument. */ +void Instruments::free_userinst() +{ + UserInstrument *p, *next; + + for (p = userinst_first; p != NULL; p = next) { + next = p->next; + free(p); + } + userinst_first = userinst_last = NULL; +} + + + + +/*! recompute GS user instrument. */ +/*! get pointer to requested GS user instrument. +if it's not found, allocate a new item first. */ +Instruments::UserInstrument *Instruments::get_userinst(int bank, int prog) +{ + UserInstrument *p; + + for (p = userinst_first; p != NULL; p = p->next) { + if (p->bank == bank && p->prog == prog) { return p; } + } + + p = (UserInstrument *)safe_malloc(sizeof(UserInstrument)); + memset(p, 0, sizeof(UserInstrument)); + p->next = NULL; + if (userinst_first == NULL) { + userinst_first = p; + userinst_last = p; + } + else { + userinst_last->next = p; + userinst_last = p; + } + p->bank = bank; + p->prog = prog; + + return p; +} + + + +void Instruments::recompute_userinst(int bank, int prog) +{ + auto p = get_userinst(bank, prog); + auto source_bank = p->source_bank; + auto source_prog = p->source_prog; + free_tone_bank_element(&tonebank[bank]->tone[prog]); + if (tonebank[source_bank]) { + if (tonebank[source_bank]->tone[source_prog].name) + { + copy_tone_bank_element(&tonebank[bank]->tone[prog], &tonebank[source_bank]->tone[source_prog]); + } + else if (tonebank[0]->tone[source_prog].name) + { + copy_tone_bank_element(&tonebank[bank]->tone[prog], &tonebank[0]->tone[source_prog]); + } + } +} + +/*! get pointer to requested GS user drumset. +if it's not found, allocate a new item first. */ +Instruments::UserDrumset *Instruments::get_userdrum(int bank, int prog) +{ + UserDrumset *p; + + for (p = userdrum_first; p != NULL; p = p->next) { + if (p->bank == bank && p->prog == prog) { return p; } + } + + p = (UserDrumset *)safe_malloc(sizeof(UserDrumset)); + memset(p, 0, sizeof(UserDrumset)); + p->next = NULL; + if (userdrum_first == NULL) { + userdrum_first = p; + userdrum_last = p; + } + else { + userdrum_last->next = p; + userdrum_last = p; + } + p->bank = bank; + p->prog = prog; + + return p; +} + + +/*! recompute GS user drumset. */ +Instrument *Instruments::recompute_userdrum(int bank, int prog) +{ + Instrument *ip = NULL; + + auto p = get_userdrum(bank, prog); + auto source_note = p->source_note; + auto source_prog = p->source_prog; + + free_tone_bank_element(&drumset[bank]->tone[prog]); + if (drumset[source_prog]) { + ToneBankElement *source_tone = &drumset[source_prog]->tone[source_note]; + + if (source_tone->name == NULL /* NULL if "soundfont" directive is used */ + && source_tone->instrument == NULL) { + if ((ip = load_instrument(1, source_prog, source_note)) == NULL) { + ip = MAGIC_ERROR_INSTRUMENT; + } + source_tone->instrument = ip; + } + if (source_tone->name) + { + copy_tone_bank_element(&drumset[bank]->tone[prog], source_tone); + } + else if (drumset[0]->tone[source_note].name) + { + copy_tone_bank_element(&drumset[bank]->tone[prog], &drumset[0]->tone[source_note]); + } + else { + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, "Referring user drum set %d, note %d not found - this instrument will not be heard as expected", bank, prog); + } + } + return ip; +} + +/*! convert GS user drumset assign groups to internal "alternate assign". */ +void Instruments::recompute_userdrum_altassign(int bank, int group) +{ + int number = 0, i; + char *params[131], param[10]; + ToneBank *bk; + UserDrumset *p; + + for (p = userdrum_first; p != NULL; p = p->next) { + if (p->assign_group == group) { + sprintf(param, "%d", p->prog); + params[number] = safe_strdup(param); + number++; + } + } + params[number] = NULL; + + alloc_instrument_bank(1, bank); + bk = drumset[bank]; + bk->alt = add_altassign_string(bk->alt, params, number); + for (i = number - 1; i >= 0; i--) + free(params[i]); +} + + + +} \ No newline at end of file diff --git a/src/sound/timidity++/instrum.h b/src/sound/timidity++/instrum.h new file mode 100644 index 0000000000..f8bd519820 --- /dev/null +++ b/src/sound/timidity++/instrum.h @@ -0,0 +1,560 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + instrum.h + + */ + +#ifndef ___INSTRUM_H_ +#define ___INSTRUM_H_ + +#include "common.h" +#include "sysdep.h" +#include "sffile.h" +#include "sflayer.h" +#include "sfitem.h" + + +namespace TimidityPlus +{ + +enum +{ + READ_CONFIG_SUCCESS = 0, + READ_CONFIG_ERROR = 1, + READ_CONFIG_RECURSION = 2, /* Too much recursion */ + READ_CONFIG_FILE_NOT_FOUND = 3, /* Returned only w. allow_missing_file */ +}; + + +struct Sample +{ + splen_t + loop_start, loop_end, data_length; + int32_t + sample_rate, low_freq, high_freq, root_freq; + int8_t panning, note_to_use; + int32_t + envelope_rate[6], envelope_offset[6], + modenv_rate[6], modenv_offset[6]; + double + volume; + sample_t + *data; + int32_t + tremolo_sweep_increment, tremolo_phase_increment, + vibrato_sweep_increment, vibrato_control_ratio; + int16_t + tremolo_depth; + int16_t vibrato_depth; + uint8_t + modes, data_alloced, + low_vel, high_vel; + int32_t cutoff_freq; /* in Hz, [1, 20000] */ + int16_t resonance; /* in centibels, [0, 960] */ + /* in cents, [-12000, 12000] */ + int16_t tremolo_to_pitch, tremolo_to_fc, modenv_to_pitch, modenv_to_fc, + envelope_keyf[6], envelope_velf[6], modenv_keyf[6], modenv_velf[6], + vel_to_fc, key_to_fc; + int16_t vel_to_resonance; /* in centibels, [-960, 960] */ + int8_t envelope_velf_bpo, modenv_velf_bpo, + key_to_fc_bpo, vel_to_fc_threshold; /* in notes */ + int32_t vibrato_delay, tremolo_delay, envelope_delay, modenv_delay; /* in samples */ + int16_t scale_freq; /* in notes */ + int16_t scale_factor; /* in 1024divs/key */ + int8_t inst_type; + int32_t sf_sample_index, sf_sample_link; /* for stereo SoundFont */ + uint16_t sample_type; /* 1 = Mono, 2 = Right, 4 = Left, 8 = Linked, $8000 = ROM */ + double root_freq_detected; /* root freq from pitch detection */ + int transpose_detected; /* note offset from detected root */ + int chord; /* type of chord for detected pitch */ +}; + +/* Bits in modes: */ +enum +{ + MODES_16BIT = (1 << 0), + MODES_UNSIGNED = (1 << 1), + MODES_LOOPING = (1 << 2), + MODES_PINGPONG = (1 << 3), + MODES_REVERSE = (1 << 4), + MODES_SUSTAIN = (1 << 5), + MODES_ENVELOPE = (1 << 6), + MODES_CLAMPED = (1 << 7), /* ?? (for last envelope??) */ + + INST_GUS = 0, + INST_SF2 = 1, + INST_MOD = 2, + INST_PCM = 3, /* %sample */ + + /* sfSampleType */ + SF_SAMPLETYPE_MONO = 1, + SF_SAMPLETYPE_RIGHT = 2, + SF_SAMPLETYPE_LEFT = 4, + SF_SAMPLETYPE_LINKED = 8, + SF_SAMPLETYPE_ROM = 0x8000, +}; + +struct Instrument +{ + int type; + int samples; + Sample *sample; + char *instname; +}; + +struct ToneBankElement +{ + char *name; + char *comment; + Instrument *instrument; + int8_t note, pan, strip_loop, strip_envelope, strip_tail, loop_timeout, + font_preset, font_keynote, legato, tva_level, play_note, damper_mode; + uint8_t font_bank; + uint8_t instype; /* 0: Normal + 1: %font + 2: %sample + 3-255: reserved + */ + int16_t amp; + int16_t rnddelay; + int tunenum; + float *tune; + int sclnotenum; + int16_t *sclnote; + int scltunenum; + int16_t *scltune; + int fcnum; + int16_t *fc; + int resonum; + int16_t *reso; + int trempitchnum, tremfcnum, modpitchnum, modfcnum; + int16_t *trempitch, *tremfc, *modpitch, *modfc; + int envratenum, envofsnum; + int **envrate, **envofs; + int modenvratenum, modenvofsnum; + int **modenvrate, **modenvofs; + int envvelfnum, envkeyfnum; + int **envvelf, **envkeyf; + int modenvvelfnum, modenvkeyfnum; + int **modenvvelf, **modenvkeyf; + int tremnum, vibnum; + struct Quantity_ **trem, **vib; + int16_t vel_to_fc, key_to_fc, vel_to_resonance; + int8_t reverb_send, chorus_send, delay_send; +}; + +/* A hack to delay instrument loading until after reading the + entire MIDI file. */ +#define MAGIC_LOAD_INSTRUMENT ((Instrument *)(-1)) +#define MAGIC_ERROR_INSTRUMENT ((Instrument *)(-2)) +#define IS_MAGIC_INSTRUMENT(ip) ((ip) == MAGIC_LOAD_INSTRUMENT || (ip) == MAGIC_ERROR_INSTRUMENT) + +#define DYNAMIC_INSTRUMENT_NAME "" + +struct AlternateAssign +{ + /* 128 bit vector: + * bits[(note >> 5) & 0x3] & (1 << (note & 0x1F)) + */ + uint32_t bits[4]; + AlternateAssign* next; +}; + +struct ToneBank +{ + ToneBankElement tone[128]; + AlternateAssign *alt; +}; + +struct SpecialPatch /* To be used MIDI Module play mode */ +{ + int type; + int samples; + Sample *sample; + char *name; + int32_t sample_offset; +}; + +enum instrument_mapID +{ + INST_NO_MAP = 0, + SC_55_TONE_MAP, + SC_55_DRUM_MAP, + SC_88_TONE_MAP, + SC_88_DRUM_MAP, + SC_88PRO_TONE_MAP, + SC_88PRO_DRUM_MAP, + SC_8850_TONE_MAP, + SC_8850_DRUM_MAP, + XG_NORMAL_MAP, + XG_SFX64_MAP, + XG_SFX126_MAP, + XG_DRUM_MAP, + GM2_TONE_MAP, + GM2_DRUM_MAP, + NUM_INST_MAP +}; + +enum +{ + MAP_BANK_COUNT = 256, + NSPECIAL_PATCH = 256, + SPECIAL_PROGRAM = -1, + MAX_MREL = 5000, + DEFAULT_MREL = 800, +}; + +struct SFInsts; +struct InstList; +struct SampleList; +struct AIFFCommonChunk; +struct AIFFSoundDataChunk; +struct SampleImporter; + +class Instruments +{ + PathList pathlist; + + ToneBank standard_tonebank, standard_drumset; + + enum + { + INSTRUMENT_HASH_SIZE = 128, + }; + + struct InstrumentCache + { + char *name; + int panning, amp, note_to_use, strip_loop, strip_envelope, strip_tail; + Instrument *ip; + InstrumentCache *next; + }; + + InstrumentCache *instrument_cache[INSTRUMENT_HASH_SIZE] = { nullptr }; + + /* bank mapping (mapped bank) */ + struct bank_map_elem + { + int16_t used = 0, mapid = 0; + int bankno = 0; + }; + bank_map_elem map_bank[MAP_BANK_COUNT], map_drumset[MAP_BANK_COUNT]; + int map_bank_counter = 0; + + struct inst_map_elem + { + int set, elem, mapped; + }; + + inst_map_elem *inst_map_table[NUM_INST_MAP][128] = { { nullptr} }; + + struct UserInstrument + { + int8_t bank; + int8_t prog; + int8_t source_map; + int8_t source_bank; + int8_t source_prog; + int8_t vibrato_rate; + int8_t vibrato_depth; + int8_t cutoff_freq; + int8_t resonance; + int8_t env_attack; + int8_t env_decay; + int8_t env_release; + int8_t vibrato_delay; + UserInstrument *next; + }; + + UserInstrument *userinst_first = (UserInstrument *)NULL; + UserInstrument *userinst_last = (UserInstrument *)NULL; + + struct UserDrumset { + int8_t bank; + int8_t prog; + int8_t play_note; + int8_t level; + int8_t assign_group; + int8_t pan; + int8_t reverb_send_level; + int8_t chorus_send_level; + int8_t rx_note_off; + int8_t rx_note_on; + int8_t delay_send_level; + int8_t source_map; + int8_t source_prog; + int8_t source_note; + UserDrumset *next; + }; + + struct SFBags + { + int nbags; + uint16_t *bag; + int ngens; + SFGenRec *gen; + }; + + SFBags prbags, inbags; + + UserDrumset *userdrum_first = (UserDrumset *)NULL; + UserDrumset *userdrum_last = (UserDrumset *)NULL; + + + /* Some functions get aggravated if not even the standard banks are available. */ + ToneBank + *tonebank[128 + MAP_BANK_COUNT] = { &standard_tonebank }, + *drumset[128 + MAP_BANK_COUNT] = { &standard_drumset }; + + Instrument *default_instrument = 0; + SpecialPatch *special_patch[NSPECIAL_PATCH] = { nullptr }; + int default_program[MAX_CHANNELS] = { 0 }; /* This is only used for tracks that don't specify a program */ + + char *default_instrument_name = nullptr; + int progbase = 0; + int32_t modify_release = 0; + bool opt_sf_close_each_file = true; + char def_instr_name[256] = ""; + SFInsts *sfrecs = nullptr; + SFInsts *current_sfrec = nullptr; + + int last_sample_type = 0; + int last_sample_instrument = 0; + int last_sample_keyrange = 0; + SampleList *last_sample_list = nullptr; + + LayerItem layer_items[SF_EOF]; + + /* convert from 8bit value to fractional offset (15.15) */ + int32_t to_offset_22(int offset) + { + return (int32_t)offset << (7 + 15); + } + + int32_t calc_rate_i(int diff, double msec); + int32_t convert_envelope_rate(uint8_t rate); + int32_t convert_envelope_offset(uint8_t offset); + int32_t convert_tremolo_sweep(uint8_t sweep); + int32_t convert_vibrato_sweep(uint8_t sweep, int32_t vib_control_ratio); + int32_t convert_tremolo_rate(uint8_t rate); + int32_t convert_vibrato_rate(uint8_t rate); + void reverse_data(int16_t *sp, int32_t ls, int32_t le); + int name_hash(char *name); + Instrument *search_instrument_cache(char *name, int panning, int amp, int note_to_use, int strip_loop, int strip_envelope, int strip_tail); + void store_instrument_cache(Instrument *ip, char *name, int panning, int amp, int note_to_use, int strip_loop, int strip_envelope, int strip_tail); + int32_t to_rate(int rate); + void apply_bank_parameter(Instrument *ip, ToneBankElement *tone); + Instrument *load_gus_instrument(char *name, ToneBank *bank, int dr, int prog); + int fill_bank(int dr, int b, int *rc); + void free_tone_bank_list(ToneBank *tb[]); + void free_tone_bank(void); + void free_instrument_map(void); + int set_default_instrument(char *name); + void *safe_memdup(void *s, size_t size); + void MarkInstrument(int banknum, int percussion, int instr); + + //smplfile.c + Instrument *extract_sample_file(char *); + int32_t convert_envelope_rate_s(uint8_t rate); + void initialize_sample(Instrument *inst, int frames, int sample_bits, int sample_rate); + int get_importers(const char *sample_file, int limit, SampleImporter **importers); + int get_next_importer(char *sample_file, int start, int count, SampleImporter **importers); + + int import_wave_discriminant(char *sample_file); + int import_wave_load(char *sample_file, Instrument *inst); + int import_aiff_discriminant(char *sample_file); + int import_aiff_load(char *sample_file, Instrument *inst); + int read_AIFFCommonChunk(struct timidity_file *tf, AIFFCommonChunk *comm, int csize, int compressed); + int read_AIFFSoundData(struct timidity_file *tf, Instrument *inst, AIFFCommonChunk *common); + int read_AIFFSoundDataChunk(struct timidity_file *tf, AIFFSoundDataChunk *sound, int csize, int mode); + + // sndfont.cpp + + SFInsts *find_soundfont(char *sf_file); + SFInsts *new_soundfont(char *sf_file); + void init_sf(SFInsts *rec); + void end_soundfont(SFInsts *rec); + Instrument *try_load_soundfont(SFInsts *rec, int order, int bank, int preset, int keynote); + Instrument *load_from_file(SFInsts *rec, InstList *ip); + int is_excluded(SFInsts *rec, int bank, int preset, int keynote); + int is_ordered(SFInsts *rec, int bank, int preset, int keynote); + int load_font(SFInfo *sf, int pridx); + int parse_layer(SFInfo *sf, int pridx, LayerTable *tbl, int level); + int is_global(SFGenLayer *layer); + void clear_table(LayerTable *tbl); + void set_to_table(SFInfo *sf, LayerTable *tbl, SFGenLayer *lay, int level); + void add_item_to_table(LayerTable *tbl, int oper, int amount, int level); + void merge_table(SFInfo *sf, LayerTable *dst, LayerTable *src); + void init_and_merge_table(SFInfo *sf, LayerTable *dst, LayerTable *src); + int sanity_range(LayerTable *tbl); + int make_patch(SFInfo *sf, int pridx, LayerTable *tbl); + void make_info(SFInfo *sf, SampleList *vp, LayerTable *tbl); + double calc_volume(LayerTable *tbl); + void set_sample_info(SFInfo *sf, SampleList *vp, LayerTable *tbl); + void set_init_info(SFInfo *sf, SampleList *vp, LayerTable *tbl); + void reset_last_sample_info(void); + int abscent_to_Hz(int abscents); + void set_rootkey(SFInfo *sf, SampleList *vp, LayerTable *tbl); + void set_rootfreq(SampleList *vp); + int32_t to_offset(int32_t offset); + int32_t to_rate(int32_t diff, int timecent); + int32_t calc_rate(int32_t diff, double msec); + double to_msec(int timecent); + int32_t calc_sustain(int sust_cB); + void convert_volume_envelope(SampleList *vp, LayerTable *tbl); + void convert_tremolo(SampleList *vp, LayerTable *tbl); + void convert_vibrato(SampleList *vp, LayerTable *tbl); + void set_envelope_parameters(SampleList *vp); + + // configfile + + int set_patchconf(const char *name, int line, ToneBank *bank, char *w[], int dr, int mapid, int bankmapfrom, int bankno); + int strip_trailing_comment(char *string, int next_token_index); + char *expand_variables(char *string, MBlockList *varbuf, const char *basedir); + int set_gus_patchconf(const char *name, int line, ToneBankElement *tone, char *pat, char **opts); + void reinit_tone_bank_element(ToneBankElement *tone); + int set_gus_patchconf_opts(const char *name, int line, char *opts, ToneBankElement *tone); + int copymap(int mapto, int mapfrom, int isdrum); + void copybank(ToneBank *to, ToneBank *from, int mapid, int bankmapfrom, int bankno); + + // sffile.cpp + + int chunkid(char *id); + int process_list(int size, SFInfo *sf, struct timidity_file *fd); + int process_info(int size, SFInfo *sf, struct timidity_file *fd); + int process_sdta(int size, SFInfo *sf, struct timidity_file *fd); + int process_pdta(int size, SFInfo *sf, struct timidity_file *fd); + void load_sample_names(int size, SFInfo *sf, struct timidity_file *fd); + void load_preset_header(int size, SFInfo *sf, struct timidity_file *fd); + void load_inst_header(int size, SFInfo *sf, struct timidity_file *fd); + void load_bag(int size, SFBags *bagp, struct timidity_file *fd); + void load_gen(int size, SFBags *bagp, struct timidity_file *fd); + void load_sample_info(int size, SFInfo *sf, struct timidity_file *fd); + void convert_layers(SFInfo *sf); + void generate_layers(SFHeader *hdr, SFHeader *next, SFBags *bags); + void free_layer(SFHeader *hdr); + int load_soundfont(SFInfo *sf, struct timidity_file *fd); + void free_soundfont(SFInfo *sf); + void correct_samples(SFInfo *sf); + + +public: + + Instruments(); + bool load(const char *config); + ~Instruments(); + + const ToneBank *toneBank(int i) const + { + return tonebank[i]; + } + + int defaultProgram(int i) const + { + return default_program[i]; + } + + const ToneBank *drumSet(int i) const + { + return drumset[i]; + } + + const SpecialPatch *specialPatch(int i) const + { + return special_patch[i]; + } + + void setSpecialPatchOffset(int i, int32_t ofs) + { + special_patch[i]->sample_offset = ofs; + } + Instrument *defaultInstrument() const + { + return default_instrument; + } + + /* instrum.c */ + int load_missing_instruments(int *rc); + void free_instruments(int reload_default_inst); + void free_special_patch(int id); + void clear_magic_instruments(void); + Instrument *load_instrument(int dr, int b, int prog); + int find_instrument_map_bank(int dr, int map, int bk); + int alloc_instrument_map_bank(int dr, int map, int bk); + void alloc_instrument_bank(int dr, int bankset); + int instrument_map(int mapID, int *set_in_out, int *elem_in_out) const; + void set_instrument_map(int mapID, int set_from, int elem_from, int set_to, int elem_to); + AlternateAssign *add_altassign_string(AlternateAssign *old, char **params, int n); + AlternateAssign *find_altassign(AlternateAssign *altassign, int note); + void copy_tone_bank_element(ToneBankElement *elm, const ToneBankElement *src); + void free_tone_bank_element(ToneBankElement *elm); + void free_instrument(Instrument *ip); + void squash_sample_16to8(Sample *sp); + Instrument *play_midi_load_instrument(int dr, int bk, int prog, bool *pLoad_success); + void recompute_userinst(int bank, int prog); + Instrument *recompute_userdrum(int bank, int prog); + UserInstrument *get_userinst(int bank, int prog); + UserDrumset *get_userdrum(int bank, int prog); + void recompute_userdrum_altassign(int bank, int group); + /*! initialize GS user drumset. */ + void init_userdrum(); + void free_userdrum(); + void init_userinst() { free_userinst(); } + void free_userinst(); + + void mark_instrument(int newbank, int newprog) + { + if (!(tonebank[newbank]->tone[newprog].instrument)) + tonebank[newbank]->tone[newprog].instrument = + MAGIC_LOAD_INSTRUMENT; + } + + void mark_drumset(int newbank, int newprog) + { + if (!(drumset[newbank]->tone[newprog].instrument)) + drumset[newbank]->tone[newprog].instrument = + MAGIC_LOAD_INSTRUMENT; + } + + /* sndfont.c */ + void add_soundfont(char *sf_file, int sf_order, int cutoff_allowed, int resonance_allowed, int amp); + void remove_soundfont(char *sf_file); + void init_load_soundfont(void); + Instrument *load_soundfont_inst(int order, int bank, int preset, int keynote); + Instrument *extract_soundfont(char *sf_file, int bank, int preset, int keynote); + int exclude_soundfont(int bank, int preset, int keynote); + int order_soundfont(int bank, int preset, int keynote, int order); + char *soundfont_preset_name(int bank, int preset, int keynote, char **sndfile); + void free_soundfonts(void); + void PrecacheInstruments(const uint16_t *instruments, int count); + + + int read_config_file(const char *name, int self, int allow_missing_file); + + void set_default_instrument() + { + if (def_instr_name) set_default_instrument(def_instr_name); + } + +}; + + +} +#endif /* ___INSTRUM_H_ */ diff --git a/src/sound/timidity++/mblock.cpp b/src/sound/timidity++/mblock.cpp new file mode 100644 index 0000000000..5ad8fd6584 --- /dev/null +++ b/src/sound/timidity++/mblock.cpp @@ -0,0 +1,169 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include + +#include + +#include "timidity.h" +#include "common.h" + +namespace TimidityPlus +{ + +static MBlockNode *free_mblock_list = NULL; +#define ADDRALIGN 8 +/* #define DEBUG */ + +void init_mblock(MBlockList *mblock) +{ + mblock->first = NULL; + mblock->allocated = 0; +} + +static MBlockNode *new_mblock_node(size_t n) +{ + MBlockNode *p; + + if (n > MIN_MBLOCK_SIZE) + { + if ((p = (MBlockNode *)safe_malloc(n + sizeof(MBlockNode))) == NULL) + return NULL; + p->block_size = n; + } + else if (free_mblock_list == NULL) + { + if ((p = (MBlockNode *)safe_malloc(sizeof(MBlockNode) + MIN_MBLOCK_SIZE)) == NULL) + return NULL; + p->block_size = MIN_MBLOCK_SIZE; + } + else + { + p = free_mblock_list; + free_mblock_list = free_mblock_list->next; + } + + p->offset = 0; + p->next = NULL; + + return p; +} + +static int enough_block_memory(MBlockList *mblock, size_t n) +{ + size_t newoffset; + + if(mblock->first == NULL) + return 0; + + newoffset = mblock->first->offset + n; + + if(newoffset < mblock->first->offset) /* exceed representable in size_t */ + return 0; + + if(newoffset > mblock->first->block_size) + return 0; + + return 1; +} + +void *new_segment(MBlockList *mblock, size_t nbytes) +{ + MBlockNode *p; + void *addr; + + /* round up to ADDRALIGN */ + nbytes = ((nbytes + ADDRALIGN - 1) & ~(ADDRALIGN - 1)); + if (!enough_block_memory(mblock, nbytes)) + { + p = new_mblock_node(nbytes); + p->next = mblock->first; + mblock->first = p; + mblock->allocated += p->block_size; + } + else + p = mblock->first; + + addr = (void *)(p->buffer + p->offset); + p->offset += nbytes; + + return addr; +} + +static void reuse_mblock1(MBlockNode *p) +{ + if (p->block_size > MIN_MBLOCK_SIZE) + free(p); + else /* p->block_size <= MIN_MBLOCK_SIZE */ + { + p->next = free_mblock_list; + free_mblock_list = p; + } +} + +void reuse_mblock(MBlockList *mblock) +{ + MBlockNode *p; + + if ((p = mblock->first) == NULL) + return; /* There is nothing to collect memory */ + + while (p) + { + MBlockNode *tmp; + + tmp = p; + p = p->next; + reuse_mblock1(tmp); + } + init_mblock(mblock); +} + +char *strdup_mblock(MBlockList *mblock, const char *str) +{ + int len; + char *p; + + len = (int)strlen(str); + p = (char *)new_segment(mblock, len + 1); /* for '\0' */ + memcpy(p, str, len + 1); + return p; +} + +int free_global_mblock(void) +{ + int cnt; + + cnt = 0; + while (free_mblock_list) + { + MBlockNode *tmp; + + tmp = free_mblock_list; + free_mblock_list = free_mblock_list->next; + free(tmp); + cnt++; + } + return cnt; +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/mblock.h b/src/sound/timidity++/mblock.h new file mode 100644 index 0000000000..379926666e --- /dev/null +++ b/src/sound/timidity++/mblock.h @@ -0,0 +1,78 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef ___MBLOCK_H_ +#define ___MBLOCK_H_ + +namespace TimidityPlus +{ + struct MBlockNode; + +/* Memory block for decreasing malloc + * + * +------+ +------+ +-------+ + * |BLOCK1|--->|BLOCK2|---> ... --->|BLOCK N|---> NULL + * +------+ +------+ +-------+ + * + * + * BLOCK: + * +-----------------------+ + * | memory 1 | + * | | + * +-----------------------+ + * | memory 2 | + * +-----------------------+ + * | memory 3 | + * | | + * | | + * +-----------------------+ + * | unused ... | + * +-----------------------+ + */ + + +#define MIN_MBLOCK_SIZE 8192 + +struct MBlockNode +{ + size_t block_size; + size_t offset; + MBlockNode *next; +#ifndef MBLOCK_NOPAD + void *pad; +#endif /* MBLOCK_NOPAD */ + char buffer[1]; +}; + +struct MBlockList +{ + MBlockNode *first; + size_t allocated; +}; + +extern void init_mblock(MBlockList *mblock); +extern void *new_segment(MBlockList *mblock, size_t nbytes); +extern void reuse_mblock(MBlockList *mblock); +extern char *strdup_mblock(MBlockList *mblock, const char *str); +extern int free_global_mblock(void); + +} +#endif /* ___MBLOCK_H_ */ diff --git a/src/sound/timidity++/mix.cpp b/src/sound/timidity++/mix.cpp new file mode 100644 index 0000000000..110f9748c1 --- /dev/null +++ b/src/sound/timidity++/mix.cpp @@ -0,0 +1,1632 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + mix.c +*/ + +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "tables.h" +#include "resample.h" +#include "mix.h" +#include "optcode.h" +#include "c_cvars.h" + +CUSTOM_CVAR(Float, min_sustain_time, 5000, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0) self = 0; +} + +namespace TimidityPlus +{ + + +#define FROM_FINAL_VOLUME(a) (a) + +#define OFFSET_MAX (0x3FFFFFFFL) + +typedef int32_t mix_t; + +#define MIXATION(a) *lp++ += (a) * s + +#define DELAYED_MIXATION(a) *lp++ += pan_delay_buf[pan_delay_spt]; \ + if (++pan_delay_spt == PAN_DELAY_BUF_MAX) {pan_delay_spt = 0;} \ + pan_delay_buf[pan_delay_wpt] = (a) * s; \ + if (++pan_delay_wpt == PAN_DELAY_BUF_MAX) {pan_delay_wpt = 0;} + + + + +/**************** interface function ****************/ +void Mixer::mix_voice(int32_t *buf, int v, int32_t c) +{ + Resampler re(player); + Voice *vp = player->voice + v; + resample_t *sp; + + if (vp->status == VOICE_DIE) + { + if (c >= MAX_DIE_TIME) + c = MAX_DIE_TIME; + sp = re.resample_voice(v, &c); + if (do_voice_filter(v, sp, filter_buffer, c)) { sp = filter_buffer; } + if (c > 0) + ramp_out(sp, buf, v, c); + player->free_voice(v); + } + else { + vp->delay_counter = c; + if (vp->delay) { + if (c < vp->delay) { + vp->delay -= c; + if (vp->tremolo_phase_increment) + update_tremolo(v); + if (opt_modulation_envelope && vp->sample->modes & MODES_ENVELOPE) + update_modulation_envelope(v); + return; + } + buf += vp->delay * 2; + c -= vp->delay; + vp->delay = 0; + } + sp = re.resample_voice(v, &c); + if (do_voice_filter(v, sp, filter_buffer, c)) { sp = filter_buffer; } + + if (vp->panned == PANNED_MYSTERY) { + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_mystery_signal(sp, buf, v, c); + else + mix_mystery(sp, buf, v, c); + } + else if (vp->panned == PANNED_CENTER) { + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_center_signal(sp, buf, v, c); + else + mix_center(sp, buf, v, c); + } + else { + /* It's either full left or full right. In either case, + * every other sample is 0. Just get the offset right: + */ + if (vp->panned == PANNED_RIGHT) + buf++; + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_single_signal(sp, buf, v, c); + else + mix_single(sp, buf, v, c); + } + } +} + +/* return 1 if filter is enabled. */ +int Mixer::do_voice_filter(int v, resample_t *sp, mix_t *lp, int32_t count) +{ + FilterCoefficients *fc = &(player->voice[v].fc); + int32_t i, f, q, p, b0, b1, b2, b3, b4, t1, t2, x; + + if (fc->type == 1) { /* copy with applying Chamberlin's lowpass filter. */ + recalc_voice_resonance(v); + recalc_voice_fc(v); + f = fc->f, q = fc->q, b0 = fc->b0, b1 = fc->b1, b2 = fc->b2; + for(i = 0; i < count; i++) { + b0 = b0 + imuldiv24(b2, f); + b1 = sp[i] - b0 - imuldiv24(b2, q); + b2 = imuldiv24(b1, f) + b2; + lp[i] = b0; + } + fc->b0 = b0, fc->b1 = b1, fc->b2 = b2; + return 1; + } else if(fc->type == 2) { /* copy with applying Moog lowpass VCF. */ + recalc_voice_resonance(v); + recalc_voice_fc(v); + f = fc->f, q = fc->q, p = fc->p, b0 = fc->b0, b1 = fc->b1, + b2 = fc->b2, b3 = fc->b3, b4 = fc->b4; + for(i = 0; i < count; i++) { + x = sp[i] - imuldiv24(q, b4); /* feedback */ + t1 = b1; b1 = imuldiv24(x + b0, p) - imuldiv24(b1, f); + t2 = b2; b2 = imuldiv24(b1 + t1, p) - imuldiv24(b2, f); + t1 = b3; b3 = imuldiv24(b2 + t2, p) - imuldiv24(b3, f); + lp[i] = b4 = imuldiv24(b3 + t1, p) - imuldiv24(b4, f); + b0 = x; + } + fc->b0 = b0, fc->b1 = b1, fc->b2 = b2, fc->b3 = b3, fc->b4 = b4; + return 1; + } else { + return 0; + } +} + +//#define MOOG_RESONANCE_MAX 0.897638f +#define MOOG_RESONANCE_MAX 0.88f + +void Mixer::recalc_voice_resonance(int v) +{ + double q; + FilterCoefficients *fc = &(player->voice[v].fc); + + if (fc->reso_dB != fc->last_reso_dB || fc->q == 0) { + fc->last_reso_dB = fc->reso_dB; + if(fc->type == 1) { + q = 1.0 / chamberlin_filter_db_to_q_table[(int)(fc->reso_dB * 4)]; + fc->q = TIM_FSCALE(q, 24); + if(fc->q <= 0) {fc->q = 1;} /* must never be 0. */ + } else if(fc->type == 2) { + fc->reso_lin = fc->reso_dB * MOOG_RESONANCE_MAX / 20.0f; + if (fc->reso_lin > MOOG_RESONANCE_MAX) {fc->reso_lin = MOOG_RESONANCE_MAX;} + else if(fc->reso_lin < 0) {fc->reso_lin = 0;} + } + fc->last_freq = -1; + } +} + +void Mixer::recalc_voice_fc(int v) +{ + double f, p, q, fr; + FilterCoefficients *fc = &(player->voice[v].fc); + + if (fc->freq != fc->last_freq) { + if(fc->type == 1) { + f = 2.0 * sin(M_PI * (double)fc->freq / (double)playback_rate); + fc->f = TIM_FSCALE(f, 24); + } else if(fc->type == 2) { + fr = 2.0 * (double)fc->freq / (double)playback_rate; + q = 1.0 - fr; + p = fr + 0.8 * fr * q; + f = p + p - 1.0; + q = fc->reso_lin * (1.0 + 0.5 * q * (1.0 - q + 5.6 * q * q)); + fc->f = TIM_FSCALE(f, 24); + fc->p = TIM_FSCALE(p, 24); + fc->q = TIM_FSCALE(q, 24); + } + fc->last_freq = fc->freq; + } +} + +/* Ramp a note out in c samples */ +void Mixer::ramp_out(mix_t *sp, int32_t *lp, int v, int32_t c) +{ + /* should be final_volume_t, but uint8_t gives trouble. */ + int32_t left, right, li, ri, i; + /* silly warning about uninitialized s */ + mix_t s = 0; + Voice *vp = &player->voice[v]; + int32_t pan_delay_wpt = vp->pan_delay_wpt, *pan_delay_buf = vp->pan_delay_buf, + pan_delay_spt = vp->pan_delay_spt; + + left = player->voice[v].left_mix; + li = -(left / c); + if (! li) + li = -1; + if (true) { + if (player->voice[v].panned == PANNED_MYSTERY) { + right = player->voice[v].right_mix; + ri = -(right / c); + if(vp->pan_delay_rpt == 0) { + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + left = 0; + right += ri; + if (right < 0) + right = 0; + s = *sp++; + MIXATION(left); + MIXATION(right); + } + } else if(vp->panning < 64) { + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + left = 0; + right += ri; + if (right < 0) + right = 0; + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + } + } else { + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + left = 0; + right += ri; + if (right < 0) + right = 0; + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + } + } + vp->pan_delay_wpt = pan_delay_wpt; + vp->pan_delay_spt = pan_delay_spt; + } else if (player->voice[v].panned == PANNED_CENTER) + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + return; + s = *sp++; + MIXATION(left); + MIXATION(left); + } + else if (player->voice[v].panned == PANNED_LEFT) + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + return; + s = *sp++; + MIXATION(left); + lp++; + } + else if (player->voice[v].panned == PANNED_RIGHT) + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + return; + s = *sp++; + lp++; + MIXATION(left); + } + } else + /* Mono output. */ + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + return; + s = *sp++; + MIXATION(left); + } +} + +void Mixer::mix_mono_signal( + mix_t *sp, int32_t *lp, int v, int count) +{ + Voice *vp = player->voice + v; + final_volume_t left = vp->left_mix; + int cc, i; + mix_t s; + int32_t linear_left; + + if (! (cc = vp->control_counter)) { + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + } + compute_mix_smoothing(vp); + while (count) + if (cc < count) { + count -= cc; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < cc; i++) { + s = *sp++; + MIXATION(left); + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = linear_left; + cc -= i; + for (i = 0; i < cc; i++) { + s = *sp++; + MIXATION(left); + } + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + compute_mix_smoothing(vp); + } else { + vp->control_counter = cc - count; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < count; i++) { + s = *sp++; + MIXATION(left); + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = linear_left; + count -= i; + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + } + return; + } +} + + +void Mixer::mix_mystery_signal( + mix_t *sp, int32_t *lp, int v, int count) +{ + Voice *vp = player->voice + v; + final_volume_t left = vp->left_mix, right = vp->right_mix; + int cc, i; + mix_t s; + int32_t linear_left, linear_right; + int32_t pan_delay_wpt = vp->pan_delay_wpt, *pan_delay_buf = vp->pan_delay_buf, + pan_delay_spt = vp->pan_delay_spt; + + if (! (cc = vp->control_counter)) { + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + right = vp->right_mix; + } + compute_mix_smoothing(vp); + while (count) + if (cc < count) { + count -= cc; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + linear_right = FROM_FINAL_VOLUME(right); + if (vp->right_mix_offset) { + linear_right += vp->right_mix_offset; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + if(vp->pan_delay_rpt == 0) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < cc; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else if(vp->panning < 64) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < cc; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < cc; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } + vp->old_left_mix = linear_left; + vp->old_right_mix = linear_right; + cc -= i; + if(vp->pan_delay_rpt == 0) { + for (i = 0; i < cc; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + } + } else if(vp->panning < 64) { + for (i = 0; i < cc; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + } + } else { + for (i = 0; i < cc; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + } + } + + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + right = vp->right_mix; + compute_mix_smoothing(vp); + } else { + vp->control_counter = cc - count; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + linear_right = FROM_FINAL_VOLUME(right); + if (vp->right_mix_offset) { + linear_right += vp->right_mix_offset; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + if(vp->pan_delay_rpt == 0) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else if(vp->panning < 64) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } + + vp->old_left_mix = linear_left; + vp->old_right_mix = linear_right; + count -= i; + if(vp->pan_delay_rpt == 0) { + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + } + } else if(vp->panning < 64) { + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + } + } else { + for (i = 0; i < count; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + } + } + vp->pan_delay_wpt = pan_delay_wpt; + vp->pan_delay_spt = pan_delay_spt; + return; + } +} + +void Mixer::mix_mystery(mix_t *sp, int32_t *lp, int v, int count) +{ + final_volume_t left = player->voice[v].left_mix, right = player->voice[v].right_mix; + mix_t s; + int i; + Voice *vp = player->voice + v; + int32_t linear_left, linear_right; + int32_t pan_delay_wpt = vp->pan_delay_wpt, *pan_delay_buf = vp->pan_delay_buf, + pan_delay_spt = vp->pan_delay_spt; + + compute_mix_smoothing(vp); + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + linear_right = FROM_FINAL_VOLUME(right); + if (vp->right_mix_offset) { + linear_right += vp->right_mix_offset; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + if(vp->pan_delay_rpt == 0) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else if(vp->panning < 64) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } + + vp->old_left_mix = linear_left; + vp->old_right_mix = linear_right; + count -= i; + if(vp->pan_delay_rpt == 0) { + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + } + } else if(vp->panning < 64) { + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + } + } else { + for (i = 0; i < count; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + } + } + vp->pan_delay_wpt = pan_delay_wpt; + vp->pan_delay_spt = pan_delay_spt; +} + + +void Mixer::mix_center_signal( + mix_t *sp, int32_t *lp, int v, int count) +{ + Voice *vp = player->voice + v; + final_volume_t left=vp->left_mix; + int cc, i; + mix_t s; + int32_t linear_left; + + if (! (cc = vp->control_counter)) { + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + } + compute_mix_smoothing(vp); + while (count) + if (cc < count) { + count -= cc; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < cc; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = vp->old_right_mix = linear_left; + cc -= i; + for (i = 0; i < cc; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + } + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + compute_mix_smoothing(vp); + } else { + vp->control_counter = cc - count; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = vp->old_right_mix = linear_left; + count -= i; + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + } + return; + } +} + +void Mixer::mix_center(mix_t *sp, int32_t *lp, int v, int count) +{ + final_volume_t left = player->voice[v].left_mix; + mix_t s; + int i; + Voice *vp = player->voice + v; + int32_t linear_left; + + compute_mix_smoothing(vp); + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = vp->old_right_mix = linear_left; + count -= i; + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + } +} + +void Mixer::mix_single_signal(mix_t *sp, int32_t *lp, int v, int count) +{ + Voice *vp = player->voice + v; + final_volume_t left = vp->left_mix; + int cc, i; + mix_t s; + int32_t linear_left; + + if (!(cc = vp->control_counter)) { + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + } + compute_mix_smoothing(vp); + while (count) + if (cc < count) { + count -= cc; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < cc; i++) { + s = *sp++; + MIXATION(left); + lp++; + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = linear_left; + cc -= i; + for (i = 0; i < cc; i++) { + s = *sp++; + MIXATION(left); + lp++; + } + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + compute_mix_smoothing(vp); + } else { + vp->control_counter = cc - count; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < count; i++) { + s = *sp++; + MIXATION(left); + lp++; + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = linear_left; + count -= i; + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + lp++; + } + return; + } +} + +void Mixer::mix_single(mix_t *sp, int32_t *lp, int v, int count) +{ + final_volume_t left = player->voice[v].left_mix; + mix_t s; + int i; + Voice *vp = player->voice + v; + int32_t linear_left; + + compute_mix_smoothing(vp); + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < count; i++) { + s = *sp++; + MIXATION(left); + lp++; + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = linear_left; + count -= i; + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + lp++; + } +} + +/* Returns 1 if the note died */ +int Mixer::update_signal(int v) +{ + Voice *vp = &player->voice[v]; + + if (vp->envelope_increment && update_envelope(v)) + return 1; + if (vp->tremolo_phase_increment) + update_tremolo(v); + if (opt_modulation_envelope && vp->sample->modes & MODES_ENVELOPE) + update_modulation_envelope(v); + return apply_envelope_to_amp(v); +} + +int Mixer::update_envelope(int v) +{ + Voice *vp = &player->voice[v]; + + vp->envelope_volume += vp->envelope_increment; + if ((vp->envelope_increment < 0) + ^ (vp->envelope_volume > vp->envelope_target)) { + vp->envelope_volume = vp->envelope_target; + if (recompute_envelope(v)) + return 1; + } + return 0; +} + +int Mixer::get_eg_stage(int v, int stage) +{ + int eg_stage; + Voice *vp = &player->voice[v]; + + eg_stage = stage; + if (vp->sample->inst_type == INST_SF2) { + if (stage >= EG_SF_RELEASE) { + eg_stage = EG_RELEASE; + } + } else { + if (stage == EG_GUS_DECAY) { + eg_stage = EG_DECAY; + } else if (stage == EG_GUS_SUSTAIN) { + eg_stage = EG_NULL; + } else if (stage >= EG_GUS_RELEASE1) { + eg_stage = EG_RELEASE; + } + } + return eg_stage; +} + + +/* Returns 1 if envelope runs out */ +int Mixer::recompute_envelope(int v) +{ + int stage, ch; + double sustain_time; + int32_t envelope_width; + Voice *vp = &player->voice[v]; + + stage = vp->envelope_stage; + if (stage > EG_GUS_RELEASE3) { + voice_ran_out(v); + return 1; + } else if (stage > EG_GUS_SUSTAIN && vp->envelope_volume <= 0) { + /* Remove silent player->voice in the release stage */ + voice_ran_out(v); + return 1; + } + + /* Routine to decay the sustain envelope + * + * Disabled if !min_sustain_time. + * min_sustain_time is given in msec, and is the minimum + * time it will take to decay a note to zero. + * 2000-3000 msec seem to be decent values to use. + */ + if (stage == EG_GUS_RELEASE1 && vp->sample->modes & MODES_ENVELOPE + && vp->status & (VOICE_ON | VOICE_SUSTAINED)) { + + int32_t new_rate; + + ch = vp->channel; + + /* Don't adjust the current rate if VOICE_ON */ + if (vp->status & VOICE_ON) + return 0; + + if (min_sustain_time > 0 || player->channel[ch].loop_timeout > 0) { + if (min_sustain_time == 1) + /* The sustain stage is ignored. */ + return next_stage(v); + + if (player->channel[ch].loop_timeout > 0 && + player->channel[ch].loop_timeout * 1000 < min_sustain_time) { + /* timeout (See also "#extension timeout" line in *.cfg file */ + sustain_time = player->channel[ch].loop_timeout * 1000; + } + else { + sustain_time = min_sustain_time; + } + + /* Sustain must not be 0 or else lots of dead notes! */ + if (player->channel[ch].sostenuto == 0 && + player->channel[ch].sustain > 0) { + sustain_time *= (double)player->channel[ch].sustain / 127.0f; + } + + /* Calculate the width of the envelope */ + envelope_width = sustain_time * playback_rate + / (1000.0f * (double)control_ratio); + + if (vp->sample->inst_type == INST_SF2) { + /* If the instrument is SoundFont, it sustains at the sustain stage. */ + vp->envelope_increment = -1; + vp->envelope_target = vp->envelope_volume - envelope_width; + if (vp->envelope_target < 0) {vp->envelope_target = 0;} + } else { + /* Otherwise, it decays at the sustain stage. */ + vp->envelope_target = 0; + new_rate = vp->envelope_volume / envelope_width; + /* Use the Release1 rate if slower than new rate */ + if (vp->sample->envelope_rate[EG_GUS_RELEASE1] && + vp->sample->envelope_rate[EG_GUS_RELEASE1] < new_rate) + new_rate = vp->sample->envelope_rate[EG_GUS_RELEASE1]; + /* Use the Sustain rate if slower than new rate */ + /* (Sustain rate exists only in GUS patches) */ + if (vp->sample->inst_type == INST_GUS && + vp->sample->envelope_rate[EG_GUS_SUSTAIN] && + vp->sample->envelope_rate[EG_GUS_SUSTAIN] < new_rate) + new_rate = vp->sample->envelope_rate[EG_GUS_SUSTAIN]; + /* Avoid freezing */ + if (!new_rate) + new_rate = 1; + vp->envelope_increment = -new_rate; + } + } + return 0; + } + return next_stage(v); +} + +/* Envelope ran out. */ +void Mixer::voice_ran_out(int v) +{ + /* Already displayed as dead */ + int died = (player->voice[v].status == VOICE_DIE); + + player->free_voice(v); +} + +int Mixer::next_stage(int v) +{ + int stage, ch, eg_stage; + int32_t offset, val; + double rate, temp_rate; + Voice *vp = &player->voice[v]; + + stage = vp->envelope_stage++; + offset = vp->sample->envelope_offset[stage]; + rate = vp->sample->envelope_rate[stage]; + if (vp->envelope_volume == offset + || (stage > EG_GUS_SUSTAIN && vp->envelope_volume < offset)) + return recompute_envelope(v); + ch = vp->channel; + /* there is some difference between GUS patch and Soundfont at envelope. */ + eg_stage = get_eg_stage(v, stage); + + /* HACK -- force ramps to occur over 20 msec windows to avoid pops */ + /* Do not apply to attack envelope */ + if (eg_stage > EG_ATTACK) + { + temp_rate = control_ratio * (labs(vp->envelope_volume - offset) / + (playback_rate * 0.02)); + if (temp_rate < 1) + temp_rate = 1; + if (rate < 0) + temp_rate = -temp_rate; + if (fabs(temp_rate) < fabs(rate)) + rate = temp_rate; + } + + /* envelope generator (see also playmidi.[ch]) */ + if (player->ISDRUMCHANNEL(ch)) + val = (player->channel[ch].drums[vp->note] != NULL) + ? player->channel[ch].drums[vp->note]->drum_envelope_rate[eg_stage] + : -1; + else { + if (vp->sample->envelope_keyf[stage]) /* envelope key-follow */ + rate *= pow(2.0, (double) (player->voice[v].note - 60) + * (double)vp->sample->envelope_keyf[stage] / 1200.0f); + val = player->channel[ch].envelope_rate[eg_stage]; + } + if (vp->sample->envelope_velf[stage]) /* envelope velocity-follow */ + rate *= pow(2.0, (double) (player->voice[v].velocity - vp->sample->envelope_velf_bpo) + * (double)vp->sample->envelope_velf[stage] / 1200.0f); + + /* just before release-phase, some modifications are necessary */ + if (stage > EG_GUS_SUSTAIN) { + /* adjusting release-rate for consistent release-time */ + rate *= (double) vp->envelope_volume + / vp->sample->envelope_offset[EG_GUS_ATTACK]; + /* calculating current envelope scale and a inverted value for optimization */ + vp->envelope_scale = vp->last_envelope_volume; + vp->inv_envelope_scale = TIM_FSCALE(OFFSET_MAX / (double)vp->envelope_volume, 16); + } + + /* regularizing envelope */ + if (offset < vp->envelope_volume) { /* decaying phase */ + if (val != -1) { + if(eg_stage > EG_DECAY) { + rate *= sc_eg_release_table[val & 0x7f]; + } else { + rate *= sc_eg_decay_table[val & 0x7f]; + } + + if (fabs(rate) > OFFSET_MAX) + rate = (rate > 0) ? OFFSET_MAX : -OFFSET_MAX; + else if (fabs(rate) < 1) + rate = (rate > 0) ? 1 : -1; + } + if(stage < EG_SF_DECAY && rate > OFFSET_MAX) { /* instantaneous decay */ + vp->envelope_volume = offset; + return recompute_envelope(v); + } else if(rate > vp->envelope_volume - offset) { /* fastest decay */ + rate = -vp->envelope_volume + offset - 1; + } else if (rate < 1) { /* slowest decay */ + rate = -1; + } + else { /* ordinary decay */ + rate = -rate; + } + } else { /* attacking phase */ + if (val != -1) { + rate *= sc_eg_attack_table[val & 0x7f]; + + if (fabs(rate) > OFFSET_MAX) + rate = (rate > 0) ? OFFSET_MAX : -OFFSET_MAX; + else if (fabs(rate) < 1) + rate = (rate > 0) ? 1 : -1; + } + if(stage < EG_SF_DECAY && rate > OFFSET_MAX) { /* instantaneous attack */ + vp->envelope_volume = offset; + return recompute_envelope(v); + } else if(rate > offset - vp->envelope_volume) { /* fastest attack */ + rate = offset - vp->envelope_volume + 1; + } else if (rate < 1) {rate = 1;} /* slowest attack */ + } + + /* HACK -- force ramps to occur over 20 msec windows to avoid pops */ + /* Do not apply to attack envelope */ + /* Must check again in case the above conditions shortened it */ + if (eg_stage > EG_ATTACK) + { + temp_rate = control_ratio * (labs(vp->envelope_volume - offset) / + (playback_rate * 0.02)); + if (temp_rate < 1) + temp_rate = 1; + if (rate < 0) + temp_rate = -temp_rate; + if (fabs(temp_rate) < fabs(rate)) + rate = temp_rate; + } + + vp->envelope_increment = (int32_t)rate; + vp->envelope_target = offset; + + return 0; +} + +void Mixer::update_tremolo(int v) +{ + Voice *vp = &player->voice[v]; + int32_t depth = vp->tremolo_depth << 7; + + if(vp->tremolo_delay > 0) + { + vp->tremolo_delay -= vp->delay_counter; + if(vp->tremolo_delay > 0) { + vp->tremolo_volume = 1.0; + return; + } + vp->tremolo_delay = 0; + } + if (vp->tremolo_sweep) { + /* Update sweep position */ + vp->tremolo_sweep_position += vp->tremolo_sweep; + if (vp->tremolo_sweep_position >= 1 << SWEEP_SHIFT) + /* Swept to max amplitude */ + vp->tremolo_sweep = 0; + else { + /* Need to adjust depth */ + depth *= vp->tremolo_sweep_position; + depth >>= SWEEP_SHIFT; + } + } + vp->tremolo_phase += vp->tremolo_phase_increment; + + if(vp->sample->inst_type == INST_SF2) { + vp->tremolo_volume = 1.0 + TIM_FSCALENEG( + lookup_sine(vp->tremolo_phase >> RATE_SHIFT) + * depth * TREMOLO_AMPLITUDE_TUNING, 17); + } else { + vp->tremolo_volume = 1.0 + TIM_FSCALENEG( + lookup_sine(vp->tremolo_phase >> RATE_SHIFT) + * depth * TREMOLO_AMPLITUDE_TUNING, 17); + } + /* I'm not sure about the +1.0 there -- it makes tremoloed voices' + * volumes on average the lower the higher the tremolo amplitude. + */ +} + +int Mixer::apply_envelope_to_amp(int v) +{ + Voice *vp = &player->voice[v]; + double lamp = vp->left_amp, ramp, + *v_table = vp->sample->inst_type == INST_SF2 ? sb_vol_table : player->vol_table; + int32_t la, ra; + + if (vp->panned == PANNED_MYSTERY) { + ramp = vp->right_amp; + if (vp->tremolo_phase_increment) { + lamp *= vp->tremolo_volume; + ramp *= vp->tremolo_volume; + } + if (vp->sample->modes & MODES_ENVELOPE) { + if (vp->envelope_stage > 3) + vp->last_envelope_volume = v_table[ + imuldiv16(vp->envelope_volume, + vp->inv_envelope_scale) >> 20] + * vp->envelope_scale; + else if (vp->envelope_stage > 1) + vp->last_envelope_volume = v_table[ + vp->envelope_volume >> 20]; + else + vp->last_envelope_volume = attack_vol_table[ + vp->envelope_volume >> 20]; + lamp *= vp->last_envelope_volume; + ramp *= vp->last_envelope_volume; + } + la = TIM_FSCALE(lamp, AMP_BITS); + if (la > MAX_AMP_VALUE) + la = MAX_AMP_VALUE; + ra = TIM_FSCALE(ramp, AMP_BITS); + if (ra > MAX_AMP_VALUE) + ra = MAX_AMP_VALUE; + if ((vp->status & (VOICE_OFF | VOICE_SUSTAINED)) + && (la | ra) <= 0) { + player->free_voice(v); + return 1; + } + vp->left_mix = FINAL_VOLUME(la); + vp->right_mix = FINAL_VOLUME(ra); + } else { + if (vp->tremolo_phase_increment) + lamp *= vp->tremolo_volume; + if (vp->sample->modes & MODES_ENVELOPE) { + if (vp->envelope_stage > 3) + vp->last_envelope_volume = v_table[ + imuldiv16(vp->envelope_volume, + vp->inv_envelope_scale) >> 20] + * vp->envelope_scale; + else if (vp->envelope_stage > 1) + vp->last_envelope_volume = v_table[ + vp->envelope_volume >> 20]; + else + vp->last_envelope_volume = attack_vol_table[ + vp->envelope_volume >> 20]; + lamp *= vp->last_envelope_volume; + } + la = TIM_FSCALE(lamp, AMP_BITS); + if (la > MAX_AMP_VALUE) + la = MAX_AMP_VALUE; + if ((vp->status & (VOICE_OFF | VOICE_SUSTAINED)) + && la <= 0) { + player->free_voice(v); + return 1; + } + vp->left_mix = FINAL_VOLUME(la); + } + return 0; +} + +void Mixer::compute_mix_smoothing(Voice *vp) +{ + int32_t max_win, delta; + + /* reduce popping -- ramp the amp over a 20 msec window */ + max_win = (playback_rate * 0.02) / control_ratio; + delta = FROM_FINAL_VOLUME(vp->left_mix) - vp->old_left_mix; + if (labs(delta) > max_win) { + vp->left_mix_inc = delta / max_win; + vp->left_mix_offset = vp->left_mix_inc * (1 - max_win); + } else if (delta) { + vp->left_mix_inc = -1; + if (delta > 0) + vp->left_mix_inc = 1; + vp->left_mix_offset = vp->left_mix_inc - delta; + } + delta = FROM_FINAL_VOLUME(vp->right_mix) - vp->old_right_mix; + if (labs(delta) > max_win) { + vp->right_mix_inc = delta / max_win; + vp->right_mix_offset = vp->right_mix_inc * (1 - max_win); + } else if (delta) { + vp->right_mix_inc = -1; + if (delta > 0) + vp->right_mix_inc = 1; + vp->right_mix_offset = vp->right_mix_inc - delta; + } +} + +int Mixer::update_modulation_envelope(int v) +{ + Voice *vp = &player->voice[v]; + + if(vp->modenv_delay > 0) { + vp->modenv_delay -= vp->delay_counter; + if(vp->modenv_delay > 0) {return 1;} + vp->modenv_delay = 0; + } + vp->modenv_volume += vp->modenv_increment; + if ((vp->modenv_increment < 0) + ^ (vp->modenv_volume > vp->modenv_target)) { + vp->modenv_volume = vp->modenv_target; + if (recompute_modulation_envelope(v)) { + apply_modulation_envelope(v); + return 1; + } + } + + apply_modulation_envelope(v); + + return 0; +} + +int Mixer::apply_modulation_envelope(int v) +{ + Voice *vp = &player->voice[v]; + + if(!opt_modulation_envelope) {return 0;} + + if (vp->sample->modes & MODES_ENVELOPE) { + vp->last_modenv_volume = modenv_vol_table[vp->modenv_volume >> 20]; + } + + player->recompute_voice_filter(v); + if(!(vp->porta_control_ratio && vp->porta_control_counter == 0)) { + player->recompute_freq(v); + } + return 0; +} + +int Mixer::modenv_next_stage(int v) +{ + int stage, ch, eg_stage; + int32_t offset, val; + double rate; + Voice *vp = &player->voice[v]; + + stage = vp->modenv_stage++; + offset = vp->sample->modenv_offset[stage]; + rate = vp->sample->modenv_rate[stage]; + if (vp->modenv_volume == offset + || (stage > EG_GUS_SUSTAIN && vp->modenv_volume < offset)) + return recompute_modulation_envelope(v); + else if(stage < EG_SF_DECAY && rate > OFFSET_MAX) { /* instantaneous attack */ + vp->modenv_volume = offset; + return recompute_modulation_envelope(v); + } + ch = vp->channel; + /* there is some difference between GUS patch and Soundfont at envelope. */ + eg_stage = get_eg_stage(v, stage); + + /* envelope generator (see also playmidi.[ch]) */ + if (player->ISDRUMCHANNEL(ch)) + val = (player->channel[ch].drums[vp->note] != NULL) + ? player->channel[ch].drums[vp->note]->drum_envelope_rate[eg_stage] + : -1; + else { + if (vp->sample->modenv_keyf[stage]) /* envelope key-follow */ + rate *= pow(2.0, (double) (player->voice[v].note - 60) + * (double)vp->sample->modenv_keyf[stage] / 1200.0f); + val = player->channel[ch].envelope_rate[eg_stage]; + } + if (vp->sample->modenv_velf[stage]) + rate *= pow(2.0, (double) (player->voice[v].velocity - vp->sample->modenv_velf_bpo) + * (double)vp->sample->modenv_velf[stage] / 1200.0f); + + /* just before release-phase, some modifications are necessary */ + if (stage > EG_GUS_SUSTAIN) { + /* adjusting release-rate for consistent release-time */ + rate *= (double) vp->modenv_volume + / vp->sample->modenv_offset[EG_GUS_ATTACK]; + } + + /* regularizing envelope */ + if (offset < vp->modenv_volume) { /* decaying phase */ + if (val != -1) { + if(stage > EG_DECAY) { + rate *= sc_eg_release_table[val & 0x7f]; + } else { + rate *= sc_eg_decay_table[val & 0x7f]; + } + } + if(rate > vp->modenv_volume - offset) { /* fastest decay */ + rate = -vp->modenv_volume + offset - 1; + } else if (rate < 1) { /* slowest decay */ + rate = -1; + } else { /* ordinary decay */ + rate = -rate; + } + } else { /* attacking phase */ + if (val != -1) + rate *= sc_eg_attack_table[val & 0x7f]; + if(rate > offset - vp->modenv_volume) { /* fastest attack */ + rate = offset - vp->modenv_volume + 1; + } else if (rate < 1) {rate = 1;} /* slowest attack */ + } + + vp->modenv_increment = (int32_t)rate; + vp->modenv_target = offset; + + return 0; +} + +int Mixer::recompute_modulation_envelope(int v) +{ + int stage, ch; + double sustain_time; + int32_t modenv_width; + Voice *vp = &player->voice[v]; + + if(!opt_modulation_envelope) {return 0;} + + stage = vp->modenv_stage; + if (stage > EG_GUS_RELEASE3) {return 1;} + else if (stage > EG_GUS_SUSTAIN && vp->modenv_volume <= 0) { + return 1; + } + + /* Routine to sustain modulation envelope + * + * Disabled if !min_sustain_time. + * min_sustain_time is given in msec, and is the minimum + * time it will take to sustain a note. + * 2000-3000 msec seem to be decent values to use. + */ + if (stage == EG_GUS_RELEASE1 && vp->sample->modes & MODES_ENVELOPE + && vp->status & (VOICE_ON | VOICE_SUSTAINED)) { + ch = vp->channel; + + /* Don't adjust the current rate if VOICE_ON */ + if (vp->status & VOICE_ON) + return 0; + + if (min_sustain_time > 0 || player->channel[ch].loop_timeout > 0) { + if (min_sustain_time == 1) + /* The sustain stage is ignored. */ + return modenv_next_stage(v); + + if (player->channel[ch].loop_timeout > 0 && + player->channel[ch].loop_timeout * 1000 < min_sustain_time) { + /* timeout (See also "#extension timeout" line in *.cfg file */ + sustain_time = player->channel[ch].loop_timeout * 1000; + } + else { + sustain_time = min_sustain_time; + } + + /* Sustain must not be 0 or else lots of dead notes! */ + if (player->channel[ch].sostenuto == 0 && + player->channel[ch].sustain > 0) { + sustain_time *= (double)player->channel[ch].sustain / 127.0f; + } + + /* Calculate the width of the envelope */ + modenv_width = sustain_time * playback_rate + / (1000.0f * (double)control_ratio); + vp->modenv_increment = -1; + vp->modenv_target = vp->modenv_volume - modenv_width; + if (vp->modenv_target < 0) {vp->modenv_target = 0;} + } + return 0; + } + return modenv_next_stage(v); +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/mix.h b/src/sound/timidity++/mix.h new file mode 100644 index 0000000000..15c7cc7759 --- /dev/null +++ b/src/sound/timidity++/mix.h @@ -0,0 +1,76 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + In case you haven't heard, this program is free software; + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) + any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + mix.h + +*/ + +#ifndef ___MIX_H_ +#define ___MIX_H_ + +#include "resample.h" + +namespace TimidityPlus +{ + + +typedef int32_t mix_t; +class Player; + +class Mixer +{ + Player *player; + int32_t filter_buffer[AUDIO_BUFFER_SIZE]; + + int do_voice_filter(int, resample_t*, mix_t*, int32_t); + void recalc_voice_resonance(int); + void recalc_voice_fc(int); + void ramp_out(mix_t *, int32_t *, int, int32_t); + void mix_mono_signal(mix_t *, int32_t *, int, int); + void mix_mystery_signal(mix_t *, int32_t *, int, int); + void mix_mystery(mix_t *, int32_t *, int, int); + void mix_center_signal(mix_t *, int32_t *, int, int); + void mix_center(mix_t *, int32_t *, int, int); + void mix_single_signal(mix_t *, int32_t *, int, int); + void mix_single(mix_t *, int32_t *, int, int); + int update_signal(int); + int update_envelope(int); + int update_modulation_envelope(int); + void voice_ran_out(int); + int next_stage(int); + int modenv_next_stage(int); + void update_tremolo(int); + void compute_mix_smoothing(Voice *); + int get_eg_stage(int v, int stage); + +public: + Mixer(Player *p) + { + player = p; + } + void mix_voice(int32_t *, int, int32_t); + int recompute_envelope(int); + int apply_envelope_to_amp(int); + int recompute_modulation_envelope(int); + int apply_modulation_envelope(int); +}; + +} +#endif /* ___MIX_H_ */ diff --git a/src/sound/timidity++/optcode.h b/src/sound/timidity++/optcode.h new file mode 100644 index 0000000000..2280212559 --- /dev/null +++ b/src/sound/timidity++/optcode.h @@ -0,0 +1,65 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef OPTCODE_H_INCLUDED +#define OPTCODE_H_INCLUDED 1 + +#include + +namespace TimidityPlus +{ + +/*****************************************************************************/ + + +/*****************************************************************************/ + +/* Generic version of imuldiv. */ +inline int32_t imuldiv8(int32_t a, int32_t b) +{ + return (int32_t)(((int64_t)(a) * (int64_t)(b)) >> 8); +} + +inline int32_t imuldiv16(int32_t a, int32_t b) +{ + return (int32_t)(((int64_t)(a) * (int64_t)(b)) >> 16); +} + +inline int32_t imuldiv24(int32_t a, int32_t b) +{ + return (int32_t)(((int64_t)(a) * (int64_t)(b)) >> 24); +} + +inline int32_t imuldiv28(int32_t a, int32_t b) +{ + return (int32_t)(((int64_t)(a) * (int64_t)(b)) >> 28); +} + + +static inline int32_t signlong(int32_t a) +{ + return ((a | 0x7fffffff) >> 30); +} + + +} +/*****************************************************************************/ + +#endif /* OPTCODE_H_INCLUDED */ diff --git a/src/sound/timidity++/output.cpp b/src/sound/timidity++/output.cpp new file mode 100644 index 0000000000..2448f0ac29 --- /dev/null +++ b/src/sound/timidity++/output.cpp @@ -0,0 +1,92 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + output.c + + Audio output (to file / device) functions. +*/ + +#include "timidity.h" +#include "common.h" +#include "sysdep.h" +#include "tables.h" + +namespace TimidityPlus +{ + + +int audio_buffer_bits = DEFAULT_AUDIO_BUFFER_BITS; + +extern PlayMode w32_play_mode; +#define DEV_PLAY_MODE &w32_play_mode + +extern PlayMode raw_play_mode; + + +PlayMode *play_mode = DEV_PLAY_MODE; + +/*****************************************************************/ +/* Some functions to convert signed 32-bit data to other formats */ + +void s32tos16(int32_t *lp, int32_t c) +{ + int16_t *sp=(int16_t *)(lp); + int32_t l, i; + + for(i = 0; i < c; i++) + { + l=(lp[i])>>(32-16-GUARD_BITS); + if (l > 32767) l=32767; + else if (l<-32768) l=-32768; + sp[i] = (int16_t)(l); + } +} + +// This only gets used as intermediate so we do not care about byte order +#define STORE_S24(cp, l) *cp++ = l & 0xFF, *cp++ = l >> 8 & 0xFF, *cp++ = l >> 16 +#define MAX_24BIT_SIGNED (8388607) +#define MIN_24BIT_SIGNED (-8388608) + +void s32tos24(int32_t *lp, int32_t c) +{ + uint8_t *cp = (uint8_t *)(lp); + int32_t l, i; + + for(i = 0; i < c; i++) + { + l = (lp[i]) >> (32 - 24 - GUARD_BITS); + l = (l > MAX_24BIT_SIGNED) ? MAX_24BIT_SIGNED + : (l < MIN_24BIT_SIGNED) ? MIN_24BIT_SIGNED : l; + STORE_S24(cp, l); + } +} + +/* return: number of bytes */ +int32_t general_output_convert(int32_t *buf, int32_t count) +{ + int32_t bytes; + + count *= 2; /* Stereo samples */ + bytes = count; + bytes *= 2; + s32tos16(buf, count); + return bytes; +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/output.h b/src/sound/timidity++/output.h new file mode 100644 index 0000000000..edbbf9b02a --- /dev/null +++ b/src/sound/timidity++/output.h @@ -0,0 +1,94 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + output.h + +*/ + +#ifndef ___OUTPUT_H_ +#define ___OUTPUT_H_ + +namespace TimidityPlus +{ + + + +/* Data format encoding bits */ +#define PE_16BIT (1u<<2) /* versus 8-bit */ +#define PE_24BIT (1u<<6) /* versus 8-bit, 16-bit */ + +/* for play_mode->acntl() */ +enum { + PM_REQ_DISCARD, /* ARG: not-used + * Discard the audio device buffer and returns + * immediatly. + */ + + PM_REQ_FLUSH, /* ARG: not-used + * Wait until all audio data is out. + */ + +}; + + +/* Flag bits */ +#define PF_PCM_STREAM (1u<<0) /* Enable output PCM data */ +#define PF_BUFF_FRAGM_OPT (1u<<3) /* Enable set extra_param[0] to specify + the number of audio buffer fragments */ +#define PF_AUTO_SPLIT_FILE (1u<<4) /* Split PCM files automatically */ +#define PF_FILE_OUTPUT (1u<<5) /* Output is to file rather than device */ + +struct PlayMode { + int32_t rate, encoding, flag; + int fd; /* file descriptor for the audio device + -1 means closed otherwise opened. It must be -1 by default. */ + int32_t extra_param[5]; /* System depended parameters + e.g. buffer fragments, ... */ + const char *id_name; + char id_character; + const char *name; /* default device or file name */ + int (* open_output)(void); /* 0=success, 1=warning, -1=fatal error */ + void (* close_output)(void); + + int (* output_data)(char *buf, int32_t bytes); + /* return: -1=error, otherwise success */ + + int (* acntl)(int request, void *arg); /* see PM_REQ_* above + * return: 0=success, -1=fail + */ + int (* detect)(void); /* 0=not available, 1=available */ +}; + +extern PlayMode *play_mode_list[], *play_mode; +extern int audio_buffer_bits; +#define audio_buffer_size (1< + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + playmidi.c -- random stuff in need of rearrangement +*/ + +#include +#include + +#include +#include +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "mix.h" +#include "recache.h" +#include "reverb.h" +#include "aq.h" +#include "freq.h" +#include "quantity.h" +#include "c_cvars.h" +#include "tables.h" + +namespace TimidityPlus +{ + int32_t control_ratio = 44; +} + +// CVARs may not be placed in namespaces (but the above variable needs to.) +CUSTOM_CVAR(Int, playback_rate, 44100, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + const int CONTROLS_PER_SECOND = 1000; + const int MAX_CONTROL_RATIO = 255; + + TimidityPlus::control_ratio = playback_rate / CONTROLS_PER_SECOND; + if (TimidityPlus::control_ratio < 1) + TimidityPlus::control_ratio = 1; + else if (TimidityPlus::control_ratio > MAX_CONTROL_RATIO) + TimidityPlus::control_ratio = MAX_CONTROL_RATIO; +} + + +CVAR(Bool, opt_modulation_wheel, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_portamento, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_nrpn_vibrato, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +/* +* reverb=0 no reverb 0 +* reverb=1 old reverb 1 +* reverb=1,n set reverb level to n (-1 to -127) +* reverb=2 "global" old reverb 2 +* reverb=2,n set reverb level to n (-1 to -127) - 128 +* reverb=3 new reverb 3 +* reverb=3,n set reverb level to n (-1 to -127) - 256 +* reverb=4 "global" new reverb 4 +* reverb=4,n set reverb level to n (-1 to -127) - 384 +*/ +CVAR(Int, opt_reverb_control, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, opt_chorus_control, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_surround_chorus, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_channel_pressure, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, opt_lpf_def, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_temper_control, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_modulation_envelope, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_overlap_voice_allow, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_delay_control, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_eq_control, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_tva_attack, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_tva_decay, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_tva_release, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_insertion_effect, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_drum_effect, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, opt_pan_delay, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +CUSTOM_CVAR(Int, opt_drum_power, 100, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) /* coef. of drum amplitude */ +{ + if (self < 0) self = 0; + else if (self > MAX_AMPLIFICATION) self = MAX_AMPLIFICATION; +} +CUSTOM_CVAR(Int, key_adjust, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < -24) self = -24; + else if (self > 24) self = 24; +} +// For testing mainly. +CUSTOM_CVAR(Float, tempo_adjust, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0.25) self = 0.25; + else if (self > 10) self = 10; +} + + +namespace TimidityPlus +{ + + +extern Instruments *instruments; + +#define PLAY_INTERLEAVE_SEC 1.0 +#define PORTAMENTO_TIME_TUNING (1.0 / 5000.0) +#define PORTAMENTO_CONTROL_RATIO 256 /* controls per sec */ +#define DEFAULT_CHORUS_DELAY1 0.02 +#define DEFAULT_CHORUS_DELAY2 0.003 +#define CHORUS_OPPOSITE_THRESHOLD 32 +#define EOT_PRESEARCH_LEN 32 +#define SPEED_CHANGE_RATE 1.0594630943592953 /* 2^(1/12) */ +#define DEFAULT_AMPLIFICATION 70 +#define VIBRATO_DEPTH_MAX 384 /* 600 cent */ + +Player::Player() +{ + // init one-time global stuff - this should go to the device class once it exists. + initialize_resampler_coeffs(); + init_tables(); + + memset(this, 0, sizeof(*this)); + new_midi_file_info(); + init_mblock(&playmidi_pool); + + reverb = new Reverb; + reverb->init_effect_status(play_system_mode); + + mixer = new Mixer(this); + recache = new Recache(this); + aq = new AudioQueue(play_mode, audio_buffer_size, reverb); + aq->setup(); + + for (int i = 0; i < MAX_CHANNELS; i++) + init_channel_layer(i); + + instruments->init_userdrum(); + instruments->init_userinst(); + + master_volume_ratio = 0xFFFF; + vol_table = def_vol_table; + + play_system_mode = DEFAULT_SYSTEM_MODE; + midi_streaming = 0; + stream_max_compute = 500; /* compute time limit (in msec) when streaming */ + current_keysig = 0; + current_temper_keysig = 0; + temper_adj = 0; + current_play_tempo = 500000; + opt_realtime_playing = 0; + check_eot_flag; + playmidi_seek_flag = 0; + opt_pure_intonation = 0; + current_freq_table = 0; + current_temper_freq_table = 0; + master_tuning = 0; + + make_rvid_flag = 0; /* For reverb optimization */ + + voices = DEFAULT_VOICES; + amplification = DEFAULT_AMPLIFICATION; + + + static const int drums[] = { 10, -1 }; + + CLEAR_CHANNELMASK(default_drumchannels); + for (int i = 0; drums[i] > 0; i++) + { + SET_CHANNELMASK(default_drumchannels, drums[i] - 1); + } + for (int i = 16; i < MAX_CHANNELS; i++) + { + if (IS_SET_CHANNELMASK(default_drumchannels, i & 0xF)) + SET_CHANNELMASK(default_drumchannels, i); + } + COPY_CHANNELMASK(drumchannels, default_drumchannels); + COPY_CHANNELMASK(drumchannel_mask, default_drumchannel_mask); + +} + +Player::~Player() +{ + reuse_mblock(&playmidi_pool); + if (reverb_buffer != nullptr) free(reverb_buffer); + for (int i = 0; i < MAX_CHANNELS; i++) free_drum_effect(i); + aq->flush(1); + delete aq; + delete mixer; + delete recache; + delete reverb; +} + + +bool Player::IS_SYSEX_EVENT_TYPE(MidiEvent *event) +{ + return ((event)->type == ME_NONE || (event)->type >= ME_RANDOM_PAN || (event)->b == SYSEX_TAG); +} + + +void Player::init_freq_table_user(void) +{ + int p, i, j, k, l; + double f; + + for (p = 0; p < 4; p++) + for (i = 0; i < 12; i++) + for (j = -1; j < 11; j++) { + f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5); + for (k = 0; k < 12; k++) { + l = i + j * 12 + k; + if (l < 0 || l >= 128) + continue; + freq_table_user[p][i][l] = f * 1000 + 0.5; + freq_table_user[p][i + 12][l] = f * 1000 + 0.5; + freq_table_user[p][i + 24][l] = f * 1000 + 0.5; + freq_table_user[p][i + 36][l] = f * 1000 + 0.5; + } + } +} + + +/*! convert Hz to internal vibrato control ratio. */ +double Player::cnv_Hz_to_vib_ratio(double freq) +{ + return ((double)(playback_rate) / (freq * 2.0f * VIBRATO_SAMPLE_INCREMENTS)); +} + +void Player::adjust_amplification(void) +{ + static const double compensation_ratio = 1.0; + /* compensate master volume */ + master_volume = (double)(amplification) / 100.0 * + ((double)master_volume_ratio * (compensation_ratio/0xFFFF)); +} + +int Player::new_vidq(int ch, int note) +{ + int i; + + if(opt_overlap_voice_allow) + { + i = ch * 128 + note; + return vidq_head[i]++; + } + return 0; +} + +int Player::last_vidq(int ch, int note) +{ + int i; + + if(opt_overlap_voice_allow) + { + i = ch * 128 + note; + if(vidq_head[i] == vidq_tail[i]) + { + return -1; + } + return vidq_tail[i]++; + } + return 0; +} + +void Player::reset_voices(void) +{ + int i; + for(i = 0; i < max_voices; i++) + { + voice[i].status = VOICE_FREE; + voice[i].temper_instant = 0; + voice[i].chorus_link = i; + } + upper_voices = 0; + memset(vidq_head, 0, sizeof(vidq_head)); + memset(vidq_tail, 0, sizeof(vidq_tail)); +} + +void Player::kill_note(int i) +{ + voice[i].status = VOICE_DIE; +} + +void Player::kill_all_voices(void) +{ + int i, uv = upper_voices; + + for(i = 0; i < uv; i++) + if(voice[i].status & ~(VOICE_FREE | VOICE_DIE)) + kill_note(i); + memset(vidq_head, 0, sizeof(vidq_head)); + memset(vidq_tail, 0, sizeof(vidq_tail)); +} + +void Player::reset_drum_controllers(struct DrumParts *d[], int note) +{ + int i, j; + + if (note == -1) + { + for (i = 0; i < 128; i++) + if (d[i] != NULL) + { + d[i]->drum_panning = NO_PANNING; + for (j = 0; j < 6; j++) { d[i]->drum_envelope_rate[j] = -1; } + d[i]->pan_random = 0; + d[i]->drum_level = 1.0f; + d[i]->coarse = 0; + d[i]->fine = 0; + d[i]->delay_level = -1; + d[i]->chorus_level = -1; + d[i]->reverb_level = -1; + d[i]->play_note = -1; + d[i]->drum_cutoff_freq = 0; + d[i]->drum_resonance = 0; + init_rx_drum(d[i]); + } + } + else + { + d[note]->drum_panning = NO_PANNING; + for (j = 0; j < 6; j++) { d[note]->drum_envelope_rate[j] = -1; } + d[note]->pan_random = 0; + d[note]->drum_level = 1.0f; + d[note]->coarse = 0; + d[note]->fine = 0; + d[note]->delay_level = -1; + d[note]->chorus_level = -1; + d[note]->reverb_level = -1; + d[note]->play_note = -1; + d[note]->drum_cutoff_freq = 0; + d[note]->drum_resonance = 0; + init_rx_drum(d[note]); + } +} + +void Player::reset_nrpn_controllers(int c) +{ + int i; + + /* NRPN */ + reset_drum_controllers(channel[c].drums, -1); + channel[c].vibrato_ratio = 1.0; + channel[c].vibrato_depth = 0; + channel[c].vibrato_delay = 0; + channel[c].param_cutoff_freq = 0; + channel[c].param_resonance = 0; + channel[c].cutoff_freq_coef = 1.0; + channel[c].resonance_dB = 0; + + /* System Exclusive */ + channel[c].dry_level = 127; + channel[c].eq_gs = 1; + channel[c].insertion_effect = 0; + channel[c].velocity_sense_depth = 0x40; + channel[c].velocity_sense_offset = 0x40; + channel[c].pitch_offset_fine = 0; + if (play_system_mode == GS_SYSTEM_MODE) { channel[c].assign_mode = 1; } + else { + if (ISDRUMCHANNEL(c)) { channel[c].assign_mode = 1; } + else { channel[c].assign_mode = 2; } + } + for (i = 0; i < 12; i++) + channel[c].scale_tuning[i] = 0; + channel[c].prev_scale_tuning = 0; + channel[c].temper_type = 0; + + init_channel_layer(c); + init_part_eq_xg(&(channel[c].eq_xg)); + + /* channel pressure & polyphonic key pressure control */ + init_midi_controller(&(channel[c].mod)); + init_midi_controller(&(channel[c].bend)); + init_midi_controller(&(channel[c].caf)); + init_midi_controller(&(channel[c].paf)); + init_midi_controller(&(channel[c].cc1)); + init_midi_controller(&(channel[c].cc2)); + channel[c].bend.pitch = 2; + + init_rx(c); + channel[c].note_limit_high = 127; + channel[c].note_limit_low = 0; + channel[c].vel_limit_high = 127; + channel[c].vel_limit_low = 0; + + free_drum_effect(c); + + channel[c].legato = 0; + channel[c].damper_mode = 0; + channel[c].loop_timeout = 0; + + channel[c].sysex_gs_msb_addr = channel[c].sysex_gs_msb_val = + channel[c].sysex_xg_msb_addr = channel[c].sysex_xg_msb_val = + channel[c].sysex_msb_addr = channel[c].sysex_msb_val = 0; +} + +/* Process the Reset All Controllers event */ +void Player::reset_controllers(int c) +{ + int j; + /* Some standard says, although the SCC docs say 0. */ + + if (play_system_mode == XG_SYSTEM_MODE) + channel[c].volume = 100; + else + channel[c].volume = 90; + + channel[c].expression = 127; /* SCC-1 does this. */ + channel[c].sustain = 0; + channel[c].sostenuto = 0; + channel[c].pitchbend = 0x2000; + channel[c].pitchfactor = 0; /* to be computed */ + channel[c].mod.val = 0; + channel[c].bend.val = 0; + channel[c].caf.val = 0; + channel[c].paf.val = 0; + channel[c].cc1.val = 0; + channel[c].cc2.val = 0; + channel[c].portamento_time_lsb = 0; + channel[c].portamento_time_msb = 0; + channel[c].porta_control_ratio = 0; + channel[c].portamento = 0; + channel[c].last_note_fine = -1; + for (j = 0; j < 6; j++) { channel[c].envelope_rate[j] = -1; } + update_portamento_controls(c); + set_reverb_level(c, -1); + if (opt_chorus_control == 1) + channel[c].chorus_level = 0; + else + channel[c].chorus_level = -opt_chorus_control; + channel[c].mono = 0; + channel[c].delay_level = 0; +} + +int Player::get_default_mapID(int ch) +{ + if (play_system_mode == XG_SYSTEM_MODE) + return ISDRUMCHANNEL(ch) ? XG_DRUM_MAP : XG_NORMAL_MAP; + return INST_NO_MAP; +} + +void Player::reset_midi(int playing) +{ + int i; + + for (i = 0; i < MAX_CHANNELS; i++) { + reset_controllers(i); + reset_nrpn_controllers(i); + channel[i].tone_map0_number = 0; + channel[i].mod.lfo1_pitch_depth = 50; + /* The rest of these are unaffected + * by the Reset All Controllers event + */ + channel[i].program = instruments->defaultProgram(i); + channel[i].panning = NO_PANNING; + channel[i].pan_random = 0; + /* tone bank or drum set */ + if (ISDRUMCHANNEL(i)) { + channel[i].bank = 0; + channel[i].altassign = instruments->drumSet(0)->alt; + } else { + if (special_tonebank >= 0) + channel[i].bank = special_tonebank; + else + channel[i].bank = default_tonebank; + } + channel[i].bank_lsb = channel[i].bank_msb = 0; + if (play_system_mode == XG_SYSTEM_MODE && i % 16 == 9) + channel[i].bank_msb = 127; /* Use MSB=127 for XG */ + update_rpn_map(i, RPN_ADDR_FFFF, 0); + channel[i].special_sample = 0; + channel[i].key_shift = 0; + channel[i].mapID = get_default_mapID(i); + channel[i].lasttime = 0; + } + if (playing) { + kill_all_voices(); + if (temper_type_mute) { + if (temper_type_mute & 1) + FILL_CHANNELMASK(channel_mute); + else + CLEAR_CHANNELMASK(channel_mute); + } + } else + reset_voices(); + master_volume_ratio = 0xffff; + adjust_amplification(); + master_tuning = 0; + if (current_file_info) { + COPY_CHANNELMASK(drumchannels, current_file_info->drumchannels); + COPY_CHANNELMASK(drumchannel_mask, + current_file_info->drumchannel_mask); + } else { + COPY_CHANNELMASK(drumchannels, default_drumchannels); + COPY_CHANNELMASK(drumchannel_mask, default_drumchannel_mask); + } +} + +void Player::recompute_freq(int v) +{ + int i; + int ch = voice[v].channel; + int note = voice[v].note; + int32_t tuning = 0; + int8_t st = channel[ch].scale_tuning[note % 12]; + int8_t tt = channel[ch].temper_type; + uint8_t tp = channel[ch].rpnmap[RPN_ADDR_0003]; + int32_t f; + int pb = channel[ch].pitchbend; + int32_t tmp; + double pf, root_freq; + int32_t a; + Voice *vp = &(voice[v]); + + if (! voice[v].sample->sample_rate) + return; + if (! opt_modulation_wheel) + channel[ch].mod.val = 0; + if (! opt_portamento) + voice[v].porta_control_ratio = 0; + voice[v].vibrato_control_ratio = voice[v].orig_vibrato_control_ratio; + if (voice[v].vibrato_control_ratio || channel[ch].mod.val > 0) { + /* This instrument has vibrato. Invalidate any precomputed + * sample_increments. + */ + + /* MIDI controllers LFO pitch depth */ + if (opt_channel_pressure || opt_modulation_wheel) { + vp->vibrato_depth = vp->sample->vibrato_depth + channel[ch].vibrato_depth; + vp->vibrato_depth += get_midi_controller_pitch_depth(&(channel[ch].mod)) + + get_midi_controller_pitch_depth(&(channel[ch].bend)) + + get_midi_controller_pitch_depth(&(channel[ch].caf)) + + get_midi_controller_pitch_depth(&(channel[ch].paf)) + + get_midi_controller_pitch_depth(&(channel[ch].cc1)) + + get_midi_controller_pitch_depth(&(channel[ch].cc2)); + if (vp->vibrato_depth > VIBRATO_DEPTH_MAX) {vp->vibrato_depth = VIBRATO_DEPTH_MAX;} + else if (vp->vibrato_depth < 1) {vp->vibrato_depth = 1;} + if (vp->sample->vibrato_depth < 0) { /* in opposite phase */ + vp->vibrato_depth = -vp->vibrato_depth; + } + } + + /* fill parameters for modulation wheel */ + if (channel[ch].mod.val > 0) { + if(vp->vibrato_control_ratio == 0) { + vp->vibrato_control_ratio = + vp->orig_vibrato_control_ratio = (int)(cnv_Hz_to_vib_ratio(5.0) * channel[ch].vibrato_ratio); + } + vp->vibrato_delay = 0; + } + + for (i = 0; i < VIBRATO_SAMPLE_INCREMENTS; i++) + vp->vibrato_sample_increment[i] = 0; + vp->cache = NULL; + } + /* At least for GM2, it's recommended not to apply master_tuning for drum channels */ + tuning = ISDRUMCHANNEL(ch) ? 0 : master_tuning; + /* fine: [0..128] => [-256..256] + * 1 coarse = 256 fine (= 1 note) + * 1 fine = 2^5 tuning + */ + tuning += (channel[ch].rpnmap[RPN_ADDR_0001] - 0x40 + + (channel[ch].rpnmap[RPN_ADDR_0002] - 0x40) * 64) << 7; + /* for NRPN Coarse Pitch of Drum (GS) & Fine Pitch of Drum (XG) */ + if (ISDRUMCHANNEL(ch) && channel[ch].drums[note] != NULL + && (channel[ch].drums[note]->fine + || channel[ch].drums[note]->coarse)) { + tuning += (channel[ch].drums[note]->fine + + channel[ch].drums[note]->coarse * 64) << 7; + } + /* MIDI controllers pitch control */ + if (opt_channel_pressure) { + tuning += get_midi_controller_pitch(&(channel[ch].mod)) + + get_midi_controller_pitch(&(channel[ch].bend)) + + get_midi_controller_pitch(&(channel[ch].caf)) + + get_midi_controller_pitch(&(channel[ch].paf)) + + get_midi_controller_pitch(&(channel[ch].cc1)) + + get_midi_controller_pitch(&(channel[ch].cc2)); + } + if (opt_modulation_envelope) { + if (voice[v].sample->tremolo_to_pitch) { + tuning += lookup_triangular(voice[v].tremolo_phase >> RATE_SHIFT) + * (voice[v].sample->tremolo_to_pitch << 13) / 100.0 + 0.5; + channel[ch].pitchfactor = 0; + } + if (voice[v].sample->modenv_to_pitch) { + tuning += voice[v].last_modenv_volume + * (voice[v].sample->modenv_to_pitch << 13) / 100.0 + 0.5; + channel[ch].pitchfactor = 0; + } + } + /* GS/XG - Scale Tuning */ + if (! ISDRUMCHANNEL(ch)) { + tuning += ((st << 13) + 50) / 100; + if (st != channel[ch].prev_scale_tuning) { + channel[ch].pitchfactor = 0; + channel[ch].prev_scale_tuning = st; + } + } + if (! opt_pure_intonation + && opt_temper_control && voice[v].temper_instant) { + switch (tt) { + case 0: + f = freq_table_tuning[tp][note]; + break; + case 1: + if (current_temper_keysig < 8) + f = freq_table_pytha[current_temper_freq_table][note]; + else + f = freq_table_pytha[current_temper_freq_table + 12][note]; + break; + case 2: + if (current_temper_keysig < 8) + f = freq_table_meantone[current_temper_freq_table + + ((temper_adj) ? 36 : 0)][note]; + else + f = freq_table_meantone[current_temper_freq_table + + ((temper_adj) ? 24 : 12)][note]; + break; + case 3: + if (current_temper_keysig < 8) + f = freq_table_pureint[current_temper_freq_table + + ((temper_adj) ? 36 : 0)][note]; + else + f = freq_table_pureint[current_temper_freq_table + + ((temper_adj) ? 24 : 12)][note]; + break; + default: /* user-defined temperament */ + if ((tt -= 0x40) >= 0 && tt < 4) { + if (current_temper_keysig < 8) + f = freq_table_user[tt][current_temper_freq_table + + ((temper_adj) ? 36 : 0)][note]; + else + f = freq_table_user[tt][current_temper_freq_table + + ((temper_adj) ? 24 : 12)][note]; + } else + f = freq_table[note]; + break; + } + voice[v].orig_frequency = f; + } + if (! voice[v].porta_control_ratio) { + if (tuning == 0 && pb == 0x2000) + voice[v].frequency = voice[v].orig_frequency; + else { + pb -= 0x2000; + if (! channel[ch].pitchfactor) { + /* Damn. Somebody bent the pitch. */ + tmp = pb * channel[ch].rpnmap[RPN_ADDR_0000] + tuning; + if (tmp >= 0) + channel[ch].pitchfactor = bend_fine[tmp >> 5 & 0xff] + * bend_coarse[tmp >> 13 & 0x7f]; + else + channel[ch].pitchfactor = 1.0 / + (bend_fine[-tmp >> 5 & 0xff] + * bend_coarse[-tmp >> 13 & 0x7f]); + } + voice[v].frequency = + voice[v].orig_frequency * channel[ch].pitchfactor; + if (voice[v].frequency != voice[v].orig_frequency) + voice[v].cache = NULL; + } + } else { /* Portamento */ + pb -= 0x2000; + tmp = pb * channel[ch].rpnmap[RPN_ADDR_0000] + + (voice[v].porta_pb << 5) + tuning; + if (tmp >= 0) + pf = bend_fine[tmp >> 5 & 0xff] + * bend_coarse[tmp >> 13 & 0x7f]; + else + pf = 1.0 / (bend_fine[-tmp >> 5 & 0xff] + * bend_coarse[-tmp >> 13 & 0x7f]); + voice[v].frequency = voice[v].orig_frequency * pf; + voice[v].cache = NULL; + } + root_freq = voice[v].sample->root_freq; + a = TIM_FSCALE(((double) voice[v].sample->sample_rate + * ((double)voice[v].frequency + channel[ch].pitch_offset_fine)) + / (root_freq * playback_rate), FRACTION_BITS) + 0.5; + /* need to preserve the loop direction */ + voice[v].sample_increment = (voice[v].sample_increment >= 0) ? a : -a; +} + +int32_t Player::calc_velocity(int32_t ch,int32_t vel) +{ + int32_t velocity; + velocity = channel[ch].velocity_sense_depth * vel / 64 + (channel[ch].velocity_sense_offset - 64) * 2; + if(velocity > 127) {velocity = 127;} + return velocity; +} + +void Player::recompute_voice_tremolo(int v) +{ + Voice *vp = &(voice[v]); + int ch = vp->channel; + int32_t depth = vp->sample->tremolo_depth; + depth += get_midi_controller_amp_depth(&(channel[ch].mod)) + + get_midi_controller_amp_depth(&(channel[ch].bend)) + + get_midi_controller_amp_depth(&(channel[ch].caf)) + + get_midi_controller_amp_depth(&(channel[ch].paf)) + + get_midi_controller_amp_depth(&(channel[ch].cc1)) + + get_midi_controller_amp_depth(&(channel[ch].cc2)); + if(depth > 256) {depth = 256;} + vp->tremolo_depth = depth; +} + +void Player::recompute_amp(int v) +{ + double tempamp; + int ch = voice[v].channel; + + /* master_volume and sample->volume are percentages, used to scale + * amplitude directly, NOT perceived volume + * + * all other MIDI volumes are linear in perceived volume, 0-127 + * use a lookup table for the non-linear scalings + */ + if (play_system_mode == GM2_SYSTEM_MODE) { + tempamp = master_volume * + voice[v].sample->volume * + gm2_vol_table[calc_velocity(ch, voice[v].velocity)] * /* velocity: not in GM2 standard */ + gm2_vol_table[channel[ch].volume] * + gm2_vol_table[channel[ch].expression]; /* 21 bits */ + } + else if (play_system_mode == GS_SYSTEM_MODE) { /* use measured curve */ + tempamp = master_volume * + voice[v].sample->volume * + sc_vel_table[calc_velocity(ch, voice[v].velocity)] * + sc_vol_table[channel[ch].volume] * + sc_vol_table[channel[ch].expression]; /* 21 bits */ + } + else { /* use generic exponential curve */ + tempamp = master_volume * + voice[v].sample->volume * + perceived_vol_table[calc_velocity(ch, voice[v].velocity)] * + perceived_vol_table[channel[ch].volume] * + perceived_vol_table[channel[ch].expression]; /* 21 bits */ + } + + /* every digital effect increases amplitude, + * so that it must be reduced in advance. + */ + if ( + (opt_reverb_control || opt_chorus_control || opt_delay_control + || (opt_eq_control && (reverb->eq_status_gs.low_gain != 0x40 + || reverb->eq_status_gs.high_gain != 0x40)) + || opt_insertion_effect)) + tempamp *= 1.35f * 0.55f; + else + tempamp *= 1.35f; + + /* Reduce amplitude for chorus partners. + * 2x voices -> 2x power -> sqrt(2)x amplitude. + * 1 / sqrt(2) = ~0.7071, which is very close to the old + * CHORUS_VELOCITY_TUNING1 value of 0.7. + * + * The previous amp scaling for the various digital effects should + * really be redone to split them into separate scalings for each + * effect, rather than a single scaling if any one of them is used + * (which is NOT correct). As it is now, if partner chorus is the + * only effect in use, then it is reduced in volume twice and winds + * up too quiet. Compare the output of "-EFreverb=0 -EFchorus=0", + * "-EFreverb=0 -EFchorus=2", "-EFreverb=4 -EFchorus=2", and + * "-EFreverb=4 -EFchorus=0" to see how the digital effect volumes + * are not scaled properly. Idealy, all the resulting output should + * have the same volume, regardless of effects used. This will + * require empirically determining the amount to scale for each + * individual effect. + */ + if (voice[v].chorus_link != v) + tempamp *= 0.7071067811865f; + + /* NRPN - drum instrument tva level */ + if (ISDRUMCHANNEL(ch)) { + if (channel[ch].drums[voice[v].note] != NULL) { + tempamp *= channel[ch].drums[voice[v].note]->drum_level; + } + tempamp *= (double)opt_drum_power * 0.01f; /* global drum power */ + } + + /* MIDI controllers amplitude control */ + if (opt_channel_pressure) { + tempamp *= get_midi_controller_amp(&(channel[ch].mod)) + * get_midi_controller_amp(&(channel[ch].bend)) + * get_midi_controller_amp(&(channel[ch].caf)) + * get_midi_controller_amp(&(channel[ch].paf)) + * get_midi_controller_amp(&(channel[ch].cc1)) + * get_midi_controller_amp(&(channel[ch].cc2)); + recompute_voice_tremolo(v); + } + + if (voice[v].fc.type != 0) { + tempamp *= voice[v].fc.gain; /* filter gain */ + } + + /* applying panning to amplitude */ + if (true) + { + if (voice[v].panning == 64) + { + voice[v].panned = PANNED_CENTER; + voice[v].left_amp = voice[v].right_amp = TIM_FSCALENEG(tempamp * pan_table[64], 27); + } + else if (voice[v].panning < 2) + { + voice[v].panned = PANNED_LEFT; + voice[v].left_amp = TIM_FSCALENEG(tempamp, 20); + voice[v].right_amp = 0; + } + else if (voice[v].panning == 127) + { + if (voice[v].panned == PANNED_MYSTERY) { + voice[v].old_left_mix = voice[v].old_right_mix; + voice[v].old_right_mix = 0; + } + voice[v].panned = PANNED_RIGHT; + voice[v].left_amp = TIM_FSCALENEG(tempamp, 20); + voice[v].right_amp = 0; + } + else + { + if (voice[v].panned == PANNED_RIGHT) { + voice[v].old_right_mix = voice[v].old_left_mix; + voice[v].old_left_mix = 0; + } + voice[v].panned = PANNED_MYSTERY; + voice[v].left_amp = TIM_FSCALENEG(tempamp * pan_table[128 - voice[v].panning], 27); + voice[v].right_amp = TIM_FSCALENEG(tempamp * pan_table[voice[v].panning], 27); + } + } + else + { + voice[v].panned = PANNED_CENTER; + voice[v].left_amp = TIM_FSCALENEG(tempamp, 21); + } +} + +#define RESONANCE_COEFF 0.2393 + +void Player::recompute_channel_filter(int ch, int note) +{ + double coef = 1.0f, reso = 0; + + if(channel[ch].special_sample > 0) {return;} + + /* Soft Pedal */ + if(channel[ch].soft_pedal != 0) { + if(note > 49) { /* tre corde */ + coef *= 1.0 - 0.20 * ((double)channel[ch].soft_pedal) / 127.0f; + } else { /* una corda (due corde) */ + coef *= 1.0 - 0.25 * ((double)channel[ch].soft_pedal) / 127.0f; + } + } + + if(!ISDRUMCHANNEL(ch)) { + /* NRPN Filter Cutoff */ + coef *= pow(1.26, (double)(channel[ch].param_cutoff_freq) / 8.0f); + /* NRPN Resonance */ + reso = (double)channel[ch].param_resonance * RESONANCE_COEFF; + } + + channel[ch].cutoff_freq_coef = coef; + channel[ch].resonance_dB = reso; +} + +void Player::init_voice_filter(int i) +{ + memset(&(voice[i].fc), 0, sizeof(FilterCoefficients)); + if(opt_lpf_def && voice[i].sample->cutoff_freq) { + voice[i].fc.orig_freq = voice[i].sample->cutoff_freq; + voice[i].fc.orig_reso_dB = (double)voice[i].sample->resonance / 10.0f - 3.01f; + if (voice[i].fc.orig_reso_dB < 0.0f) {voice[i].fc.orig_reso_dB = 0.0f;} + if (opt_lpf_def == 2) { + voice[i].fc.gain = 1.0; + voice[i].fc.type = 2; + } else if(opt_lpf_def == 1) { + voice[i].fc.gain = pow(10.0f, -voice[i].fc.orig_reso_dB / 2.0f / 20.0f); + voice[i].fc.type = 1; + } + voice[i].fc.start_flag = 0; + } else { + voice[i].fc.type = 0; + } +} + +#define CHAMBERLIN_RESONANCE_MAX 24.0 + +void Player::recompute_voice_filter(int v) +{ + int ch = voice[v].channel, note = voice[v].note; + double coef = 1.0, reso = 0, cent = 0, depth_cent = 0, freq; + FilterCoefficients *fc = &(voice[v].fc); + Sample *sp = (Sample *) &voice[v].sample; + + if(fc->type == 0) {return;} + coef = channel[ch].cutoff_freq_coef; + + if(ISDRUMCHANNEL(ch) && channel[ch].drums[note] != NULL) { + /* NRPN Drum Instrument Filter Cutoff */ + coef *= pow(1.26, (double)(channel[ch].drums[note]->drum_cutoff_freq) / 8.0f); + /* NRPN Drum Instrument Filter Resonance */ + reso += (double)channel[ch].drums[note]->drum_resonance * RESONANCE_COEFF; + } + + /* MIDI controllers filter cutoff control and LFO filter depth */ + if(opt_channel_pressure) { + cent += get_midi_controller_filter_cutoff(&(channel[ch].mod)) + + get_midi_controller_filter_cutoff(&(channel[ch].bend)) + + get_midi_controller_filter_cutoff(&(channel[ch].caf)) + + get_midi_controller_filter_cutoff(&(channel[ch].paf)) + + get_midi_controller_filter_cutoff(&(channel[ch].cc1)) + + get_midi_controller_filter_cutoff(&(channel[ch].cc2)); + depth_cent += get_midi_controller_filter_depth(&(channel[ch].mod)) + + get_midi_controller_filter_depth(&(channel[ch].bend)) + + get_midi_controller_filter_depth(&(channel[ch].caf)) + + get_midi_controller_filter_depth(&(channel[ch].paf)) + + get_midi_controller_filter_depth(&(channel[ch].cc1)) + + get_midi_controller_filter_depth(&(channel[ch].cc2)); + } + + if(sp->vel_to_fc) { /* velocity to filter cutoff frequency */ + if(voice[v].velocity > sp->vel_to_fc_threshold) + cent += sp->vel_to_fc * (double)(127 - voice[v].velocity) / 127.0f; + else + coef += sp->vel_to_fc * (double)(127 - sp->vel_to_fc_threshold) / 127.0f; + } + if(sp->vel_to_resonance) { /* velocity to filter resonance */ + reso += (double)voice[v].velocity * sp->vel_to_resonance / 127.0f / 10.0f; + } + if(sp->key_to_fc) { /* filter cutoff key-follow */ + cent += sp->key_to_fc * (double)(voice[v].note - sp->key_to_fc_bpo); + } + + if(opt_modulation_envelope) { + if(voice[v].sample->tremolo_to_fc + (int16_t)depth_cent) { + cent += ((double)voice[v].sample->tremolo_to_fc + depth_cent) * lookup_triangular(voice[v].tremolo_phase >> RATE_SHIFT); + } + if(voice[v].sample->modenv_to_fc) { + cent += (double)voice[v].sample->modenv_to_fc * voice[v].last_modenv_volume; + } + } + + if(cent != 0) {coef *= pow(2.0, cent / 1200.0f);} + + freq = (double)fc->orig_freq * coef; + + if (freq > playback_rate / 2) {freq = playback_rate / 2;} + else if(freq < 5) {freq = 5;} + fc->freq = (int32_t)freq; + + fc->reso_dB = fc->orig_reso_dB + channel[ch].resonance_dB + reso; + if(fc->reso_dB < 0.0f) {fc->reso_dB = 0.0f;} + else if(fc->reso_dB > 96.0f) {fc->reso_dB = 96.0f;} + + if(fc->type == 1) { /* Chamberlin filter */ + if(fc->freq > playback_rate / 6) { + if (fc->start_flag == 0) {fc->type = 0;} /* turn off. */ + else {fc->freq = playback_rate / 6;} + } + if(fc->reso_dB > CHAMBERLIN_RESONANCE_MAX) {fc->reso_dB = CHAMBERLIN_RESONANCE_MAX;} + } else if(fc->type == 2) { /* Moog VCF */ + if(fc->reso_dB > fc->orig_reso_dB / 2) { + fc->gain = pow(10.0f, (fc->reso_dB - fc->orig_reso_dB / 2) / 20.0f); + } + } + fc->start_flag = 1; /* filter is started. */ +} + +float Player::calc_drum_tva_level(int ch, int note, int level) +{ + int def_level, nbank, nprog; + const ToneBank *bank; + + if(channel[ch].special_sample > 0) {return 1.0;} + + nbank = channel[ch].bank; + nprog = note; + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + + if(ISDRUMCHANNEL(ch)) { + bank = instruments->drumSet(nbank); + if(bank == NULL) {bank = instruments->drumSet(0);} + } else { + return 1.0; + } + + def_level = bank->tone[nprog].tva_level; + + if(def_level == -1 || def_level == 0) {def_level = 127;} + else if(def_level > 127) {def_level = 127;} + + return (sc_drum_level_table[level] / sc_drum_level_table[def_level]); +} + +int32_t Player::calc_random_delay(int ch, int note) +{ + int nbank, nprog; + const ToneBank *bank; + + if(channel[ch].special_sample > 0) {return 0;} + + nbank = channel[ch].bank; + + if(ISDRUMCHANNEL(ch)) { + nprog = note; + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + bank = instruments->drumSet(nbank); + if (bank == NULL) {bank = instruments->drumSet(0);} + } else { + nprog = channel[ch].program; + if(nprog == SPECIAL_PROGRAM) {return 0;} + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + bank = instruments->toneBank(nbank); + if(bank == NULL) {bank = instruments->toneBank(0);} + } + + if (bank->tone[nprog].rnddelay == 0) {return 0;} + else {return (int32_t)((double)bank->tone[nprog].rnddelay * playback_rate / 1000.0 + * (reverb->get_pink_noise_light(&reverb->global_pink_noise_light) + 1.0f) * 0.5);} +} + +void Player::recompute_bank_parameter(int ch, int note) +{ + int nbank, nprog; + const ToneBank *bank; + struct DrumParts *drum; + + if(channel[ch].special_sample > 0) {return;} + + nbank = channel[ch].bank; + + if(ISDRUMCHANNEL(ch)) { + nprog = note; + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + bank = instruments->drumSet(nbank); + if (bank == NULL) {bank = instruments->drumSet(0);} + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + drum = channel[ch].drums[note]; + if (drum->reverb_level == -1 && bank->tone[nprog].reverb_send != -1) { + drum->reverb_level = bank->tone[nprog].reverb_send; + } + if (drum->chorus_level == -1 && bank->tone[nprog].chorus_send != -1) { + drum->chorus_level = bank->tone[nprog].chorus_send; + } + if (drum->delay_level == -1 && bank->tone[nprog].delay_send != -1) { + drum->delay_level = bank->tone[nprog].delay_send; + } + } else { + nprog = channel[ch].program; + if (nprog == SPECIAL_PROGRAM) {return;} + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + bank = instruments->toneBank(nbank); + if (bank == NULL) {bank = instruments->toneBank(0);} + channel[ch].legato = bank->tone[nprog].legato; + channel[ch].damper_mode = bank->tone[nprog].damper_mode; + channel[ch].loop_timeout = bank->tone[nprog].loop_timeout; + } +} + + +/* this reduces voices while maintaining sound quality */ +int Player::reduce_voice(void) +{ + int32_t lv, v; + int i, j, lowest=-0x7FFFFFFF; + + i = upper_voices; + lv = 0x7FFFFFFF; + + /* Look for the decaying note with the smallest volume */ + /* Protect drum decays. Truncating them early sounds bad, especially on + snares and cymbals */ + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE || + (voice[j].sample->note_to_use && ISDRUMCHANNEL(voice[j].channel))) + continue; + + if(voice[j].status & ~(VOICE_ON | VOICE_DIE | VOICE_SUSTAINED)) + { + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + if(lowest != -0x7FFFFFFF) + { + /* This can still cause a click, but if we had a free voice to + spare for ramping down this note, we wouldn't need to kill it + in the first place... Still, this needs to be fixed. Perhaps + we could use a reserve of voices to play dying notes only. */ + + cut_notes++; + free_voice(lowest); + return lowest; + } + + /* try to remove VOICE_DIE before VOICE_ON */ + lv = 0x7FFFFFFF; + lowest = -1; + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE) + continue; + if(voice[j].status & ~(VOICE_ON | VOICE_SUSTAINED)) + { + /* continue protecting drum decays */ + if (voice[j].status & ~(VOICE_DIE) && + (voice[j].sample->note_to_use && ISDRUMCHANNEL(voice[j].channel))) + continue; + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + if(lowest != -1) + { + cut_notes++; + free_voice(lowest); + return lowest; + } + + /* try to remove VOICE_SUSTAINED before VOICE_ON */ + lv = 0x7FFFFFFF; + lowest = -0x7FFFFFFF; + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE) + continue; + if(voice[j].status & VOICE_SUSTAINED) + { + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + if(lowest != -0x7FFFFFFF) + { + cut_notes++; + free_voice(lowest); + return lowest; + } + + /* try to remove chorus before VOICE_ON */ + lv = 0x7FFFFFFF; + lowest = -0x7FFFFFFF; + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE) + continue; + if(voice[j].chorus_link < j) + { + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + if(lowest != -0x7FFFFFFF) + { + cut_notes++; + + /* fix pan */ + j = voice[lowest].chorus_link; + voice[j].panning = channel[voice[lowest].channel].panning; + recompute_amp(j); + mixer->apply_envelope_to_amp(j); + + free_voice(lowest); + return lowest; + } + + lost_notes++; + + /* remove non-drum VOICE_ON */ + lv = 0x7FFFFFFF; + lowest = -0x7FFFFFFF; + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE || + (voice[j].sample->note_to_use && ISDRUMCHANNEL(voice[j].channel))) + continue; + + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + if(lowest != -0x7FFFFFFF) + { + free_voice(lowest); + return lowest; + } + + /* remove all other types of notes */ + lv = 0x7FFFFFFF; + lowest = 0; + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE) + continue; + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + + free_voice(lowest); + return lowest; +} + +void Player::free_voice(int v1) +{ + int v2; + +#ifdef ENABLE_PAN_DELAY + if (voice[v1].pan_delay_buf != NULL) { + free(voice[v1].pan_delay_buf); + voice[v1].pan_delay_buf = NULL; + } +#endif /* ENABLE_PAN_DELAY */ + + v2 = voice[v1].chorus_link; + if(v1 != v2) + { + /* Unlink chorus link */ + voice[v1].chorus_link = v1; + voice[v2].chorus_link = v2; + } + voice[v1].status = VOICE_FREE; + voice[v1].temper_instant = 0; +} + +int Player::find_free_voice(void) +{ + int i, nv = voices, lowest; + int32_t lv, v; + + for(i = 0; i < nv; i++) + if(voice[i].status == VOICE_FREE) + { + if(upper_voices <= i) + upper_voices = i + 1; + return i; + } + + upper_voices = voices; + + /* Look for the decaying note with the lowest volume */ + lv = 0x7FFFFFFF; + lowest = -1; + for(i = 0; i < nv; i++) + { + if(voice[i].status & ~(VOICE_ON | VOICE_DIE) && + !(voice[i].sample && voice[i].sample->note_to_use && ISDRUMCHANNEL(voice[i].channel))) + { + v = voice[i].left_mix; + if((voice[i].panned==PANNED_MYSTERY) && (voice[i].right_mix>v)) + v = voice[i].right_mix; + if(vchannel; + if (channel[ch].special_sample > 0) { + if ((s = instruments->specialPatch(channel[ch].special_sample)) == NULL) { + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, + "Strange: Special patch %d is not installed", + channel[ch].special_sample); + return 0; + } + note = e->a + channel[ch].key_shift + note_key_offset; + note = (note < 0) ? 0 : ((note > 127) ? 127 : note); + return select_play_sample(s->sample, s->samples, ¬e, vlist, e); + } + bank = channel[ch].bank; + if (ISDRUMCHANNEL(ch)) { + note = e->a & 0x7f; + instruments->instrument_map(channel[ch].mapID, &bank, ¬e); + if (! (ip = play_midi_load_instrument(1, bank, note))) + return 0; /* No instrument? Then we can't play. */ + /* if (ip->type == INST_GUS && ip->samples != 1) + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, + "Strange: percussion instrument with %d samples!", + ip->samples); */ + /* "keynum" of SF2, and patch option "note=" */ + if (ip->sample->note_to_use) + note = ip->sample->note_to_use; + } else { + if ((prog = channel[ch].program) == SPECIAL_PROGRAM) + ip = instruments->defaultInstrument(); + else { + instruments->instrument_map(channel[ch].mapID, &bank, &prog); + if (! (ip = play_midi_load_instrument(0, bank, prog))) + return 0; /* No instrument? Then we can't play. */ + } + note = ((ip->sample->note_to_use) ? ip->sample->note_to_use : e->a) + + channel[ch].key_shift + note_key_offset; + note = (note < 0) ? 0 : ((note > 127) ? 127 : note); + } + nv = select_play_sample(ip->sample, ip->samples, ¬e, vlist, e); + /* Replace the sample if the sample is cached. */ + if (ip->sample->note_to_use) + note = MIDI_EVENT_NOTE(e); + for (i = 0; i < nv; i++) { + j = vlist[i]; + if (! opt_realtime_playing && allocate_cache_size > 0 + && ! channel[ch].portamento) { + voice[j].cache = recache->resamp_cache_fetch(voice[j].sample, note); + if (voice[j].cache) /* cache hit */ + voice[j].sample = voice[j].cache->resampled; + } else + voice[j].cache = NULL; + } + return nv; +} + +int Player::select_play_sample(Sample *splist, int nsp, int *note, int *vlist, MidiEvent *e) +{ + int ch = e->channel, kn = e->a & 0x7f, vel = e->b; + int32_t f, fs, ft, fst, fc, fr, cdiff, diff, sample_link; + int8_t tt = channel[ch].temper_type; + uint8_t tp = channel[ch].rpnmap[RPN_ADDR_0003]; + Sample *sp, *spc, *spr; + int16_t sf, sn; + double ratio; + int i, j, k, nv, nvc; + + if (ISDRUMCHANNEL(ch)) + f = fs = freq_table[*note]; + else { + if (opt_pure_intonation) { + if (current_keysig < 8) + f = freq_table_pureint[current_freq_table][*note]; + else + f = freq_table_pureint[current_freq_table + 12][*note]; + } else if (opt_temper_control) + switch (tt) { + case 0: + f = freq_table_tuning[tp][*note]; + break; + case 1: + if (current_temper_keysig < 8) + f = freq_table_pytha[ + current_temper_freq_table][*note]; + else + f = freq_table_pytha[ + current_temper_freq_table + 12][*note]; + break; + case 2: + if (current_temper_keysig < 8) + f = freq_table_meantone[current_temper_freq_table + + ((temper_adj) ? 36 : 0)][*note]; + else + f = freq_table_meantone[current_temper_freq_table + + ((temper_adj) ? 24 : 12)][*note]; + break; + case 3: + if (current_temper_keysig < 8) + f = freq_table_pureint[current_temper_freq_table + + ((temper_adj) ? 36 : 0)][*note]; + else + f = freq_table_pureint[current_temper_freq_table + + ((temper_adj) ? 24 : 12)][*note]; + break; + default: /* user-defined temperament */ + if ((tt -= 0x40) >= 0 && tt < 4) { + if (current_temper_keysig < 8) + f = freq_table_user[tt][current_temper_freq_table + + ((temper_adj) ? 36 : 0)][*note]; + else + f = freq_table_user[tt][current_temper_freq_table + + ((temper_adj) ? 24 : 12)][*note]; + } else + f = freq_table[*note]; + break; + } + else + f = freq_table[*note]; + if (! opt_pure_intonation && opt_temper_control + && tt == 0 && f != freq_table[*note]) { + *note = log(f / 440000.0) / log(2) * 12 + 69.5; + *note = (*note < 0) ? 0 : ((*note > 127) ? 127 : *note); + fs = freq_table[*note]; + } else + fs = freq_table[*note]; + } + nv = 0; + for (i = 0, sp = splist; i < nsp; i++, sp++) { + /* GUS/SF2 - Scale Tuning */ + if ((sf = sp->scale_factor) != 1024) { + sn = sp->scale_freq; + ratio = pow(2.0, (*note - sn) * (sf - 1024) / 12288.0); + ft = f * ratio + 0.5, fst = fs * ratio + 0.5; + } else + ft = f, fst = fs; + if (ISDRUMCHANNEL(ch) && channel[ch].drums[kn] != NULL) + if ((ratio = get_play_note_ratio(ch, kn)) != 1.0) + ft = ft * ratio + 0.5, fst = fst * ratio + 0.5; + if (sp->low_freq <= fst && sp->high_freq >= fst + && sp->low_vel <= vel && sp->high_vel >= vel + && ! (sp->inst_type == INST_SF2 + && sp->sample_type == SF_SAMPLETYPE_RIGHT)) { + j = vlist[nv] = find_voice(e); + voice[j].orig_frequency = ft; + voice[j].sample = sp; + voice[j].status = VOICE_ON; + nv++; + } + } + if (nv == 0) { /* we must select at least one sample. */ + fr = fc = 0; + spc = spr = NULL; + cdiff = 0x7fffffff; + for (i = 0, sp = splist; i < nsp; i++, sp++) { + /* GUS/SF2 - Scale Tuning */ + if ((sf = sp->scale_factor) != 1024) { + sn = sp->scale_freq; + ratio = pow(2.0, (*note - sn) * (sf - 1024) / 12288.0); + ft = f * ratio + 0.5, fst = fs * ratio + 0.5; + } else + ft = f, fst = fs; + if (ISDRUMCHANNEL(ch) && channel[ch].drums[kn] != NULL) + if ((ratio = get_play_note_ratio(ch, kn)) != 1.0) + ft = ft * ratio + 0.5, fst = fst * ratio + 0.5; + diff = abs(sp->root_freq - fst); + if (diff < cdiff) { + if (sp->inst_type == INST_SF2 + && sp->sample_type == SF_SAMPLETYPE_RIGHT) { + fr = ft; /* reserve */ + spr = sp; /* reserve */ + } else { + fc = ft; + spc = sp; + cdiff = diff; + } + } + } + /* If spc is not NULL, a makeshift sample is found. */ + /* Otherwise, it's a lonely right sample, but better than nothing. */ + j = vlist[nv] = find_voice(e); + voice[j].orig_frequency = (spc) ? fc : fr; + voice[j].sample = (spc) ? spc : spr; + voice[j].status = VOICE_ON; + nv++; + } + nvc = nv; + for (i = 0; i < nvc; i++) { + spc = voice[vlist[i]].sample; + /* If it's left sample, there must be right sample. */ + if (spc->inst_type == INST_SF2 + && spc->sample_type == SF_SAMPLETYPE_LEFT) { + sample_link = spc->sf_sample_link; + for (j = 0, sp = splist; j < nsp; j++, sp++) + if (sp->inst_type == INST_SF2 + && sp->sample_type == SF_SAMPLETYPE_RIGHT + && sp->sf_sample_index == sample_link) { + /* right sample is found. */ + /* GUS/SF2 - Scale Tuning */ + if ((sf = sp->scale_factor) != 1024) { + sn = sp->scale_freq; + ratio = pow(2.0, (*note - sn) * (sf - 1024) / 12288.0); + ft = f * ratio + 0.5; + } else + ft = f; + if (ISDRUMCHANNEL(ch) && channel[ch].drums[kn] != NULL) + if ((ratio = get_play_note_ratio(ch, kn)) != 1.0) + ft = ft * ratio + 0.5; + k = vlist[nv] = find_voice(e); + voice[k].orig_frequency = ft; + voice[k].sample = sp; + voice[k].status = VOICE_ON; + nv++; + break; + } + } + } + return nv; +} + +double Player::get_play_note_ratio(int ch, int note) +{ + int play_note = channel[ch].drums[note]->play_note; + int bank = channel[ch].bank; + const ToneBank *dbank; + int def_play_note; + + if (play_note == -1) + return 1.0; + instruments->instrument_map(channel[ch].mapID, &bank, ¬e); + dbank = (instruments->drumSet(bank)) ? instruments->drumSet(bank) : instruments->drumSet(0); + if ((def_play_note = dbank->tone[note].play_note) == -1) + return 1.0; + if (play_note >= def_play_note) + return bend_coarse[(play_note - def_play_note) & 0x7f]; + else + return 1 / bend_coarse[(def_play_note - play_note) & 0x7f]; +} + +/* Only one instance of a note can be playing on a single channel. */ +int Player::find_voice(MidiEvent *e) +{ + int ch = e->channel; + int note = MIDI_EVENT_NOTE(e); + int status_check, mono_check; + AlternateAssign *altassign; + int i, lowest = -1; + + status_check = (opt_overlap_voice_allow) + ? (VOICE_OFF | VOICE_SUSTAINED) : 0xff; + mono_check = channel[ch].mono; + altassign = instruments->find_altassign(channel[ch].altassign, note); + for (i = 0; i < upper_voices; i++) + if (voice[i].status == VOICE_FREE) { + lowest = i; /* lower volume */ + break; + } + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE && voice[i].channel == ch) { + if (voice[i].note == note && (voice[i].status & status_check)) + kill_note(i); + else if (mono_check) + kill_note(i); + else if (altassign && instruments->find_altassign(altassign, voice[i].note)) + kill_note(i); + else if (voice[i].note == note && (channel[ch].assign_mode == 0 + || (channel[ch].assign_mode == 1 && + voice[i].proximate_flag == 0))) + kill_note(i); + } + for (i = 0; i < upper_voices; i++) + if (voice[i].channel == ch && voice[i].note == note) + voice[i].proximate_flag = 0; + if (lowest != -1) /* Found a free voice. */ + return lowest; + if (upper_voices < voices) + return upper_voices++; + return reduce_voice(); +} + +int Player::get_panning(int ch, int note,int v) +{ + int pan; + + if(channel[ch].panning != NO_PANNING) {pan = (int)channel[ch].panning - 64;} + else {pan = 0;} + if(ISDRUMCHANNEL(ch) && + channel[ch].drums[note] != NULL && + channel[ch].drums[note]->drum_panning != NO_PANNING) { + pan += channel[ch].drums[note]->drum_panning; + } else { + pan += voice[v].sample->panning; + } + + if (pan > 127) pan = 127; + else if (pan < 0) pan = 0; + + return pan; +} + +/*! initialize vibrato parameters for a voice. */ +void Player::init_voice_vibrato(int v) +{ + Voice *vp = &(voice[v]); + int ch = vp->channel, j, nrpn_vib_flag; + double ratio; + + /* if NRPN vibrato is set, it's believed that there must be vibrato. */ + nrpn_vib_flag = opt_nrpn_vibrato + && (channel[ch].vibrato_ratio != 1.0 || channel[ch].vibrato_depth != 0); + + /* vibrato sweep */ + vp->vibrato_sweep = vp->sample->vibrato_sweep_increment; + vp->vibrato_sweep_position = 0; + + /* vibrato rate */ + if (nrpn_vib_flag) { + if(vp->sample->vibrato_control_ratio == 0) { + ratio = cnv_Hz_to_vib_ratio(5.0) * channel[ch].vibrato_ratio; + } else { + ratio = (double)vp->sample->vibrato_control_ratio * channel[ch].vibrato_ratio; + } + if (ratio < 0) {ratio = 0;} + vp->vibrato_control_ratio = (int)ratio; + } else { + vp->vibrato_control_ratio = vp->sample->vibrato_control_ratio; + } + + /* vibrato depth */ + if (nrpn_vib_flag) { + vp->vibrato_depth = vp->sample->vibrato_depth + channel[ch].vibrato_depth; + if (vp->vibrato_depth > VIBRATO_DEPTH_MAX) {vp->vibrato_depth = VIBRATO_DEPTH_MAX;} + else if (vp->vibrato_depth < 1) {vp->vibrato_depth = 1;} + if (vp->sample->vibrato_depth < 0) { /* in opposite phase */ + vp->vibrato_depth = -vp->vibrato_depth; + } + } else { + vp->vibrato_depth = vp->sample->vibrato_depth; + } + + /* vibrato delay */ + vp->vibrato_delay = vp->sample->vibrato_delay + channel[ch].vibrato_delay; + + /* internal parameters */ + vp->orig_vibrato_control_ratio = vp->vibrato_control_ratio; + vp->vibrato_control_counter = vp->vibrato_phase = 0; + for (j = 0; j < VIBRATO_SAMPLE_INCREMENTS; j++) { + vp->vibrato_sample_increment[j] = 0; + } +} + +/*! initialize panning-delay for a voice. */ +void Player::init_voice_pan_delay(int v) +{ +#ifdef ENABLE_PAN_DELAY + Voice *vp = &(voice[v]); + int ch = vp->channel; + double pan_delay_diff; + + if (vp->pan_delay_buf != NULL) { + free(vp->pan_delay_buf); + vp->pan_delay_buf = NULL; + } + vp->pan_delay_rpt = 0; + if (opt_pan_delay && channel[ch].insertion_effect == 0 && !opt_surround_chorus) { + if (vp->panning == 64) {vp->delay += pan_delay_table[64] * playback_rate / 1000;} + else { + if(pan_delay_table[vp->panning] > pan_delay_table[127 - vp->panning]) { + pan_delay_diff = pan_delay_table[vp->panning] - pan_delay_table[127 - vp->panning]; + vp->delay += (pan_delay_table[vp->panning] - pan_delay_diff) * playback_rate / 1000; + } else { + pan_delay_diff = pan_delay_table[127 - vp->panning] - pan_delay_table[vp->panning]; + vp->delay += (pan_delay_table[127 - vp->panning] - pan_delay_diff) * playback_rate / 1000; + } + vp->pan_delay_rpt = pan_delay_diff * playback_rate / 1000; + } + if(vp->pan_delay_rpt < 1) {vp->pan_delay_rpt = 0;} + vp->pan_delay_wpt = 0; + vp->pan_delay_spt = vp->pan_delay_wpt - vp->pan_delay_rpt; + if (vp->pan_delay_spt < 0) {vp->pan_delay_spt += PAN_DELAY_BUF_MAX;} + vp->pan_delay_buf = (int32_t *)safe_malloc(sizeof(int32_t) * PAN_DELAY_BUF_MAX); + memset(vp->pan_delay_buf, 0, sizeof(int32_t) * PAN_DELAY_BUF_MAX); + } +#endif /* ENABLE_PAN_DELAY */ +} + +/*! initialize portamento or legato for a voice. */ +void Player::init_voice_portamento(int v) +{ + Voice *vp = &(voice[v]); + int ch = vp->channel; + + vp->porta_control_counter = 0; + if (channel[ch].legato && channel[ch].legato_flag) { + update_legato_controls(ch); + } + else if (channel[ch].portamento && !channel[ch].porta_control_ratio) { + update_portamento_controls(ch); + } + vp->porta_control_ratio = 0; + if (channel[ch].porta_control_ratio) + { + if (channel[ch].last_note_fine == -1) { + /* first on */ + channel[ch].last_note_fine = vp->note * 256; + channel[ch].porta_control_ratio = 0; + } + else { + vp->porta_control_ratio = channel[ch].porta_control_ratio; + vp->porta_dpb = channel[ch].porta_dpb; + vp->porta_pb = channel[ch].last_note_fine - + vp->note * 256; + if (vp->porta_pb == 0) { vp->porta_control_ratio = 0; } + } + } +} + +/*! initialize tremolo for a voice. */ +void Player::init_voice_tremolo(int v) +{ + Voice *vp = &(voice[v]); + + vp->tremolo_delay = vp->sample->tremolo_delay; + vp->tremolo_phase = 0; + vp->tremolo_phase_increment = vp->sample->tremolo_phase_increment; + vp->tremolo_sweep = vp->sample->tremolo_sweep_increment; + vp->tremolo_sweep_position = 0; + vp->tremolo_depth = vp->sample->tremolo_depth; +} + +void Player::start_note(MidiEvent *e, int i, int vid, int cnt) +{ + int j, ch, note; + + ch = e->channel; + + note = MIDI_EVENT_NOTE(e); + voice[i].status = VOICE_ON; + voice[i].channel = ch; + voice[i].note = note; + voice[i].velocity = e->b; + voice[i].chorus_link = i; /* No link */ + voice[i].proximate_flag = 1; + + j = channel[ch].special_sample; + if (j == 0 || instruments->specialPatch(j) == NULL) + voice[i].sample_offset = 0; + else + { + voice[i].sample_offset = instruments->specialPatch(j)->sample_offset << FRACTION_BITS; + if (voice[i].sample->modes & MODES_LOOPING) + { + if (voice[i].sample_offset > voice[i].sample->loop_end) + voice[i].sample_offset = voice[i].sample->loop_start; + } + else if (voice[i].sample_offset > voice[i].sample->data_length) + { + free_voice(i); + return; + } + } + voice[i].sample_increment = 0; /* make sure it isn't negative */ + voice[i].vid = vid; + voice[i].delay = voice[i].sample->envelope_delay; + voice[i].modenv_delay = voice[i].sample->modenv_delay; + voice[i].delay_counter = 0; + + init_voice_tremolo(i); /* tremolo */ + init_voice_filter(i); /* resonant lowpass filter */ + init_voice_vibrato(i); /* vibrato */ + voice[i].panning = get_panning(ch, note, i); /* pan */ + init_voice_pan_delay(i); /* panning-delay */ + init_voice_portamento(i); /* portamento or legato */ + + if (cnt == 0) + channel[ch].last_note_fine = voice[i].note * 256; + + /* initialize modulation envelope */ + if (voice[i].sample->modes & MODES_ENVELOPE) + { + voice[i].modenv_stage = EG_GUS_ATTACK; + voice[i].modenv_volume = 0; + mixer->recompute_modulation_envelope(i); + mixer->apply_modulation_envelope(i); + } + else + { + voice[i].modenv_increment = 0; + mixer->apply_modulation_envelope(i); + } + recompute_freq(i); + recompute_voice_filter(i); + + recompute_amp(i); + /* initialize volume envelope */ + if (voice[i].sample->modes & MODES_ENVELOPE) + { + /* Ramp up from 0 */ + voice[i].envelope_stage = EG_GUS_ATTACK; + voice[i].envelope_volume = 0; + voice[i].control_counter = 0; + mixer->recompute_envelope(i); + mixer->apply_envelope_to_amp(i); + } + else + { + voice[i].envelope_increment = 0; + mixer->apply_envelope_to_amp(i); + } + + voice[i].timeout = -1; +} + +void Player::finish_note(int i) +{ + if (voice[i].sample->modes & MODES_ENVELOPE) + { + /* We need to get the envelope out of Sustain stage. */ + /* Note that voice[i].envelope_stage < EG_GUS_RELEASE1 */ + voice[i].status = VOICE_OFF; + voice[i].envelope_stage = EG_GUS_RELEASE1; + mixer->recompute_envelope(i); + voice[i].modenv_stage = EG_GUS_RELEASE1; + mixer->recompute_modulation_envelope(i); + mixer->apply_modulation_envelope(i); + mixer->apply_envelope_to_amp(i); + } + else + { + /* Set status to OFF so resample_voice() will let this voice out + of its loop, if any. In any case, this voice dies when it + hits the end of its data (ofs>=data_length). */ + if(voice[i].status != VOICE_OFF) + { + voice[i].status = VOICE_OFF; + } + } +} + +void Player::set_envelope_time(int ch, int val, int stage) +{ + val = val & 0x7F; + switch(stage) { + case EG_ATTACK: /* Attack */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Attack Time (CH:%d VALUE:%d)", ch, val); + break; + case EG_DECAY: /* Decay */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Decay Time (CH:%d VALUE:%d)", ch, val); + break; + case EG_RELEASE: /* Release */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Release Time (CH:%d VALUE:%d)", ch, val); + break; + default: + ctl_cmsg(CMSG_INFO,VERB_NOISY,"? Time (CH:%d VALUE:%d)", ch, val); + } + channel[ch].envelope_rate[stage] = val; +} + + + +/* Yet another chorus implementation + * by Eric A. Welsh . + */ +void Player::new_chorus_voice_alternate(int v1, int level) +{ + int v2, ch, panlevel; + uint8_t pan; + double delay; + double freq, frac; + int note_adjusted; + + if((v2 = find_free_voice()) == -1) + return; + ch = voice[v1].channel; + voice[v2] = voice[v1]; + + /* NRPN Chorus Send Level of Drum */ + if(ISDRUMCHANNEL(ch) && channel[ch].drums[voice[v1].note] != NULL) { + level *= (double)channel[ch].drums[voice[v1].note]->chorus_level / 127.0; + } + + /* for our purposes, hard left will be equal to 1 instead of 0 */ + pan = voice[v1].panning; + if (!pan) pan = 1; + + /* Choose lower voice index for base voice (v1) */ + if(v1 > v2) + { + v1 ^= v2; + v2 ^= v1; + v1 ^= v2; + } + + /* Make doubled link v1 and v2 */ + voice[v1].chorus_link = v2; + voice[v2].chorus_link = v1; + + /* detune notes for chorus effect */ + level >>= 2; /* scale to a "better" value */ + if (level) + { + if(channel[ch].pitchbend + level < 0x2000) + voice[v2].orig_frequency *= bend_fine[level]; + else + voice[v2].orig_frequency /= bend_fine[level]; + voice[v2].cache = NULL; + } + + delay = 0.003; + + /* Try to keep the delayed voice from cancelling out the other voice */ + /* Pitch detection is used to find the real pitches for drums and MODs */ + note_adjusted = voice[v1].note + voice[v1].sample->transpose_detected; + if (note_adjusted > 127) note_adjusted = 127; + else if (note_adjusted < 0) note_adjusted = 0; + freq = pitch_freq_table[note_adjusted]; + delay *= freq; + frac = delay - floor(delay); + + /* force the delay away from 0.5 period */ + if (frac < 0.5 && frac > 0.40) + { + delay = (floor(delay) + 0.40) / freq; + delay += (0.5 - frac) * (1.0 - labs(64 - pan) / 63.0) / freq; + } + else if (frac >= 0.5 && frac < 0.60) + { + delay = (floor(delay) + 0.60) / freq; + delay += (0.5 - frac) * (1.0 - labs(64 - pan) / 63.0) / freq; + } + else + delay = 0.003; + + /* set panning & delay for pseudo-surround effect */ + { + panlevel = 63; + if (pan - panlevel < 1) panlevel = pan - 1; + if (pan + panlevel > 127) panlevel = 127 - pan; + voice[v1].panning -= panlevel; + voice[v2].panning += panlevel; + + /* choose which voice is delayed based on panning */ + if (voice[v1].panned == PANNED_CENTER) { + /* randomly choose which voice is delayed */ + if (int_rand(2)) + voice[v1].delay += (int)(playback_rate * delay); + else + voice[v2].delay += (int)(playback_rate * delay); + } + else if (pan - 64 < 0) { + voice[v2].delay += (int)(playback_rate * delay); + } + else { + voice[v1].delay += (int)(playback_rate * delay); + } + } + + /* check for similar drums playing simultaneously with center pans */ + if (ISDRUMCHANNEL(ch) && voice[v1].panned == PANNED_CENTER) + { + int i, j; + + /* force Rimshot (37), Snare1 (38), Snare2 (40), and XG #34 to have + * the same delay, otherwise there will be bad voice cancellation. + */ + if (voice[v1].note == 37 || + voice[v1].note == 38 || + voice[v1].note == 40 || + (voice[v1].note == 34 && play_system_mode == XG_SYSTEM_MODE)) + { + for (i = 0; i < upper_voices; i++) + { + if (voice[i].status & (VOICE_DIE | VOICE_FREE)) + continue; + + if (!ISDRUMCHANNEL(voice[i].channel)) + continue; + + if (i == v1 || i == v2) + continue; + + if (voice[i].note == 37 || + voice[i].note == 38 || + voice[i].note == 40 || + (voice[i].note == 34 && + play_system_mode == XG_SYSTEM_MODE)) + { + j = voice[i].chorus_link; + + if (voice[i].panned == PANNED_LEFT && + voice[j].panned == PANNED_RIGHT) + { + voice[v1].delay = voice[i].delay; + voice[v2].delay = voice[j].delay; + + break; + } + } + } + } + + /* force Kick1 (35), Kick2 (36), and XG Kick #33 to have the same + * delay, otherwise there will be bad voice cancellation. + */ + if (voice[v1].note == 35 || + voice[v1].note == 36 || + (voice[v1].note == 33 && play_system_mode == XG_SYSTEM_MODE)) + { + for (i = 0; i < upper_voices; i++) + { + if (voice[i].status & (VOICE_DIE | VOICE_FREE)) + continue; + + if (!ISDRUMCHANNEL(voice[i].channel)) + continue; + + if (i == v1 || i == v2) + continue; + + if (voice[i].note == 35 || + voice[i].note == 36 || + (voice[i].note == 33 && + play_system_mode == XG_SYSTEM_MODE)) + { + j = voice[i].chorus_link; + + if (voice[i].panned == PANNED_LEFT && + voice[j].panned == PANNED_RIGHT) + { + voice[v1].delay = voice[i].delay; + voice[v2].delay = voice[j].delay; + + break; + } + } + } + } + } + + init_voice_pan_delay(v1); + init_voice_pan_delay(v2); + + recompute_amp(v1); + mixer->apply_envelope_to_amp(v1); + recompute_amp(v2); + mixer->apply_envelope_to_amp(v2); + if (level) recompute_freq(v2); +} + +void Player::note_on(MidiEvent *e) +{ + int i, nv, v, ch, note; + int vlist[32]; + int vid; + int32_t random_delay = 0; + + ch = e->channel; + note = MIDI_EVENT_NOTE(e); + + if(ISDRUMCHANNEL(ch) && + channel[ch].drums[note] != NULL && + !get_rx_drum(channel[ch].drums[note], RX_NOTE_ON)) { /* Rx. Note On */ + return; + } + if(channel[ch].note_limit_low > note || + channel[ch].note_limit_high < note || + channel[ch].vel_limit_low > e->b || + channel[ch].vel_limit_high < e->b) { + return; + } + if((nv = find_samples(e, vlist)) == 0) + return; + + vid = new_vidq(e->channel, note); + + recompute_bank_parameter(ch, note); + recompute_channel_filter(ch, note); + random_delay = calc_random_delay(ch, note); + + for(i = 0; i < nv; i++) + { + v = vlist[i]; + if(ISDRUMCHANNEL(ch) && + channel[ch].drums[note] != NULL && + channel[ch].drums[note]->pan_random) + channel[ch].drums[note]->drum_panning = int_rand(128); + else if(channel[ch].pan_random) + { + channel[ch].panning = int_rand(128); + } + start_note(e, v, vid, nv - i - 1); + voice[v].delay += random_delay; + voice[v].modenv_delay += random_delay; + voice[v].old_left_mix = voice[v].old_right_mix = + voice[v].left_mix_inc = voice[v].left_mix_offset = + voice[v].right_mix_inc = voice[v].right_mix_offset = 0; + if(opt_surround_chorus) + new_chorus_voice_alternate(v, 0); + } + + channel[ch].legato_flag = 1; +} + +/*! sostenuto is now implemented as an instant sustain */ +void Player::update_sostenuto_controls(int ch) +{ + int uv = upper_voices, i; + + if(ISDRUMCHANNEL(ch) || channel[ch].sostenuto == 0) {return;} + + for(i = 0; i < uv; i++) + { + if ((voice[i].status & (VOICE_ON | VOICE_OFF)) + && voice[i].channel == ch) + { + voice[i].status = VOICE_SUSTAINED; + voice[i].envelope_stage = EG_GUS_RELEASE1; + mixer->recompute_envelope(i); + } + } +} + +/*! redamper effect for piano instruments */ +void Player::update_redamper_controls(int ch) +{ + int uv = upper_voices, i; + + if(ISDRUMCHANNEL(ch) || channel[ch].damper_mode == 0) {return;} + + for(i = 0; i < uv; i++) + { + if ((voice[i].status & (VOICE_ON | VOICE_OFF)) + && voice[i].channel == ch) + { + voice[i].status = VOICE_SUSTAINED; + voice[i].envelope_stage = EG_GUS_RELEASE1; + mixer->recompute_envelope(i); + } + } +} + +void Player::note_off(MidiEvent *e) +{ + int uv = upper_voices, i; + int ch, note, vid, sustain; + + ch = e->channel; + note = MIDI_EVENT_NOTE(e); + + if(ISDRUMCHANNEL(ch)) + { + int nbank, nprog; + + nbank = channel[ch].bank; + nprog = note; + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + + if (channel[ch].drums[nprog] != NULL && + get_rx_drum(channel[ch].drums[nprog], RX_NOTE_OFF)) + { + auto bank = instruments->drumSet(nbank); + if(bank == NULL) bank = instruments->drumSet(0); + + /* uh oh, this drum doesn't have an instrument loaded yet */ + if (bank->tone[nprog].instrument == NULL) + return; + + /* this drum is not loaded for some reason (error occured?) */ + if (IS_MAGIC_INSTRUMENT(bank->tone[nprog].instrument)) + return; + + /* only disallow Note Off if the drum sample is not looped */ + if (!(bank->tone[nprog].instrument->sample->modes & MODES_LOOPING)) + return; /* Note Off is not allowed. */ + } + } + + if ((vid = last_vidq(ch, note)) == -1) + return; + sustain = channel[ch].sustain; + for (i = 0; i < uv; i++) + { + if(voice[i].status == VOICE_ON && + voice[i].channel == ch && + voice[i].note == note && + voice[i].vid == vid) + { + if(sustain) + { + voice[i].status = VOICE_SUSTAINED; + } + else + finish_note(i); + } + } + + channel[ch].legato_flag = 0; +} + +/* Process the All Notes Off event */ +void Player::all_notes_off(int c) +{ + int i, uv = upper_voices; + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "All notes off on channel %d", c); + for(i = 0; i < uv; i++) + if (voice[i].status==VOICE_ON && + voice[i].channel==c) + { + if (channel[c].sustain) + { + voice[i].status=VOICE_SUSTAINED; + } + else + finish_note(i); + } + for(i = 0; i < 128; i++) + vidq_head[c * 128 + i] = vidq_tail[c * 128 + i] = 0; +} + +/* Process the All Sounds Off event */ +void Player::all_sounds_off(int c) +{ + int i, uv = upper_voices; + for(i = 0; i < uv; i++) + if (voice[i].channel==c && + (voice[i].status & ~(VOICE_FREE | VOICE_DIE))) + { + kill_note(i); + } + for(i = 0; i < 128; i++) + vidq_head[c * 128 + i] = vidq_tail[c * 128 + i] = 0; +} + +/*! adjust polyphonic key pressure (PAf, PAT) */ +void Player::adjust_pressure(MidiEvent *e) +{ + int i, uv = upper_voices; + int note, ch; + + if(opt_channel_pressure) + { + ch = e->channel; + note = MIDI_EVENT_NOTE(e); + channel[ch].paf.val = e->b; + if(channel[ch].paf.pitch != 0) {channel[ch].pitchfactor = 0;} + + for(i = 0; i < uv; i++) + if(voice[i].status == VOICE_ON && + voice[i].channel == ch && + voice[i].note == note) + { + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + recompute_freq(i); + recompute_voice_filter(i); + } + } +} + +/*! adjust channel pressure (channel aftertouch, CAf, CAT) */ +void Player::adjust_channel_pressure(MidiEvent *e) +{ + if(opt_channel_pressure) + { + int i, uv = upper_voices; + int ch; + + ch = e->channel; + channel[ch].caf.val = e->a; + if(channel[ch].caf.pitch != 0) {channel[ch].pitchfactor = 0;} + + for(i = 0; i < uv; i++) + { + if(voice[i].status == VOICE_ON && voice[i].channel == ch) + { + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + recompute_freq(i); + recompute_voice_filter(i); + } + } + } +} + +void Player::adjust_panning(int c) +{ + int i, uv = upper_voices, pan = channel[c].panning; + for(i = 0; i < uv; i++) + { + if ((voice[i].channel==c) && + (voice[i].status & (VOICE_ON | VOICE_SUSTAINED))) + { + /* adjust pan to include drum/sample pan offsets */ + pan = get_panning(c, voice[i].note, i); + + /* Hack to handle -EFchorus=2 in a "reasonable" way */ + if(opt_surround_chorus && voice[i].chorus_link != i) + { + int v1, v2; + + if(i >= voice[i].chorus_link) + /* `i' is not base chorus voice. + * This sub voice is already updated. + */ + continue; + + v1 = i; /* base voice */ + v2 = voice[i].chorus_link; /* sub voice (detuned) */ + + if(opt_surround_chorus) /* Surround chorus mode by Eric. */ + { + int panlevel; + + if (!pan) pan = 1; /* make hard left be 1 instead of 0 */ + panlevel = 63; + if (pan - panlevel < 1) panlevel = pan - 1; + if (pan + panlevel > 127) panlevel = 127 - pan; + voice[v1].panning = pan - panlevel; + voice[v2].panning = pan + panlevel; + } + else + { + voice[v1].panning = pan; + if(pan > 60 && pan < 68) /* PANNED_CENTER */ + voice[v2].panning = + 64 + int_rand(40) - 20; /* 64 +- rand(20) */ + else if(pan < CHORUS_OPPOSITE_THRESHOLD) + voice[v2].panning = 127; + else if(pan > 127 - CHORUS_OPPOSITE_THRESHOLD) + voice[v2].panning = 0; + else + voice[v2].panning = (pan < 64 ? 0 : 127); + } + recompute_amp(v2); + mixer->apply_envelope_to_amp(v2); + /* v1 == i, so v1 will be updated next */ + } + else + voice[i].panning = pan; + + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + } + } +} + +void Player::play_midi_setup_drums(int ch, int note) +{ + channel[ch].drums[note] = (struct DrumParts *) + new_segment(&playmidi_pool, sizeof(struct DrumParts)); + reset_drum_controllers(channel[ch].drums, note); +} + +void Player::adjust_drum_panning(int ch, int note) +{ + int i, uv = upper_voices; + + for(i = 0; i < uv; i++) { + if(voice[i].channel == ch && + voice[i].note == note && + (voice[i].status & (VOICE_ON | VOICE_SUSTAINED))) + { + voice[i].panning = get_panning(ch, note, i); + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + } + } +} + +void Player::drop_sustain(int c) +{ + int i, uv = upper_voices; + for(i = 0; i < uv; i++) + if (voice[i].status == VOICE_SUSTAINED && voice[i].channel == c) + finish_note(i); +} + +void Player::adjust_all_pitch(void) +{ + int ch, i, uv = upper_voices; + + for (ch = 0; ch < MAX_CHANNELS; ch++) + channel[ch].pitchfactor = 0; + for (i = 0; i < uv; i++) + if (voice[i].status != VOICE_FREE) + recompute_freq(i); +} + +void Player::adjust_pitch(int c) +{ + int i, uv = upper_voices; + for(i = 0; i < uv; i++) + if (voice[i].status != VOICE_FREE && voice[i].channel == c) + recompute_freq(i); +} + +void Player::adjust_volume(int c) +{ + int i, uv = upper_voices; + for(i = 0; i < uv; i++) + if (voice[i].channel == c && + (voice[i].status & (VOICE_ON | VOICE_SUSTAINED))) + { + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + } +} + +void Player::set_reverb_level(int ch, int level) +{ + if (level == -1) { + channel[ch].reverb_level = channel[ch].reverb_id = + (opt_reverb_control < 0) + ? -opt_reverb_control & 0x7f : DEFAULT_REVERB_SEND_LEVEL; + make_rvid_flag = 1; + return; + } + channel[ch].reverb_level = level; + make_rvid_flag = 0; /* to update reverb_id */ +} + +int Player::get_reverb_level(int ch) +{ + if (channel[ch].reverb_level == -1) + return (opt_reverb_control < 0) + ? -opt_reverb_control & 0x7f : DEFAULT_REVERB_SEND_LEVEL; + return channel[ch].reverb_level; +} + +int Player::get_chorus_level(int ch) +{ +#ifdef DISALLOW_DRUM_BENDS + if(ISDRUMCHANNEL(ch)) + return 0; /* Not supported drum channel chorus */ +#endif + if(opt_chorus_control == 1) + return channel[ch].chorus_level; + return -opt_chorus_control; +} + + +void Player::free_drum_effect(int ch) +{ + int i; + if (channel[ch].drum_effect != NULL) { + for (i = 0; i < channel[ch].drum_effect_num; i++) { + if (channel[ch].drum_effect[i].buf != NULL) { + free(channel[ch].drum_effect[i].buf); + channel[ch].drum_effect[i].buf = NULL; + } + } + free(channel[ch].drum_effect); + channel[ch].drum_effect = NULL; + } + channel[ch].drum_effect_num = 0; + channel[ch].drum_effect_flag = 0; +} + +void Player::make_drum_effect(int ch) +{ + int i, note, num = 0; + int8_t note_table[128]; + struct DrumParts *drum; + struct DrumPartEffect *de; + + if (channel[ch].drums == NULL) {return;} + + if (channel[ch].drum_effect_flag == 0) { + free_drum_effect(ch); + memset(note_table, 0, sizeof(int8_t) * 128); + + for(i = 0; i < 128; i++) { + if ((drum = channel[ch].drums[i]) != NULL) + { + if (drum->reverb_level != -1 + || drum->chorus_level != -1 || drum->delay_level != -1) { + note_table[num++] = i; + } + } + } + + channel[ch].drum_effect = (struct DrumPartEffect *)safe_malloc(sizeof(struct DrumPartEffect) * num); + + for(i = 0; i < num; i++) { + de = &(channel[ch].drum_effect[i]); + de->note = note = note_table[i]; + drum = channel[ch].drums[note]; + de->reverb_send = (int32_t)drum->reverb_level * (int32_t)get_reverb_level(ch) / 127; + de->chorus_send = (int32_t)drum->chorus_level * (int32_t)channel[ch].chorus_level / 127; + de->delay_send = (int32_t)drum->delay_level * (int32_t)channel[ch].delay_level / 127; + de->buf = (int32_t *)safe_malloc(AUDIO_BUFFER_SIZE * 8); + memset(de->buf, 0, AUDIO_BUFFER_SIZE * 8); + } + + channel[ch].drum_effect_num = num; + channel[ch].drum_effect_flag = 1; + } +} + +void Player::adjust_master_volume(void) +{ + int i, uv = upper_voices; + adjust_amplification(); + for(i = 0; i < uv; i++) + if(voice[i].status & (VOICE_ON | VOICE_SUSTAINED)) + { + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + } +} + +int Player::midi_drumpart_change(int ch, int isdrum) +{ + if (IS_SET_CHANNELMASK(drumchannel_mask, ch)) + return 0; + if (isdrum) { + SET_CHANNELMASK(drumchannels, ch); + SET_CHANNELMASK(current_file_info->drumchannels, ch); + } else { + UNSET_CHANNELMASK(drumchannels, ch); + UNSET_CHANNELMASK(current_file_info->drumchannels, ch); + } + return 1; +} + +void Player::midi_program_change(int ch, int prog) +{ + int dr = ISDRUMCHANNEL(ch); + int newbank, b, p, map; + + switch (play_system_mode) { + case GS_SYSTEM_MODE: /* GS */ + if ((map = channel[ch].bank_lsb) == 0) { + map = channel[ch].tone_map0_number; + } + switch (map) { + case 0: /* No change */ + break; + case 1: + channel[ch].mapID = (dr) ? SC_55_DRUM_MAP : SC_55_TONE_MAP; + break; + case 2: + channel[ch].mapID = (dr) ? SC_88_DRUM_MAP : SC_88_TONE_MAP; + break; + case 3: + channel[ch].mapID = (dr) ? SC_88PRO_DRUM_MAP : SC_88PRO_TONE_MAP; + break; + case 4: + channel[ch].mapID = (dr) ? SC_8850_DRUM_MAP : SC_8850_TONE_MAP; + break; + default: + break; + } + newbank = channel[ch].bank_msb; + break; + case XG_SYSTEM_MODE: /* XG */ + switch (channel[ch].bank_msb) { + case 0: /* Normal */ +#if 0 + if (ch == 9 && channel[ch].bank_lsb == 127 + && channel[ch].mapID == XG_DRUM_MAP) + /* FIXME: Why this part is drum? Is this correct? */ + break; +#endif +/* Eric's explanation for the FIXME (March 2004): + * + * I don't have the original email from my archived inbox, but I found a + * reply I made in my archived sent-mail from 1999. A September 5th message + * to Masanao Izumo is discussing a problem with a "reapxg.mid", a file which + * I still have, and how it issues an MSB=0 with a program change on ch 9, + * thus turning it into a melodic channel. The strange thing is, this doesn't + * happen on XG hardware, nor on the XG softsynth. It continues to play as a + * normal drum. The author of the midi file obviously intended it to be + * drumset 16 too. The original fix was to detect LSB == -1, then break so + * as to not set it to a melodic channel. I'm guessing that this somehow got + * mutated into checking for 127 instead, and the current FIXME is related to + * the original hack from Sept 1999. The Sept 5th email discusses patches + * being applied to version 2.5.1 to get XG drums to work properly, and a + * Sept 7th email to someone else discusses the fixes being part of the + * latest 2.6.0-beta3. A September 23rd email to Masanao Izumo specifically + * mentions the LSB == -1 hack (and reapxg.mid not playing "correctly" + * anymore), as well as new changes in 2.6.0 that broke a lot of other XG + * files (XG drum support was extremely buggy in 1999 and we were still trying + * to figure out how to initialize things to reproduce hardware behavior). An + * October 5th email says that 2.5.1 was correct, 2.6.0 had very broken XG + * drum changes, and 2.6.1 still has problems. Further discussions ensued + * over what was "correct": to follow the XG spec, or to reproduce + * "features" / bugs in the hardware. I can't find the rest of the + * discussions, but I think it ended with us agreeing to just follow the spec + * and not try to reproduce the hardware strangeness. I don't know how the + * current FIXME wound up the way it is now. I'm still going to guess it is + * related to the old reapxg.mid hack. + * + * Now that reset_midi() initializes channel[ch].bank_lsb to 0 instead of -1, + * checking for LSB == -1 won't do anything anymore, so changing the above + * FIXME to the original == -1 won't do any good. It is best to just #if 0 + * it out and leave it here as a reminder that there is at least one XG + * hardware / softsynth "bug" that is not reproduced by timidity at the + * moment. + * + * If the current FIXME actually reproduces some other XG hadware bug that + * I don't know about, then it may have a valid purpose. I just don't know + * what that purpose is at the moment. Perhaps someone else does? I still + * have src going back to 2.10.4, and the FIXME comment was already there by + * then. I don't see any entries in the Changelog that could explain it + * either. If someone has src from 2.5.1 through 2.10.3 and wants to + * investigate this further, go for it :) + */ + midi_drumpart_change(ch, 0); + channel[ch].mapID = XG_NORMAL_MAP; + dr = ISDRUMCHANNEL(ch); + break; + case 64: /* SFX voice */ + midi_drumpart_change(ch, 0); + channel[ch].mapID = XG_SFX64_MAP; + dr = ISDRUMCHANNEL(ch); + break; + case 126: /* SFX kit */ + midi_drumpart_change(ch, 1); + channel[ch].mapID = XG_SFX126_MAP; + dr = ISDRUMCHANNEL(ch); + break; + case 127: /* Drum kit */ + midi_drumpart_change(ch, 1); + channel[ch].mapID = XG_DRUM_MAP; + dr = ISDRUMCHANNEL(ch); + break; + default: + break; + } + newbank = channel[ch].bank_lsb; + break; + case GM2_SYSTEM_MODE: /* GM2 */ + if ((channel[ch].bank_msb & 0xfe) == 0x78) { /* 0x78/0x79 */ + midi_drumpart_change(ch, channel[ch].bank_msb == 0x78); + dr = ISDRUMCHANNEL(ch); + } + channel[ch].mapID = (dr) ? GM2_DRUM_MAP : GM2_TONE_MAP; + newbank = channel[ch].bank_lsb; + break; + default: + newbank = channel[ch].bank_msb; + break; + } + if (dr) { + channel[ch].bank = prog; /* newbank is ignored */ + channel[ch].program = prog; + if (instruments->drumSet(prog) == NULL || instruments->drumSet(prog)->alt == NULL) + channel[ch].altassign = instruments->drumSet(0)->alt; + else + channel[ch].altassign = instruments->drumSet(prog)->alt; + } else { + channel[ch].bank = (special_tonebank >= 0) + ? special_tonebank : newbank; + channel[ch].program = (instruments->defaultProgram(ch) == SPECIAL_PROGRAM) + ? SPECIAL_PROGRAM : prog; + channel[ch].altassign = NULL; + if (opt_realtime_playing && (play_mode->flag & PF_PCM_STREAM)) { + b = channel[ch].bank, p = prog; + instruments->instrument_map(channel[ch].mapID, &b, &p); + play_midi_load_instrument(0, b, p); + } + } +} + + +/*! add a new layer. */ +void Player::add_channel_layer(int to_ch, int from_ch) +{ + if (to_ch >= MAX_CHANNELS || from_ch >= MAX_CHANNELS) + return; + /* add a channel layer */ + UNSET_CHANNELMASK(channel[to_ch].channel_layer, to_ch); + SET_CHANNELMASK(channel[to_ch].channel_layer, from_ch); + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Channel Layer (CH:%d -> CH:%d)", from_ch, to_ch); +} + +/*! remove all layers for this channel. */ +void Player::remove_channel_layer(int ch) +{ + int i, offset; + + if (ch >= MAX_CHANNELS) + return; + /* remove channel layers */ + offset = ch & ~0xf; + for (i = offset; i < offset + REDUCE_CHANNELS; i++) + UNSET_CHANNELMASK(channel[i].channel_layer, ch); + SET_CHANNELMASK(channel[ch].channel_layer, ch); +} + +/*! process system exclusive sent from parse_sysex_event_multi(). */ +void Player::process_sysex_event(int ev, int ch, int val, int b) +{ + int temp, msb, note; + + if (ch >= MAX_CHANNELS) + return; + if (ev == ME_SYSEX_MSB) { + channel[ch].sysex_msb_addr = b; + channel[ch].sysex_msb_val = val; + } else if(ev == ME_SYSEX_GS_MSB) { + channel[ch].sysex_gs_msb_addr = b; + channel[ch].sysex_gs_msb_val = val; + } else if(ev == ME_SYSEX_XG_MSB) { + channel[ch].sysex_xg_msb_addr = b; + channel[ch].sysex_xg_msb_val = val; + } else if(ev == ME_SYSEX_LSB) { /* Universal system exclusive message */ + msb = channel[ch].sysex_msb_addr; + note = channel[ch].sysex_msb_val; + channel[ch].sysex_msb_addr = channel[ch].sysex_msb_val = 0; + switch(b) + { + case 0x00: /* CAf Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].caf.pitch = val - 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAf Pitch Control (CH:%d %d semitones)", ch, channel[ch].caf.pitch); + break; + case 0x01: /* CAf Filter Cutoff Control */ + channel[ch].caf.cutoff = (val - 64) * 150; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAf Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].caf.cutoff); + break; + case 0x02: /* CAf Amplitude Control */ + channel[ch].caf.amp = (float)val / 64.0f - 1.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAf Amplitude Control (CH:%d %.2f)", ch, channel[ch].caf.amp); + break; + case 0x03: /* CAf LFO1 Rate Control */ + channel[ch].caf.lfo1_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAf LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].caf.lfo1_rate); + break; + case 0x04: /* CAf LFO1 Pitch Depth */ + channel[ch].caf.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAf LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].caf.lfo1_pitch_depth); + break; + case 0x05: /* CAf LFO1 Filter Depth */ + channel[ch].caf.lfo1_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAf LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].caf.lfo1_tvf_depth); + break; + case 0x06: /* CAf LFO1 Amplitude Depth */ + channel[ch].caf.lfo1_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAf LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].caf.lfo1_tva_depth); + break; + case 0x07: /* CAf LFO2 Rate Control */ + channel[ch].caf.lfo2_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAf LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].caf.lfo2_rate); + break; + case 0x08: /* CAf LFO2 Pitch Depth */ + channel[ch].caf.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAf LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].caf.lfo2_pitch_depth); + break; + case 0x09: /* CAf LFO2 Filter Depth */ + channel[ch].caf.lfo2_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAf LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].caf.lfo2_tvf_depth); + break; + case 0x0A: /* CAf LFO2 Amplitude Depth */ + channel[ch].caf.lfo2_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAf LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].caf.lfo2_tva_depth); + break; + case 0x0B: /* PAf Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].paf.pitch = val - 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "PAf Pitch Control (CH:%d %d semitones)", ch, channel[ch].paf.pitch); + break; + case 0x0C: /* PAf Filter Cutoff Control */ + channel[ch].paf.cutoff = (val - 64) * 150; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "PAf Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].paf.cutoff); + break; + case 0x0D: /* PAf Amplitude Control */ + channel[ch].paf.amp = (float)val / 64.0f - 1.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "PAf Amplitude Control (CH:%d %.2f)", ch, channel[ch].paf.amp); + break; + case 0x0E: /* PAf LFO1 Rate Control */ + channel[ch].paf.lfo1_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "PAf LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].paf.lfo1_rate); + break; + case 0x0F: /* PAf LFO1 Pitch Depth */ + channel[ch].paf.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "PAf LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].paf.lfo1_pitch_depth); + break; + case 0x10: /* PAf LFO1 Filter Depth */ + channel[ch].paf.lfo1_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "PAf LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].paf.lfo1_tvf_depth); + break; + case 0x11: /* PAf LFO1 Amplitude Depth */ + channel[ch].paf.lfo1_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "PAf LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].paf.lfo1_tva_depth); + break; + case 0x12: /* PAf LFO2 Rate Control */ + channel[ch].paf.lfo2_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "PAf LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].paf.lfo2_rate); + break; + case 0x13: /* PAf LFO2 Pitch Depth */ + channel[ch].paf.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "PAf LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].paf.lfo2_pitch_depth); + break; + case 0x14: /* PAf LFO2 Filter Depth */ + channel[ch].paf.lfo2_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "PAf LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].paf.lfo2_tvf_depth); + break; + case 0x15: /* PAf LFO2 Amplitude Depth */ + channel[ch].paf.lfo2_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "PAf LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].paf.lfo2_tva_depth); + break; + case 0x16: /* MOD Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].mod.pitch = val - 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MOD Pitch Control (CH:%d %d semitones)", ch, channel[ch].mod.pitch); + break; + case 0x17: /* MOD Filter Cutoff Control */ + channel[ch].mod.cutoff = (val - 64) * 150; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MOD Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].mod.cutoff); + break; + case 0x18: /* MOD Amplitude Control */ + channel[ch].mod.amp = (float)val / 64.0f - 1.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MOD Amplitude Control (CH:%d %.2f)", ch, channel[ch].mod.amp); + break; + case 0x19: /* MOD LFO1 Rate Control */ + channel[ch].mod.lfo1_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MOD LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].mod.lfo1_rate); + break; + case 0x1A: /* MOD LFO1 Pitch Depth */ + channel[ch].mod.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MOD LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].mod.lfo1_pitch_depth); + break; + case 0x1B: /* MOD LFO1 Filter Depth */ + channel[ch].mod.lfo1_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MOD LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].mod.lfo1_tvf_depth); + break; + case 0x1C: /* MOD LFO1 Amplitude Depth */ + channel[ch].mod.lfo1_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MOD LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].mod.lfo1_tva_depth); + break; + case 0x1D: /* MOD LFO2 Rate Control */ + channel[ch].mod.lfo2_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MOD LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].mod.lfo2_rate); + break; + case 0x1E: /* MOD LFO2 Pitch Depth */ + channel[ch].mod.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MOD LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].mod.lfo2_pitch_depth); + break; + case 0x1F: /* MOD LFO2 Filter Depth */ + channel[ch].mod.lfo2_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MOD LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].mod.lfo2_tvf_depth); + break; + case 0x20: /* MOD LFO2 Amplitude Depth */ + channel[ch].mod.lfo2_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MOD LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].mod.lfo2_tva_depth); + break; + case 0x21: /* BEND Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].bend.pitch = val - 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND Pitch Control (CH:%d %d semitones)", ch, channel[ch].bend.pitch); + break; + case 0x22: /* BEND Filter Cutoff Control */ + channel[ch].bend.cutoff = (val - 64) * 150; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].bend.cutoff); + break; + case 0x23: /* BEND Amplitude Control */ + channel[ch].bend.amp = (float)val / 64.0f - 1.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND Amplitude Control (CH:%d %.2f)", ch, channel[ch].bend.amp); + break; + case 0x24: /* BEND LFO1 Rate Control */ + channel[ch].bend.lfo1_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].bend.lfo1_rate); + break; + case 0x25: /* BEND LFO1 Pitch Depth */ + channel[ch].bend.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].bend.lfo1_pitch_depth); + break; + case 0x26: /* BEND LFO1 Filter Depth */ + channel[ch].bend.lfo1_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].bend.lfo1_tvf_depth); + break; + case 0x27: /* BEND LFO1 Amplitude Depth */ + channel[ch].bend.lfo1_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].bend.lfo1_tva_depth); + break; + case 0x28: /* BEND LFO2 Rate Control */ + channel[ch].bend.lfo2_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].bend.lfo2_rate); + break; + case 0x29: /* BEND LFO2 Pitch Depth */ + channel[ch].bend.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].bend.lfo2_pitch_depth); + break; + case 0x2A: /* BEND LFO2 Filter Depth */ + channel[ch].bend.lfo2_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].bend.lfo2_tvf_depth); + break; + case 0x2B: /* BEND LFO2 Amplitude Depth */ + channel[ch].bend.lfo2_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].bend.lfo2_tva_depth); + break; + case 0x2C: /* CC1 Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].cc1.pitch = val - 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 Pitch Control (CH:%d %d semitones)", ch, channel[ch].cc1.pitch); + break; + case 0x2D: /* CC1 Filter Cutoff Control */ + channel[ch].cc1.cutoff = (val - 64) * 150; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].cc1.cutoff); + break; + case 0x2E: /* CC1 Amplitude Control */ + channel[ch].cc1.amp = (float)val / 64.0f - 1.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 Amplitude Control (CH:%d %.2f)", ch, channel[ch].cc1.amp); + break; + case 0x2F: /* CC1 LFO1 Rate Control */ + channel[ch].cc1.lfo1_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].cc1.lfo1_rate); + break; + case 0x30: /* CC1 LFO1 Pitch Depth */ + channel[ch].cc1.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].cc1.lfo1_pitch_depth); + break; + case 0x31: /* CC1 LFO1 Filter Depth */ + channel[ch].cc1.lfo1_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].cc1.lfo1_tvf_depth); + break; + case 0x32: /* CC1 LFO1 Amplitude Depth */ + channel[ch].cc1.lfo1_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].cc1.lfo1_tva_depth); + break; + case 0x33: /* CC1 LFO2 Rate Control */ + channel[ch].cc1.lfo2_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].cc1.lfo2_rate); + break; + case 0x34: /* CC1 LFO2 Pitch Depth */ + channel[ch].cc1.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].cc1.lfo2_pitch_depth); + break; + case 0x35: /* CC1 LFO2 Filter Depth */ + channel[ch].cc1.lfo2_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].cc1.lfo2_tvf_depth); + break; + case 0x36: /* CC1 LFO2 Amplitude Depth */ + channel[ch].cc1.lfo2_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].cc1.lfo2_tva_depth); + break; + case 0x37: /* CC2 Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].cc2.pitch = val - 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 Pitch Control (CH:%d %d semitones)", ch, channel[ch].cc2.pitch); + break; + case 0x38: /* CC2 Filter Cutoff Control */ + channel[ch].cc2.cutoff = (val - 64) * 150; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].cc2.cutoff); + break; + case 0x39: /* CC2 Amplitude Control */ + channel[ch].cc2.amp = (float)val / 64.0f - 1.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 Amplitude Control (CH:%d %.2f)", ch, channel[ch].cc2.amp); + break; + case 0x3A: /* CC2 LFO1 Rate Control */ + channel[ch].cc2.lfo1_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].cc2.lfo1_rate); + break; + case 0x3B: /* CC2 LFO1 Pitch Depth */ + channel[ch].cc2.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].cc2.lfo1_pitch_depth); + break; + case 0x3C: /* CC2 LFO1 Filter Depth */ + channel[ch].cc2.lfo1_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].cc2.lfo1_tvf_depth); + break; + case 0x3D: /* CC2 LFO1 Amplitude Depth */ + channel[ch].cc2.lfo1_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].cc2.lfo1_tva_depth); + break; + case 0x3E: /* CC2 LFO2 Rate Control */ + channel[ch].cc2.lfo2_rate = (float)(val - 64) / 6.4f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].cc2.lfo2_rate); + break; + case 0x3F: /* CC2 LFO2 Pitch Depth */ + channel[ch].cc2.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].cc2.lfo2_pitch_depth); + break; + case 0x40: /* CC2 LFO2 Filter Depth */ + channel[ch].cc2.lfo2_tvf_depth = conv_lfo_filter_depth(val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].cc2.lfo2_tvf_depth); + break; + case 0x41: /* CC2 LFO2 Amplitude Depth */ + channel[ch].cc2.lfo2_tva_depth = (float)val / 127.0f; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].cc2.lfo2_tva_depth); + break; + case 0x42: /* Note Limit Low */ + channel[ch].note_limit_low = val; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Note Limit Low (CH:%d VAL:%d)", ch, val); + break; + case 0x43: /* Note Limit High */ + channel[ch].note_limit_high = val; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Note Limit High (CH:%d VAL:%d)", ch, val); + break; + case 0x44: /* Velocity Limit Low */ + channel[ch].vel_limit_low = val; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Velocity Limit Low (CH:%d VAL:%d)", ch, val); + break; + case 0x45: /* Velocity Limit High */ + channel[ch].vel_limit_high = val; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Velocity Limit High (CH:%d VAL:%d)", ch, val); + break; + case 0x46: /* Rx. Note Off */ + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + set_rx_drum(channel[ch].drums[note], RX_NOTE_OFF, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Drum Instrument Rx. Note Off (CH:%d NOTE:%d VAL:%d)", + ch, note, val); + break; + case 0x47: /* Rx. Note On */ + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + set_rx_drum(channel[ch].drums[note], RX_NOTE_ON, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Drum Instrument Rx. Note On (CH:%d NOTE:%d VAL:%d)", + ch, note, val); + break; + case 0x48: /* Rx. Pitch Bend */ + set_rx(ch, RX_PITCH_BEND, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Pitch Bend (CH:%d VAL:%d)", ch, val); + break; + case 0x49: /* Rx. Channel Pressure */ + set_rx(ch, RX_CH_PRESSURE, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Channel Pressure (CH:%d VAL:%d)", ch, val); + break; + case 0x4A: /* Rx. Program Change */ + set_rx(ch, RX_PROGRAM_CHANGE, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Program Change (CH:%d VAL:%d)", ch, val); + break; + case 0x4B: /* Rx. Control Change */ + set_rx(ch, RX_CONTROL_CHANGE, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Control Change (CH:%d VAL:%d)", ch, val); + break; + case 0x4C: /* Rx. Poly Pressure */ + set_rx(ch, RX_POLY_PRESSURE, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Poly Pressure (CH:%d VAL:%d)", ch, val); + break; + case 0x4D: /* Rx. Note Message */ + set_rx(ch, RX_NOTE_MESSAGE, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Note Message (CH:%d VAL:%d)", ch, val); + break; + case 0x4E: /* Rx. RPN */ + set_rx(ch, RX_RPN, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. RPN (CH:%d VAL:%d)", ch, val); + break; + case 0x4F: /* Rx. NRPN */ + set_rx(ch, RX_NRPN, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. NRPN (CH:%d VAL:%d)", ch, val); + break; + case 0x50: /* Rx. Modulation */ + set_rx(ch, RX_MODULATION, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Modulation (CH:%d VAL:%d)", ch, val); + break; + case 0x51: /* Rx. Volume */ + set_rx(ch, RX_VOLUME, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Volume (CH:%d VAL:%d)", ch, val); + break; + case 0x52: /* Rx. Panpot */ + set_rx(ch, RX_PANPOT, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Panpot (CH:%d VAL:%d)", ch, val); + break; + case 0x53: /* Rx. Expression */ + set_rx(ch, RX_EXPRESSION, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Expression (CH:%d VAL:%d)", ch, val); + break; + case 0x54: /* Rx. Hold1 */ + set_rx(ch, RX_HOLD1, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Hold1 (CH:%d VAL:%d)", ch, val); + break; + case 0x55: /* Rx. Portamento */ + set_rx(ch, RX_PORTAMENTO, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Portamento (CH:%d VAL:%d)", ch, val); + break; + case 0x56: /* Rx. Sostenuto */ + set_rx(ch, RX_SOSTENUTO, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Sostenuto (CH:%d VAL:%d)", ch, val); + break; + case 0x57: /* Rx. Soft */ + set_rx(ch, RX_SOFT, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Soft (CH:%d VAL:%d)", ch, val); + break; + case 0x58: /* Rx. Bank Select */ + set_rx(ch, RX_BANK_SELECT, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Bank Select (CH:%d VAL:%d)", ch, val); + break; + case 0x59: /* Rx. Bank Select LSB */ + set_rx(ch, RX_BANK_SELECT_LSB, val); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Rx. Bank Select LSB (CH:%d VAL:%d)", ch, val); + break; + case 0x60: /* Reverb Type (GM2) */ + if (val > 8) {val = 8;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Reverb Type (%d)", val); + reverb->set_reverb_macro_gm2(val); + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + break; + case 0x61: /* Chorus Type (GM2) */ + if (val > 5) {val = 5;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Chorus Type (%d)", val); + reverb->set_chorus_macro_gs(val); + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + break; + default: + break; + } + return; + } else if(ev == ME_SYSEX_GS_LSB) { /* GS system exclusive message */ + msb = channel[ch].sysex_gs_msb_addr; + note = channel[ch].sysex_gs_msb_val; + channel[ch].sysex_gs_msb_addr = channel[ch].sysex_gs_msb_val = 0; + switch(b) + { + case 0x00: /* EQ ON/OFF */ + if(!opt_eq_control) {break;} + channel[ch].eq_gs = val; + break; + case 0x01: /* EQ LOW FREQ */ + if(!opt_eq_control) {break;} + reverb->eq_status_gs.low_freq = val; + reverb->recompute_eq_status_gs(); + break; + case 0x02: /* EQ LOW GAIN */ + if(!opt_eq_control) {break;} + reverb->eq_status_gs.low_gain = val; + reverb->recompute_eq_status_gs(); + break; + case 0x03: /* EQ HIGH FREQ */ + if(!opt_eq_control) {break;} + reverb->eq_status_gs.high_freq = val; + reverb->recompute_eq_status_gs(); + break; + case 0x04: /* EQ HIGH GAIN */ + if(!opt_eq_control) {break;} + reverb->eq_status_gs.high_gain = val; + reverb->recompute_eq_status_gs(); + break; + case 0x05: /* Reverb Macro */ + if (val > 7) {val = 7;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Reverb Macro (%d)",val); + reverb->set_reverb_macro_gs(val); + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + break; + case 0x06: /* Reverb Character */ + if (val > 7) {val = 7;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Reverb Character (%d)",val); + if (reverb->reverb_status_gs.character != val) { + reverb->reverb_status_gs.character = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } + break; + case 0x07: /* Reverb Pre-LPF */ + if (val > 7) {val = 7;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Reverb Pre-LPF (%d)",val); + if(reverb->reverb_status_gs.pre_lpf != val) { + reverb->reverb_status_gs.pre_lpf = val; + reverb->recompute_reverb_status_gs(); + } + break; + case 0x08: /* Reverb Level */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Reverb Level (%d)",val); + if(reverb->reverb_status_gs.level != val) { + reverb->reverb_status_gs.level = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } + break; + case 0x09: /* Reverb Time */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Reverb Time (%d)",val); + if(reverb->reverb_status_gs.time != val) { + reverb->reverb_status_gs.time = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } + break; + case 0x0A: /* Reverb Delay Feedback */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Reverb Delay Feedback (%d)",val); + if(reverb->reverb_status_gs.delay_feedback != val) { + reverb->reverb_status_gs.delay_feedback = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } + break; + case 0x0C: /* Reverb Predelay Time */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Reverb Predelay Time (%d)",val); + if(reverb->reverb_status_gs.pre_delay_time != val) { + reverb->reverb_status_gs.pre_delay_time = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } + break; + case 0x0D: /* Chorus Macro */ + if (val > 7) {val = 7;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Chorus Macro (%d)",val); + reverb->set_chorus_macro_gs(val); + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + break; + case 0x0E: /* Chorus Pre-LPF */ + if (val > 7) {val = 7;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Chorus Pre-LPF (%d)",val); + if (reverb->chorus_status_gs.pre_lpf != val) { + reverb->chorus_status_gs.pre_lpf = val; + reverb->recompute_chorus_status_gs(); + } + break; + case 0x0F: /* Chorus Level */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Chorus Level (%d)",val); + if (reverb->chorus_status_gs.level != val) { + reverb->chorus_status_gs.level = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x10: /* Chorus Feedback */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Chorus Feedback (%d)",val); + if (reverb->chorus_status_gs.feedback != val) { + reverb->chorus_status_gs.feedback = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x11: /* Chorus Delay */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Chorus Delay (%d)",val); + if (reverb->chorus_status_gs.delay != val) { + reverb->chorus_status_gs.delay = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x12: /* Chorus Rate */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Chorus Rate (%d)",val); + if (reverb->chorus_status_gs.rate != val) { + reverb->chorus_status_gs.rate = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x13: /* Chorus Depth */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Chorus Depth (%d)",val); + if (reverb->chorus_status_gs.depth != val) { + reverb->chorus_status_gs.depth = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x14: /* Chorus Send Level to Reverb */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Chorus Send Level to Reverb (%d)",val); + if (reverb->chorus_status_gs.send_reverb != val) { + reverb->chorus_status_gs.send_reverb = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x15: /* Chorus Send Level to Delay */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Chorus Send Level to Delay (%d)",val); + if (reverb->chorus_status_gs.send_delay != val) { + reverb->chorus_status_gs.send_delay = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x16: /* Delay Macro */ + if (val > 7) {val = 7;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Delay Macro (%d)",val); + reverb->set_delay_macro_gs(val); + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + break; + case 0x17: /* Delay Pre-LPF */ + if (val > 7) {val = 7;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Delay Pre-LPF (%d)",val); + val &= 0x7; + if (reverb->delay_status_gs.pre_lpf != val) { + reverb->delay_status_gs.pre_lpf = val; + reverb->recompute_delay_status_gs(); + } + break; + case 0x18: /* Delay Time Center */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Delay Time Center (%d)",val); + if (reverb->delay_status_gs.time_c != val) { + reverb->delay_status_gs.time_c = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x19: /* Delay Time Ratio Left */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Delay Time Ratio Left (%d)",val); + if (val == 0) {val = 1;} + if (reverb->delay_status_gs.time_l != val) { + reverb->delay_status_gs.time_l = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1A: /* Delay Time Ratio Right */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Delay Time Ratio Right (%d)",val); + if (val == 0) {val = 1;} + if (reverb->delay_status_gs.time_r != val) { + reverb->delay_status_gs.time_r = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1B: /* Delay Level Center */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Delay Level Center (%d)",val); + if (reverb->delay_status_gs.level_center != val) { + reverb->delay_status_gs.level_center = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1C: /* Delay Level Left */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Delay Level Left (%d)",val); + if (reverb->delay_status_gs.level_left != val) { + reverb->delay_status_gs.level_left = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1D: /* Delay Level Right */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Delay Level Right (%d)",val); + if (reverb->delay_status_gs.level_right != val) { + reverb->delay_status_gs.level_right = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1E: /* Delay Level */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Delay Level (%d)",val); + if (reverb->delay_status_gs.level != val) { + reverb->delay_status_gs.level = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1F: /* Delay Feedback */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Delay Feedback (%d)",val); + if (reverb->delay_status_gs.feedback != val) { + reverb->delay_status_gs.feedback = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x20: /* Delay Send Level to Reverb */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Delay Send Level to Reverb (%d)",val); + if (reverb->delay_status_gs.send_reverb != val) { + reverb->delay_status_gs.send_reverb = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x21: /* Velocity Sense Depth */ + channel[ch].velocity_sense_depth = val; + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Velocity Sense Depth (CH:%d VAL:%d)",ch,val); + break; + case 0x22: /* Velocity Sense Offset */ + channel[ch].velocity_sense_offset = val; + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Velocity Sense Offset (CH:%d VAL:%d)",ch,val); + break; + case 0x23: /* Insertion Effect ON/OFF */ + if(!opt_insertion_effect) {break;} + if(channel[ch].insertion_effect != val) { + if(val) {ctl_cmsg(CMSG_INFO,VERB_NOISY,"EFX ON (CH:%d)",ch);} + else {ctl_cmsg(CMSG_INFO,VERB_NOISY,"EFX OFF (CH:%d)",ch);} + } + channel[ch].insertion_effect = val; + break; + case 0x24: /* Assign Mode */ + channel[ch].assign_mode = val; + if(val == 0) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Assign Mode: Single (CH:%d)",ch); + } else if(val == 1) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Assign Mode: Limited-Multi (CH:%d)",ch); + } else if(val == 2) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Assign Mode: Full-Multi (CH:%d)",ch); + } + break; + case 0x25: /* TONE MAP-0 NUMBER */ + channel[ch].tone_map0_number = val; + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Tone Map-0 Number (CH:%d VAL:%d)",ch,val); + break; + case 0x26: /* Pitch Offset Fine */ + channel[ch].pitch_offset_fine = (double)((((int32_t)val << 4) | (int32_t)val) - 0x80) / 10.0; + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Pitch Offset Fine (CH:%d %3fHz)",ch,channel[ch].pitch_offset_fine); + break; + case 0x27: /* Insertion Effect Parameter */ + if(!opt_insertion_effect) {break;} + temp = reverb->insertion_effect_gs.type; + reverb->insertion_effect_gs.type_msb = val; + reverb->insertion_effect_gs.type = ((int32_t)reverb->insertion_effect_gs.type_msb << 8) | (int32_t)reverb->insertion_effect_gs.type_lsb; + if(temp == reverb->insertion_effect_gs.type) { + reverb->recompute_insertion_effect_gs(); + } else { + reverb->realloc_insertion_effect_gs(); + } + break; + case 0x28: /* Insertion Effect Parameter */ + if(!opt_insertion_effect) {break;} + temp = reverb->insertion_effect_gs.type; + reverb->insertion_effect_gs.type_lsb = val; + reverb->insertion_effect_gs.type = ((int32_t)reverb->insertion_effect_gs.type_msb << 8) | (int32_t)reverb->insertion_effect_gs.type_lsb; + if(temp == reverb->insertion_effect_gs.type) { + reverb->recompute_insertion_effect_gs(); + } else { + reverb->realloc_insertion_effect_gs(); + } + break; + case 0x29: + reverb->insertion_effect_gs.parameter[0] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2A: + reverb->insertion_effect_gs.parameter[1] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2B: + reverb->insertion_effect_gs.parameter[2] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2C: + reverb->insertion_effect_gs.parameter[3] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2D: + reverb->insertion_effect_gs.parameter[4] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2E: + reverb->insertion_effect_gs.parameter[5] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2F: + reverb->insertion_effect_gs.parameter[6] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x30: + reverb->insertion_effect_gs.parameter[7] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x31: + reverb->insertion_effect_gs.parameter[8] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x32: + reverb->insertion_effect_gs.parameter[9] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x33: + reverb->insertion_effect_gs.parameter[10] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x34: + reverb->insertion_effect_gs.parameter[11] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x35: + reverb->insertion_effect_gs.parameter[12] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x36: + reverb->insertion_effect_gs.parameter[13] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x37: + reverb->insertion_effect_gs.parameter[14] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x38: + reverb->insertion_effect_gs.parameter[15] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x39: + reverb->insertion_effect_gs.parameter[16] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3A: + reverb->insertion_effect_gs.parameter[17] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3B: + reverb->insertion_effect_gs.parameter[18] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3C: + reverb->insertion_effect_gs.parameter[19] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3D: + reverb->insertion_effect_gs.send_reverb = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3E: + reverb->insertion_effect_gs.send_chorus = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3F: + reverb->insertion_effect_gs.send_delay = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x40: + reverb->insertion_effect_gs.control_source1 = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x41: + reverb->insertion_effect_gs.control_depth1 = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x42: + reverb->insertion_effect_gs.control_source2 = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x43: + reverb->insertion_effect_gs.control_depth2 = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x44: + reverb->insertion_effect_gs.send_eq_switch = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x45: /* Rx. Channel */ + reset_controllers(ch); + all_notes_off(ch); + if (val == 0x80) + remove_channel_layer(ch); + else + add_channel_layer(ch, val); + break; + case 0x46: /* Channel Msg Rx Port */ + reset_controllers(ch); + all_notes_off(ch); + channel[ch].port_select = val; + break; + case 0x47: /* Play Note Number */ + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + channel[ch].drums[note]->play_note = val; + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Drum Instrument Play Note (CH:%d NOTE:%d VAL:%d)", + ch, note, channel[ch].drums[note]->play_note); + channel[ch].pitchfactor = 0; + break; + default: + break; + } + return; + } else if(ev == ME_SYSEX_XG_LSB) { /* XG system exclusive message */ + msb = channel[ch].sysex_xg_msb_addr; + note = channel[ch].sysex_xg_msb_val; + if (note == 3 && msb == 0) { /* Effect 2 */ + note = 0; /* force insertion effect num 0 ?? */ + if (note >= XG_INSERTION_EFFECT_NUM || note < 0) {return;} + switch(b) + { + case 0x00: /* Insertion Effect Type MSB */ + if (reverb->insertion_effect_xg[note].type_msb != val) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Insertion Effect Type MSB (%d %02X)", note, val); + reverb->insertion_effect_xg[note].type_msb = val; + reverb->realloc_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x01: /* Insertion Effect Type LSB */ + if (reverb->insertion_effect_xg[note].type_lsb != val) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Insertion Effect Type LSB (%d %02X)", note, val); + reverb->insertion_effect_xg[note].type_lsb = val; + reverb->realloc_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x02: /* Insertion Effect Parameter 1 - 10 */ + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + if (reverb->insertion_effect_xg[note].use_msb) {break;} + temp = b - 0x02; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Insertion Effect Parameter %d (%d %d)", temp + 1, note, val); + if (reverb->insertion_effect_xg[note].param_lsb[temp] != val) { + reverb->insertion_effect_xg[note].param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x0C: /* Insertion Effect Part */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Insertion Effect Part (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].part != val) { + reverb->insertion_effect_xg[note].part = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x0D: /* MW Insertion Control Depth */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MW Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].mw_depth != val) { + reverb->insertion_effect_xg[note].mw_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x0E: /* BEND Insertion Control Depth */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].bend_depth != val) { + reverb->insertion_effect_xg[note].bend_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x0F: /* CAT Insertion Control Depth */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAT Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].cat_depth != val) { + reverb->insertion_effect_xg[note].cat_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x10: /* AC1 Insertion Control Depth */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "AC1 Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].ac1_depth != val) { + reverb->insertion_effect_xg[note].ac1_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x11: /* AC2 Insertion Control Depth */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "AC2 Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].ac2_depth != val) { + reverb->insertion_effect_xg[note].ac2_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x12: /* CBC1 Insertion Control Depth */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CBC1 Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].cbc1_depth != val) { + reverb->insertion_effect_xg[note].cbc1_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x13: /* CBC2 Insertion Control Depth */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CBC2 Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].cbc2_depth != val) { + reverb->insertion_effect_xg[note].cbc2_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x20: /* Insertion Effect Parameter 11 - 16 */ + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + temp = b - 0x20 + 10; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Insertion Effect Parameter %d (%d %d)", temp + 1, note, val); + if (reverb->insertion_effect_xg[note].param_lsb[temp] != val) { + reverb->insertion_effect_xg[note].param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x30: /* Insertion Effect Parameter 1 - 10 MSB */ + case 0x32: + case 0x34: + case 0x36: + case 0x38: + case 0x3A: + case 0x3C: + case 0x3E: + case 0x40: + case 0x42: + if (!reverb->insertion_effect_xg[note].use_msb) {break;} + temp = (b - 0x30) / 2; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Insertion Effect Parameter %d MSB (%d %d)", temp + 1, note, val); + if (reverb->insertion_effect_xg[note].param_msb[temp] != val) { + reverb->insertion_effect_xg[note].param_msb[temp] = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x31: /* Insertion Effect Parameter 1 - 10 LSB */ + case 0x33: + case 0x35: + case 0x37: + case 0x39: + case 0x3B: + case 0x3D: + case 0x3F: + case 0x41: + case 0x43: + if (!reverb->insertion_effect_xg[note].use_msb) {break;} + temp = (b - 0x31) / 2; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Insertion Effect Parameter %d LSB (%d %d)", temp + 1, note, val); + if (reverb->insertion_effect_xg[note].param_lsb[temp] != val) { + reverb->insertion_effect_xg[note].param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + default: + break; + } + } else if (note == 2 && msb == 1) { /* Effect 1 */ + note = 0; /* force variation effect num 0 ?? */ + switch(b) + { + case 0x00: /* Reverb Type MSB */ + if (reverb->reverb_status_xg.type_msb != val) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Reverb Type MSB (%02X)", val); + reverb->reverb_status_xg.type_msb = val; + reverb->realloc_effect_xg(&reverb->reverb_status_xg); + } + break; + case 0x01: /* Reverb Type LSB */ + if (reverb->reverb_status_xg.type_lsb != val) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Reverb Type LSB (%02X)", val); + reverb->reverb_status_xg.type_lsb = val; + reverb->realloc_effect_xg(&reverb->reverb_status_xg); + } + break; + case 0x02: /* Reverb Parameter 1 - 10 */ + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Reverb Parameter %d (%d)", b - 0x02 + 1, val); + if (reverb->reverb_status_xg.param_lsb[b - 0x02] != val) { + reverb->reverb_status_xg.param_lsb[b - 0x02] = val; + reverb->recompute_effect_xg(&reverb->reverb_status_xg); + } + break; + case 0x0C: /* Reverb Return */ +#if 0 /* XG specific reverb is not currently implemented */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Reverb Return (%d)", val); + if (reverb->reverb_status_xg.ret != val) { + reverb->reverb_status_xg.ret = val; + reverb->recompute_effect_xg(&reverb->reverb_status_xg); + } +#else /* use GS reverb instead */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Reverb Return (%d)", val); + if (reverb->reverb_status_gs.level != val) { + reverb->reverb_status_gs.level = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } +#endif + break; + case 0x0D: /* Reverb Pan */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Reverb Pan (%d)", val); + if (reverb->reverb_status_xg.pan != val) { + reverb->reverb_status_xg.pan = val; + reverb->recompute_effect_xg(&reverb->reverb_status_xg); + } + break; + case 0x10: /* Reverb Parameter 11 - 16 */ + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + temp = b - 0x10 + 10; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Reverb Parameter %d (%d)", temp + 1, val); + if (reverb->reverb_status_xg.param_lsb[temp] != val) { + reverb->reverb_status_xg.param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->reverb_status_xg); + } + break; + case 0x20: /* Chorus Type MSB */ + if (reverb->chorus_status_xg.type_msb != val) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Chorus Type MSB (%02X)", val); + reverb->chorus_status_xg.type_msb = val; + reverb->realloc_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x21: /* Chorus Type LSB */ + if (reverb->chorus_status_xg.type_lsb != val) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Chorus Type LSB (%02X)", val); + reverb->chorus_status_xg.type_lsb = val; + reverb->realloc_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x22: /* Chorus Parameter 1 - 10 */ + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Chorus Parameter %d (%d)", b - 0x22 + 1, val); + if (reverb->chorus_status_xg.param_lsb[b - 0x22] != val) { + reverb->chorus_status_xg.param_lsb[b - 0x22] = val; + reverb->recompute_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x2C: /* Chorus Return */ +#if 0 /* XG specific chorus is not currently implemented */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Chorus Return (%d)", val); + if (reverb->chorus_status_xg.ret != val) { + reverb->chorus_status_xg.ret = val; + reverb->recompute_effect_xg(&reverb->chorus_status_xg); + } +#else /* use GS chorus instead */ + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Chorus Return (%d)", val); + if (reverb->chorus_status_gs.level != val) { + reverb->chorus_status_gs.level = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } +#endif + break; + case 0x2D: /* Chorus Pan */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Chorus Pan (%d)", val); + if (reverb->chorus_status_xg.pan != val) { + reverb->chorus_status_xg.pan = val; + reverb->recompute_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x2E: /* Send Chorus To Reverb */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Send Chorus To Reverb (%d)", val); + if (reverb->chorus_status_xg.send_reverb != val) { + reverb->chorus_status_xg.send_reverb = val; + reverb->recompute_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x30: /* Chorus Parameter 11 - 16 */ + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + temp = b - 0x30 + 10; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Chorus Parameter %d (%d)", temp + 1, val); + if (reverb->chorus_status_xg.param_lsb[temp] != val) { + reverb->chorus_status_xg.param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x40: /* Variation Type MSB */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + if (reverb->variation_effect_xg[note].type_msb != val) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Variation Type MSB (%02X)", val); + reverb->variation_effect_xg[note].type_msb = val; + reverb->realloc_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x41: /* Variation Type LSB */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + if (reverb->variation_effect_xg[note].type_lsb != val) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Variation Type LSB (%02X)", val); + reverb->variation_effect_xg[note].type_lsb = val; + reverb->realloc_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x42: /* Variation Parameter 1 - 10 MSB */ + case 0x44: + case 0x46: + case 0x48: + case 0x4A: + case 0x4C: + case 0x4E: + case 0x50: + case 0x52: + case 0x54: + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + temp = (b - 0x42) / 2; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Variation Parameter %d MSB (%d)", temp, val); + if (reverb->variation_effect_xg[note].param_msb[temp] != val) { + reverb->variation_effect_xg[note].param_msb[temp] = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x43: /* Variation Parameter 1 - 10 LSB */ + case 0x45: + case 0x47: + case 0x49: + case 0x4B: + case 0x4D: + case 0x4F: + case 0x51: + case 0x53: + case 0x55: + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + temp = (b - 0x43) / 2; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Variation Parameter %d LSB (%d)", temp, val); + if (reverb->variation_effect_xg[note].param_lsb[temp] != val) { + reverb->variation_effect_xg[note].param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x56: /* Variation Return */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Variation Return (%d)", val); + if (reverb->variation_effect_xg[note].ret != val) { + reverb->variation_effect_xg[note].ret = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x57: /* Variation Pan */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Variation Pan (%d)", val); + if (reverb->variation_effect_xg[note].pan != val) { + reverb->variation_effect_xg[note].pan = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x58: /* Send Variation To Reverb */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Send Variation To Reverb (%d)", val); + if (reverb->variation_effect_xg[note].send_reverb != val) { + reverb->variation_effect_xg[note].send_reverb = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x59: /* Send Variation To Chorus */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Send Variation To Chorus (%d)", val); + if (reverb->variation_effect_xg[note].send_chorus != val) { + reverb->variation_effect_xg[note].send_chorus = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5A: /* Variation Connection */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Variation Connection (%d)", val); + if (reverb->variation_effect_xg[note].connection != val) { + reverb->variation_effect_xg[note].connection = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5B: /* Variation Part */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Variation Part (%d)", val); + if (reverb->variation_effect_xg[note].part != val) { + reverb->variation_effect_xg[note].part = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5C: /* MW Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MW Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].mw_depth != val) { + reverb->variation_effect_xg[note].mw_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5D: /* BEND Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "BEND Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].bend_depth != val) { + reverb->variation_effect_xg[note].bend_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5E: /* CAT Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CAT Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].cat_depth != val) { + reverb->variation_effect_xg[note].cat_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5F: /* AC1 Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "AC1 Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].ac1_depth != val) { + reverb->variation_effect_xg[note].ac1_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x60: /* AC2 Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "AC2 Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].ac2_depth != val) { + reverb->variation_effect_xg[note].ac2_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x61: /* CBC1 Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CBC1 Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].cbc1_depth != val) { + reverb->variation_effect_xg[note].cbc1_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x62: /* CBC2 Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CBC2 Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].cbc2_depth != val) { + reverb->variation_effect_xg[note].cbc2_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x70: /* Variation Parameter 11 - 16 */ + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + temp = b - 0x70 + 10; + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Variation Parameter %d (%d)", temp + 1, val); + if (reverb->variation_effect_xg[note].param_lsb[temp] != val) { + reverb->variation_effect_xg[note].param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + default: + break; + } + } else if (note == 2 && msb == 40) { /* Multi EQ */ + switch(b) + { + case 0x00: /* EQ type */ + if(opt_eq_control) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ type (%d)", val); + reverb->multi_eq_xg.type = val; + reverb->set_multi_eq_type_xg(val); + reverb->recompute_multi_eq_xg(); + } + break; + case 0x01: /* EQ gain1 */ + if(opt_eq_control) { + if(val > 0x4C) {val = 0x4C;} + else if(val < 0x34) {val = 0x34;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ gain1 (%d dB)", val - 0x40); + reverb->multi_eq_xg.gain1 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x02: /* EQ frequency1 */ + if(opt_eq_control) { + if(val > 60) {val = 60;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ frequency1 (%d Hz)", (int32_t)eq_freq_table_xg[val]); + reverb->multi_eq_xg.freq1 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x03: /* EQ Q1 */ + if(opt_eq_control) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ Q1 (%f)", (double)val / 10.0); + reverb->multi_eq_xg.q1 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x04: /* EQ shape1 */ + if(opt_eq_control) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ shape1 (%d)", val); + reverb->multi_eq_xg.shape1 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x05: /* EQ gain2 */ + if(opt_eq_control) { + if(val > 0x4C) {val = 0x4C;} + else if(val < 0x34) {val = 0x34;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ gain2 (%d dB)", val - 0x40); + reverb->multi_eq_xg.gain2 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x06: /* EQ frequency2 */ + if(opt_eq_control) { + if(val > 60) {val = 60;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ frequency2 (%d Hz)", (int32_t)eq_freq_table_xg[val]); + reverb->multi_eq_xg.freq2 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x07: /* EQ Q2 */ + if(opt_eq_control) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ Q2 (%f)", (double)val / 10.0); + reverb->multi_eq_xg.q2 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x09: /* EQ gain3 */ + if(opt_eq_control) { + if(val > 0x4C) {val = 0x4C;} + else if(val < 0x34) {val = 0x34;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ gain3 (%d dB)", val - 0x40); + reverb->multi_eq_xg.gain3 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x0A: /* EQ frequency3 */ + if(opt_eq_control) { + if(val > 60) {val = 60;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ frequency3 (%d Hz)", (int32_t)eq_freq_table_xg[val]); + reverb->multi_eq_xg.freq3 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x0B: /* EQ Q3 */ + if(opt_eq_control) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ Q3 (%f)", (double)val / 10.0); + reverb->multi_eq_xg.q3 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x0D: /* EQ gain4 */ + if(opt_eq_control) { + if(val > 0x4C) {val = 0x4C;} + else if(val < 0x34) {val = 0x34;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ gain4 (%d dB)", val - 0x40); + reverb->multi_eq_xg.gain4 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x0E: /* EQ frequency4 */ + if(opt_eq_control) { + if(val > 60) {val = 60;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ frequency4 (%d Hz)", (int32_t)eq_freq_table_xg[val]); + reverb->multi_eq_xg.freq4 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x0F: /* EQ Q4 */ + if(opt_eq_control) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ Q4 (%f)", (double)val / 10.0); + reverb->multi_eq_xg.q4 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x11: /* EQ gain5 */ + if(opt_eq_control) { + if(val > 0x4C) {val = 0x4C;} + else if(val < 0x34) {val = 0x34;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ gain5 (%d dB)", val - 0x40); + reverb->multi_eq_xg.gain5 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x12: /* EQ frequency5 */ + if(opt_eq_control) { + if(val > 60) {val = 60;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ frequency5 (%d Hz)", (int32_t)eq_freq_table_xg[val]); + reverb->multi_eq_xg.freq5 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x13: /* EQ Q5 */ + if(opt_eq_control) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ Q5 (%f)", (double)val / 10.0); + reverb->multi_eq_xg.q5 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x14: /* EQ shape5 */ + if(opt_eq_control) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ shape5 (%d)", val); + reverb->multi_eq_xg.shape5 = val; + reverb->recompute_multi_eq_xg(); + } + break; + } + } else if (note == 8 && msb == 0) { /* Multi Part */ + switch(b) + { + case 0x99: /* Rcv CHANNEL, remapped from 0x04 */ + reset_controllers(ch); + all_notes_off(ch); + if (val == 0x7f) + remove_channel_layer(ch); + else { + if((ch < REDUCE_CHANNELS) != (val < REDUCE_CHANNELS)) { + channel[ch].port_select = ch < REDUCE_CHANNELS ? 1 : 0; + } + if((ch % REDUCE_CHANNELS) != (val % REDUCE_CHANNELS)) { + add_channel_layer(ch, val); + } + } + break; + case 0x06: /* Same Note Number Key On Assign */ + if(val == 0) { + channel[ch].assign_mode = 0; + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Same Note Number Key On Assign: Single (CH:%d)",ch); + } else if(val == 1) { + channel[ch].assign_mode = 2; + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Same Note Number Key On Assign: Multi (CH:%d)",ch); + } else if(val == 2) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"Same Note Number Key On Assign: Inst is not supported. (CH:%d)",ch); + } + break; + case 0x11: /* Dry Level */ + channel[ch].dry_level = val; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Dry Level (CH:%d VAL:%d)", ch, val); + break; + } + } else if ((note & 0xF0) == 0x30) { /* Drum Setup */ + note = msb; + switch(b) + { + case 0x0E: /* EG Decay1 */ + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Drum Instrument EG Decay1 (CH:%d NOTE:%d VAL:%d)", + ch, note, val); + channel[ch].drums[note]->drum_envelope_rate[EG_DECAY1] = val; + break; + case 0x0F: /* EG Decay2 */ + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Drum Instrument EG Decay2 (CH:%d NOTE:%d VAL:%d)", + ch, note, val); + channel[ch].drums[note]->drum_envelope_rate[EG_DECAY2] = val; + break; + default: + break; + } + } + return; + } +} + +/*! convert GS NRPN to vibrato rate ratio. */ +/* from 0 to 3.0. */ +double Player::gs_cnv_vib_rate(int rate) +{ + double ratio; + + if(rate == 0) { + ratio = 1.6 / 100.0; + } else if(rate == 64) { + ratio = 1.0; + } else if(rate <= 100) { + ratio = (double)rate * 1.6 / 100.0; + } else { + ratio = (double)(rate - 101) * 1.33 / 26.0 + 1.67; + } + return (1.0 / ratio); +} + +/*! convert GS NRPN to vibrato depth. */ +/* from -9.6 cents to +9.45 cents. */ +int32_t Player::gs_cnv_vib_depth(int depth) +{ + double cent; + cent = (double)(depth - 64) * 0.15; + + return (int32_t)(cent * 256.0 / 400.0); +} + +/*! convert GS NRPN to vibrato delay. */ +/* from 0 ms to 5074 ms. */ +int32_t Player::gs_cnv_vib_delay(int delay) +{ + double ms; + ms = 0.2092 * exp(0.0795 * (double)delay); + if(delay == 0) {ms = 0;} + return (int32_t)((double)playback_rate * ms * 0.001); +} + +int Player::last_rpn_addr(int ch) +{ + int lsb, msb, addr, i; + const struct rpn_tag_map_t *addrmap; + struct rpn_tag_map_t { + int addr, mask, tag; + }; + static const struct rpn_tag_map_t nrpn_addr_map[] = { + {0x0108, 0xffff, NRPN_ADDR_0108}, + {0x0109, 0xffff, NRPN_ADDR_0109}, + {0x010a, 0xffff, NRPN_ADDR_010A}, + {0x0120, 0xffff, NRPN_ADDR_0120}, + {0x0121, 0xffff, NRPN_ADDR_0121}, + {0x0130, 0xffff, NRPN_ADDR_0130}, + {0x0131, 0xffff, NRPN_ADDR_0131}, + {0x0134, 0xffff, NRPN_ADDR_0134}, + {0x0135, 0xffff, NRPN_ADDR_0135}, + {0x0163, 0xffff, NRPN_ADDR_0163}, + {0x0164, 0xffff, NRPN_ADDR_0164}, + {0x0166, 0xffff, NRPN_ADDR_0166}, + {0x1400, 0xff00, NRPN_ADDR_1400}, + {0x1500, 0xff00, NRPN_ADDR_1500}, + {0x1600, 0xff00, NRPN_ADDR_1600}, + {0x1700, 0xff00, NRPN_ADDR_1700}, + {0x1800, 0xff00, NRPN_ADDR_1800}, + {0x1900, 0xff00, NRPN_ADDR_1900}, + {0x1a00, 0xff00, NRPN_ADDR_1A00}, + {0x1c00, 0xff00, NRPN_ADDR_1C00}, + {0x1d00, 0xff00, NRPN_ADDR_1D00}, + {0x1e00, 0xff00, NRPN_ADDR_1E00}, + {0x1f00, 0xff00, NRPN_ADDR_1F00}, + {0x3000, 0xff00, NRPN_ADDR_3000}, + {0x3100, 0xff00, NRPN_ADDR_3100}, + {0x3400, 0xff00, NRPN_ADDR_3400}, + {0x3500, 0xff00, NRPN_ADDR_3500}, + {-1, -1, 0} + }; + static const struct rpn_tag_map_t rpn_addr_map[] = { + {0x0000, 0xffff, RPN_ADDR_0000}, + {0x0001, 0xffff, RPN_ADDR_0001}, + {0x0002, 0xffff, RPN_ADDR_0002}, + {0x0003, 0xffff, RPN_ADDR_0003}, + {0x0004, 0xffff, RPN_ADDR_0004}, + {0x0005, 0xffff, RPN_ADDR_0005}, + {0x7f7f, 0xffff, RPN_ADDR_7F7F}, + {0xffff, 0xffff, RPN_ADDR_FFFF}, + {-1, -1} + }; + + if (channel[ch].nrpn == -1) + return -1; + lsb = channel[ch].lastlrpn; + msb = channel[ch].lastmrpn; + if (lsb == 0xff || msb == 0xff) + return -1; + addr = (msb << 8 | lsb); + if (channel[ch].nrpn) + addrmap = nrpn_addr_map; + else + addrmap = rpn_addr_map; + for (i = 0; addrmap[i].addr != -1; i++) + if (addrmap[i].addr == (addr & addrmap[i].mask)) + return addrmap[i].tag; + return -1; +} + +void Player::update_rpn_map(int ch, int addr, int update_now) +{ + int val, drumflag, i, note; + + val = channel[ch].rpnmap[addr]; + drumflag = 0; + switch (addr) { + case NRPN_ADDR_0108: /* Vibrato Rate */ + if (opt_nrpn_vibrato) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Vibrato Rate (CH:%d VAL:%d)", ch, val - 64); + channel[ch].vibrato_ratio = gs_cnv_vib_rate(val); + } + if (update_now) + adjust_pitch(ch); + break; + case NRPN_ADDR_0109: /* Vibrato Depth */ + if (opt_nrpn_vibrato) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Vibrato Depth (CH:%d VAL:%d)", ch, val - 64); + channel[ch].vibrato_depth = gs_cnv_vib_depth(val); + } + if (update_now) + adjust_pitch(ch); + break; + case NRPN_ADDR_010A: /* Vibrato Delay */ + if (opt_nrpn_vibrato) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Vibrato Delay (CH:%d VAL:%d)", ch, val); + channel[ch].vibrato_delay = gs_cnv_vib_delay(val); + } + if (update_now) + adjust_pitch(ch); + break; + case NRPN_ADDR_0120: /* Filter Cutoff Frequency */ + if (opt_lpf_def) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Filter Cutoff (CH:%d VAL:%d)", ch, val - 64); + channel[ch].param_cutoff_freq = val - 64; + } + break; + case NRPN_ADDR_0121: /* Filter Resonance */ + if (opt_lpf_def) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Filter Resonance (CH:%d VAL:%d)", ch, val - 64); + channel[ch].param_resonance = val - 64; + } + break; + case NRPN_ADDR_0130: /* EQ BASS */ + if (opt_eq_control) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ BASS (CH:%d %.2f dB)", ch, 0.19 * (double)(val - 0x40)); + channel[ch].eq_xg.bass = val; + recompute_part_eq_xg(&(channel[ch].eq_xg)); + } + break; + case NRPN_ADDR_0131: /* EQ TREBLE */ + if (opt_eq_control) { + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ TREBLE (CH:%d %.2f dB)", ch, 0.19 * (double)(val - 0x40)); + channel[ch].eq_xg.treble = val; + recompute_part_eq_xg(&(channel[ch].eq_xg)); + } + break; + case NRPN_ADDR_0134: /* EQ BASS frequency */ + if (opt_eq_control) { + if(val < 4) {val = 4;} + else if(val > 40) {val = 40;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ BASS frequency (CH:%d %d Hz)", ch, (int32_t)eq_freq_table_xg[val]); + channel[ch].eq_xg.bass_freq = val; + recompute_part_eq_xg(&(channel[ch].eq_xg)); + } + break; + case NRPN_ADDR_0135: /* EQ TREBLE frequency */ + if (opt_eq_control) { + if(val < 28) {val = 28;} + else if(val > 58) {val = 58;} + ctl_cmsg(CMSG_INFO,VERB_NOISY,"EQ TREBLE frequency (CH:%d %d Hz)", ch, (int32_t)eq_freq_table_xg[val]); + channel[ch].eq_xg.treble_freq = val; + recompute_part_eq_xg(&(channel[ch].eq_xg)); + } + break; + case NRPN_ADDR_0163: /* Attack Time */ + if (opt_tva_attack) {set_envelope_time(ch, val, EG_ATTACK);} + break; + case NRPN_ADDR_0164: /* EG Decay Time */ + if (opt_tva_decay) {set_envelope_time(ch, val, EG_DECAY);} + break; + case NRPN_ADDR_0166: /* EG Release Time */ + if (opt_tva_release) {set_envelope_time(ch, val, EG_RELEASE);} + break; + case NRPN_ADDR_1400: /* Drum Filter Cutoff (XG) */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Drum Instrument Filter Cutoff (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_cutoff_freq = val - 64; + break; + case NRPN_ADDR_1500: /* Drum Filter Resonance (XG) */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Drum Instrument Filter Resonance (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_resonance = val - 64; + break; + case NRPN_ADDR_1600: /* Drum EG Attack Time (XG) */ + drumflag = 1; + if (opt_tva_attack) { + val = val & 0x7f; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + val -= 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Drum Instrument Attack Time (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_envelope_rate[EG_ATTACK] = val; + } + break; + case NRPN_ADDR_1700: /* Drum EG Decay Time (XG) */ + drumflag = 1; + if (opt_tva_decay) { + val = val & 0x7f; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + val -= 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Drum Instrument Decay Time (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_envelope_rate[EG_DECAY1] = + channel[ch].drums[note]->drum_envelope_rate[EG_DECAY2] = val; + } + break; + case NRPN_ADDR_1800: /* Coarse Pitch of Drum (GS) */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + channel[ch].drums[note]->coarse = val - 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Drum Instrument Pitch Coarse (CH:%d NOTE:%d VAL:%d)", ch, note, channel[ch].drums[note]->coarse); + channel[ch].pitchfactor = 0; + break; + case NRPN_ADDR_1900: /* Fine Pitch of Drum (XG) */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + channel[ch].drums[note]->fine = val - 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Drum Instrument Pitch Fine (CH:%d NOTE:%d VAL:%d)", ch, note, channel[ch].drums[note]->fine); + channel[ch].pitchfactor = 0; + break; + case NRPN_ADDR_1A00: /* Level of Drum */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Drum Instrument TVA Level (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_level = + calc_drum_tva_level(ch, note, val); + break; + case NRPN_ADDR_1C00: /* Panpot of Drum */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + if(val == 0) { + val = int_rand(128); + channel[ch].drums[note]->pan_random = 1; + } else + channel[ch].drums[note]->pan_random = 0; + channel[ch].drums[note]->drum_panning = val; + if (update_now && adjust_panning_immediately + && ! channel[ch].pan_random) + adjust_drum_panning(ch, note); + break; + case NRPN_ADDR_1D00: /* Reverb Send Level of Drum */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Reverb Send Level of Drum (CH:%d NOTE:%d VALUE:%d)", ch, note, val); + if (channel[ch].drums[note]->reverb_level != val) { + channel[ch].drum_effect_flag = 0; + } + channel[ch].drums[note]->reverb_level = val; + break; + case NRPN_ADDR_1E00: /* Chorus Send Level of Drum */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Chorus Send Level of Drum (CH:%d NOTE:%d VALUE:%d)", ch, note, val); + if (channel[ch].drums[note]->chorus_level != val) { + channel[ch].drum_effect_flag = 0; + } + channel[ch].drums[note]->chorus_level = val; + + break; + case NRPN_ADDR_1F00: /* Variation Send Level of Drum */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Delay Send Level of Drum (CH:%d NOTE:%d VALUE:%d)", ch, note, val); + if (channel[ch].drums[note]->delay_level != val) { + channel[ch].drum_effect_flag = 0; + } + channel[ch].drums[note]->delay_level = val; + break; + case NRPN_ADDR_3000: /* Drum EQ BASS */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + break; + case NRPN_ADDR_3100: /* Drum EQ TREBLE */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + break; + case NRPN_ADDR_3400: /* Drum EQ BASS frequency */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + break; + case NRPN_ADDR_3500: /* Drum EQ TREBLE frequency */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + break; + case RPN_ADDR_0000: /* Pitch bend sensitivity */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Pitch Bend Sensitivity (CH:%d VALUE:%d)", ch, val); + /* for mod2mid.c, arpeggio */ + if (channel[ch].rpnmap[RPN_ADDR_0000] > 24) + channel[ch].rpnmap[RPN_ADDR_0000] = 24; + channel[ch].pitchfactor = 0; + break; + case RPN_ADDR_0001: /* Master Fine Tuning */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Master Fine Tuning (CH:%d VALUE:%d)", ch, val); + channel[ch].pitchfactor = 0; + break; + case RPN_ADDR_0002: /* Master Coarse Tuning */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Master Coarse Tuning (CH:%d VALUE:%d)", ch, val); + channel[ch].pitchfactor = 0; + break; + case RPN_ADDR_0003: /* Tuning Program Select */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Tuning Program Select (CH:%d VALUE:%d)", ch, val); + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + case RPN_ADDR_0004: /* Tuning Bank Select */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Tuning Bank Select (CH:%d VALUE:%d)", ch, val); + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + case RPN_ADDR_0005: /* GM2: Modulation Depth Range */ + channel[ch].mod.lfo1_pitch_depth = (((int32_t)channel[ch].rpnmap[RPN_ADDR_0005] << 7) | channel[ch].rpnmap_lsb[RPN_ADDR_0005]) * 100 / 128; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Modulation Depth Range (CH:%d VALUE:%d)", ch, channel[ch].rpnmap[RPN_ADDR_0005]); + break; + case RPN_ADDR_7F7F: /* RPN reset */ + channel[ch].rpn_7f7f_flag = 1; + break; + case RPN_ADDR_FFFF: /* RPN initialize */ + /* All reset to defaults */ + channel[ch].rpn_7f7f_flag = 0; + memset(channel[ch].rpnmap, 0, sizeof(channel[ch].rpnmap)); + channel[ch].lastlrpn = channel[ch].lastmrpn = 0; + channel[ch].nrpn = 0; + channel[ch].rpnmap[RPN_ADDR_0000] = 2; + channel[ch].rpnmap[RPN_ADDR_0001] = 0x40; + channel[ch].rpnmap[RPN_ADDR_0002] = 0x40; + channel[ch].rpnmap_lsb[RPN_ADDR_0005] = 0x40; + channel[ch].rpnmap[RPN_ADDR_0005] = 0; /* +- 50 cents */ + channel[ch].pitchfactor = 0; + break; + } + drumflag = 0; + if (drumflag && midi_drumpart_change(ch, 1)) { + midi_program_change(ch, channel[ch].program); + } +} + +void Player::voice_increment(int n) +{ + int i; + for(i = 0; i < n; i++) + { + if(voices == max_voices) + break; + voice[voices].status = VOICE_FREE; + voice[voices].temper_instant = 0; + voice[voices].chorus_link = voices; + voices++; + } +} + +void Player::voice_decrement(int n) +{ + int i, j, lowest; + int32_t lv, v; + + /* decrease voice */ + for(i = 0; i < n && voices > 0; i++) + { + voices--; + if(voice[voices].status == VOICE_FREE) + continue; /* found */ + + for(j = 0; j < voices; j++) + if(voice[j].status == VOICE_FREE) + break; + if(j != voices) + { + voice[j] = voice[voices]; + continue; /* found */ + } + + /* Look for the decaying note with the lowest volume */ + lv = 0x7FFFFFFF; + lowest = -1; + for(j = 0; j <= voices; j++) + { + if(voice[j].status & ~(VOICE_ON | VOICE_DIE)) + { + v = voice[j].left_mix; + if((voice[j].panned==PANNED_MYSTERY) && + (voice[j].right_mix > v)) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + + if(lowest != -1) + { + cut_notes++; + free_voice(lowest); + voice[lowest] = voice[voices]; + } + else + lost_notes++; + } + if(upper_voices > voices) + upper_voices = voices; +} + +/* EAW -- do not throw away good notes, stop decrementing */ +void Player::voice_decrement_conservative(int n) +{ + int i, j, lowest, finalnv; + int32_t lv, v; + + /* decrease voice */ + finalnv = voices - n; + for(i = 1; i <= n && voices > 0; i++) + { + if(voice[voices-1].status == VOICE_FREE) { + voices--; + continue; /* found */ + } + + for(j = 0; j < finalnv; j++) + if(voice[j].status == VOICE_FREE) + break; + if(j != finalnv) + { + voice[j] = voice[voices-1]; + voices--; + continue; /* found */ + } + + /* Look for the decaying note with the lowest volume */ + lv = 0x7FFFFFFF; + lowest = -1; + for(j = 0; j < voices; j++) + { + if(voice[j].status & ~(VOICE_ON | VOICE_DIE) && + !(voice[j].sample->note_to_use && + ISDRUMCHANNEL(voice[j].channel))) + { + v = voice[j].left_mix; + if((voice[j].panned==PANNED_MYSTERY) && + (voice[j].right_mix > v)) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + + if(lowest != -1) + { + voices--; + cut_notes++; + free_voice(lowest); + voice[lowest] = voice[voices]; + } + else break; + } + if(upper_voices > voices) + upper_voices = voices; +} + +void Player::mix_signal(int32_t *dest, int32_t *src, int32_t count) +{ + int32_t i; + for (i = 0; i < count; i++) { + dest[i] += src[i]; + } +} + +int Player::is_insertion_effect_xg(int ch) +{ + int i; + for (i = 0; i < XG_INSERTION_EFFECT_NUM; i++) { + if (reverb->insertion_effect_xg[i].part == ch) { + return 1; + } + } + for (i = 0; i < XG_VARIATION_EFFECT_NUM; i++) { + if (reverb->variation_effect_xg[i].connection == XG_CONN_INSERTION + && reverb->variation_effect_xg[i].part == ch) { + return 1; + } + } + return 0; +} + +/* do_compute_data_midi() with DSP Effect */ +void Player::do_compute_data(int32_t count) +{ + int i, j, uv, stereo, n, ch, note; + int32_t *vpblist[MAX_CHANNELS]; + int channel_effect, channel_reverb, channel_chorus, channel_delay, channel_eq; + int32_t cnt = count * 2, rev_max_delay_out; + struct DrumPartEffect *de; + + stereo = true; + n = count * ((stereo) ? 8 : 4); /* in bytes */ + + memset(buffer_pointer, 0, n); + memset(insertion_effect_buffer, 0, n); + + if (opt_reverb_control == 3) { + rev_max_delay_out = 0x7fffffff; /* disable */ + } else { + rev_max_delay_out = REVERB_MAX_DELAY_OUT; + } + + /* are effects valid? / don't supported in mono */ + channel_reverb = (stereo && (opt_reverb_control == 1 + || opt_reverb_control == 3 + || (opt_reverb_control < 0 && opt_reverb_control & 0x80))); + channel_chorus = (stereo && opt_chorus_control && !opt_surround_chorus); + channel_delay = 0; + + /* is EQ valid? */ + channel_eq = 0; + + channel_effect = (stereo && (channel_reverb || channel_chorus + || channel_delay || channel_eq || opt_insertion_effect)); + + uv = upper_voices; + for(i = 0; i < uv; i++) { + if(voice[i].status != VOICE_FREE) { + channel[voice[i].channel].lasttime = current_sample + count; + } + } + + /* appropriate buffers for channels */ + if(channel_effect) { + int buf_index = 0; + + if(reverb_buffer == NULL) { /* allocating buffer for channel effect */ + reverb_buffer = (char *)safe_malloc(MAX_CHANNELS * AUDIO_BUFFER_SIZE * 8); + } + + for(i = 0; i < MAX_CHANNELS; i++) { + if(opt_insertion_effect && channel[i].insertion_effect) { + vpblist[i] = insertion_effect_buffer; + } else if(channel[i].eq_gs || (get_reverb_level(i) != DEFAULT_REVERB_SEND_LEVEL + && current_sample - channel[i].lasttime < rev_max_delay_out) + || channel[i].chorus_level > 0 || channel[i].delay_level > 0 + || channel[i].eq_xg.valid + || channel[i].dry_level != 127 + || (opt_drum_effect && ISDRUMCHANNEL(i)) + || is_insertion_effect_xg(i)) { + vpblist[i] = (int32_t*)(reverb_buffer + buf_index); + buf_index += n; + } else { + vpblist[i] = buffer_pointer; + } + /* clear buffers of drum-part effect */ + if (opt_drum_effect && ISDRUMCHANNEL(i)) { + for (j = 0; j < channel[i].drum_effect_num; j++) { + if (channel[i].drum_effect[j].buf != NULL) { + memset(channel[i].drum_effect[j].buf, 0, n); + } + } + } + } + + if(buf_index) {memset(reverb_buffer, 0, buf_index);} + } + + for (i = 0; i < uv; i++) { + if (voice[i].status != VOICE_FREE) { + int32_t *vpb = NULL; + int8_t flag; + + if (channel_effect) { + flag = 0; + ch = voice[i].channel; + if (opt_drum_effect && ISDRUMCHANNEL(ch)) { + make_drum_effect(ch); + note = voice[i].note; + for (j = 0; j < channel[ch].drum_effect_num; j++) { + if (channel[ch].drum_effect[j].note == note) { + vpb = channel[ch].drum_effect[j].buf; + flag = 1; + } + } + if (flag == 0) {vpb = vpblist[ch];} + } else { + vpb = vpblist[ch]; + } + } else { + vpb = buffer_pointer; + } + + if(!IS_SET_CHANNELMASK(channel_mute, voice[i].channel)) { + mixer->mix_voice(vpb, i, count); + } else { + free_voice(i); + } + + if(voice[i].timeout == 1 && voice[i].timeout < current_sample) { + free_voice(i); + } + } + } + + while(uv > 0 && voice[uv - 1].status == VOICE_FREE) {uv--;} + upper_voices = uv; + + if(play_system_mode == XG_SYSTEM_MODE && channel_effect) { /* XG */ + if (opt_insertion_effect) { /* insertion effect */ + for (i = 0; i < XG_INSERTION_EFFECT_NUM; i++) { + if (reverb->insertion_effect_xg[i].part <= MAX_CHANNELS) { + reverb->do_insertion_effect_xg(vpblist[reverb->insertion_effect_xg[i].part], cnt, &reverb->insertion_effect_xg[i]); + } + } + for (i = 0; i < XG_VARIATION_EFFECT_NUM; i++) { + if (reverb->variation_effect_xg[i].part <= MAX_CHANNELS) { + reverb->do_insertion_effect_xg(vpblist[reverb->variation_effect_xg[i].part], cnt, &reverb->variation_effect_xg[i]); + } + } + } + for(i = 0; i < MAX_CHANNELS; i++) { /* system effects */ + int32_t *p; + p = vpblist[i]; + if(p != buffer_pointer) { + if (opt_drum_effect && ISDRUMCHANNEL(i)) { + for (j = 0; j < channel[i].drum_effect_num; j++) { + de = &(channel[i].drum_effect[j]); + if (de->reverb_send > 0) { + reverb->set_ch_reverb(de->buf, cnt, de->reverb_send); + } + if (de->chorus_send > 0) { + reverb->set_ch_chorus(de->buf, cnt, de->chorus_send); + } + if (de->delay_send > 0) { + reverb->set_ch_delay(de->buf, cnt, de->delay_send); + } + mix_signal(p, de->buf, cnt); + } + } else { + if(channel_eq && channel[i].eq_xg.valid) { + reverb->do_ch_eq_xg(p, cnt, &(channel[i].eq_xg)); + } + if(channel_chorus && channel[i].chorus_level > 0) { + reverb->set_ch_chorus(p, cnt, channel[i].chorus_level); + } + if(channel_delay && channel[i].delay_level > 0) { + reverb->set_ch_delay(p, cnt, channel[i].delay_level); + } + if(channel_reverb && channel[i].reverb_level > 0 + && current_sample - channel[i].lasttime < rev_max_delay_out) { + reverb->set_ch_reverb(p, cnt, channel[i].reverb_level); + } + } + if(channel[i].dry_level == 127) { + reverb->set_dry_signal(p, cnt); + } else { + reverb->set_dry_signal_xg(p, cnt, channel[i].dry_level); + } + } + } + + if(channel_reverb) { + reverb->set_ch_reverb(buffer_pointer, cnt, DEFAULT_REVERB_SEND_LEVEL); + } + reverb->set_dry_signal(buffer_pointer, cnt); + + /* mixing signal and applying system effects */ + reverb->mix_dry_signal(buffer_pointer, cnt); + if(channel_delay) { reverb->do_variation_effect1_xg(buffer_pointer, cnt);} + if(channel_chorus) { reverb->do_ch_chorus_xg(buffer_pointer, cnt);} + if(channel_reverb) { reverb->do_ch_reverb(buffer_pointer, cnt);} + if(reverb->multi_eq_xg.valid) { reverb->do_multi_eq_xg(buffer_pointer, cnt);} + } else if(channel_effect) { /* GM & GS */ + if(opt_insertion_effect) { /* insertion effect */ + /* applying insertion effect */ + reverb->do_insertion_effect_gs(insertion_effect_buffer, cnt); + /* sending insertion effect voice to channel effect */ + reverb->set_ch_chorus(insertion_effect_buffer, cnt, reverb->insertion_effect_gs.send_chorus); + reverb->set_ch_delay(insertion_effect_buffer, cnt, reverb->insertion_effect_gs.send_delay); + reverb->set_ch_reverb(insertion_effect_buffer, cnt, reverb->insertion_effect_gs.send_reverb); + if(reverb->insertion_effect_gs.send_eq_switch && channel_eq) { + reverb->set_ch_eq_gs(insertion_effect_buffer, cnt); + } else { + reverb->set_dry_signal(insertion_effect_buffer, cnt); + } + } + + for(i = 0; i < MAX_CHANNELS; i++) { /* system effects */ + int32_t *p; + p = vpblist[i]; + if(p != buffer_pointer && p != insertion_effect_buffer) { + if (opt_drum_effect && ISDRUMCHANNEL(i)) { + for (j = 0; j < channel[i].drum_effect_num; j++) { + de = &(channel[i].drum_effect[j]); + if (de->reverb_send > 0) { + reverb->set_ch_reverb(de->buf, cnt, de->reverb_send); + } + if (de->chorus_send > 0) { + reverb->set_ch_chorus(de->buf, cnt, de->chorus_send); + } + if (de->delay_send > 0) { + reverb->set_ch_delay(de->buf, cnt, de->delay_send); + } + mix_signal(p, de->buf, cnt); + } + } else { + if(channel_chorus && channel[i].chorus_level > 0) { + reverb->set_ch_chorus(p, cnt, channel[i].chorus_level); + } + if(channel_delay && channel[i].delay_level > 0) { + reverb->set_ch_delay(p, cnt, channel[i].delay_level); + } + if(channel_reverb && channel[i].reverb_level > 0 + && current_sample - channel[i].lasttime < rev_max_delay_out) { + reverb->set_ch_reverb(p, cnt, channel[i].reverb_level); + } + } + if(channel_eq && channel[i].eq_gs) { + reverb->set_ch_eq_gs(p, cnt); + } else { + reverb->set_dry_signal(p, cnt); + } + } + } + + if(channel_reverb) { + reverb->set_ch_reverb(buffer_pointer, cnt, DEFAULT_REVERB_SEND_LEVEL); + } + reverb->set_dry_signal(buffer_pointer, cnt); + + /* mixing signal and applying system effects */ + reverb->mix_dry_signal(buffer_pointer, cnt); + if(channel_eq) { reverb->do_ch_eq_gs(buffer_pointer, cnt);} + if(channel_chorus) { reverb->do_ch_chorus(buffer_pointer, cnt);} + if(channel_delay) { reverb->do_ch_delay(buffer_pointer, cnt);} + if(channel_reverb) { reverb->do_ch_reverb(buffer_pointer, cnt);} + } + + current_sample += count; +} + +int Player::check_midi_play_end(MidiEvent *e, int len) +{ + int i, type; + + for(i = 0; i < len; i++) + { + type = e[i].type; + if(type == ME_NOTEON || type == ME_LAST || type == ME_WRD || type == ME_SHERRY) + return 0; + if(type == ME_EOT) + return i + 1; + } + return 0; +} + +int Player::midi_play_end(void) +{ + int i, rc = RC_TUNE_END; + + check_eot_flag = 0; + + if(opt_realtime_playing && current_sample == 0) + { + reset_voices(); + return RC_TUNE_END; + } + + if(upper_voices > 0) + { + int fadeout_cnt; + + rc = compute_data(playback_rate); + if(RC_IS_SKIP_FILE(rc)) + goto midi_end; + + for(i = 0; i < upper_voices; i++) + if(voice[i].status & (VOICE_ON | VOICE_SUSTAINED)) + finish_note(i); + if(opt_realtime_playing) + fadeout_cnt = 3; + else + fadeout_cnt = 6; + for(i = 0; i < fadeout_cnt && upper_voices > 0; i++) + { + rc = compute_data(playback_rate / 2); + if(RC_IS_SKIP_FILE(rc)) + goto midi_end; + } + + /* kill voices */ + kill_all_voices(); + rc = compute_data(MAX_DIE_TIME); + if(RC_IS_SKIP_FILE(rc)) + goto midi_end; + upper_voices = 0; + } + + /* clear reverb echo sound */ + reverb->init_reverb(); + for(i = 0; i < MAX_CHANNELS; i++) + { + channel[i].reverb_level = -1; + channel[i].reverb_id = -1; + make_rvid_flag = 1; + } + + /* output null sound */ + if(opt_realtime_playing) + rc = compute_data((int32_t)(playback_rate * PLAY_INTERLEAVE_SEC/2)); + else + rc = compute_data((int32_t)(playback_rate * PLAY_INTERLEAVE_SEC)); + if(RC_IS_SKIP_FILE(rc)) + goto midi_end; + + compute_data(0); /* flush buffer to device */ + + rc = aq->softFlush(); + + midi_end: + if(RC_IS_SKIP_FILE(rc)) + aq->flush(1); + + ctl_cmsg(CMSG_INFO, VERB_VERBOSE, "Playing time: ~%d seconds", + current_sample/playback_rate+2); + ctl_cmsg(CMSG_INFO, VERB_VERBOSE, "Notes cut: %d", + cut_notes); + ctl_cmsg(CMSG_INFO, VERB_VERBOSE, "Notes lost totally: %d", + lost_notes); + if(RC_IS_SKIP_FILE(rc)) + return rc; + return RC_TUNE_END; +} + +/* count=0 means flush remaining buffered data to output device, then + flush the device itself */ +int Player::compute_data(int32_t count) +{ + if (!count) + { + if (buffered_count) + { + if (aq->add(common_buffer, buffered_count) == -1) + return RC_ERROR; + } + buffer_pointer = common_buffer; + buffered_count = 0; + return RC_OK; + } + + while ((count + buffered_count) >= audio_buffer_size) + { + int i; + + do_compute_data(audio_buffer_size - buffered_count); + count -= audio_buffer_size - buffered_count; + + if (aq->add(common_buffer, audio_buffer_size) == -1) + return RC_ERROR; + + buffer_pointer = common_buffer; + buffered_count = 0; + + /* check break signals */ + + if (upper_voices == 0 && check_eot_flag && + (i = check_midi_play_end(current_event, EOT_PRESEARCH_LEN)) > 0) + { + if (i > 1) + ctl_cmsg(CMSG_INFO, VERB_VERBOSE, + "Last %d MIDI events are ignored", i - 1); + return midi_play_end(); + } + } + if (count > 0) + { + do_compute_data(count); + buffered_count += count; + buffer_pointer += count * 2; + } + return RC_OK; +} + +void Player::update_modulation_wheel(int ch) +{ + int i, uv = upper_voices; + channel[ch].pitchfactor = 0; + for(i = 0; i < uv; i++) + if(voice[i].status != VOICE_FREE && voice[i].channel == ch) + { + /* Set/Reset mod-wheel */ + voice[i].vibrato_control_counter = voice[i].vibrato_phase = 0; + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + recompute_freq(i); + recompute_voice_filter(i); + } +} + +void Player::drop_portamento(int ch) +{ + int i, uv = upper_voices; + + channel[ch].porta_control_ratio = 0; + for(i = 0; i < uv; i++) + if(voice[i].status != VOICE_FREE && + voice[i].channel == ch && + voice[i].porta_control_ratio) + { + voice[i].porta_control_ratio = 0; + recompute_freq(i); + } + channel[ch].last_note_fine = -1; +} + +void Player::update_portamento_controls(int ch) +{ + if(!channel[ch].portamento || + (channel[ch].portamento_time_msb | channel[ch].portamento_time_lsb) + == 0) + drop_portamento(ch); + else + { + double mt, dc; + int d; + + mt = midi_time_table[channel[ch].portamento_time_msb & 0x7F] * + midi_time_table2[channel[ch].portamento_time_lsb & 0x7F] * + PORTAMENTO_TIME_TUNING; + dc = playback_rate * mt; + d = (int)(1.0 / (mt * PORTAMENTO_CONTROL_RATIO)); + d++; + channel[ch].porta_control_ratio = (int)(d * dc + 0.5); + channel[ch].porta_dpb = d; + } +} + +void Player::update_portamento_time(int ch) +{ + int i, uv = upper_voices; + int dpb; + int32_t ratio; + + update_portamento_controls(ch); + dpb = channel[ch].porta_dpb; + ratio = channel[ch].porta_control_ratio; + + for(i = 0; i < uv; i++) + { + if(voice[i].status != VOICE_FREE && + voice[i].channel == ch && + voice[i].porta_control_ratio) + { + voice[i].porta_control_ratio = ratio; + voice[i].porta_dpb = dpb; + recompute_freq(i); + } + } +} + +void Player::update_legato_controls(int ch) +{ + double mt, dc; + int d; + + mt = 0.06250 * PORTAMENTO_TIME_TUNING * 0.3; + dc = playback_rate * mt; + d = (int)(1.0 / (mt * PORTAMENTO_CONTROL_RATIO)); + d++; + channel[ch].porta_control_ratio = (int)(d * dc + 0.5); + channel[ch].porta_dpb = d; +} + +int Player::play_event(MidiEvent *ev) +{ + //printf("Event type %03d, channel %03d, a %03d, b %03d, time %d\n", ev->type, ev->channel, ev->a, ev->b, ev->time); + int32_t i, j, cet; + int k, l, ch, orig_ch, port_ch, offset, layered; + + current_event = ev; + cet = MIDI_EVENT_TIME(ev); + + if (cet > current_sample) + { + int rc; + + if (midi_streaming != 0 && (cet - current_sample) * 1000 / playback_rate > stream_max_compute) + { + kill_all_voices(); + current_sample = cet; + } + + rc = compute_data(cet - current_sample); + if (rc != RC_OK) + return rc; + } + +#ifndef SUPPRESS_CHANNEL_LAYER + orig_ch = ev->channel; + layered = !IS_SYSEX_EVENT_TYPE(ev); + for (k = 0; k < MAX_CHANNELS; k += 16) { + port_ch = (orig_ch + k) % MAX_CHANNELS; + offset = port_ch & ~0xf; + for (l = offset; l < offset + 16; l++) { + if (!layered && (k || l != offset)) + continue; + if (layered) { + if (!IS_SET_CHANNELMASK(channel[l].channel_layer, port_ch) + || channel[l].port_select != (orig_ch >> 4)) + continue; + ev->channel = l; + } +#endif + ch = ev->channel; + + switch (ev->type) + { + /* MIDI Events */ + case ME_NOTEOFF: + note_off(ev); + break; + + case ME_NOTEON: + note_on(ev); + break; + + case ME_KEYPRESSURE: + adjust_pressure(ev); + break; + + case ME_PROGRAM: + midi_program_change(ch, ev->a); + break; + + case ME_CHANNEL_PRESSURE: + adjust_channel_pressure(ev); + break; + + case ME_PITCHWHEEL: + channel[ch].pitchbend = ev->a + ev->b * 128; + channel[ch].pitchfactor = 0; + /* Adjust pitch for notes already playing */ + adjust_pitch(ch); + break; + + /* Controls */ + case ME_TONE_BANK_MSB: + channel[ch].bank_msb = ev->a; + break; + + case ME_TONE_BANK_LSB: + channel[ch].bank_lsb = ev->a; + break; + + case ME_MODULATION_WHEEL: + channel[ch].mod.val = ev->a; + update_modulation_wheel(ch); + break; + + case ME_MAINVOLUME: + channel[ch].volume = ev->a; + adjust_volume(ch); + break; + + case ME_PAN: + channel[ch].panning = ev->a; + channel[ch].pan_random = 0; + if (adjust_panning_immediately && !channel[ch].pan_random) + adjust_panning(ch); + break; + + case ME_EXPRESSION: + channel[ch].expression = ev->a; + adjust_volume(ch); + break; + + case ME_SUSTAIN: + if (channel[ch].sustain == 0 && ev->a >= 64) { + update_redamper_controls(ch); + } + channel[ch].sustain = ev->a; + if (channel[ch].damper_mode == 0) { /* half-damper is not allowed. */ + if (channel[ch].sustain >= 64) { channel[ch].sustain = 127; } + else { channel[ch].sustain = 0; } + } + if (channel[ch].sustain == 0 && channel[ch].sostenuto == 0) + drop_sustain(ch); + break; + + case ME_SOSTENUTO: + channel[ch].sostenuto = (ev->a >= 64); + if (channel[ch].sustain == 0 && channel[ch].sostenuto == 0) + drop_sustain(ch); + else { update_sostenuto_controls(ch); } + break; + + case ME_LEGATO_FOOTSWITCH: + channel[ch].legato = (ev->a >= 64); + break; + + case ME_HOLD2: + break; + + case ME_BREATH: + break; + + case ME_FOOT: + break; + + case ME_BALANCE: + break; + + case ME_PORTAMENTO_TIME_MSB: + channel[ch].portamento_time_msb = ev->a; + update_portamento_time(ch); + break; + + case ME_PORTAMENTO_TIME_LSB: + channel[ch].portamento_time_lsb = ev->a; + update_portamento_time(ch); + break; + + case ME_PORTAMENTO: + channel[ch].portamento = (ev->a >= 64); + if (!channel[ch].portamento) + drop_portamento(ch); + break; + + case ME_SOFT_PEDAL: + if (opt_lpf_def) { + channel[ch].soft_pedal = ev->a; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Soft Pedal (CH:%d VAL:%d)", ch, channel[ch].soft_pedal); + } + break; + + case ME_HARMONIC_CONTENT: + if (opt_lpf_def) { + channel[ch].param_resonance = ev->a - 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Harmonic Content (CH:%d VAL:%d)", ch, channel[ch].param_resonance); + } + break; + + case ME_BRIGHTNESS: + if (opt_lpf_def) { + channel[ch].param_cutoff_freq = ev->a - 64; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Brightness (CH:%d VAL:%d)", ch, channel[ch].param_cutoff_freq); + } + break; + + case ME_DATA_ENTRY_MSB: + if (channel[ch].rpn_7f7f_flag) /* disable */ + break; + if ((i = last_rpn_addr(ch)) >= 0) + { + channel[ch].rpnmap[i] = ev->a; + update_rpn_map(ch, i, 1); + } + break; + + case ME_DATA_ENTRY_LSB: + if (channel[ch].rpn_7f7f_flag) /* disable */ + break; + if ((i = last_rpn_addr(ch)) >= 0) + { + channel[ch].rpnmap_lsb[i] = ev->a; + } + break; + + case ME_REVERB_EFFECT: + if (opt_reverb_control) { + if (ISDRUMCHANNEL(ch) && get_reverb_level(ch) != ev->a) { channel[ch].drum_effect_flag = 0; } + set_reverb_level(ch, ev->a); + } + break; + + case ME_CHORUS_EFFECT: + if (opt_chorus_control) + { + if (opt_chorus_control == 1) { + if (ISDRUMCHANNEL(ch) && channel[ch].chorus_level != ev->a) { channel[ch].drum_effect_flag = 0; } + channel[ch].chorus_level = ev->a; + } + else { + channel[ch].chorus_level = -opt_chorus_control; + } + if (ev->a) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Chorus Send (CH:%d LEVEL:%d)", ch, ev->a); + } + } + break; + + case ME_TREMOLO_EFFECT: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Tremolo Send (CH:%d LEVEL:%d)", ch, ev->a); + break; + + case ME_CELESTE_EFFECT: + if (opt_delay_control) { + if (ISDRUMCHANNEL(ch) && channel[ch].delay_level != ev->a) { channel[ch].drum_effect_flag = 0; } + channel[ch].delay_level = ev->a; + if (play_system_mode == XG_SYSTEM_MODE) { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Variation Send (CH:%d LEVEL:%d)", ch, ev->a); + } + else { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Delay Send (CH:%d LEVEL:%d)", ch, ev->a); + } + } + break; + + case ME_ATTACK_TIME: + if (!opt_tva_attack) { break; } + set_envelope_time(ch, ev->a, EG_ATTACK); + break; + + case ME_RELEASE_TIME: + if (!opt_tva_release) { break; } + set_envelope_time(ch, ev->a, EG_RELEASE); + break; + + case ME_PHASER_EFFECT: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Phaser Send (CH:%d LEVEL:%d)", ch, ev->a); + break; + + case ME_RPN_INC: + if (channel[ch].rpn_7f7f_flag) /* disable */ + break; + if ((i = last_rpn_addr(ch)) >= 0) + { + if (channel[ch].rpnmap[i] < 127) + channel[ch].rpnmap[i]++; + update_rpn_map(ch, i, 1); + } + break; + + case ME_RPN_DEC: + if (channel[ch].rpn_7f7f_flag) /* disable */ + break; + if ((i = last_rpn_addr(ch)) >= 0) + { + if (channel[ch].rpnmap[i] > 0) + channel[ch].rpnmap[i]--; + update_rpn_map(ch, i, 1); + } + break; + + case ME_NRPN_LSB: + channel[ch].lastlrpn = ev->a; + channel[ch].nrpn = 1; + break; + + case ME_NRPN_MSB: + channel[ch].lastmrpn = ev->a; + channel[ch].nrpn = 1; + break; + + case ME_RPN_LSB: + channel[ch].lastlrpn = ev->a; + channel[ch].nrpn = 0; + break; + + case ME_RPN_MSB: + channel[ch].lastmrpn = ev->a; + channel[ch].nrpn = 0; + break; + + case ME_ALL_SOUNDS_OFF: + all_sounds_off(ch); + break; + + case ME_RESET_CONTROLLERS: + reset_controllers(ch); + break; + + case ME_ALL_NOTES_OFF: + all_notes_off(ch); + break; + + case ME_MONO: + channel[ch].mono = 1; + all_notes_off(ch); + break; + + case ME_POLY: + channel[ch].mono = 0; + all_notes_off(ch); + break; + + /* TiMidity Extensionals */ + case ME_RANDOM_PAN: + channel[ch].panning = int_rand(128); + channel[ch].pan_random = 1; + if (adjust_panning_immediately && !channel[ch].pan_random) + adjust_panning(ch); + break; + + case ME_SET_PATCH: + i = channel[ch].special_sample = current_event->a; + instruments->setSpecialPatchOffset(i, 0); + break; + + case ME_TEMPO: + current_play_tempo = ch + ev->b * 256 + ev->a * 65536; + break; + + case ME_CHORUS_TEXT: + case ME_LYRIC: + case ME_MARKER: + case ME_INSERT_TEXT: + case ME_TEXT: + case ME_KARAOKE_LYRIC: + case ME_GSLCD: + break; + + case ME_MASTER_VOLUME: + master_volume_ratio = (int32_t)ev->a + 256 * (int32_t)ev->b; + adjust_master_volume(); + break; + + case ME_RESET: + change_system_mode(ev->a); + reset_midi(1); + break; + + case ME_PATCH_OFFS: + i = channel[ch].special_sample; + instruments->setSpecialPatchOffset(i, current_event->a | (256 * current_event->b)); + break; + + case ME_WRD: + break; + + case ME_SHERRY: + break; + + case ME_DRUMPART: + if (midi_drumpart_change(ch, current_event->a)) + { + /* Update bank information */ + midi_program_change(ch, channel[ch].program); + } + break; + + case ME_KEYSHIFT: + i = (int)current_event->a - 0x40; + if (i != channel[ch].key_shift) + { + all_sounds_off(ch); + channel[ch].key_shift = (int8_t)i; + } + break; + + case ME_KEYSIG: + if (opt_init_keysig != 8) + break; + current_keysig = current_event->a + current_event->b * 16; + if (opt_force_keysig != 8) { + i = current_keysig - ((current_keysig < 8) ? 0 : 16), j = 0; + while (i != opt_force_keysig && i != opt_force_keysig + 12) + i += (i > 0) ? -5 : 7, j++; + while (abs(j - note_key_offset) > 7) + j += (j > note_key_offset) ? -12 : 12; + if (abs(j - key_adjust) >= 12) + j += (j > key_adjust) ? -12 : 12; + note_key_offset = j; + kill_all_voices(); + } + i = current_keysig + ((current_keysig < 8) ? 7 : -9), j = 0; + while (i != 7) + i += (i < 7) ? 5 : -7, j++; + j += note_key_offset, j -= floor(j / 12.0) * 12; + current_freq_table = j; + break; + + case ME_MASTER_TUNING: + set_master_tuning((ev->b << 8) | ev->a); + adjust_all_pitch(); + break; + + case ME_SCALE_TUNING: + recache->resamp_cache_refer_alloff(ch, current_event->time); + channel[ch].scale_tuning[current_event->a] = current_event->b; + adjust_pitch(ch); + break; + + case ME_BULK_TUNING_DUMP: + set_single_note_tuning(ch, current_event->a, current_event->b, 0); + break; + + case ME_SINGLE_NOTE_TUNING: + set_single_note_tuning(ch, current_event->a, current_event->b, 1); + break; + + case ME_TEMPER_KEYSIG: + current_temper_keysig = (current_event->a + 8) % 32 - 8; + temper_adj = ((current_event->a + 8) & 0x20) ? 1 : 0; + i = current_temper_keysig + ((current_temper_keysig < 8) ? 7 : -9); + j = 0; + while (i != 7) + i += (i < 7) ? 5 : -7, j++; + j += note_key_offset, j -= floor(j / 12.0) * 12; + current_temper_freq_table = j; + if (current_event->b) + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + + case ME_TEMPER_TYPE: + channel[ch].temper_type = current_event->a; + if (temper_type_mute) { + if (temper_type_mute & (1 << (current_event->a + - ((current_event->a >= 0x40) ? 0x3c : 0)))) { + SET_CHANNELMASK(channel_mute, ch); + } + else { + UNSET_CHANNELMASK(channel_mute, ch); + } + } + if (current_event->b) + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + + case ME_MASTER_TEMPER_TYPE: + for (i = 0; i < MAX_CHANNELS; i++) { + channel[i].temper_type = current_event->a; + } + if (temper_type_mute) { + if (temper_type_mute & (1 << (current_event->a + - ((current_event->a >= 0x40) ? 0x3c : 0)))) { + FILL_CHANNELMASK(channel_mute); + } + else { + CLEAR_CHANNELMASK(channel_mute); + } + } + if (current_event->b) + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + + case ME_USER_TEMPER_ENTRY: + set_user_temper_entry(ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_LSB: + process_sysex_event(ME_SYSEX_LSB, ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_MSB: + process_sysex_event(ME_SYSEX_MSB, ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_GS_LSB: + process_sysex_event(ME_SYSEX_GS_LSB, ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_GS_MSB: + process_sysex_event(ME_SYSEX_GS_MSB, ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_XG_LSB: + process_sysex_event(ME_SYSEX_XG_LSB, ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_XG_MSB: + process_sysex_event(ME_SYSEX_XG_MSB, ch, current_event->a, current_event->b); + break; + + case ME_NOTE_STEP: + break; + + case ME_EOT: + return midi_play_end(); + } +#ifndef SUPPRESS_CHANNEL_LAYER + } + } + ev->channel = orig_ch; +#endif + + return RC_OK; +} + +void Player::set_master_tuning(int tune) +{ + if (tune & 0x4000) /* 1/8192 semitones + 0x2000 | 0x4000 */ + tune = (tune & 0x3FFF) - 0x2000; + else if (tune & 0x8000) /* 1 semitones | 0x8000 */ + tune = ((tune & 0x7F) - 0x40) << 13; + else /* millisemitones + 0x400 */ + tune = (((tune - 0x400) << 13) + 500) / 1000; + master_tuning = tune; +} + +void Player::set_single_note_tuning(int part, int a, int b, int rt) +{ + static int tp; /* tuning program number */ + static int kn; /* MIDI key number */ + static int st; /* the nearest equal-tempered semitone */ + double f, fst; /* fraction of semitone */ + int i; + + switch (part) { + case 0: + tp = a; + break; + case 1: + kn = a, st = b; + break; + case 2: + if (st == 0x7f && a == 0x7f && b == 0x7f) /* no change */ + break; + f = 440 * pow(2.0, (st - 69) / 12.0); + fst = pow(2.0, (a << 7 | b) / 196608.0); + freq_table_tuning[tp][kn] = f * fst * 1000 + 0.5; + if (rt) + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + } +} + +void Player::set_user_temper_entry(int part, int a, int b) +{ + static int tp; /* temperament program number */ + static int ll; /* number of formula */ + static int fh, fl; /* applying pitch bit mask (forward) */ + static int bh, bl; /* applying pitch bit mask (backward) */ + static int aa, bb; /* fraction (aa/bb) */ + static int cc, dd; /* power (cc/dd)^(ee/ff) */ + static int ee, ff; + static int ifmax, ibmax, count; + static double rf[11], rb[11]; + int i, j, k, l, n, m; + double ratio[12], f, sc; + + switch (part) { + case 0: + for (i = 0; i < 11; i++) + rf[i] = rb[i] = 1; + ifmax = ibmax = 0; + count = 0; + tp = a, ll = b; + break; + case 1: + fh = a, fl = b; + break; + case 2: + bh = a, bl = b; + break; + case 3: + aa = a, bb = b; + break; + case 4: + cc = a, dd = b; + break; + case 5: + ee = a, ff = b; + for (i = 0; i < 11; i++) { + if (((fh & 0xf) << 7 | fl) & 1 << i) { + rf[i] *= (double) aa / bb + * pow((double) cc / dd, (double) ee / ff); + if (ifmax < i + 1) + ifmax = i + 1; + } + if (((bh & 0xf) << 7 | bl) & 1 << i) { + rb[i] *= (double) aa / bb + * pow((double) cc / dd, (double) ee / ff); + if (ibmax < i + 1) + ibmax = i + 1; + } + } + if (++count < ll) + break; + ratio[0] = 1; + for (i = n = m = 0; i < ifmax; i++, m = n) { + n += (n > 4) ? -5 : 7; + ratio[n] = ratio[m] * rf[i]; + if (ratio[n] > 2) + ratio[n] /= 2; + } + for (i = n = m = 0; i < ibmax; i++, m = n) { + n += (n > 6) ? -7 : 5; + ratio[n] = ratio[m] / rb[i]; + if (ratio[n] < 1) + ratio[n] *= 2; + } + sc = 27 / ratio[9] / 16; /* syntonic comma */ + for (i = 0; i < 12; i++) + for (j = -1; j < 11; j++) { + f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5); + for (k = 0; k < 12; k++) { + l = i + j * 12 + k; + if (l < 0 || l >= 128) + continue; + if (! (fh & 0x40)) { /* major */ + freq_table_user[tp][i][l] = + f * ratio[k] * 1000 + 0.5; + freq_table_user[tp][i + 36][l] = + f * ratio[k] * sc * 1000 + 0.5; + } + if (! (bh & 0x40)) { /* minor */ + freq_table_user[tp][i + 12][l] = + f * ratio[k] * sc * 1000 + 0.5; + freq_table_user[tp][i + 24][l] = + f * ratio[k] * 1000 + 0.5; + } + } + } + break; + } +} + + + + +struct midi_file_info *Player::new_midi_file_info() +{ + struct midi_file_info *p = &midifileinfo; + + /* Initialize default members */ + memset(p, 0, sizeof(struct midi_file_info)); + p->hdrsiz = -1; + p->format = -1; + p->tracks = -1; + p->divisions = -1; + p->time_sig_n = p->time_sig_d = -1; + p->samples = -1; + p->max_channel = -1; + COPY_CHANNELMASK(p->drumchannels, default_drumchannels); + COPY_CHANNELMASK(p->drumchannel_mask, default_drumchannel_mask); + return p; +} + +struct midi_file_info *Player::get_midi_file_info(const char *filename, int newp) +{ + return &midifileinfo; +} + + + +/* + * For MIDI stream player. + */ +void Player::playmidi_stream_init(void) +{ + int i; + static int first = 1; + + note_key_offset = key_adjust; + midi_time_ratio = tempo_adjust; + CLEAR_CHANNELMASK(channel_mute); + if (temper_type_mute & 1) + FILL_CHANNELMASK(channel_mute); + if(first) + { + first = 0; + init_mblock(&playmidi_pool); + current_file_info = get_midi_file_info("TiMidity", 1); + midi_streaming=1; + } + else + reuse_mblock(&playmidi_pool); + + /* Fill in current_file_info */ + current_file_info->readflag = 1; + current_file_info->seq_name = safe_strdup("TiMidity server"); + current_file_info->karaoke_title = current_file_info->first_text = NULL; + current_file_info->hdrsiz = 0; + current_file_info->format = 0; + current_file_info->tracks = 0; + current_file_info->divisions = 192; /* ?? */ + current_file_info->time_sig_n = 4; /* 4/ */ + current_file_info->time_sig_d = 4; /* /4 */ + current_file_info->time_sig_c = 24; /* clock */ + current_file_info->time_sig_b = 8; /* q.n. */ + current_file_info->samples = 0; + current_file_info->max_channel = MAX_CHANNELS; + current_file_info->compressed = 0; + + current_play_tempo = 500000; + check_eot_flag = 0; + + /* Setup default drums */ + COPY_CHANNELMASK(current_file_info->drumchannels, default_drumchannels); + COPY_CHANNELMASK(current_file_info->drumchannel_mask, default_drumchannel_mask); + for(i = 0; i < MAX_CHANNELS; i++) + memset(channel[i].drums, 0, sizeof(channel[i].drums)); + change_system_mode(DEFAULT_SYSTEM_MODE); + reset_midi(0); + + playmidi_tmr_reset(); +} + +void Player::playmidi_tmr_reset(void) +{ + int i; + + aq->flush(0); + current_sample = 0; + buffered_count = 0; + buffer_pointer = common_buffer; + for(i = 0; i < MAX_CHANNELS; i++) + channel[i].lasttime = 0; +} + + +/*! initialize Part EQ (XG) */ +void Player::init_part_eq_xg(struct part_eq_xg *p) +{ + p->bass = 0x40; + p->treble = 0x40; + p->bass_freq = 0x0C; + p->treble_freq = 0x36; + p->valid = 0; +} + +/*! recompute Part EQ (XG) */ +void Player::recompute_part_eq_xg(struct part_eq_xg *p) +{ + int8_t vbass, vtreble; + + if(p->bass_freq >= 4 && p->bass_freq <= 40 && p->bass != 0x40) { + vbass = 1; + p->basss.q = 0.7; + p->basss.freq = eq_freq_table_xg[p->bass_freq]; + if(p->bass == 0) {p->basss.gain = -12.0;} + else {p->basss.gain = 0.19 * (double)(p->bass - 0x40);} + reverb->calc_filter_shelving_low(&(p->basss)); + } else {vbass = 0;} + if(p->treble_freq >= 28 && p->treble_freq <= 58 && p->treble != 0x40) { + vtreble = 1; + p->trebles.q = 0.7; + p->trebles.freq = eq_freq_table_xg[p->treble_freq]; + if(p->treble == 0) {p->trebles.gain = -12.0;} + else {p->trebles.gain = 0.19 * (double)(p->treble - 0x40);} + reverb->calc_filter_shelving_high(&(p->trebles)); + } else {vtreble = 0;} + p->valid = vbass || vtreble; +} + +void Player::init_midi_controller(midi_controller *p) +{ + p->val = 0; + p->pitch = 0; + p->cutoff = 0; + p->amp = 0.0; + p->lfo1_rate = p->lfo2_rate = p->lfo1_tva_depth = p->lfo2_tva_depth = 0; + p->lfo1_pitch_depth = p->lfo2_pitch_depth = p->lfo1_tvf_depth = p->lfo2_tvf_depth = 0; + p->variation_control_depth = p->insertion_control_depth = 0; +} + +float Player::get_midi_controller_amp(midi_controller *p) +{ + return (1.0 + (float)p->val * (1.0f / 127.0f) * p->amp); +} + +float Player::get_midi_controller_filter_cutoff(midi_controller *p) +{ + return ((float)p->val * (1.0f / 127.0f) * (float)p->cutoff); +} + +float Player::get_midi_controller_filter_depth(midi_controller *p) +{ + return ((float)p->val * (1.0f / 127.0f) * (float)p->lfo1_tvf_depth); +} + +int32_t Player::get_midi_controller_pitch(midi_controller *p) +{ + return ((int32_t)(p->val * p->pitch) << 6); +} + +int16_t Player::get_midi_controller_pitch_depth(midi_controller *p) +{ + return (int16_t)((float)p->val * (float)p->lfo1_pitch_depth * (1.0f / 127.0f * 256.0 / 400.0)); +} + +int16_t Player::get_midi_controller_amp_depth(midi_controller *p) +{ + return (int16_t)((float)p->val * (float)p->lfo1_tva_depth * (1.0f / 127.0f * 256.0)); +} + +void Player::init_rx(int ch) +{ + channel[ch].rx = 0xFFFFFFFF; /* all on */ +} + +void Player::set_rx(int ch, int32_t rx, int flag) +{ + if(ch > MAX_CHANNELS) {return;} + if(flag) {channel[ch].rx |= rx;} + else {channel[ch].rx &= ~rx;} +} + +void Player::init_rx_drum(struct DrumParts *p) +{ + p->rx = 0xFFFFFFFF; /* all on */ +} + +void Player::set_rx_drum(struct DrumParts *p, int32_t rx, int flag) +{ + if(flag) {p->rx |= rx;} + else {p->rx &= ~rx;} +} + +int32_t Player::get_rx_drum(struct DrumParts *p, int32_t rx) +{ + return (p->rx & rx); +} + +Instrument *Player::play_midi_load_instrument(int dr, int bk, int prog) +{ + bool load_success; + // The inner workings of this function which alters the instrument data has been put into the Instruments class. + auto instr = instruments->play_midi_load_instrument(dr, bk, prog, &load_success); + if (load_success) + aq->add(NULL, 0); /* Update software buffer */ + return instr; +} + +void Player::change_system_mode(int mode) +{ + pan_table = sc_pan_table; + switch (mode) + { + case GM_SYSTEM_MODE: + if (play_system_mode == DEFAULT_SYSTEM_MODE) + { + play_system_mode = GM_SYSTEM_MODE; + vol_table = def_vol_table; + } + break; + case GM2_SYSTEM_MODE: + play_system_mode = GM2_SYSTEM_MODE; + vol_table = def_vol_table; + pan_table = gm2_pan_table; + break; + case GS_SYSTEM_MODE: + play_system_mode = GS_SYSTEM_MODE; + vol_table = gs_vol_table; + break; + case XG_SYSTEM_MODE: + if (play_system_mode != XG_SYSTEM_MODE) { reverb->init_all_effect_xg(); } + play_system_mode = XG_SYSTEM_MODE; + vol_table = xg_vol_table; + break; + default: + play_system_mode = DEFAULT_SYSTEM_MODE; + vol_table = def_vol_table; + break; + } +} + + +/*! initialize channel layers. */ +void Player::init_channel_layer(int ch) +{ + if (ch >= MAX_CHANNELS) + return; + CLEAR_CHANNELMASK(channel[ch].channel_layer); + SET_CHANNELMASK(channel[ch].channel_layer, ch); + channel[ch].port_select = ch >> 4; +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/playmidi.h b/src/sound/timidity++/playmidi.h new file mode 100644 index 0000000000..2ce0b6f51f --- /dev/null +++ b/src/sound/timidity++/playmidi.h @@ -0,0 +1,759 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + playmidi.h +*/ + +#ifndef ___PLAYMIDI_H_ +#define ___PLAYMIDI_H_ +#include + +namespace TimidityPlus +{ + +struct AlternateAssign; + +struct MidiEvent +{ + int32_t time; + uint8_t type, channel, a, b; +}; + +#define REDUCE_CHANNELS 16 +#define REVERB_MAX_DELAY_OUT (4 * playback_rate) + +#define SYSEX_TAG 0xFF + +/* Midi events */ +enum midi_event_t +{ + ME_NONE, + + /* MIDI events */ + ME_NOTEOFF, + ME_NOTEON, + ME_KEYPRESSURE, + ME_PROGRAM, + ME_CHANNEL_PRESSURE, + ME_PITCHWHEEL, + + /* Controls */ + ME_TONE_BANK_MSB, + ME_TONE_BANK_LSB, + ME_MODULATION_WHEEL, + ME_BREATH, + ME_FOOT, + ME_MAINVOLUME, + ME_BALANCE, + ME_PAN, + ME_EXPRESSION, + ME_SUSTAIN, + ME_PORTAMENTO_TIME_MSB, + ME_PORTAMENTO_TIME_LSB, + ME_PORTAMENTO, + ME_PORTAMENTO_CONTROL, + ME_DATA_ENTRY_MSB, + ME_DATA_ENTRY_LSB, + ME_SOSTENUTO, + ME_SOFT_PEDAL, + ME_LEGATO_FOOTSWITCH, + ME_HOLD2, + ME_HARMONIC_CONTENT, + ME_RELEASE_TIME, + ME_ATTACK_TIME, + ME_BRIGHTNESS, + ME_REVERB_EFFECT, + ME_TREMOLO_EFFECT, + ME_CHORUS_EFFECT, + ME_CELESTE_EFFECT, + ME_PHASER_EFFECT, + ME_RPN_INC, + ME_RPN_DEC, + ME_NRPN_LSB, + ME_NRPN_MSB, + ME_RPN_LSB, + ME_RPN_MSB, + ME_ALL_SOUNDS_OFF, + ME_RESET_CONTROLLERS, + ME_ALL_NOTES_OFF, + ME_MONO, + ME_POLY, + + /* TiMidity Extensionals */ + ME_MASTER_TUNING, /* Master tuning */ + ME_SCALE_TUNING, /* Scale tuning */ + ME_BULK_TUNING_DUMP, /* Bulk tuning dump */ + ME_SINGLE_NOTE_TUNING, /* Single-note tuning */ + ME_RANDOM_PAN, + ME_SET_PATCH, /* Install special instrument */ + ME_DRUMPART, + ME_KEYSHIFT, + ME_PATCH_OFFS, /* Change special instrument sample position + * Channel, LSB, MSB + */ + + /* Global channel events */ + ME_TEMPO, + ME_CHORUS_TEXT, + ME_LYRIC, + ME_GSLCD, /* GS L.C.D. Exclusive message event */ + ME_MARKER, + ME_INSERT_TEXT, /* for SC */ + ME_TEXT, + ME_KARAOKE_LYRIC, /* for KAR format */ + ME_MASTER_VOLUME, + ME_RESET, /* Reset and change system mode */ + ME_NOTE_STEP, + + ME_TIMESIG, /* Time signature */ + ME_KEYSIG, /* Key signature */ + ME_TEMPER_KEYSIG, /* Temperament key signature */ + ME_TEMPER_TYPE, /* Temperament type */ + ME_MASTER_TEMPER_TYPE, /* Master temperament type */ + ME_USER_TEMPER_ENTRY, /* User-defined temperament entry */ + + ME_SYSEX_LSB, /* Universal system exclusive message (LSB) */ + ME_SYSEX_MSB, /* Universal system exclusive message (MSB) */ + ME_SYSEX_GS_LSB, /* GS system exclusive message (LSB) */ + ME_SYSEX_GS_MSB, /* GS system exclusive message (MSB) */ + ME_SYSEX_XG_LSB, /* XG system exclusive message (LSB) */ + ME_SYSEX_XG_MSB, /* XG system exclusive message (MSB) */ + + ME_WRD, /* for MIMPI WRD tracer */ + ME_SHERRY, /* for Sherry WRD tracer */ + ME_BARMARKER, + ME_STEP, /* for Metronome */ + + ME_LAST = 254, /* Last sequence of MIDI list. + * This event is reserved for realtime player. + */ + ME_EOT = 255 /* End of MIDI. Finish to play */ +}; + +#define GLOBAL_CHANNEL_EVENT_TYPE(type) \ + ((type) == ME_NONE || (type) >= ME_TEMPO) + +enum rpn_data_address_t /* NRPN/RPN */ +{ + NRPN_ADDR_0108, + NRPN_ADDR_0109, + NRPN_ADDR_010A, + NRPN_ADDR_0120, + NRPN_ADDR_0121, + NRPN_ADDR_0130, + NRPN_ADDR_0131, + NRPN_ADDR_0134, + NRPN_ADDR_0135, + NRPN_ADDR_0163, + NRPN_ADDR_0164, + NRPN_ADDR_0166, + NRPN_ADDR_1400, + NRPN_ADDR_1500, + NRPN_ADDR_1600, + NRPN_ADDR_1700, + NRPN_ADDR_1800, + NRPN_ADDR_1900, + NRPN_ADDR_1A00, + NRPN_ADDR_1C00, + NRPN_ADDR_1D00, + NRPN_ADDR_1E00, + NRPN_ADDR_1F00, + NRPN_ADDR_3000, + NRPN_ADDR_3100, + NRPN_ADDR_3400, + NRPN_ADDR_3500, + RPN_ADDR_0000, + RPN_ADDR_0001, + RPN_ADDR_0002, + RPN_ADDR_0003, + RPN_ADDR_0004, + RPN_ADDR_0005, + RPN_ADDR_7F7F, + RPN_ADDR_FFFF, + RPN_MAX_DATA_ADDR +}; + +#define RX_PITCH_BEND (1<<0) +#define RX_CH_PRESSURE (1<<1) +#define RX_PROGRAM_CHANGE (1<<2) +#define RX_CONTROL_CHANGE (1<<3) +#define RX_POLY_PRESSURE (1<<4) +#define RX_NOTE_MESSAGE (1<<5) +#define RX_RPN (1<<6) +#define RX_NRPN (1<<7) +#define RX_MODULATION (1<<8) +#define RX_VOLUME (1<<9) +#define RX_PANPOT (1<<10) +#define RX_EXPRESSION (1<<11) +#define RX_HOLD1 (1<<12) +#define RX_PORTAMENTO (1<<13) +#define RX_SOSTENUTO (1<<14) +#define RX_SOFT (1<<15) +#define RX_NOTE_ON (1<<16) +#define RX_NOTE_OFF (1<<17) +#define RX_BANK_SELECT (1<<18) +#define RX_BANK_SELECT_LSB (1<<19) + +enum { + EG_ATTACK = 0, + EG_DECAY = 2, + EG_DECAY1 = 1, + EG_DECAY2 = 2, + EG_RELEASE = 3, + EG_NULL = 5, + EG_GUS_ATTACK = 0, + EG_GUS_DECAY = 1, + EG_GUS_SUSTAIN = 2, + EG_GUS_RELEASE1 = 3, + EG_GUS_RELEASE2 = 4, + EG_GUS_RELEASE3 = 5, + EG_SF_ATTACK = 0, + EG_SF_HOLD = 1, + EG_SF_DECAY = 2, + EG_SF_RELEASE = 3, +}; + +#ifndef PART_EQ_XG +#define PART_EQ_XG +/*! shelving filter */ +struct filter_shelving +{ + double freq, gain, q; + int32_t x1l, x2l, y1l, y2l, x1r, x2r, y1r, y2r; + int32_t a1, a2, b0, b1, b2; +}; + +/*! Part EQ (XG) */ +struct part_eq_xg { + int8_t bass, treble, bass_freq, treble_freq; + filter_shelving basss, trebles; + int8_t valid; +}; +#endif /* PART_EQ_XG */ + +struct midi_controller +{ + int16_t val; + int8_t pitch; /* in +-semitones [-24, 24] */ + int16_t cutoff; /* in +-cents [-9600, 9600] */ + float amp; /* [-1.0, 1.0] */ + /* in GS, LFO1 means LFO for voice 1, LFO2 means LFO for voice2. + LFO2 is not supported. */ + float lfo1_rate, lfo2_rate; /* in +-Hz [-10.0, 10.0] */ + int16_t lfo1_pitch_depth, lfo2_pitch_depth; /* in cents [0, 600] */ + int16_t lfo1_tvf_depth, lfo2_tvf_depth; /* in cents [0, 2400] */ + float lfo1_tva_depth, lfo2_tva_depth; /* [0, 1.0] */ + int8_t variation_control_depth, insertion_control_depth; +}; + +struct DrumPartEffect +{ + int32_t *buf; + int8_t note, reverb_send, chorus_send, delay_send; +}; + +struct DrumParts +{ + int8_t drum_panning; + int32_t drum_envelope_rate[6]; /* drum instrument envelope */ + int8_t pan_random; /* flag for drum random pan */ + float drum_level; + + int8_t chorus_level, reverb_level, delay_level, coarse, fine, + play_note, drum_cutoff_freq, drum_resonance; + int32_t rx; +}; + +struct Channel +{ + int8_t bank_msb, bank_lsb, bank, program, volume, + expression, sustain, panning, mono, portamento, + key_shift, loop_timeout; + + /* chorus, reverb... Coming soon to a 300-MHz, eight-way superscalar + processor near you */ + int8_t chorus_level, /* Chorus level */ + reverb_level; /* Reverb level. */ + int reverb_id; /* Reverb ID used for reverb optimize implementation + >=0 reverb_level + -1: DEFAULT_REVERB_SEND_LEVEL + */ + int8_t delay_level; /* Delay Send Level */ + int8_t eq_gs; /* EQ ON/OFF (GS) */ + int8_t insertion_effect; + + /* Special sample ID. (0 means Normal sample) */ + uint8_t special_sample; + + int pitchbend; + + double + pitchfactor; /* precomputed pitch bend factor to save some fdiv's */ + + /* For portamento */ + uint8_t portamento_time_msb, portamento_time_lsb; + int porta_control_ratio, porta_dpb; + int32_t last_note_fine; + + /* For Drum part */ + struct DrumParts *drums[128]; + + /* For NRPN Vibrato */ + int32_t vibrato_depth, vibrato_delay; + float vibrato_ratio; + + /* For RPN */ + uint8_t rpnmap[RPN_MAX_DATA_ADDR]; /* pseudo RPN address map */ + uint8_t rpnmap_lsb[RPN_MAX_DATA_ADDR]; + uint8_t lastlrpn, lastmrpn; + int8_t nrpn; /* 0:RPN, 1:NRPN, -1:Undefined */ + int rpn_7f7f_flag; /* Boolean flag used for RPN 7F/7F */ + + /* For channel envelope */ + int32_t envelope_rate[6]; /* for Envelope Generator in mix.c + * 0: value for attack rate + * 2: value for decay rate + * 3: value for release rate + */ + + int mapID; /* Program map ID */ + AlternateAssign *altassign; /* Alternate assign patch table */ + int32_t lasttime; /* Last sample time of computed voice on this channel */ + + /* flag for random pan */ + int pan_random; + + /* for Voice LPF / Resonance */ + int8_t param_resonance, param_cutoff_freq; /* -64 ~ 63 */ + float cutoff_freq_coef, resonance_dB; + + int8_t velocity_sense_depth, velocity_sense_offset; + + int8_t scale_tuning[12], prev_scale_tuning; + int8_t temper_type; + + int8_t soft_pedal; + int8_t sostenuto; + int8_t damper_mode; + + int8_t tone_map0_number; + double pitch_offset_fine; /* in Hz */ + int8_t assign_mode; + + int8_t legato; /* legato footswitch */ + int8_t legato_flag; /* note-on flag for legato */ + + midi_controller mod, bend, caf, paf, cc1, cc2; + + ChannelBitMask channel_layer; + int port_select; + + struct part_eq_xg eq_xg; + + int8_t dry_level; + int8_t note_limit_high, note_limit_low; /* Note Limit (Keyboard Range) */ + int8_t vel_limit_high, vel_limit_low; /* Velocity Limit */ + int32_t rx; /* Rx. ~ (Rcv ~) */ + + int drum_effect_num; + int8_t drum_effect_flag; + struct DrumPartEffect *drum_effect; + + int8_t sysex_gs_msb_addr, sysex_gs_msb_val, + sysex_xg_msb_addr, sysex_xg_msb_val, sysex_msb_addr, sysex_msb_val; +}; + +/* Causes the instrument's default panning to be used. */ +#define NO_PANNING -1 + +typedef struct { + int16_t freq, last_freq, orig_freq; + double reso_dB, last_reso_dB, orig_reso_dB, reso_lin; + int8_t type; /* filter type. 0: Off, 1: 12dB/oct, 2: 24dB/oct */ + int32_t f, q, p; /* coefficients in fixed-point */ + int32_t b0, b1, b2, b3, b4; + float gain; + int8_t start_flag; +} FilterCoefficients; + +#define ENABLE_PAN_DELAY +#define PAN_DELAY_BUF_MAX 48 /* 0.5ms in 96kHz */ + +typedef struct { + uint8_t + status, channel, note, velocity; + int vid, temper_instant; + Sample *sample; + int64_t sample_offset; /* sample_offset must be signed */ + int32_t + orig_frequency, frequency, sample_increment, + envelope_volume, envelope_target, envelope_increment, + tremolo_sweep, tremolo_sweep_position, + tremolo_phase, tremolo_phase_increment, + vibrato_sweep, vibrato_sweep_position; + + final_volume_t left_mix, right_mix; + int32_t old_left_mix, old_right_mix, + left_mix_offset, right_mix_offset, + left_mix_inc, right_mix_inc; + + double + left_amp, right_amp, tremolo_volume; + int32_t + vibrato_sample_increment[VIBRATO_SAMPLE_INCREMENTS], vibrato_delay; + int + vibrato_phase, orig_vibrato_control_ratio, vibrato_control_ratio, + vibrato_depth, vibrato_control_counter, + envelope_stage, control_counter, panning, panned; + int16_t tremolo_depth; + + /* for portamento */ + int porta_control_ratio, porta_control_counter, porta_dpb; + int32_t porta_pb; + + int delay; /* Note ON delay samples */ + int32_t timeout; + struct cache_hash *cache; + + uint8_t chorus_link; /* Chorus link */ + int8_t proximate_flag; + + FilterCoefficients fc; + + double envelope_scale, last_envelope_volume; + int32_t inv_envelope_scale; + + int modenv_stage; + int32_t + modenv_volume, modenv_target, modenv_increment; + double last_modenv_volume; + int32_t tremolo_delay, modenv_delay; + + int32_t delay_counter; + +#ifdef ENABLE_PAN_DELAY + int32_t *pan_delay_buf, pan_delay_rpt, pan_delay_wpt, pan_delay_spt; +#endif /* ENABLE_PAN_DELAY */ +} Voice; + +/* Voice status options: */ +#define VOICE_FREE (1<<0) +#define VOICE_ON (1<<1) +#define VOICE_SUSTAINED (1<<2) +#define VOICE_OFF (1<<3) +#define VOICE_DIE (1<<4) + +/* Voice panned options: */ +#define PANNED_MYSTERY 0 +#define PANNED_LEFT 1 +#define PANNED_RIGHT 2 +#define PANNED_CENTER 3 +/* Anything but PANNED_MYSTERY only uses the left volume */ + +enum { + MODULE_TIMIDITY_DEFAULT = 0x0, + /* GS modules */ + MODULE_SC55 = 0x1, + MODULE_SC88 = 0x2, + MODULE_SC88PRO = 0x3, + MODULE_SC8850 = 0x4, + /* XG modules */ + MODULE_MU50 = 0x10, + MODULE_MU80 = 0x11, + MODULE_MU90 = 0x12, + MODULE_MU100 = 0x13, + /* GM modules */ + MODULE_SBLIVE = 0x20, + MODULE_SBAUDIGY = 0x21, + /* Special modules */ + MODULE_TIMIDITY_SPECIAL1 = 0x70, + MODULE_TIMIDITY_DEBUG = 0x7f, +}; + + + +struct midi_file_info +{ + int readflag; + char *seq_name; + char *karaoke_title; + char *first_text; + int16_t hdrsiz; + int16_t format; + int16_t tracks; + int32_t divisions; + int time_sig_n, time_sig_d, time_sig_c, time_sig_b; /* Time signature */ + int drumchannels_isset; + ChannelBitMask drumchannels; + ChannelBitMask drumchannel_mask; + int32_t samples; + int max_channel; + int compressed; /* True if midi_data is compressed */ +}; + + +class Recache; +class Mixer; +class Reverb; +class AudioQueue; + +class Player +{ +public: + Channel channel[MAX_CHANNELS]; + Voice voice[max_voices]; + ChannelBitMask default_drumchannel_mask; + ChannelBitMask default_drumchannels; + ChannelBitMask drumchannel_mask; + ChannelBitMask drumchannels; + double *vol_table; + +private: + Recache *recache; + Mixer *mixer; + Reverb *reverb; + AudioQueue *aq; + + + MidiEvent *current_event; + int32_t sample_count; /* Length of event_list */ + int32_t current_sample; /* Number of calclated samples */ + double midi_time_ratio; /* For speed up/down */ + + int note_key_offset = 0; /* For key up/down */ + ChannelBitMask channel_mute; /* For channel mute */ + double master_volume; + int32_t master_volume_ratio; + + int play_system_mode; + int midi_streaming; + int volatile stream_max_compute; /* compute time limit (in msec) when streaming */ + int8_t current_keysig; + int8_t current_temper_keysig; + int temper_adj; + int32_t current_play_tempo; + int opt_realtime_playing; + int check_eot_flag; + int playmidi_seek_flag; + int opt_pure_intonation; + int current_freq_table; + int current_temper_freq_table; + int master_tuning; + + int make_rvid_flag; /* For reverb optimization */ + + int32_t amplification; + int voices, upper_voices; + + struct midi_file_info midifileinfo, *current_file_info; + MBlockList playmidi_pool; + int32_t freq_table_user[4][48][128]; + char *reverb_buffer; /* MAX_CHANNELS*AUDIO_BUFFER_SIZE*8 */ + + int32_t lost_notes, cut_notes; + int32_t common_buffer[AUDIO_BUFFER_SIZE * 2], *buffer_pointer; /* stereo samples */ + int16_t wav_buffer[AUDIO_BUFFER_SIZE * 2]; + int32_t buffered_count; + + int32_t insertion_effect_buffer[AUDIO_BUFFER_SIZE * 2]; + + + /* Ring voice id for each notes. This ID enables duplicated note. */ + uint8_t vidq_head[128 * MAX_CHANNELS], vidq_tail[128 * MAX_CHANNELS]; + + int MIDI_EVENT_NOTE(MidiEvent *ep) + { + return (ISDRUMCHANNEL((ep)->channel) ? (ep)->a : (((int)(ep)->a + note_key_offset + channel[ep->channel].key_shift) & 0x7f)); + } + + int32_t MIDI_EVENT_TIME(MidiEvent *ep) + { + return ((int32_t)((ep)->time * midi_time_ratio + 0.5)); + } + + int16_t conv_lfo_pitch_depth(float val) + { + return (int16_t)(0.0318f * val * val + 0.6858f * val + 0.5f); + } + + int16_t conv_lfo_filter_depth(float val) + { + return (int16_t)((0.0318f * val * val + 0.6858f * val) * 4.0f + 0.5f); + } + + bool IS_SYSEX_EVENT_TYPE(MidiEvent *event); + double cnv_Hz_to_vib_ratio(double freq); + int new_vidq(int ch, int note); + int last_vidq(int ch, int note); + void reset_voices(void); + void kill_note(int i); + void kill_all_voices(void); + void reset_drum_controllers(struct DrumParts *d[], int note); + void reset_nrpn_controllers(int c); + void reset_controllers(int c); + int32_t calc_velocity(int32_t ch, int32_t vel); + void recompute_voice_tremolo(int v); + void recompute_amp(int v); + void reset_midi(int playing); + void recompute_channel_filter(int ch, int note); + void init_voice_filter(int i); + int reduce_voice(void); + int find_free_voice(void); + int get_panning(int ch, int note, int v); + void init_voice_vibrato(int v); + void init_voice_pan_delay(int v); + void init_voice_portamento(int v); + void init_voice_tremolo(int v); + void start_note(MidiEvent *e, int i, int vid, int cnt); + void set_envelope_time(int ch, int val, int stage); + void new_chorus_voice_alternate(int v1, int level); + void note_on(MidiEvent *e); + void update_sostenuto_controls(int ch); + void update_redamper_controls(int ch); + void note_off(MidiEvent *e); + void all_notes_off(int c); + void all_sounds_off(int c); + void adjust_pressure(MidiEvent *e); + void adjust_channel_pressure(MidiEvent *e); + void adjust_panning(int c); + void adjust_drum_panning(int ch, int note); + void drop_sustain(int c); + void adjust_all_pitch(void); + void adjust_pitch(int c); + void adjust_volume(int c); + void set_reverb_level(int ch, int level); + void make_drum_effect(int ch); + void adjust_master_volume(void); + void add_channel_layer(int to_ch, int from_ch); + void remove_channel_layer(int ch); + void process_sysex_event(int ev, int ch, int val, int b); + double gs_cnv_vib_rate(int rate); + int32_t gs_cnv_vib_depth(int depth); + int32_t gs_cnv_vib_delay(int delay); + int last_rpn_addr(int ch); + void voice_increment(int n); + void voice_decrement(int n); + void voice_decrement_conservative(int n); + void mix_signal(int32_t *dest, int32_t *src, int32_t count); + int is_insertion_effect_xg(int ch); + void do_compute_data(int32_t count); + int check_midi_play_end(MidiEvent *e, int len); + int midi_play_end(void); + int compute_data(int32_t count); + void update_modulation_wheel(int ch); + void drop_portamento(int ch); + void update_portamento_time(int ch); + void update_legato_controls(int ch); + void set_master_tuning(int tune); + struct midi_file_info *new_midi_file_info(); + + + void adjust_amplification(void); + void init_freq_table_user(void); + int find_samples(MidiEvent *, int *); + int select_play_sample(Sample *, int, int *, int *, MidiEvent *); + double get_play_note_ratio(int, int); + int find_voice(MidiEvent *); + void finish_note(int i); + void update_portamento_controls(int ch); + void update_rpn_map(int ch, int addr, int update_now); + void set_single_note_tuning(int, int, int, int); + void set_user_temper_entry(int, int, int); + void recompute_bank_parameter(int, int); + float calc_drum_tva_level(int ch, int note, int level); + int32_t calc_random_delay(int ch, int note); + + + + /* XG Part EQ */ + void init_part_eq_xg(struct part_eq_xg *); + void recompute_part_eq_xg(struct part_eq_xg *); + /* MIDI controllers (MW, Bend, CAf, PAf,...) */ + void init_midi_controller(midi_controller *); + float get_midi_controller_amp(midi_controller *); + float get_midi_controller_filter_cutoff(midi_controller *); + float get_midi_controller_filter_depth(midi_controller *); + int32_t get_midi_controller_pitch(midi_controller *); + int16_t get_midi_controller_pitch_depth(midi_controller *); + int16_t get_midi_controller_amp_depth(midi_controller *); + /* Rx. ~ (Rcv ~) */ + void init_rx(int); + void set_rx(int, int32_t, int); + void init_rx_drum(struct DrumParts *); + void set_rx_drum(struct DrumParts *, int32_t, int); + int32_t get_rx_drum(struct DrumParts *, int32_t); + + +public: + Player(); + ~Player(); + + bool ISDRUMCHANNEL(int c) + { + return !!IS_SET_CHANNELMASK(drumchannels, c); + } + + int midi_drumpart_change(int ch, int isdrum); + int get_reverb_level(int ch); + int get_chorus_level(int ch); + Instrument *play_midi_load_instrument(int dr, int bk, int prog); + void midi_program_change(int ch, int prog); + void free_voice(int v); + void play_midi_setup_drums(int ch, int note); + + /* For stream player */ + void playmidi_stream_init(void); + void playmidi_tmr_reset(void); + int play_event(MidiEvent *ev); + + void recompute_voice_filter(int); + + void free_drum_effect(int); + void change_system_mode(int mode); + struct midi_file_info *get_midi_file_info(const char *filename, int newp); + void recompute_freq(int v); + int get_default_mapID(int ch); + void init_channel_layer(int ch); + + // Only until streaming works. + void skip_to(int32_t until_time, MidiEvent *evt_start); + int play_midi(MidiEvent *eventlist, int32_t samples); + friend MidiEvent *groom_list(int32_t divisions, int32_t *eventsp, int32_t *samplesp); + + +}; + +class SysexConvert +{ + const int midi_port_number = 0; + uint8_t rhythm_part[2] = { 0,0 }; /* for GS */ + uint8_t drum_setup_xg[16] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; /* for XG */ + +public: + int parse_sysex_event_multi(uint8_t *val, int32_t len, MidiEvent *evm, Instruments *instruments); + int parse_sysex_event(uint8_t *val, int32_t len, MidiEvent *ev, Instruments *instruments); +}; + +// really extern! +int convert_midi_control_change(int chn, int type, int val, MidiEvent *ev_ret); + + +} + +#endif /* ___PLAYMIDI_H_ */ diff --git a/src/sound/timidity++/quantity.cpp b/src/sound/timidity++/quantity.cpp new file mode 100644 index 0000000000..d75a12dc21 --- /dev/null +++ b/src/sound/timidity++/quantity.cpp @@ -0,0 +1,340 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + quantity.c + + string -> quantity -> native value convertion + by Kentaro Sato +*/ + +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "tables.h" + +#include "quantity.h" + + +namespace TimidityPlus +{ + typedef int32_t(*QuantityToIntProc)(int32_t value, int32_t param); + typedef double(*QuantityToFloatProc)(double value, int32_t param); + typedef union { + QuantityToIntProc i; + QuantityToFloatProc f; + } QuantityConvertProc; + + typedef struct { + const char *suffix; + uint16_t type, id; + int float_type; /* is floating-point type */ + QuantityConvertProc convert; + } QuantityHint; + + +/* + Guide To Add New Unit Types/Units + + append QUANTITY_UNIT_TYPE() + QUANTITY_UNIT_NAME() + ... to enum quantity_units (in quantity.h) + append QUANTITY_TYPE_INT/FLOAT() + REGISTER_TYPE_INT/FLOAT("", ); + ... + END_QUANTITY_TYPE; to GetQuantityHints() + write convert__NUM(int32_t/double value, int32_t param) + convert_(int32_t/double value, int32_t param) + ... functions. +*/ + +/*************** conversion functions ***************/ + +static int32_t convert_DIRECT_INT_NUM(int32_t value, int32_t param) +{ + return value; +} + +static double convert_DIRECT_FLOAT_NUM(double value, int32_t param) +{ + return value; +} + +/* from instrum.c, convert_tremolo_sweep() */ +static int32_t convert_TREMOLO_SWEEP_NUM(int32_t value, int32_t param) +{ + uint8_t sweep = value; + if (!sweep) + return 0; + + return + ((control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (playback_rate * sweep); +} + +static int32_t convert_TREMOLO_SWEEP_MS(int32_t value, int32_t param) +{ + if (value <= 0) + return 0; + #if SWEEP_SHIFT <= 16 + return ((uint32_t)(control_ratio * (1000 >> 2)) << SWEEP_SHIFT) / ((playback_rate * value) >> 2); + #else + #error "overflow" + #endif +} + +/* from instrum.c, convert_tremolo_rate() */ +static int32_t convert_TREMOLO_RATE_NUM(int32_t value, int32_t param) +{ + uint8_t rate = value; + return + ((SINE_CYCLE_LENGTH * control_ratio * rate) << RATE_SHIFT) / + (TREMOLO_RATE_TUNING * playback_rate); +} + +static int32_t convert_TREMOLO_RATE_MS(int32_t value, int32_t param) +{ + #if RATE_SHIFT <= 5 + return ((SINE_CYCLE_LENGTH * control_ratio * (1000 >> 1)) << RATE_SHIFT) / + ((playback_rate * (uint32_t)value) >> 1); + #else + #error "overflow" + #endif +} + +static double convert_TREMOLO_RATE_HZ(double value, int32_t param) +{ + if (value <= 0) + return 0; + return ((SINE_CYCLE_LENGTH * control_ratio) << RATE_SHIFT) * value / playback_rate; +} + +/* from instrum.c, convert_vibrato_sweep() */ +static int32_t convert_VIBRATO_SWEEP_NUM(int32_t value, int32_t vib_control_ratio) +{ + uint8_t sweep = value; + if (!sweep) + return 0; + + return (int32_t)(TIM_FSCALE((double) (vib_control_ratio) + * SWEEP_TUNING, SWEEP_SHIFT) + / (double)(playback_rate * sweep)); + + /* this was overflowing with seashore.pat + + ((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (playback_rate * sweep); */ +} + +static int32_t convert_VIBRATO_SWEEP_MS(int32_t value, int32_t vib_control_ratio) +{ + if (value <= 0) + return 0; + return (TIM_FSCALE((double)vib_control_ratio * 1000, SWEEP_SHIFT) + / (double)(playback_rate * value)); +} + +/* from instrum.c, to_control() */ +static int32_t convert_VIBRATO_RATE_NUM(int32_t control, int32_t param) +{ + return (int32_t) (0x2000 / pow(2.0, control / 31.0)); +} + +static int32_t convert_VIBRATO_RATE_MS(int32_t value, int32_t param) +{ + return 1000 * playback_rate / ((2 * VIBRATO_SAMPLE_INCREMENTS) * value); +} + +static double convert_VIBRATO_RATE_HZ(double value, int32_t param) +{ + return playback_rate / ((2 * VIBRATO_SAMPLE_INCREMENTS) * value); +} + +/*************** core functions ***************/ + +#define MAX_QUANTITY_UNITS_PER_UNIT_TYPES 8 + +static int GetQuantityHints(uint16_t type, QuantityHint *units) +{ + QuantityHint *unit; + + unit = units; + #define QUANTITY_TYPE_INT(type) \ + case QUANTITY_UNIT_TYPE(type): REGISTER_TYPE_INT("", type##_NUM) + #define QUANTITY_TYPE_FLOAT(type) \ + case QUANTITY_UNIT_TYPE(type): REGISTER_TYPE_FLOAT("", type##_NUM) + #define REGISTER_TYPE_INT(ustr, utype) REGISTER_TYPE_ENTITY_INT(ustr, utype, convert_##utype) + #define REGISTER_TYPE_FLOAT(ustr, utype) REGISTER_TYPE_ENTITY_FLOAT(ustr, utype, convert_##utype) + #define REGISTER_TYPE_ALIAS_INT(ustr, utype, atype) REGISTER_TYPE_ENTITY_INT(ustr, utype, convert_##atype) + #define REGISTER_TYPE_ALIAS_FLOAT(ustr, utype, atype) REGISTER_TYPE_ENTITY_FLOAT(ustr, utype, convert_##atype) + #define REGISTER_TYPE_ENTITY_INT(ustr, utype, ucvt) \ + unit->suffix = ustr, unit->type = type, unit->id = QUANTITY_UNIT_NAME(utype), unit->float_type = 0, unit->convert.i = ucvt, unit++ + #define REGISTER_TYPE_ENTITY_FLOAT(ustr, utype, ucvt) \ + unit->suffix = ustr, unit->type = type, unit->id = QUANTITY_UNIT_NAME(utype), unit->float_type = 1, unit->convert.f = ucvt, unit++ + #define END_QUANTITY_TYPE unit->suffix = NULL; break + switch (type) + { + QUANTITY_TYPE_INT(DIRECT_INT); + END_QUANTITY_TYPE; + QUANTITY_TYPE_FLOAT(DIRECT_FLOAT); + END_QUANTITY_TYPE; + QUANTITY_TYPE_INT(TREMOLO_SWEEP); + REGISTER_TYPE_INT("ms", TREMOLO_SWEEP_MS); + END_QUANTITY_TYPE; + QUANTITY_TYPE_INT(TREMOLO_RATE); + REGISTER_TYPE_INT("ms", TREMOLO_RATE_MS); + REGISTER_TYPE_FLOAT("Hz", TREMOLO_RATE_HZ); + END_QUANTITY_TYPE; + QUANTITY_TYPE_INT(VIBRATO_RATE); + REGISTER_TYPE_INT("ms", VIBRATO_RATE_MS); + REGISTER_TYPE_FLOAT("Hz", VIBRATO_RATE_HZ); + END_QUANTITY_TYPE; + QUANTITY_TYPE_INT(VIBRATO_SWEEP); + REGISTER_TYPE_INT("ms", VIBRATO_SWEEP_MS); + END_QUANTITY_TYPE; + default: + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "Internal parameter error (%d)", type); + return 0; + } + return 1; +} + +/* quantity is unchanged if an error occurred */ +static const char *number_to_quantity(int32_t number_i, const char *suffix_i, double number_f, const char *suffix_f, Quantity *quantity, uint16_t type) +{ + QuantityHint units[MAX_QUANTITY_UNITS_PER_UNIT_TYPES], *unit; + + if (!GetQuantityHints(type, units)) + return "Parameter error"; + unit = units; + while(unit->suffix != NULL) + { + if (suffix_i != NULL && strcmp(suffix_i, unit->suffix) == 0) /* number_i, suffix_i was valid */ + { + quantity->type = unit->type; + quantity->unit = unit->id; + if (unit->float_type) + quantity->value.f = number_i; + else + quantity->value.i = number_i; + return NULL; + } + else if (suffix_f != NULL && strcmp(suffix_f, unit->suffix) == 0) /* number_f, suffix_f was valid */ + { + if (unit->float_type) + { + quantity->type = unit->type; + quantity->unit = unit->id; + quantity->value.f = number_f; + return NULL; + } + else + return "integer expected"; + } + unit++; + } + return "invalid parameter"; +} + +const char *string_to_quantity(const char *string, Quantity *quantity, uint16_t type) +{ + int32_t number_i; + double number_f; + char *suffix_i, *suffix_f; + + number_i = strtol(string, &suffix_i, 10); /* base == 10 for compatibility with atoi() */ + if (string == suffix_i) /* doesn't start with valid character */ + return "Number expected"; + number_f = strtod(string, &suffix_f); + return number_to_quantity(number_i, suffix_i, number_f, suffix_f, quantity, type); +} + +void int_to_quantity(int32_t number, Quantity *quantity, uint16_t type) +{ + /* pass suffix_f NULL to warn if unit type is float type */ + if (number_to_quantity(number, "", number, NULL, quantity, type) != NULL) /* error */ + { + quantity->type = QUANTITY_UNIT_TYPE(DIRECT_INT); + quantity->unit = QUANTITY_UNIT_NAME(DIRECT_INT_NUM); + quantity->value.i = 0; + } +} + +void float_to_quantity(double number, Quantity *quantity, uint16_t type) +{ + /* pass suffix_i NULL to warn if unit type is integer type */ + if (number_to_quantity(number, NULL, number, "", quantity, type) != NULL) /* error */ + { + quantity->type = QUANTITY_UNIT_TYPE(DIRECT_FLOAT); + quantity->unit = QUANTITY_UNIT_NAME(DIRECT_FLOAT_NUM); + quantity->value.f = 0; + } +} + +static int GetQuantityConvertProc(const Quantity *quantity, QuantityConvertProc *proc) +{ + QuantityHint units[MAX_QUANTITY_UNITS_PER_UNIT_TYPES], *unit; + + if (!GetQuantityHints(quantity->type, units)) + return -1; /* already warned */ + unit = units; + while(unit->suffix != NULL) + { + if (quantity->unit == unit->id) + { + *proc = unit->convert; + return unit->float_type; + } + unit++; + } + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "Internal parameter error"); + return -1; +} + +int32_t quantity_to_int(const Quantity *quantity, int32_t param) +{ + QuantityConvertProc proc; + + switch (GetQuantityConvertProc(quantity, &proc)) + { + case 0: + return (*proc.i)(quantity->value.i, param); + case 1: + return (*proc.f)(quantity->value.f, param); + } + return 0; +} + +double quantity_to_float(const Quantity *quantity, int32_t param) +{ + QuantityConvertProc proc; + + switch (GetQuantityConvertProc(quantity, &proc)) + { + case 0: + return (*proc.i)(quantity->value.i, param); + case 1: + return (*proc.f)(quantity->value.f, param); + } + return 0; +} +} \ No newline at end of file diff --git a/src/sound/timidity++/quantity.h b/src/sound/timidity++/quantity.h new file mode 100644 index 0000000000..7f81938c6b --- /dev/null +++ b/src/sound/timidity++/quantity.h @@ -0,0 +1,72 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + quantity.h + + by Kentaro Sato +*/ + +#ifndef ___QUANTITY_H_ +#define ___QUANTITY_H_ + +#include + +namespace TimidityPlus +{ + + +#define QUANTITY_UNIT_TYPE(u) QUANTITY_OF_##u, QUANTITY_UNIT_NAME(u##_NUM) +#define QUANTITY_UNIT_NAME(name) QUANTITY_UNIT_##name +enum quantity_units { + QUANTITY_UNIT_TYPE(UNDEFINED), /* type only */ + QUANTITY_UNIT_TYPE(DIRECT_INT), /* internal use */ + QUANTITY_UNIT_TYPE(DIRECT_FLOAT), /* internal use */ + QUANTITY_UNIT_TYPE(TREMOLO_SWEEP), /* int */ + QUANTITY_UNIT_NAME(TREMOLO_SWEEP_MS), /* int */ + QUANTITY_UNIT_TYPE(TREMOLO_RATE), /* int */ + QUANTITY_UNIT_NAME(TREMOLO_RATE_MS), /* int */ + QUANTITY_UNIT_NAME(TREMOLO_RATE_HZ), /* float */ + QUANTITY_UNIT_TYPE(VIBRATO_SWEEP), /* int */ + QUANTITY_UNIT_NAME(VIBRATO_SWEEP_MS), /* int */ + QUANTITY_UNIT_TYPE(VIBRATO_RATE), /* int */ + QUANTITY_UNIT_NAME(VIBRATO_RATE_MS), /* int */ + QUANTITY_UNIT_NAME(VIBRATO_RATE_HZ), /* float */ +}; +#undef QUANTITY_UNIT_TYPE +#define QUANTITY_UNIT_TYPE(u) QUANTITY_OF_##u + +#define INIT_QUANTITY(q) (q).type = QUANTITY_UNIT_TYPE(UNDEFINED) +#define IS_QUANTITY_DEFINED(q) ((q).type != QUANTITY_UNIT_TYPE(UNDEFINED)) + +typedef struct Quantity_ { + uint16_t type, unit; + union { + int32_t i; + double f; + } value; +} Quantity; + +extern const char *string_to_quantity(const char *string, Quantity *quantity, uint16_t type); +extern void int_to_quantity(int32_t number, Quantity *quantity, uint16_t type); +extern void float_to_quantity(double number, Quantity *quantity, uint16_t type); +extern int32_t quantity_to_int(const Quantity *quantity, int32_t param); +extern double quantity_to_float(const Quantity *quantity, int32_t param); + +} +#endif /* ___QUANTITY_H_ */ diff --git a/src/sound/timidity++/readmidi.cpp b/src/sound/timidity++/readmidi.cpp new file mode 100644 index 0000000000..5acda6f3c0 --- /dev/null +++ b/src/sound/timidity++/readmidi.cpp @@ -0,0 +1,1611 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2009 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "tables.h" +#include "reverb.h" +#include "aq.h" +#include + +namespace TimidityPlus +{ + +struct MidiEventList +{ + MidiEvent event; + MidiEventList *next; + MidiEventList *prev; +}; + +const int midi_port_number = 0; +const int play_system_mode = 0; + +MBlockList tmpbuffer; +Player *gplayer; + +#define MAX_MIDI_EVENT ((MAX_SAFE_MALLOC_SIZE / sizeof(MidiEvent)) - 1) +#define MARKER_START_CHAR '(' +#define MARKER_END_CHAR ')' + +enum +{ + CHORUS_ST_NOT_OK = 0, + CHORUS_ST_OK +}; + +static int readmidi_error_flag = 0; + +/* Mingw gcc3 and Borland C hack */ +/* If these are not NULL initialized cause Hang up */ +/* why ? I dont know. (Keishi Suenaga) */ +static MidiEventList *evlist=NULL, *current_midi_point=NULL; +static int32_t event_count; +static MBlockList mempool; +static int current_read_track; +static int karaoke_format, karaoke_title_flag; +static MidiEvent timesig[256]; +extern Instruments *instruments; +static int32_t midi_restart_time = 0; + + +extern void free_readmidi(void); + + +static SysexConvert sc; + +/* These would both fit into 32 bits, but they are often added in + large multiples, so it's simpler to have two roomy ints */ +static int32_t sample_increment, sample_correction; /*samples per MIDI delta-t*/ + +static inline void SETMIDIEVENT(MidiEvent &e, int32_t at, uint32_t t, uint32_t ch, uint32_t pa, uint32_t pb) +{ + (e).time = (at); + (e).type = (t); + (e).channel = (uint8_t)(ch); + (e).a = (uint8_t)(pa); + (e).b = (uint8_t)(pb); +} + +void readmidi_add_event(MidiEvent *a_event); + +static inline void MIDIEVENT(int32_t at, uint32_t t, uint32_t ch, uint32_t pa, uint32_t pb) +{ + MidiEvent event; + SETMIDIEVENT(event, at, t, ch, pa, pb); + readmidi_add_event(&event); +} + + + + +void Player::skip_to(int32_t until_time, MidiEvent *evt_start) +{ + int ch; + + current_event = NULL; + + if (current_sample > until_time) + current_sample = 0; + + change_system_mode(DEFAULT_SYSTEM_MODE); + reset_midi(0); + + buffered_count = 0; + buffer_pointer = common_buffer; + current_event = evt_start; + current_play_tempo = 500000; /* 120 BPM */ + + for (ch = 0; ch < MAX_CHANNELS; ch++) + channel[ch].lasttime = current_sample; + +} + + +int Player::play_midi(MidiEvent *eventlist, int32_t samples) +{ + int rc; + static int play_count = 0; + int i, j; + + sample_count = samples; + lost_notes = cut_notes = 0; + check_eot_flag = 1; + note_key_offset = key_adjust; + midi_time_ratio = tempo_adjust; + + /* Reset key & speed each files */ + current_keysig = (opt_init_keysig == 8) ? 0 : opt_init_keysig; + i = current_keysig + ((current_keysig < 8) ? 7 : -9), j = 0; + while (i != 7) + i += (i < 7) ? 5 : -7, j++; + j += key_adjust, j -= int(floor(j / 12.0) * 12); + current_freq_table = j; + + CLEAR_CHANNELMASK(channel_mute); + if (temper_type_mute & 1) + FILL_CHANNELMASK(channel_mute); + + + reset_midi(0); + + rc = aq->flush(0); + + skip_to(0, eventlist); + + rc = RC_OK; + for (;;) + { + rc = play_event(current_event); + if (rc != RC_OK) + break; + current_event++; + } + + if (play_count++ > 3) + { + int cnt; + play_count = 0; + cnt = free_global_mblock(); /* free unused memory */ + if (cnt > 0) + ctl_cmsg(CMSG_INFO, VERB_VERBOSE, + "%d memory blocks are free", cnt); + } + return rc; +} + + + +static MidiEventList *alloc_midi_event() +{ + return (MidiEventList *)new_segment(&mempool, sizeof(MidiEventList)); +} + +static int32_t readmidi_set_track(int trackno, int rewindp) +{ + current_read_track = trackno; + if(karaoke_format == 1 && current_read_track == 2) + karaoke_format = 2; /* Start karaoke lyric */ + else if(karaoke_format == 2 && current_read_track == 3) + karaoke_format = 3; /* End karaoke lyric */ + + if(evlist == NULL) + return 0; + if(rewindp) + current_midi_point = evlist; + else + { + /* find the last event in the list */ + while(current_midi_point->next != NULL) + current_midi_point = current_midi_point->next; + } + return current_midi_point->event.time; +} + + +static void readmidi_add_event(MidiEvent *a_event) +{ + MidiEventList *newev; + int32_t at; + + if(event_count == MAX_MIDI_EVENT) + { + if(!readmidi_error_flag) + { + readmidi_error_flag = 1; + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "Maxmum number of events is exceeded"); + } + return; + } + event_count++; + + at = a_event->time; + newev = alloc_midi_event(); + newev->event = *a_event; /* assign by value!!! */ + if(at < 0) /* for safety */ + at = newev->event.time = 0; + + if(at >= current_midi_point->event.time) + { + /* Forward scan */ + MidiEventList *next = current_midi_point->next; + while (next && (next->event.time <= at)) + { + current_midi_point = next; + next = current_midi_point->next; + } + newev->prev = current_midi_point; + newev->next = next; + current_midi_point->next = newev; + if (next) + next->prev = newev; + } + else + { + /* Backward scan -- symmetrical to the one above */ + MidiEventList *prev = current_midi_point->prev; + while (prev && (prev->event.time > at)) { + current_midi_point = prev; + prev = current_midi_point->prev; + } + newev->prev = prev; + newev->next = current_midi_point; + current_midi_point->prev = newev; + if (prev) + prev->next = newev; + } + current_midi_point = newev; +} + +static void readmidi_add_ctl_event(int32_t at, int ch, int a, int b) +{ + MidiEvent ev; + + if(convert_midi_control_change(ch, a, b, &ev)) + { + ev.time = at; + readmidi_add_event(&ev); + } + else + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "(Control ch=%d %d: %d)", ch, a, b); +} + +/* Computes how many (fractional) samples one MIDI delta-time unit contains */ +static void compute_sample_increment(int32_t tempo, int32_t divisions) +{ + double a; + a = (double) (tempo) * (double) (playback_rate) * (65536.0/1000000.0) / + (double)(divisions); + + sample_correction = (int32_t)(a) & 0xFFFF; + sample_increment = (int32_t)(a) >> 16; + + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Samples per delta-t: %d (correction %d)", + sample_increment, sample_correction); +} + + +/* Free the linked event list from memory. */ +static void free_midi_list(void) +{ + if(evlist != NULL) + { + reuse_mblock(&mempool); + evlist = NULL; + } +} + +static void move_channels(int *chidx) +{ + int i, ch, maxch, newch; + MidiEventList *e; + + for (i = 0; i < 256; i++) + chidx[i] = -1; + /* check channels */ + for (i = maxch = 0, e = evlist; i < event_count; i++, e = e->next) + if (! GLOBAL_CHANNEL_EVENT_TYPE(e->event.type)) { + if ((ch = e->event.channel) < REDUCE_CHANNELS) + chidx[ch] = ch; + if (maxch < ch) + maxch = ch; + } + if (maxch >= REDUCE_CHANNELS) + /* Move channel if enable */ + for (i = maxch = 0, e = evlist; i < event_count; i++, e = e->next) + if (! GLOBAL_CHANNEL_EVENT_TYPE(e->event.type)) { + if (chidx[ch = e->event.channel] != -1) + ch = e->event.channel = chidx[ch]; + else { /* -1 */ + if (ch >= MAX_CHANNELS) { + newch = ch % REDUCE_CHANNELS; + while (newch < ch && newch < MAX_CHANNELS) { + if (chidx[newch] == -1) { + ctl_cmsg(CMSG_INFO, VERB_VERBOSE, + "channel %d => %d", ch, newch); + ch = e->event.channel = chidx[ch] = newch; + break; + } + newch += REDUCE_CHANNELS; + } + } + if (chidx[ch] == -1) { + if (ch < MAX_CHANNELS) + chidx[ch] = ch; + else { + newch = ch % MAX_CHANNELS; + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, + "channel %d => %d (mixed)", ch, newch); + ch = e->event.channel = chidx[ch] = newch; + } + } + } + if (maxch < ch) + maxch = ch; + } + for (i = 0, e = evlist; i < event_count; i++, e = e->next) + if (e->event.type == ME_SYSEX_GS_LSB) { + if (e->event.b == 0x45 || e->event.b == 0x46) + if (maxch < e->event.channel) + maxch = e->event.channel; + } else if (e->event.type == ME_SYSEX_XG_LSB) { + if (e->event.b == 0x99) + if (maxch < e->event.channel) + maxch = e->event.channel; + } + auto current_file_info = gplayer->get_midi_file_info("", 0); + current_file_info->max_channel = maxch; +} + +/* Allocate an array of MidiEvents and fill it from the linked list of + events, marking used instruments for loading. Convert event times to + samples: handle tempo changes. Strip unnecessary events from the list. + Free the linked list. */ +MidiEvent *groom_list(int32_t divisions, int32_t *eventsp, int32_t *samplesp) +{ + auto current_file_info = gplayer->get_midi_file_info("", 0); + MidiEvent *groomed_list, *lp; + MidiEventList *meep; + int32_t i, j, our_event_count, tempo, skip_this_event; + int32_t sample_cum, samples_to_do, at, st, dt, counting_time; + int ch, gch; + uint8_t current_set[MAX_CHANNELS], + warn_tonebank[128 + MAP_BANK_COUNT], warn_drumset[128 + MAP_BANK_COUNT]; + int8_t bank_lsb[MAX_CHANNELS], bank_msb[MAX_CHANNELS], mapID[MAX_CHANNELS]; + int current_program[MAX_CHANNELS]; + int chidx[256]; + int newbank, newprog; + + move_channels(chidx); + + COPY_CHANNELMASK(gplayer->drumchannels, current_file_info->drumchannels); + COPY_CHANNELMASK(gplayer->drumchannel_mask, current_file_info->drumchannel_mask); + + /* Move drumchannels */ + for (ch = REDUCE_CHANNELS; ch < MAX_CHANNELS; ch++) + { + i = chidx[ch]; + if (i != -1 && i != ch && !IS_SET_CHANNELMASK(gplayer->drumchannel_mask, i)) + { + if (IS_SET_CHANNELMASK(gplayer->drumchannels, ch)) + SET_CHANNELMASK(gplayer->drumchannels, i); + else + UNSET_CHANNELMASK(gplayer->drumchannels, i); + } + } + + memset(warn_tonebank, 0, sizeof(warn_tonebank)); + if (special_tonebank >= 0) + newbank = special_tonebank; + else + newbank = default_tonebank; + for (j = 0; j < MAX_CHANNELS; j++) + { + if (gplayer->ISDRUMCHANNEL(j)) + current_set[j] = 0; + else + { + if (instruments->toneBank(newbank) == NULL) + { + if (warn_tonebank[newbank] == 0) + { + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, + "Tone bank %d is undefined", newbank); + warn_tonebank[newbank] = 1; + } + newbank = 0; + } + current_set[j] = newbank; + } + bank_lsb[j] = bank_msb[j] = 0; + if (play_system_mode == XG_SYSTEM_MODE && j % 16 == 9) + bank_msb[j] = 127; /* Use MSB=127 for XG */ + current_program[j] = instruments->defaultProgram(j); + } + + memset(warn_drumset, 0, sizeof(warn_drumset)); + tempo = 500000; + compute_sample_increment(tempo, divisions); + + /* This may allocate a bit more than we need */ + groomed_list = lp = + (MidiEvent *)safe_malloc(sizeof(MidiEvent) * (event_count + 1)); + meep = evlist; + + our_event_count = 0; + st = at = sample_cum = 0; + counting_time = 2; /* We strip any silence before the first NOTE ON. */ + gplayer->change_system_mode(DEFAULT_SYSTEM_MODE); + + for (j = 0; j < MAX_CHANNELS; j++) + mapID[j] = gplayer->get_default_mapID(j); + + for (i = 0; i < event_count; i++) + { + skip_this_event = 0; + ch = meep->event.channel; + gch = GLOBAL_CHANNEL_EVENT_TYPE(meep->event.type); + if (!gch && ch >= MAX_CHANNELS) /* For safety */ + meep->event.channel = ch = ch % MAX_CHANNELS; + + switch (meep->event.type) + { + case ME_NONE: + skip_this_event = 1; + break; + case ME_RESET: + gplayer->change_system_mode(meep->event.a); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "MIDI reset at %d sec", + (int)((double)st / playback_rate + 0.5)); + for (j = 0; j < MAX_CHANNELS; j++) + { + if (play_system_mode == XG_SYSTEM_MODE && j % 16 == 9) + mapID[j] = XG_DRUM_MAP; + else + mapID[j] = gplayer->get_default_mapID(j); + if (gplayer->ISDRUMCHANNEL(j)) + current_set[j] = 0; + else + { + if (special_tonebank >= 0) + current_set[j] = special_tonebank; + else + current_set[j] = default_tonebank; + + if (instruments->toneBank(current_set[j]) == NULL) + current_set[j] = 0; + } + bank_lsb[j] = bank_msb[j] = 0; + if (play_system_mode == XG_SYSTEM_MODE && j % 16 == 9) + bank_msb[j] = 127; /* Use MSB=127 for XG */ + current_program[j] = instruments->defaultProgram(j); + } + break; + + case ME_PROGRAM: + if (gplayer->ISDRUMCHANNEL(ch)) + newbank = current_program[ch]; + else + newbank = current_set[ch]; + newprog = meep->event.a; + switch (play_system_mode) { + case GS_SYSTEM_MODE: /* GS */ + switch (bank_lsb[ch]) { + case 0: /* No change */ + break; + case 1: + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "(GS ch=%d SC-55 MAP)", ch); + mapID[ch] = (gplayer->ISDRUMCHANNEL(ch)) ? SC_55_DRUM_MAP + : SC_55_TONE_MAP; + break; + case 2: + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "(GS ch=%d SC-88 MAP)", ch); + mapID[ch] = (gplayer->ISDRUMCHANNEL(ch)) ? SC_88_DRUM_MAP + : SC_88_TONE_MAP; + break; + case 3: + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "(GS ch=%d SC-88Pro MAP)", ch); + mapID[ch] = (gplayer->ISDRUMCHANNEL(ch)) ? SC_88PRO_DRUM_MAP + : SC_88PRO_TONE_MAP; + break; + case 4: + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(GS ch=%d SC-8820/SC-8850 MAP)", ch); + mapID[ch] = (gplayer->ISDRUMCHANNEL(ch)) ? SC_8850_DRUM_MAP + : SC_8850_TONE_MAP; + break; + default: + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(GS: ch=%d Strange bank LSB %d)", + ch, bank_lsb[ch]); + break; + } + newbank = bank_msb[ch]; + break; + case XG_SYSTEM_MODE: /* XG */ + switch (bank_msb[ch]) { + case 0: /* Normal */ + if (ch == 9 && bank_lsb[ch] == 127 + && mapID[ch] == XG_DRUM_MAP) + /* FIXME: Why this part is drum? Is this correct? */ + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, "Warning: XG bank 0/127 is found. It may be not correctly played."); + else { + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "(XG ch=%d Normal voice)", ch); + gplayer->midi_drumpart_change(ch, 0); + mapID[ch] = XG_NORMAL_MAP; + } + break; + case 64: /* SFX voice */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "(XG ch=%d SFX voice)", ch); + gplayer->midi_drumpart_change(ch, 0); + mapID[ch] = XG_SFX64_MAP; + break; + case 126: /* SFX kit */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "(XG ch=%d SFX kit)", ch); + gplayer->midi_drumpart_change(ch, 1); + mapID[ch] = XG_SFX126_MAP; + break; + case 127: /* Drum kit */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(XG ch=%d Drum kit)", ch); + gplayer->midi_drumpart_change(ch, 1); + mapID[ch] = XG_DRUM_MAP; + break; + default: + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(XG: ch=%d Strange bank MSB %d)", + ch, bank_msb[ch]); + break; + } + newbank = bank_lsb[ch]; + break; + case GM2_SYSTEM_MODE: /* GM2 */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "(GM2 ch=%d)", ch); + if ((bank_msb[ch] & 0xfe) == 0x78) /* 0x78/0x79 */ + gplayer->midi_drumpart_change(ch, bank_msb[ch] == 0x78); + mapID[ch] = (gplayer->ISDRUMCHANNEL(ch)) ? GM2_DRUM_MAP + : GM2_TONE_MAP; + newbank = bank_lsb[ch]; + break; + default: + newbank = bank_msb[ch]; + break; + } + if (gplayer->ISDRUMCHANNEL(ch)) + current_set[ch] = newprog; + else { + if (special_tonebank >= 0) + newbank = special_tonebank; + if (current_program[ch] == SPECIAL_PROGRAM) + skip_this_event = 1; + current_set[ch] = newbank; + } + current_program[ch] = newprog; + break; + + case ME_NOTEON: + if (counting_time) + counting_time = 1; + if (gplayer->ISDRUMCHANNEL(ch)) + { + newbank = current_set[ch]; + newprog = meep->event.a; + instruments->instrument_map(mapID[ch], &newbank, &newprog); + + if (!instruments->drumSet(newbank)) /* Is this a defined drumset? */ + { + if (warn_drumset[newbank] == 0) + { + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, + "Drum set %d is undefined", newbank); + warn_drumset[newbank] = 1; + } + newbank = 0; + } + + /* Mark this instrument to be loaded */ + instruments->mark_drumset(newbank, newprog); + } + else + { + if (current_program[ch] == SPECIAL_PROGRAM) + break; + newbank = current_set[ch]; + newprog = current_program[ch]; + instruments->instrument_map(mapID[ch], &newbank, &newprog); + if (instruments->toneBank(newbank) == NULL) + { + if (warn_tonebank[newbank] == 0) + { + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, + "Tone bank %d is undefined", newbank); + warn_tonebank[newbank] = 1; + } + newbank = 0; + } + + /* Mark this instrument to be loaded */ + instruments->mark_instrument(newbank, newprog); + } + break; + + case ME_TONE_BANK_MSB: + bank_msb[ch] = meep->event.a; + break; + + case ME_TONE_BANK_LSB: + bank_lsb[ch] = meep->event.a; + break; + + case ME_CHORUS_TEXT: + case ME_LYRIC: + case ME_MARKER: + case ME_INSERT_TEXT: + case ME_TEXT: + case ME_KARAOKE_LYRIC: + if ((meep->event.a | meep->event.b) == 0) + skip_this_event = 1; + break; + + case ME_GSLCD: + skip_this_event = 1; + break; + + case ME_DRUMPART: + gplayer->midi_drumpart_change(ch, meep->event.a); + break; + + case ME_WRD: + break; + + case ME_SHERRY: + break; + + case ME_NOTE_STEP: + if (counting_time == 2) + skip_this_event = 1; + break; + } + + /* Recompute time in samples*/ + if ((dt = meep->event.time - at) && !counting_time) + { + samples_to_do = sample_increment * dt; + sample_cum += sample_correction * dt; + if (sample_cum & 0xFFFF0000) + { + samples_to_do += ((sample_cum >> 16) & 0xFFFF); + sample_cum &= 0x0000FFFF; + } + st += samples_to_do; + if (st < 0) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "Overflow the sample counter"); + free(groomed_list); + return NULL; + } + } + else if (counting_time == 1) + counting_time = 0; + + if (meep->event.type == ME_TEMPO) + { + tempo = ch + meep->event.b * 256 + meep->event.a * 65536; + compute_sample_increment(tempo, divisions); + } + + if (!skip_this_event) + { + /* Add the event to the list */ + *lp = meep->event; + lp->time = st; + lp++; + our_event_count++; + } + at = meep->event.time; + meep = meep->next; + } + /* Add an End-of-Track event */ + lp->time = st; + lp->type = ME_EOT; + our_event_count++; + free_midi_list(); + + *eventsp = our_event_count; + *samplesp = st; + return groomed_list; +} + + +static void readmidi_read_init(void) +{ + static int first = 1; + + + /* Put a do-nothing event first in the list for easier processing */ + evlist = current_midi_point = alloc_midi_event(); + evlist->event.time = 0; + evlist->event.type = ME_NONE; + evlist->event.channel = 0; + evlist->event.a = 0; + evlist->event.b = 0; + evlist->prev = NULL; + evlist->next = NULL; + readmidi_error_flag = 0; + event_count = 1; + + karaoke_format = 0; +} + +static void insert_note_steps(void) +{ + MidiEventList *e; + int32_t i, n, at, lasttime, meas, beat; + uint8_t num = 0, denom = 1, a, b; + + e = evlist; + for (i = n = 0; i < event_count - 1 && n < 256 - 1; i++, e = e->next) + if (e->event.type == ME_TIMESIG && e->event.channel == 0) { + if (n == 0 && e->event.time > 0) { /* 4/4 is default */ + SETMIDIEVENT(timesig[n], 0, ME_TIMESIG, 0, 4, 4); + n++; + } + if (n > 0 && e->event.a == timesig[n - 1].a + && e->event.b == timesig[n - 1].b) + continue; /* unchanged */ + if (n > 0 && e->event.time == timesig[n - 1].time) + n--; /* overwrite previous timesig */ + timesig[n++] = e->event; + } + if (n == 0) { + SETMIDIEVENT(timesig[n], 0, ME_TIMESIG, 0, 4, 4); + n++; + } + timesig[n] = timesig[n - 1]; + timesig[n].time = 0x7fffffff; /* stopper */ + lasttime = e->event.time; + readmidi_set_track(0, 1); + at = n = meas = beat = 0; + auto current_file_info = gplayer->get_midi_file_info("", 0); + while (at < lasttime && ! readmidi_error_flag) { + if (at >= timesig[n].time) { + if (beat != 0) + meas++, beat = 0; + num = timesig[n].a, denom = timesig[n].b, n++; + } + a = (meas + 1) & 0xff; + b = (((meas + 1) >> 8) & 0x0f) + ((beat + 1) << 4); + MIDIEVENT(at, ME_NOTE_STEP, 0, a, b); + if (++beat == num) + meas++, beat = 0; + at += current_file_info->divisions * 4 / denom; + } +} + + +static void free_readmidi(void) +{ + reuse_mblock(&mempool); + instruments->free_userdrum(); + instruments->free_userinst(); +} + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +// From here: functions for loading a .mid file. +// Will not be needed when all is done! + + +/* Read variable-length number (7 bits per byte, MSB first) */ +static int32_t getvl(struct timidity_file *tf) +{ + int32_t l; + int c; + + l = 0; + + /* 1 */ + if ((c = tf_getc(tf)) == EOF) + goto eof; + if (!(c & 0x80)) return l | c; + l = (l | (c & 0x7f)) << 7; + + /* 2 */ + if ((c = tf_getc(tf)) == EOF) + goto eof; + if (!(c & 0x80)) return l | c; + l = (l | (c & 0x7f)) << 7; + + /* 3 */ + if ((c = tf_getc(tf)) == EOF) + goto eof; + if (!(c & 0x80)) return l | c; + l = (l | (c & 0x7f)) << 7; + + /* 4 */ + if ((c = tf_getc(tf)) == EOF) + goto eof; + if (!(c & 0x80)) return l | c; + + /* Error */ + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: Illigal Variable-length quantity format.", + tf->filename.c_str()); + return -2; + +eof: + return -1; +} + +/* Print a string from the file, followed by a newline. Any non-ASCII +or unprintable characters will be converted to periods. */ +static char *dumpstring(int type, int32_t len, const char *label, int allocp, + struct timidity_file *tf) +{ + char *si, *so; + int s_maxlen = SAFE_CONVERT_LENGTH(len); + int llen, solen; + + si = (char *)new_segment(&tmpbuffer, len + 1); + so = (char *)new_segment(&tmpbuffer, s_maxlen); + + if (len != tf_read(si, 1, len, tf)) + { + reuse_mblock(&tmpbuffer); + return NULL; + } + si[len] = '\0'; + + auto current_file_info = gplayer->get_midi_file_info("", 0); + if (type == 1 && + current_file_info->format == 1 && + (strncmp(si, "@K", 2) == 0)) + /* Karaoke string should be "@KMIDI KARAOKE FILE" */ + karaoke_format = 1; + + llen = (int)strlen(label); + solen = (int)strlen(si); + if (llen + solen >= MIN_MBLOCK_SIZE) + si[MIN_MBLOCK_SIZE - llen - 1] = '\0'; + + if (allocp) + { + so = safe_strdup(si); + reuse_mblock(&tmpbuffer); + return so; + } + reuse_mblock(&tmpbuffer); + return NULL; +} + +static int read_sysex_event(int32_t at, int me, int32_t len, + struct timidity_file *tf) +{ + uint8_t *val; + MidiEvent ev, evm[260]; /* maximum number of XG bulk dump events */ + int ne, i; + + if (len == 0) + return 0; + if (me != 0xF0) + { + skip(tf, len); + return 0; + } + + val = (uint8_t *)new_segment(&tmpbuffer, len); + if (tf_read(val, 1, len, tf) != len) + { + reuse_mblock(&tmpbuffer); + return -1; + } + if (sc.parse_sysex_event(val, len, &ev, instruments)) + { + ev.time = at; + readmidi_add_event(&ev); + } + if ((ne = sc.parse_sysex_event_multi(val, len, evm, instruments))) + { + for (i = 0; i < ne; i++) { + evm[i].time = at; + readmidi_add_event(&evm[i]); + } + } + + reuse_mblock(&tmpbuffer); + + return 0; +} + +static char *fix_string(char *s) +{ + int i, j, w; + char c; + + if (s == NULL) + return NULL; + while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') + s++; + + /* s =~ tr/ \t\r\n/ /s; */ + w = 0; + for (i = j = 0; (c = s[i]) != '\0'; i++) + { + if (c == '\t' || c == '\r' || c == '\n') + c = ' '; + if (w) + w = (c == ' '); + if (!w) + { + s[j++] = c; + w = (c == ' '); + } + } + + /* s =~ s/ $//; */ + if (j > 0 && s[j - 1] == ' ') + j--; + + s[j] = '\0'; + return s; +} + +static void smf_time_signature(int32_t at, struct timidity_file *tf, int len) +{ + int n, d, c, b; + + /* Time Signature (nn dd cc bb) + * [0]: numerator + * [1]: denominator + * [2]: number of MIDI clocks in a metronome click + * [3]: number of notated 32nd-notes in a MIDI + * quarter-note (24 MIDI Clocks). + */ + + if (len != 4) + { + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Invalid time signature"); + skip(tf, len); + return; + } + + n = tf_getc(tf); + d = (1 << tf_getc(tf)); + c = tf_getc(tf); + b = tf_getc(tf); + + if (n == 0 || d == 0) + { + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Invalid time signature"); + return; + } + + MIDIEVENT(at, ME_TIMESIG, 0, n, d); + MIDIEVENT(at, ME_TIMESIG, 1, c, b); + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Time signature: %d/%d %d clock %d q.n.", n, d, c, b); + auto current_file_info = gplayer->get_midi_file_info("", 0); + if (current_file_info->time_sig_n == -1) + { + current_file_info->time_sig_n = n; + current_file_info->time_sig_d = d; + current_file_info->time_sig_c = c; + current_file_info->time_sig_b = b; + } +} + +static void smf_key_signature(int32_t at, struct timidity_file *tf, int len) +{ + int8_t sf, mi; + /* Key Signature (sf mi) + * sf = -7: 7 flats + * sf = -1: 1 flat + * sf = 0: key of C + * sf = 1: 1 sharp + * sf = 7: 7 sharps + * mi = 0: major key + * mi = 1: minor key + */ + + if (len != 2) { + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Invalid key signature"); + skip(tf, len); + return; + } + sf = tf_getc(tf); + mi = tf_getc(tf); + if (sf < -7 || sf > 7) { + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Invalid key signature"); + return; + } + if (mi != 0 && mi != 1) { + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Invalid key signature"); + return; + } + MIDIEVENT(at, ME_KEYSIG, 0, sf, mi); + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "Key signature: %d %s %s", abs(sf), + (sf < 0) ? "flat(s)" : "sharp(s)", (mi) ? "minor" : "major"); +} + +/* Read a SMF track */ +static int read_smf_track(struct timidity_file *tf, int trackno, int rewindp) +{ + int32_t len, next_pos, pos; + char tmp[4]; + int lastchan, laststatus; + int me, type, a, b, c; + int i; + int32_t smf_at_time; + int note_seen = (!opt_preserve_silence); + + smf_at_time = readmidi_set_track(trackno, rewindp); + + /* Check the formalities */ + if ((tf_read(tmp, 1, 4, tf) != 4) || (tf_read(&len, 4, 1, tf) != 1)) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: Can't read track header.", tf->filename.c_str()); + return -1; + } + len = BE_LONG(len); + next_pos = tf_tell(tf) + len; + if (strncmp(tmp, "MTrk", 4)) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: Corrupt MIDI file.", tf->filename.c_str()); + return -2; + } + + lastchan = laststatus = 0; + auto current_file_info = gplayer->get_midi_file_info("", 0); + + for (;;) + { + if (readmidi_error_flag) + return -1; + if ((len = getvl(tf)) < 0) + return -1; + smf_at_time += len; + if ((i = tf_getc(tf)) == EOF) + { + return -1; + } + + me = (uint8_t)i; + if (me == 0xF0 || me == 0xF7) /* SysEx event */ + { + if ((len = getvl(tf)) < 0) + return -1; + if ((i = read_sysex_event(smf_at_time, me, len, tf)) != 0) + return i; + } + else if (me == 0xFF) /* Meta event */ + { + type = tf_getc(tf); + if ((len = getvl(tf)) < 0) + return -1; + if (type > 0 && type < 16) + { + static const char *label[] = + { + "Text event: ", "Text: ", "Copyright: ", "Track name: ", + "Instrument: ", "Lyric: ", "Marker: ", "Cue point: " + }; + + if (type == 3 && /* Sequence or Track Name */ + (current_file_info->format == 0 || + (current_file_info->format == 1 && + current_read_track == 0))) + { + if (current_file_info->seq_name == NULL) { + char *name = dumpstring(3, len, "Sequence: ", 1, tf); + current_file_info->seq_name = safe_strdup(fix_string(name)); + free(name); + } + else + dumpstring(3, len, "Sequence: ", 0, tf); + } + else if (type == 1 && + current_file_info->first_text == NULL && + (current_file_info->format == 0 || + (current_file_info->format == 1 && + current_read_track == 0))) { + char *name = dumpstring(1, len, "Text: ", 1, tf); + current_file_info->first_text = safe_strdup(fix_string(name)); + free(name); + } + else + dumpstring(type, len, label[(type>7) ? 0 : type], 0, tf); + } + else + { + switch (type) + { + case 0x00: + if (len == 2) + { + a = tf_getc(tf); + b = tf_getc(tf); + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Sequence Number %02x %02x)", a, b); + } + else + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Sequence Number len=%d)", len); + break; + + case 0x2F: /* End of Track */ + pos = tf_tell(tf); + if (pos < next_pos) + tf_seek(tf, next_pos - pos, SEEK_CUR); + return 0; + + case 0x51: /* Tempo */ + a = tf_getc(tf); + b = tf_getc(tf); + c = tf_getc(tf); + MIDIEVENT(smf_at_time, ME_TEMPO, c, a, b); + break; + + case 0x54: + /* SMPTE Offset (hr mn se fr ff) + * hr: hours&type + * 0 1 2 3 4 5 6 7 bits + * 0 |<--type -->|<---- hours [0..23]---->| + * type: 00: 24 frames/second + * 01: 25 frames/second + * 10: 30 frames/second (drop frame) + * 11: 30 frames/second (non-drop frame) + * mn: minis [0..59] + * se: seconds [0..59] + * fr: frames [0..29] + * ff: fractional frames [0..99] + */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(SMPTE Offset meta event)"); + skip(tf, len); + break; + + case 0x58: /* Time Signature */ + smf_time_signature(smf_at_time, tf, len); + break; + + case 0x59: /* Key Signature */ + smf_key_signature(smf_at_time, tf, len); + break; + + case 0x7f: /* Sequencer-Specific Meta-Event */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Sequencer-Specific meta event, length %ld)", + len); + skip(tf, len); + break; + + case 0x20: /* MIDI channel prefix (SMF v1.0) */ + if (len == 1) + { + int midi_channel_prefix = tf_getc(tf); + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(MIDI channel prefix %d)", + midi_channel_prefix); + } + else + skip(tf, len); + break; + + case 0x21: /* MIDI port number */ + /* + if (len == 1) + { + if ((midi_port_number = tf_getc(tf)) + == EOF) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "Warning: %s: Too shorten midi file.", + tf->filename.c_str()); + return -1; + } + midi_port_number &= 0xF; + } + else + */ + skip(tf, len); + break; + + default: + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Meta event type 0x%02x, length %ld)", + type, len); + skip(tf, len); + break; + } + } + } + else /* MIDI event */ + { + a = me; + if (a & 0x80) /* status byte */ + { + #define MERGE_CHANNEL_PORT(ch) ((int)(ch) | (midi_port_number << 4)) + + lastchan = MERGE_CHANNEL_PORT(a & 0x0F); + laststatus = (a >> 4) & 0x07; + if (laststatus != 7) + a = tf_getc(tf) & 0x7F; + } + switch (laststatus) + { + case 0: /* Note off */ + b = tf_getc(tf) & 0x7F; + MIDIEVENT(smf_at_time, ME_NOTEOFF, lastchan, a, b); + break; + + case 1: /* Note on */ + b = tf_getc(tf) & 0x7F; + if (b) + { + if (!note_seen && smf_at_time > 0) + { + MIDIEVENT(0, ME_NOTEON, lastchan, a, 0); + MIDIEVENT(0, ME_NOTEOFF, lastchan, a, 0); + note_seen = 1; + } + MIDIEVENT(smf_at_time, ME_NOTEON, lastchan, a, b); + } + else /* b == 0 means Note Off */ + { + MIDIEVENT(smf_at_time, ME_NOTEOFF, lastchan, a, 0); + } + break; + + case 2: /* Key Pressure */ + b = tf_getc(tf) & 0x7F; + MIDIEVENT(smf_at_time, ME_KEYPRESSURE, lastchan, a, b); + break; + + case 3: /* Control change */ + b = tf_getc(tf); + readmidi_add_ctl_event(smf_at_time, lastchan, a, b); + break; + + case 4: /* Program change */ + MIDIEVENT(smf_at_time, ME_PROGRAM, lastchan, a, 0); + break; + + case 5: /* Channel pressure */ + MIDIEVENT(smf_at_time, ME_CHANNEL_PRESSURE, lastchan, a, 0); + break; + + case 6: /* Pitch wheel */ + b = tf_getc(tf) & 0x7F; + MIDIEVENT(smf_at_time, ME_PITCHWHEEL, lastchan, a, b); + break; + + default: /* case 7: */ + /* Ignore this event */ + switch (lastchan & 0xF) + { + case 2: /* Sys Com Song Position Pntr */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Sys Com Song Position Pntr)"); + tf_getc(tf); + tf_getc(tf); + break; + + case 3: /* Sys Com Song Select(Song #) */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Sys Com Song Select(Song #))"); + tf_getc(tf); + break; + + case 6: /* Sys Com tune request */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Sys Com tune request)"); + break; + case 8: /* Sys real time timing clock */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Sys real time timing clock)"); + break; + case 10: /* Sys real time start */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Sys real time start)"); + break; + case 11: /* Sys real time continue */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Sys real time continue)"); + break; + case 12: /* Sys real time stop */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Sys real time stop)"); + break; + case 14: /* Sys real time active sensing */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + "(Sys real time active sensing)"); + break; +#if 0 + case 15: /* Meta */ + case 0: /* SysEx */ + case 7: /* SysEx */ +#endif + default: /* 1, 4, 5, 9, 13 */ + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "*** Can't happen: status 0x%02X channel 0x%02X", + laststatus, lastchan & 0xF); + break; + } + } + } + } + /*NOTREACHED*/ +} + +static int read_smf_file(struct timidity_file *tf) +{ + int32_t len, divisions; + int16_t format, tracks, divisions_tmp; + int i; + + karaoke_title_flag = 0; + + if (tf_read(&len, 4, 1, tf) != 1) + { + return 1; + } + len = BE_LONG(len); + + tf_read(&format, 2, 1, tf); + tf_read(&tracks, 2, 1, tf); + tf_read(&divisions_tmp, 2, 1, tf); + format = BE_SHORT(format); + tracks = BE_SHORT(tracks); + divisions_tmp = BE_SHORT(divisions_tmp); + + if (divisions_tmp < 0) + { + /* SMPTE time -- totally untested. Got a MIDI file that uses this? */ + divisions = + (int32_t)(-(divisions_tmp / 256)) * (int32_t)(divisions_tmp & 0xFF); + } + else + divisions = (int32_t)divisions_tmp; + + if (len > 6) + { + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, + "%s: MIDI file header size %ld bytes", + tf->filename.c_str(), len); + skip(tf, len - 6); /* skip the excess */ + } + if (format < 0 || format > 2) + { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: Unknown MIDI file format %d", tf->filename.c_str(), format); + return 1; + } + ctl_cmsg(CMSG_INFO, VERB_VERBOSE, "Format: %d Tracks: %d Divisions: %d", + format, tracks, divisions); + + auto current_file_info = gplayer->get_midi_file_info("", 0); + current_file_info->format = format; + current_file_info->tracks = tracks; + current_file_info->divisions = divisions; + current_file_info->hdrsiz = (int16_t)tf_tell(tf); + + switch (format) + { + case 0: + if (read_smf_track(tf, 0, 1)) + { + } + break; + + case 1: + for (i = 0; i < tracks; i++) + { + if (read_smf_track(tf, i, 1)) + { + } + } + break; + + case 2: /* We simply play the tracks sequentially */ + for (i = 0; i < tracks; i++) + { + if (read_smf_track(tf, i, 0)) + { + } + } + break; + } + return 0; +} + +static MidiEvent *read_midi_file(struct timidity_file *tf, int32_t *count, int32_t *sp, + char *fn) +{ + char magic[4]; + MidiEvent *ev; + int err, macbin_check; + + macbin_check = 1; + auto current_file_info = gplayer->get_midi_file_info("", 0); + +#if 0 + COPY_CHANNELMASK(drumchannels, current_file_info->drumchannels); + COPY_CHANNELMASK(drumchannel_mask, current_file_info->drumchannel_mask); + + +#if MAX_CHANNELS > 16 + for (i = 16; i < MAX_CHANNELS; i++) + { + if (!IS_SET_CHANNELMASK(drumchannel_mask, i)) + { + if (IS_SET_CHANNELMASK(drumchannels, i & 0xF)) + SET_CHANNELMASK(drumchannels, i); + else + UNSET_CHANNELMASK(drumchannels, i); + } + } +#endif +#endif + + if (tf_read(magic, 1, 4, tf) != 4) + { + return NULL; + } + + if (memcmp(magic, "MThd", 4) == 0) + { + readmidi_read_init(); + err = read_smf_file(tf); + } + else err = 1; + + if (err) + { + free_midi_list(); + return NULL; + } + + + + insert_note_steps(); + ev = groom_list(current_file_info->divisions, count, sp); + if (ev == NULL) + { + free_midi_list(); + return NULL; + } + current_file_info->samples = *sp; + if (current_file_info->first_text == NULL) + current_file_info->first_text = safe_strdup(""); + current_file_info->readflag = 1; + return ev; +} + + +//////////////////////////////////////// MID file player + +// temporary crutch +static struct timidity_file *open_file(char *name, int decompress, int noise_mode) +{ + FileReader *fr = new FileReader; + if (!fr->Open(name)) + { + delete fr; + return nullptr; + } + auto tf = new timidity_file; + tf->url = fr; + tf->filename = name; + return tf; +} + + +static int play_midi_load_file(char *fn, + MidiEvent **event, + int32_t *nsamples) +{ + int rc; + struct timidity_file *tf; + int32_t nevents; + + *event = NULL; + + ctl_cmsg(CMSG_INFO, VERB_VERBOSE, "MIDI file: %s", fn); + if ((tf = open_file(fn, 1, OF_VERBOSE)) == NULL) + { + return RC_ERROR; + } + + *event = NULL; + rc = RC_OK; + + *event = read_midi_file(tf, &nevents, nsamples, fn); + close_file(tf); + + if (*event == NULL) + { + return RC_ERROR; + } + + ctl_cmsg(CMSG_INFO, VERB_NOISY, + "%d supported events, %d samples, time %d:%02d", + nevents, *nsamples, + *nsamples / playback_rate / 60, + (*nsamples / playback_rate) % 60); + + if ((play_mode->flag&PF_PCM_STREAM)) + { + /* Load instruments + * If opt_realtime_playing, the instruments will be loaded later. + */ + //if (!opt_realtime_playing) + { + rc = RC_OK; + instruments->load_missing_instruments(&rc); + if (RC_IS_SKIP_FILE(rc)) + { + /* Instrument loading is terminated */ + instruments->clear_magic_instruments(); + return rc; + } + } + } + else + instruments->clear_magic_instruments(); /* Clear load markers */ + + return RC_OK; +} + + +int play_midi_file(char *fn) +{ + int rc; + static int last_rc = RC_OK; + MidiEvent *event; + int32_t nsamples; + + /* Set current file information */ + auto current_file_info = gplayer->get_midi_file_info(fn, 1); + + rc = RC_OK; + + + /* Reset restart offset */ + midi_restart_time = 0; + + + rc = play_midi_load_file(fn, &event, &nsamples); + if (RC_IS_SKIP_FILE(rc)) + goto play_end; /* skip playing */ + + rc = gplayer->play_midi(event, nsamples); + +play_end: + + instruments->free_special_patch(-1); + + if (event != NULL) + free(event); + + last_rc = rc; + return rc; +} + +int dumb_pass_playing_list(int number_of_files, char *list_of_files[]) +{ + gplayer = new Player; + for (;;) + { + if (play_midi_file(list_of_files[0]) == RC_QUIT) return 0; + } + delete gplayer; +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/readmidic.cpp b/src/sound/timidity++/readmidic.cpp new file mode 100644 index 0000000000..521bf54f34 --- /dev/null +++ b/src/sound/timidity++/readmidic.cpp @@ -0,0 +1,2732 @@ +/* +TiMidity++ -- MIDI to WAVE converter and player +Copyright (C) 1999-2009 Masanao Izumo +Copyright (C) 1995 Tuukka Toivonen + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include + +#include "timidity.h" +#include "instrum.h" +#include "playmidi.h" + +namespace TimidityPlus +{ + + +inline void SETMIDIEVENT(MidiEvent &e, int32_t at, uint32_t t, uint32_t ch, uint32_t pa, uint32_t pb) +{ + (e).time = (at); + (e).type = (t); + (e).channel = (uint8_t)(ch); + (e).a = (uint8_t)(pa); + (e).b = (uint8_t)(pb); +} + +#define MERGE_CHANNEL_PORT(ch) ((int)(ch) | (midi_port_number << 4)) +#define MERGE_CHANNEL_PORT2(ch, port) ((int)(ch) | ((int)port << 4)) + + +static const struct ctl_chg_types { + unsigned char mtype; + int ttype; +} ctl_chg_list[] = { + { 0, ME_TONE_BANK_MSB }, +{ 1, ME_MODULATION_WHEEL }, +{ 2, ME_BREATH }, +{ 4, ME_FOOT }, +{ 5, ME_PORTAMENTO_TIME_MSB }, +{ 6, ME_DATA_ENTRY_MSB }, +{ 7, ME_MAINVOLUME }, +{ 8, ME_BALANCE }, +{ 10, ME_PAN }, +{ 11, ME_EXPRESSION }, +{ 32, ME_TONE_BANK_LSB }, +{ 37, ME_PORTAMENTO_TIME_LSB }, +{ 38, ME_DATA_ENTRY_LSB }, +{ 64, ME_SUSTAIN }, +{ 65, ME_PORTAMENTO }, +{ 66, ME_SOSTENUTO }, +{ 67, ME_SOFT_PEDAL }, +{ 68, ME_LEGATO_FOOTSWITCH }, +{ 69, ME_HOLD2 }, +{ 71, ME_HARMONIC_CONTENT }, +{ 72, ME_RELEASE_TIME }, +{ 73, ME_ATTACK_TIME }, +{ 74, ME_BRIGHTNESS }, +{ 84, ME_PORTAMENTO_CONTROL }, +{ 91, ME_REVERB_EFFECT }, +{ 92, ME_TREMOLO_EFFECT }, +{ 93, ME_CHORUS_EFFECT }, +{ 94, ME_CELESTE_EFFECT }, +{ 95, ME_PHASER_EFFECT }, +{ 96, ME_RPN_INC }, +{ 97, ME_RPN_DEC }, +{ 98, ME_NRPN_LSB }, +{ 99, ME_NRPN_MSB }, +{ 100, ME_RPN_LSB }, +{ 101, ME_RPN_MSB }, +{ 120, ME_ALL_SOUNDS_OFF }, +{ 121, ME_RESET_CONTROLLERS }, +{ 123, ME_ALL_NOTES_OFF }, +{ 126, ME_MONO }, +{ 127, ME_POLY }, +}; + +int convert_midi_control_change(int chn, int type, int val, MidiEvent *ev_ret) +{ + int etype = -1; + for (auto &t : ctl_chg_list) + { + if (t.mtype == type) + { + if (val > 127) val = 127; + ev_ret->type = t.ttype; + ev_ret->channel = chn; + ev_ret->a = val; + ev_ret->b = 0; + return 1; + } + } + return 0; +} + +/* Map XG types onto GS types. XG should eventually have its own tables */ +static int set_xg_reverb_type(int msb, int lsb) +{ + int type = 4; + + if ((msb == 0x00) || + (msb >= 0x05 && msb <= 0x0F) || + (msb >= 0x14)) /* NO EFFECT */ + { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "XG Set Reverb Type (NO EFFECT %d %d)", msb, lsb); + return -1; + } + + switch (msb) + { + case 0x01: + type = 3; /* Hall 1 */ + break; + case 0x02: + type = 0; /* Room 1 */ + break; + case 0x03: + type = 3; /* Stage 1 -> Hall 1 */ + break; + case 0x04: + type = 5; /* Plate */ + break; + default: + type = 4; /* unsupported -> Hall 2 */ + break; + } + if (lsb == 0x01) + { + switch (msb) + { + case 0x01: + type = 4; /* Hall 2 */ + break; + case 0x02: + type = 1; /* Room 2 */ + break; + case 0x03: + type = 4; /* Stage 2 -> Hall 2 */ + break; + default: + break; + } + } + if (lsb == 0x02 && msb == 0x02) + type = 2; /* Room 3 */ + + ctl_cmsg(CMSG_INFO, VERB_NOISY, "XG Set Reverb Type (%d)", type); + return type; +} + + +/* Map XG types onto GS types. XG should eventually have its own tables */ +static int set_xg_chorus_type(int msb, int lsb) +{ + int type = 2; + + if ((msb >= 0x00 && msb <= 0x40) || + (msb >= 0x45 && msb <= 0x47) || + (msb >= 0x49)) /* NO EFFECT */ + { + ctl_cmsg(CMSG_INFO, VERB_NOISY, "XG Set Chorus Type (NO EFFECT %d %d)", msb, lsb); + return -1; + } + + switch (msb) + { + case 0x41: + type = 0; /* Chorus 1 */ + break; + case 0x42: + type = 0; /* Celeste 1 -> Chorus 1 */ + break; + case 0x43: + type = 5; + break; + default: + type = 2; /* unsupported -> Chorus 3 */ + break; + } + if (lsb == 0x01) + { + switch (msb) + { + case 0x41: + type = 1; /* Chorus 2 */ + break; + case 0x42: + type = 1; /* Celeste 2 -> Chorus 2 */ + break; + default: + break; + } + } + else if (lsb == 0x02) + { + switch (msb) + { + case 0x41: + type = 2; /* Chorus 3 */ + break; + case 0x42: + type = 2; /* Celeste 3 -> Chorus 3 */ + break; + default: + break; + } + } + else if (lsb == 0x08) + { + switch (msb) + { + case 0x41: + type = 3; /* Chorus 4 */ + break; + case 0x42: + type = 3; /* Celeste 4 -> Chorus 4 */ + break; + default: + break; + } + } + + ctl_cmsg(CMSG_INFO, VERB_NOISY, "XG Set Chorus Type (%d)", type); + return type; +} + +static int block_to_part(int block, int port) +{ + int p; + p = block & 0x0F; + if (p == 0) { p = 9; } + else if (p <= 9) { p--; } + return MERGE_CHANNEL_PORT2(p, port); +} + +static uint16_t gs_convert_master_vol(int vol) +{ + double v; + + if (vol >= 0x7f) + return 0xffff; + v = (double)vol * (0xffff / 127.0); + if (v >= 0xffff) + return 0xffff; + return (uint16_t)v; +} + +static uint16_t gm_convert_master_vol(uint16_t v1, uint16_t v2) +{ + return (((v1 & 0x7f) | ((v2 & 0x7f) << 7)) << 2) | 3; +} + + +/* XG SysEx parsing function by Eric A. Welsh +* Also handles GS patch+bank changes +* +* This function provides basic support for XG Bulk Dump and Parameter +* Change SysEx events +*/ +int SysexConvert::parse_sysex_event_multi(uint8_t *val, int32_t len, MidiEvent *evm, Instruments *instruments) +{ + int num_events = 0; /* Number of events added */ + + uint32_t channel_tt; + int i, j; + static uint8_t xg_reverb_type_msb = 0x01, xg_reverb_type_lsb = 0x00; + static uint8_t xg_chorus_type_msb = 0x41, xg_chorus_type_lsb = 0x00; + + /* Effect 1 or Multi EQ */ + if (len >= 8 && + val[0] == 0x43 && /* Yamaha ID */ + val[2] == 0x4C && /* XG Model ID */ + ((val[1] < 0x10 && val[5] == 0x02) || /* Bulk Dump*/ + (val[1] >= 0x10 && val[3] == 0x02))) /* Parameter Change */ + { + uint8_t addhigh, addmid, addlow; /* Addresses */ + uint8_t *body; /* SysEx body */ + int ent, v; /* Entry # of sub-event */ + uint8_t *body_end; /* End of SysEx body */ + + if (val[1] < 0x10) /* Bulk Dump */ + { + addhigh = val[5]; + addmid = val[6]; + addlow = val[7]; + body = val + 8; + body_end = val + len - 3; + } + else /* Parameter Change */ + { + addhigh = val[3]; + addmid = val[4]; + addlow = val[5]; + body = val + 6; + body_end = val + len - 2; + } + + /* set the SYSEX_XG_MSB info */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, 0, addhigh, addmid); + num_events++; + + for (ent = addlow; body <= body_end; body++, ent++) { + if (addmid == 0x01) { /* Effect 1 */ + switch (ent) { + case 0x00: /* Reverb Type MSB */ + xg_reverb_type_msb = *body; +#if 0 /* XG specific reverb is not supported yet, use GS instead */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; +#endif + break; + + case 0x01: /* Reverb Type LSB */ + xg_reverb_type_lsb = *body; +#if 0 /* XG specific reverb is not supported yet, use GS instead */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; +#else + v = set_xg_reverb_type(xg_reverb_type_msb, xg_reverb_type_lsb); + if (v >= 0) { + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, v, 0x05); + num_events++; + } +#endif + break; + + case 0x0C: /* Reverb Return */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x20: /* Chorus Type MSB */ + xg_chorus_type_msb = *body; +#if 0 /* XG specific chorus is not supported yet, use GS instead */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; +#endif + break; + + case 0x21: /* Chorus Type LSB */ + xg_chorus_type_lsb = *body; +#if 0 /* XG specific chorus is not supported yet, use GS instead */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; +#else + v = set_xg_chorus_type(xg_chorus_type_msb, xg_chorus_type_lsb); + if (v >= 0) { + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, v, 0x0D); + num_events++; + } +#endif + break; + + case 0x2C: /* Chorus Return */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + default: + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + } + } + else if (addmid == 0x40) { /* Multi EQ */ + switch (ent) { + case 0x00: /* EQ type */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x01: /* EQ gain1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x02: /* EQ frequency1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x03: /* EQ Q1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x04: /* EQ shape1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x05: /* EQ gain2 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x06: /* EQ frequency2 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x07: /* EQ Q2 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x09: /* EQ gain3 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x0A: /* EQ frequency3 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x0B: /* EQ Q3 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x0D: /* EQ gain4 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x0E: /* EQ frequency4 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x0F: /* EQ Q4 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x11: /* EQ gain5 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x12: /* EQ frequency5 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x13: /* EQ Q5 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x14: /* EQ shape5 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + default: + break; + } + } + } + } + + /* Effect 2 (Insertion Effects) */ + else if (len >= 8 && + val[0] == 0x43 && /* Yamaha ID */ + val[2] == 0x4C && /* XG Model ID */ + ((val[1] < 0x10 && val[5] == 0x03) || /* Bulk Dump*/ + (val[1] >= 0x10 && val[3] == 0x03))) /* Parameter Change */ + { + uint8_t addhigh, addmid, addlow; /* Addresses */ + uint8_t *body; /* SysEx body */ + int ent; /* Entry # of sub-event */ + uint8_t *body_end; /* End of SysEx body */ + + if (val[1] < 0x10) /* Bulk Dump */ + { + addhigh = val[5]; + addmid = val[6]; + addlow = val[7]; + body = val + 8; + body_end = val + len - 3; + } + else /* Parameter Change */ + { + addhigh = val[3]; + addmid = val[4]; + addlow = val[5]; + body = val + 6; + body_end = val + len - 2; + } + + /* set the SYSEX_XG_MSB info */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, 0, addhigh, addmid); + num_events++; + + for (ent = addlow; body <= body_end; body++, ent++) { + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + } + } + + /* XG Multi Part Data parameter change */ + else if (len >= 10 && + val[0] == 0x43 && /* Yamaha ID */ + val[2] == 0x4C && /* XG Model ID */ + ((val[1] < 0x10 && val[5] == 0x08 && /* Bulk Dump */ + (val[4] == 0x29 || val[4] == 0x3F)) || /* Blocks 1 or 2 */ + (val[1] >= 0x10 && val[3] == 0x08))) /* Parameter Change */ + { + uint8_t addhigh, addmid, addlow; /* Addresses */ + uint8_t *body; /* SysEx body */ + uint8_t p; /* Channel part number [0..15] */ + int ent; /* Entry # of sub-event */ + uint8_t *body_end; /* End of SysEx body */ + + if (val[1] < 0x10) /* Bulk Dump */ + { + addhigh = val[5]; + addmid = val[6]; + addlow = val[7]; + body = val + 8; + p = addmid; + body_end = val + len - 3; + } + else /* Parameter Change */ + { + addhigh = val[3]; + addmid = val[4]; + addlow = val[5]; + body = val + 6; + p = addmid; + body_end = val + len - 2; + } + + /* set the SYSEX_XG_MSB info */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, p, addhigh, addmid); + num_events++; + + for (ent = addlow; body <= body_end; body++, ent++) { + switch (ent) { + case 0x00: /* Element Reserve */ + /* ctl_cmsg(CMSG_INFO, VERB_NOISY, "Element Reserve is not supported. (CH:%d VAL:%d)", p, *body); */ + break; + + case 0x01: /* bank select MSB */ + SETMIDIEVENT(evm[num_events], 0, ME_TONE_BANK_MSB, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x02: /* bank select LSB */ + SETMIDIEVENT(evm[num_events], 0, ME_TONE_BANK_LSB, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x03: /* program number */ + SETMIDIEVENT(evm[num_events], 0, ME_PROGRAM, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x04: /* Rcv CHANNEL */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, p, *body, 0x99); + num_events++; + break; + + case 0x05: /* mono/poly mode */ + if (*body == 0) { SETMIDIEVENT(evm[num_events], 0, ME_MONO, p, 0, SYSEX_TAG); } + else { SETMIDIEVENT(evm[num_events], 0, ME_POLY, p, 0, SYSEX_TAG); } + num_events++; + break; + + case 0x06: /* Same Note Number Key On Assign */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, p, *body, ent); + num_events++; + break; + + case 0x07: /* Part Mode */ + drum_setup_xg[*body] = p; + SETMIDIEVENT(evm[num_events], 0, ME_DRUMPART, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x08: /* note shift */ + SETMIDIEVENT(evm[num_events], 0, ME_KEYSHIFT, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x09: /* Detune 1st bit */ + break; + + case 0x0A: /* Detune 2nd bit */ + break; + + case 0x0B: /* volume */ + SETMIDIEVENT(evm[num_events], 0, ME_MAINVOLUME, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x0C: /* Velocity Sense Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, p, *body, 0x21); + num_events++; + break; + + case 0x0D: /* Velocity Sense Offset */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, p, *body, 0x22); + num_events++; + break; + + case 0x0E: /* pan */ + if (*body == 0) { + SETMIDIEVENT(evm[num_events], 0, ME_RANDOM_PAN, p, 0, SYSEX_TAG); + } + else { + SETMIDIEVENT(evm[num_events], 0, ME_PAN, p, *body, SYSEX_TAG); + } + num_events++; + break; + + case 0x0F: /* Note Limit Low */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x42); + num_events++; + break; + + case 0x10: /* Note Limit High */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x43); + num_events++; + break; + + case 0x11: /* Dry Level */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, p, *body, ent); + num_events++; + break; + + case 0x12: /* chorus send */ + SETMIDIEVENT(evm[num_events], 0, ME_CHORUS_EFFECT, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x13: /* reverb send */ + SETMIDIEVENT(evm[num_events], 0, ME_REVERB_EFFECT, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x14: /* Variation Send */ + SETMIDIEVENT(evm[num_events], 0, ME_CELESTE_EFFECT, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x15: /* Vibrato Rate */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x08, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x16: /* Vibrato Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x09, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x17: /* Vibrato Delay */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x0A, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x18: /* Filter Cutoff Frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x20, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x19: /* Filter Resonance */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x21, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x1A: /* EG Attack Time */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x63, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x1B: /* EG Decay Time */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x64, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x1C: /* EG Release Time */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x66, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x1D: /* MW Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x16); + num_events++; + break; + + case 0x1E: /* MW Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x17); + num_events++; + break; + + case 0x1F: /* MW Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x18); + num_events++; + break; + + case 0x20: /* MW LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x1A); + num_events++; + break; + + case 0x21: /* MW LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x1B); + num_events++; + break; + + case 0x22: /* MW LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x1C); + num_events++; + break; + + case 0x23: /* bend pitch control */ + SETMIDIEVENT(evm[num_events], 0, ME_RPN_MSB, p, 0, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_RPN_LSB, p, 0, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, (*body - 0x40) & 0x7F, SYSEX_TAG); + num_events += 3; + break; + + case 0x24: /* Bend Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x22); + num_events++; + break; + + case 0x25: /* Bend Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x23); + num_events++; + break; + + case 0x26: /* Bend LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x25); + num_events++; + break; + + case 0x27: /* Bend LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x26); + num_events++; + break; + + case 0x28: /* Bend LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x27); + num_events++; + break; + + case 0x30: /* Rcv Pitch Bend */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x48); + num_events++; + break; + + case 0x31: /* Rcv Channel Pressure */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x49); + num_events++; + break; + + case 0x32: /* Rcv Program Change */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4A); + num_events++; + break; + + case 0x33: /* Rcv Control Change */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4B); + num_events++; + break; + + case 0x34: /* Rcv Poly Pressure */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4C); + num_events++; + break; + + case 0x35: /* Rcv Note Message */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4D); + num_events++; + break; + + case 0x36: /* Rcv RPN */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4E); + num_events++; + break; + + case 0x37: /* Rcv NRPN */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4F); + num_events++; + break; + + case 0x38: /* Rcv Modulation */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x50); + num_events++; + break; + + case 0x39: /* Rcv Volume */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x51); + num_events++; + break; + + case 0x3A: /* Rcv Pan */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x52); + num_events++; + break; + + case 0x3B: /* Rcv Expression */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x53); + num_events++; + break; + + case 0x3C: /* Rcv Hold1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x54); + num_events++; + break; + + case 0x3D: /* Rcv Portamento */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x55); + num_events++; + break; + + case 0x3E: /* Rcv Sostenuto */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x56); + num_events++; + break; + + case 0x3F: /* Rcv Soft */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x57); + num_events++; + break; + + case 0x40: /* Rcv Bank Select */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x58); + num_events++; + break; + + case 0x41: /* scale tuning */ + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + SETMIDIEVENT(evm[num_events], 0, ME_SCALE_TUNING, p, ent - 0x41, *body - 64); + num_events++; + break; + + case 0x4D: /* CAT Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x00); + num_events++; + break; + + case 0x4E: /* CAT Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x01); + num_events++; + break; + + case 0x4F: /* CAT Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x02); + num_events++; + break; + + case 0x50: /* CAT LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x04); + num_events++; + break; + + case 0x51: /* CAT LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x05); + num_events++; + break; + + case 0x52: /* CAT LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x06); + num_events++; + break; + + case 0x53: /* PAT Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0B); + num_events++; + break; + + case 0x54: /* PAT Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0C); + num_events++; + break; + + case 0x55: /* PAT Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0D); + num_events++; + break; + + case 0x56: /* PAT LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0F); + num_events++; + break; + + case 0x57: /* PAT LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x10); + num_events++; + break; + + case 0x58: /* PAT LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x11); + num_events++; + break; + + case 0x59: /* AC1 Controller Number */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "AC1 Controller Number is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x5A: /* AC1 Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x2C); + num_events++; + break; + + case 0x5B: /* AC1 Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x2D); + num_events++; + break; + + case 0x5C: /* AC1 Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x2E); + num_events++; + break; + + case 0x5D: /* AC1 LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x30); + num_events++; + break; + + case 0x5E: /* AC1 LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x31); + num_events++; + break; + + case 0x5F: /* AC1 LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x32); + num_events++; + break; + + case 0x60: /* AC2 Controller Number */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "AC2 Controller Number is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x61: /* AC2 Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x37); + num_events++; + break; + + case 0x62: /* AC2 Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x38); + num_events++; + break; + + case 0x63: /* AC2 Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x39); + num_events++; + break; + + case 0x64: /* AC2 LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x3B); + num_events++; + break; + + case 0x65: /* AC2 LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x3C); + num_events++; + break; + + case 0x66: /* AC2 LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x3D); + num_events++; + break; + + case 0x67: /* Portamento Switch */ + SETMIDIEVENT(evm[num_events], 0, ME_PORTAMENTO, p, *body, SYSEX_TAG); + num_events++; + + case 0x68: /* Portamento Time */ + SETMIDIEVENT(evm[num_events], 0, ME_PORTAMENTO_TIME_MSB, p, *body, SYSEX_TAG); + num_events++; + + case 0x69: /* Pitch EG Initial Level */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Pitch EG Initial Level is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x6A: /* Pitch EG Attack Time */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Pitch EG Attack Time is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x6B: /* Pitch EG Release Level */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Pitch EG Release Level is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x6C: /* Pitch EG Release Time */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Pitch EG Release Time is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x6D: /* Velocity Limit Low */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x44); + num_events++; + break; + + case 0x6E: /* Velocity Limit High */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x45); + num_events++; + break; + + case 0x70: /* Bend Pitch Low Control */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Bend Pitch Low Control is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x71: /* Filter EG Depth */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Filter EG Depth is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x72: /* EQ BASS */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x30, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x73: /* EQ TREBLE */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x31, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x76: /* EQ BASS frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x34, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x77: /* EQ TREBLE frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x35, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + default: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Unsupported XG Bulk Dump SysEx. (ADDR:%02X %02X %02X VAL:%02X)", addhigh, addlow, ent, *body); + continue; + break; + } + } + } + + /* XG Drum Setup */ + else if (len >= 10 && + val[0] == 0x43 && /* Yamaha ID */ + val[2] == 0x4C && /* XG Model ID */ + ((val[1] < 0x10 && (val[5] & 0xF0) == 0x30) || /* Bulk Dump*/ + (val[1] >= 0x10 && (val[3] & 0xF0) == 0x30))) /* Parameter Change */ + { + uint8_t addhigh, addmid, addlow; /* Addresses */ + uint8_t *body; /* SysEx body */ + uint8_t dp, note; /* Channel part number [0..15] */ + int ent; /* Entry # of sub-event */ + uint8_t *body_end; /* End of SysEx body */ + + if (val[1] < 0x10) /* Bulk Dump */ + { + addhigh = val[5]; + addmid = val[6]; + addlow = val[7]; + body = val + 8; + body_end = val + len - 3; + } + else /* Parameter Change */ + { + addhigh = val[3]; + addmid = val[4]; + addlow = val[5]; + body = val + 6; + body_end = val + len - 2; + } + + dp = drum_setup_xg[(addhigh & 0x0F) + 1]; + note = addmid; + + /* set the SYSEX_XG_MSB info */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, dp, addhigh, addmid); + num_events++; + + for (ent = addlow; body <= body_end; body++, ent++) { + switch (ent) { + case 0x00: /* Pitch Coarse */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x18, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x01: /* Pitch Fine */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x19, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x02: /* Level */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1A, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x03: /* Alternate Group */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Alternate Group is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body); + break; + case 0x04: /* Pan */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1C, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x05: /* Reverb Send */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1D, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x06: /* Chorus Send */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1E, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x07: /* Variation Send */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1F, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x08: /* Key Assign */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Key Assign is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body); + break; + case 0x09: /* Rcv Note Off */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_MSB, dp, note, 0); + SETMIDIEVENT(evm[num_events + 1], 0, ME_SYSEX_LSB, dp, *body, 0x46); + num_events += 2; + break; + case 0x0A: /* Rcv Note On */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_MSB, dp, note, 0); + SETMIDIEVENT(evm[num_events + 1], 0, ME_SYSEX_LSB, dp, *body, 0x47); + num_events += 2; + break; + case 0x0B: /* Filter Cutoff Frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x14, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x0C: /* Filter Resonance */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x15, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x0D: /* EG Attack */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x16, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x0E: /* EG Decay1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, dp, *body, ent); + num_events++; + break; + case 0x0F: /* EG Decay2 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, dp, *body, ent); + num_events++; + break; + case 0x20: /* EQ BASS */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x30, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x21: /* EQ TREBLE */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x31, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x24: /* EQ BASS frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x34, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x25: /* EQ TREBLE frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x35, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x50: /* High Pass Filter Cutoff Frequency */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "High Pass Filter Cutoff Frequency is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body); + break; + case 0x60: /* Velocity Pitch Sense */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Velocity Pitch Sense is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body); + break; + case 0x61: /* Velocity LPF Cutoff Sense */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Velocity LPF Cutoff Sense is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body); + break; + default: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Unsupported XG Bulk Dump SysEx. (ADDR:%02X %02X %02X VAL:%02X)", addhigh, addmid, ent, *body); + break; + } + } + } + + /* parsing GS System Exclusive Message... + * + * val[4] == Parameter Address(High) + * val[5] == Parameter Address(Middle) + * val[6] == Parameter Address(Low) + * val[7]... == Data... + * val[last] == Checksum(== 128 - (sum of addresses&data bytes % 128)) + */ + else if (len >= 9 && + val[0] == 0x41 && /* Roland ID */ + val[1] == 0x10 && /* Device ID */ + val[2] == 0x42 && /* GS Model ID */ + val[3] == 0x12) /* Data Set Command */ + { + uint8_t p, dp, udn, gslen, port = 0; + int i, addr, addr_h, addr_m, addr_l, checksum; + p = block_to_part(val[5], midi_port_number); + + /* calculate checksum */ + checksum = 0; + for (gslen = 9; gslen < len; gslen++) + if (val[gslen] == 0xF7) + break; + for (i = 4; i> 4]; + + /* calculate user drumset number */ + udn = (val[5] & 0xF0) >> 4; + + addr_h = val[4]; + addr_m = val[5]; + addr_l = val[6]; + if (addr_h == 0x50) { /* for double module mode */ + port = 1; + p = block_to_part(val[5], port); + addr_h = 0x40; + } + else if (addr_h == 0x51) { + port = 1; + p = block_to_part(val[5], port); + addr_h = 0x41; + } + addr = (((int32_t)addr_h) << 16 | ((int32_t)addr_m) << 8 | (int32_t)addr_l); + + switch (addr_h) { + case 0x40: + if ((addr & 0xFFF000) == 0x401000) { + switch (addr & 0xFF) { + case 0x00: /* Tone Number */ + SETMIDIEVENT(evm[0], 0, ME_TONE_BANK_MSB, p, val[7], SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_PROGRAM, p, val[8], SYSEX_TAG); + num_events += 2; + break; + case 0x02: /* Rx. Channel */ + if (val[7] == 0x10) { + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, + block_to_part(val[5], + midi_port_number ^ port), 0x80, 0x45); + } + else { + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, + block_to_part(val[5], + midi_port_number ^ port), + MERGE_CHANNEL_PORT2(val[7], + midi_port_number ^ port), 0x45); + } + num_events++; + break; + case 0x03: /* Rx. Pitch Bend */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x48); + num_events++; + break; + case 0x04: /* Rx. Channel Pressure */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x49); + num_events++; + break; + case 0x05: /* Rx. Program Change */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4A); + num_events++; + break; + case 0x06: /* Rx. Control Change */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4B); + num_events++; + break; + case 0x07: /* Rx. Poly Pressure */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4C); + num_events++; + break; + case 0x08: /* Rx. Note Message */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4D); + num_events++; + break; + case 0x09: /* Rx. RPN */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4E); + num_events++; + break; + case 0x0A: /* Rx. NRPN */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4F); + num_events++; + break; + case 0x0B: /* Rx. Modulation */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x50); + num_events++; + break; + case 0x0C: /* Rx. Volume */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x51); + num_events++; + break; + case 0x0D: /* Rx. Panpot */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x52); + num_events++; + break; + case 0x0E: /* Rx. Expression */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x53); + num_events++; + break; + case 0x0F: /* Rx. Hold1 */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x54); + num_events++; + break; + case 0x10: /* Rx. Portamento */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x55); + num_events++; + break; + case 0x11: /* Rx. Sostenuto */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x56); + num_events++; + break; + case 0x12: /* Rx. Soft */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x57); + num_events++; + break; + case 0x13: /* MONO/POLY Mode */ + if (val[7] == 0) { SETMIDIEVENT(evm[0], 0, ME_MONO, p, val[7], SYSEX_TAG); } + else { SETMIDIEVENT(evm[0], 0, ME_POLY, p, val[7], SYSEX_TAG); } + num_events++; + break; + case 0x14: /* Assign Mode */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x24); + num_events++; + break; + case 0x15: /* Use for Rhythm Part */ + if (val[7]) { + rhythm_part[val[7] - 1] = p; + } + break; + case 0x16: /* Pitch Key Shift (dummy. see parse_sysex_event()) */ + break; + case 0x17: /* Pitch Offset Fine */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x26); + num_events++; + break; + case 0x19: /* Part Level */ + SETMIDIEVENT(evm[0], 0, ME_MAINVOLUME, p, val[7], SYSEX_TAG); + num_events++; + break; + case 0x1A: /* Velocity Sense Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x21); + num_events++; + break; + case 0x1B: /* Velocity Sense Offset */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x22); + num_events++; + break; + case 0x1C: /* Part Panpot */ + if (val[7] == 0) { + SETMIDIEVENT(evm[0], 0, ME_RANDOM_PAN, p, 0, SYSEX_TAG); + } + else { + SETMIDIEVENT(evm[0], 0, ME_PAN, p, val[7], SYSEX_TAG); + } + num_events++; + break; + case 0x1D: /* Keyboard Range Low */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x42); + num_events++; + break; + case 0x1E: /* Keyboard Range High */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x43); + num_events++; + break; + case 0x1F: /* CC1 Controller Number */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC1 Controller Number is not supported. (CH:%d VAL:%d)", p, val[7]); + break; + case 0x20: /* CC2 Controller Number */ + ctl_cmsg(CMSG_INFO, VERB_NOISY, "CC2 Controller Number is not supported. (CH:%d VAL:%d)", p, val[7]); + break; + case 0x21: /* Chorus Send Level */ + SETMIDIEVENT(evm[0], 0, ME_CHORUS_EFFECT, p, val[7], SYSEX_TAG); + num_events++; + break; + case 0x22: /* Reverb Send Level */ + SETMIDIEVENT(evm[0], 0, ME_REVERB_EFFECT, p, val[7], SYSEX_TAG); + num_events++; + break; + case 0x23: /* Rx. Bank Select */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x58); + num_events++; + break; + case 0x24: /* Rx. Bank Select LSB */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x59); + num_events++; + break; + case 0x2C: /* Delay Send Level */ + SETMIDIEVENT(evm[0], 0, ME_CELESTE_EFFECT, p, val[7], SYSEX_TAG); + num_events++; + break; + case 0x2A: /* Pitch Fine Tune */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x00, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + SETMIDIEVENT(evm[3], 0, ME_DATA_ENTRY_LSB, p, val[8], SYSEX_TAG); + num_events += 4; + break; + case 0x30: /* TONE MODIFY1: Vibrato Rate */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x08, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x31: /* TONE MODIFY2: Vibrato Depth */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x09, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x32: /* TONE MODIFY3: TVF Cutoff Freq */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x20, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x33: /* TONE MODIFY4: TVF Resonance */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x21, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x34: /* TONE MODIFY5: TVF&TVA Env.attack */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x63, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x35: /* TONE MODIFY6: TVF&TVA Env.decay */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x64, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x36: /* TONE MODIFY7: TVF&TVA Env.release */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x66, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x37: /* TONE MODIFY8: Vibrato Delay */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x0A, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x40: /* Scale Tuning */ + for (i = 0; i < 12; i++) { + SETMIDIEVENT(evm[i], + 0, ME_SCALE_TUNING, p, i, val[i + 7] - 64); + } + num_events += 12; + break; + default: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + else if ((addr & 0xFFF000) == 0x402000) { + switch (addr & 0xFF) { + case 0x00: /* MOD Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x16); + num_events++; + break; + case 0x01: /* MOD TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x17); + num_events++; + break; + case 0x02: /* MOD Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x18); + num_events++; + break; + case 0x03: /* MOD LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x19); + num_events++; + break; + case 0x04: /* MOD LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1A); + num_events++; + break; + case 0x05: /* MOD LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1B); + num_events++; + break; + case 0x06: /* MOD LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1C); + num_events++; + break; + case 0x07: /* MOD LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1D); + num_events++; + break; + case 0x08: /* MOD LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1E); + num_events++; + break; + case 0x09: /* MOD LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1F); + num_events++; + break; + case 0x0A: /* MOD LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x20); + num_events++; + break; + case 0x10: /* !!!FIXME!!! Bend Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_RPN_MSB, p, 0, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_RPN_LSB, p, 0, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, (val[7] - 0x40) & 0x7F, SYSEX_TAG); + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x21); + num_events += 4; + break; + case 0x11: /* Bend TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x22); + num_events++; + break; + case 0x12: /* Bend Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x23); + num_events++; + break; + case 0x13: /* Bend LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x24); + num_events++; + break; + case 0x14: /* Bend LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x25); + num_events++; + break; + case 0x15: /* Bend LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x26); + num_events++; + break; + case 0x16: /* Bend LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x27); + num_events++; + break; + case 0x17: /* Bend LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x28); + num_events++; + break; + case 0x18: /* Bend LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x29); + num_events++; + break; + case 0x19: /* Bend LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2A); + num_events++; + break; + case 0x1A: /* Bend LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2B); + num_events++; + break; + case 0x20: /* CAf Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x00); + num_events++; + break; + case 0x21: /* CAf TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x01); + num_events++; + break; + case 0x22: /* CAf Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x02); + num_events++; + break; + case 0x23: /* CAf LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x03); + num_events++; + break; + case 0x24: /* CAf LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x04); + num_events++; + break; + case 0x25: /* CAf LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x05); + num_events++; + break; + case 0x26: /* CAf LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x06); + num_events++; + break; + case 0x27: /* CAf LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x07); + num_events++; + break; + case 0x28: /* CAf LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x08); + num_events++; + break; + case 0x29: /* CAf LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x09); + num_events++; + break; + case 0x2A: /* CAf LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0A); + num_events++; + break; + case 0x30: /* PAf Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0B); + num_events++; + break; + case 0x31: /* PAf TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0C); + num_events++; + break; + case 0x32: /* PAf Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0D); + num_events++; + break; + case 0x33: /* PAf LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0E); + num_events++; + break; + case 0x34: /* PAf LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0F); + num_events++; + break; + case 0x35: /* PAf LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x10); + num_events++; + break; + case 0x36: /* PAf LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x11); + num_events++; + break; + case 0x37: /* PAf LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x12); + num_events++; + break; + case 0x38: /* PAf LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x13); + num_events++; + break; + case 0x39: /* PAf LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x14); + num_events++; + break; + case 0x3A: /* PAf LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x15); + num_events++; + break; + case 0x40: /* CC1 Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2C); + num_events++; + break; + case 0x41: /* CC1 TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2D); + num_events++; + break; + case 0x42: /* CC1 Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2E); + num_events++; + break; + case 0x43: /* CC1 LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2F); + num_events++; + break; + case 0x44: /* CC1 LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x30); + num_events++; + break; + case 0x45: /* CC1 LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x31); + num_events++; + break; + case 0x46: /* CC1 LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x32); + num_events++; + break; + case 0x47: /* CC1 LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x33); + num_events++; + break; + case 0x48: /* CC1 LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x34); + num_events++; + break; + case 0x49: /* CC1 LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x35); + num_events++; + break; + case 0x4A: /* CC1 LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x36); + num_events++; + break; + case 0x50: /* CC2 Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x37); + num_events++; + break; + case 0x51: /* CC2 TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x38); + num_events++; + break; + case 0x52: /* CC2 Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x39); + num_events++; + break; + case 0x53: /* CC2 LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3A); + num_events++; + break; + case 0x54: /* CC2 LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3B); + num_events++; + break; + case 0x55: /* CC2 LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3C); + num_events++; + break; + case 0x56: /* CC2 LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3D); + num_events++; + break; + case 0x57: /* CC2 LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3E); + num_events++; + break; + case 0x58: /* CC2 LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3F); + num_events++; + break; + case 0x59: /* CC2 LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x40); + num_events++; + break; + case 0x5A: /* CC2 LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x41); + num_events++; + break; + default: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + else if ((addr & 0xFFFF00) == 0x400100) { + switch (addr & 0xFF) { + case 0x30: /* Reverb Macro */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x05); + num_events++; + break; + case 0x31: /* Reverb Character */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x06); + num_events++; + break; + case 0x32: /* Reverb Pre-LPF */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x07); + num_events++; + break; + case 0x33: /* Reverb Level */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x08); + num_events++; + break; + case 0x34: /* Reverb Time */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x09); + num_events++; + break; + case 0x35: /* Reverb Delay Feedback */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0A); + num_events++; + break; + case 0x36: /* Unknown Reverb Parameter */ + break; + case 0x37: /* Reverb Predelay Time */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0C); + num_events++; + break; + case 0x38: /* Chorus Macro */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0D); + num_events++; + break; + case 0x39: /* Chorus Pre-LPF */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0E); + num_events++; + break; + case 0x3A: /* Chorus Level */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0F); + num_events++; + break; + case 0x3B: /* Chorus Feedback */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x10); + num_events++; + break; + case 0x3C: /* Chorus Delay */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x11); + num_events++; + break; + case 0x3D: /* Chorus Rate */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x12); + num_events++; + break; + case 0x3E: /* Chorus Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x13); + num_events++; + break; + case 0x3F: /* Chorus Send Level to Reverb */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x14); + num_events++; + break; + case 0x40: /* Chorus Send Level to Delay */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x15); + num_events++; + break; + case 0x50: /* Delay Macro */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x16); + num_events++; + break; + case 0x51: /* Delay Pre-LPF */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x17); + num_events++; + break; + case 0x52: /* Delay Time Center */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x18); + num_events++; + break; + case 0x53: /* Delay Time Ratio Left */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x19); + num_events++; + break; + case 0x54: /* Delay Time Ratio Right */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1A); + num_events++; + break; + case 0x55: /* Delay Level Center */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1B); + num_events++; + break; + case 0x56: /* Delay Level Left */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1C); + num_events++; + break; + case 0x57: /* Delay Level Right */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1D); + num_events++; + break; + case 0x58: /* Delay Level */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1E); + num_events++; + break; + case 0x59: /* Delay Feedback */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1F); + num_events++; + break; + case 0x5A: /* Delay Send Level to Reverb */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x20); + num_events++; + break; + default: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + else if ((addr & 0xFFFF00) == 0x400200) { + switch (addr & 0xFF) { /* EQ Parameter */ + case 0x00: /* EQ LOW FREQ */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x01); + num_events++; + break; + case 0x01: /* EQ LOW GAIN */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x02); + num_events++; + break; + case 0x02: /* EQ HIGH FREQ */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x03); + num_events++; + break; + case 0x03: /* EQ HIGH GAIN */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x04); + num_events++; + break; + default: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + else if ((addr & 0xFFFF00) == 0x400300) { + switch (addr & 0xFF) { /* Insertion Effect Parameter */ + case 0x00: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x27); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_GS_LSB, p, val[8], 0x28); + num_events += 2; + break; + case 0x03: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x29); + num_events++; + break; + case 0x04: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2A); + num_events++; + break; + case 0x05: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2B); + num_events++; + break; + case 0x06: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2C); + num_events++; + break; + case 0x07: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2D); + num_events++; + break; + case 0x08: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2E); + num_events++; + break; + case 0x09: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2F); + num_events++; + break; + case 0x0A: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x30); + num_events++; + break; + case 0x0B: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x31); + num_events++; + break; + case 0x0C: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x32); + num_events++; + break; + case 0x0D: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x33); + num_events++; + break; + case 0x0E: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x34); + num_events++; + break; + case 0x0F: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x35); + num_events++; + break; + case 0x10: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x36); + num_events++; + break; + case 0x11: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x37); + num_events++; + break; + case 0x12: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x38); + num_events++; + break; + case 0x13: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x39); + num_events++; + break; + case 0x14: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3A); + num_events++; + break; + case 0x15: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3B); + num_events++; + break; + case 0x16: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3C); + num_events++; + break; + case 0x17: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3D); + num_events++; + break; + case 0x18: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3E); + num_events++; + break; + case 0x19: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3F); + num_events++; + break; + case 0x1B: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x40); + num_events++; + break; + case 0x1C: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x41); + num_events++; + break; + case 0x1D: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x42); + num_events++; + break; + case 0x1E: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x43); + num_events++; + break; + case 0x1F: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x44); + num_events++; + break; + default: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + else if ((addr & 0xFFF000) == 0x404000) { + switch (addr & 0xFF) { + case 0x00: /* TONE MAP NUMBER */ + SETMIDIEVENT(evm[0], 0, ME_TONE_BANK_LSB, p, val[7], SYSEX_TAG); + num_events++; + break; + case 0x01: /* TONE MAP-0 NUMBER */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x25); + num_events++; + break; + case 0x20: /* EQ ON/OFF */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x00); + num_events++; + break; + case 0x22: /* EFX ON/OFF */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x23); + num_events++; + break; + default: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + break; + case 0x41: + switch (addr & 0xF00) { + case 0x100: /* Play Note Number */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_GS_LSB, dp, val[7], 0x47); + num_events += 2; + break; + case 0x200: + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1A, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x400: + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1C, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x500: + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1D, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x600: + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1E, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x700: /* Rx. Note Off */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x46); + num_events += 2; + break; + case 0x800: /* Rx. Note On */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x47); + num_events += 2; + break; + case 0x900: + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1F, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + default: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + break; + case 0x21: /* User Drumset */ + switch (addr & 0xF00) { + case 0x100: /* Play Note */ + instruments->get_userdrum(64 + udn, val[6])->play_note = val[7]; + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_GS_LSB, dp, val[7], 0x47); + num_events += 2; + break; + case 0x200: /* Level */ + instruments->get_userdrum(64 + udn, val[6])->level = val[7]; + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1A, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x300: /* Assign Group */ + instruments->get_userdrum(64 + udn, val[6])->assign_group = val[7]; + if (val[7] != 0) { instruments->recompute_userdrum_altassign(udn + 64, val[7]); } + break; + case 0x400: /* Panpot */ + instruments->get_userdrum(64 + udn, val[6])->pan = val[7]; + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1C, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x500: /* Reverb Send Level */ + instruments->get_userdrum(64 + udn, val[6])->reverb_send_level = val[7]; + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1D, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x600: /* Chorus Send Level */ + instruments->get_userdrum(64 + udn, val[6])->chorus_send_level = val[7]; + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1E, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x700: /* Rx. Note Off */ + instruments->get_userdrum(64 + udn, val[6])->rx_note_off = val[7]; + SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x46); + num_events += 2; + break; + case 0x800: /* Rx. Note On */ + instruments->get_userdrum(64 + udn, val[6])->rx_note_on = val[7]; + SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x47); + num_events += 2; + break; + case 0x900: /* Delay Send Level */ + instruments->get_userdrum(64 + udn, val[6])->delay_send_level = val[7]; + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1F, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0xA00: /* Source Map */ + instruments->get_userdrum(64 + udn, val[6])->source_map = val[7]; + break; + case 0xB00: /* Source Prog */ + instruments->get_userdrum(64 + udn, val[6])->source_prog = val[7]; + break; +#if !defined(TIMIDITY_TOOLS) + case 0xC00: /* Source Note */ + instruments->get_userdrum(64 + udn, val[6])->source_note = val[7]; + break; +#endif + default: + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + break; + case 0x00: /* System */ + switch (addr & 0xfff0) { + case 0x0100: /* Channel Msg Rx Port (A) */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, + block_to_part(addr & 0xf, 0), val[7], 0x46); + num_events++; + break; + case 0x0110: /* Channel Msg Rx Port (B) */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, + block_to_part(addr & 0xf, 1), val[7], 0x46); + num_events++; + break; + default: + /* ctl_cmsg(CMSG_INFO,VERB_NOISY, "Unsupported GS SysEx. " + "(ADDR:%02X %02X %02X VAL:%02X %02X)", + addr_h, addr_m, addr_l, val[7], val[8]);*/ + break; + } + break; + } + } + + /* Non-RealTime / RealTime Universal SysEx messages + * 0 0x7e(Non-RealTime) / 0x7f(RealTime) + * 1 SysEx device ID. Could be from 0x00 to 0x7f. + * 0x7f means disregard device. + * 2 Sub ID + * ... + * E 0xf7 + */ + else if (len > 4 && val[0] >= 0x7e) + switch (val[2]) { + case 0x01: /* Sample Dump header */ + case 0x02: /* Sample Dump packet */ + case 0x03: /* Dump Request */ + case 0x04: /* Device Control */ + if (val[3] == 0x05) { /* Global Parameter Control */ + if (val[7] == 0x01 && val[8] == 0x01) { /* Reverb */ + for (i = 9; i < len && val[i] != 0xf7; i += 2) { + switch (val[i]) { + case 0x00: /* Reverb Type */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, 0, val[i + 1], 0x60); + num_events++; + break; + case 0x01: /* Reverb Time */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x09); + num_events++; + break; + } + } + } + else if (val[7] == 0x01 && val[8] == 0x02) { /* Chorus */ + for (i = 9; i < len && val[i] != 0xf7; i += 2) { + switch (val[i]) { + case 0x00: /* Chorus Type */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, 0, val[i + 1], 0x61); + num_events++; + break; + case 0x01: /* Modulation Rate */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x12); + num_events++; + break; + case 0x02: /* Modulation Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x13); + num_events++; + break; + case 0x03: /* Feedback */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x10); + num_events++; + break; + case 0x04: /* Send To Reverb */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x14); + num_events++; + break; + } + } + } + } + break; + case 0x05: /* Sample Dump extensions */ + case 0x06: /* Inquiry Message */ + case 0x07: /* File Dump */ + break; + case 0x08: /* MIDI Tuning Standard */ + switch (val[3]) { + case 0x01: + SETMIDIEVENT(evm[0], 0, ME_BULK_TUNING_DUMP, 0, val[4], 0); + for (i = 0; i < 128; i++) { + SETMIDIEVENT(evm[i * 2 + 1], 0, ME_BULK_TUNING_DUMP, + 1, i, val[i * 3 + 21]); + SETMIDIEVENT(evm[i * 2 + 2], 0, ME_BULK_TUNING_DUMP, + 2, val[i * 3 + 22], val[i * 3 + 23]); + } + num_events += 257; + break; + case 0x02: + SETMIDIEVENT(evm[0], 0, ME_SINGLE_NOTE_TUNING, + 0, val[4], 0); + for (i = 0; i < val[5]; i++) { + SETMIDIEVENT(evm[i * 2 + 1], 0, ME_SINGLE_NOTE_TUNING, + 1, val[i * 4 + 6], val[i * 4 + 7]); + SETMIDIEVENT(evm[i * 2 + 2], 0, ME_SINGLE_NOTE_TUNING, + 2, val[i * 4 + 8], val[i * 4 + 9]); + } + num_events += val[5] * 2 + 1; + break; + case 0x0b: + channel_tt = ((val[4] & 0x03) << 14 | val[5] << 7 | val[6]) + << ((val[4] >> 2) * 16); + if (val[1] == 0x7f) { + SETMIDIEVENT(evm[0], 0, ME_MASTER_TEMPER_TYPE, + 0, val[7], (val[0] == 0x7f)); + num_events++; + } + else { + for (i = j = 0; i < 32; i++) + if (channel_tt & 1 << i) { + SETMIDIEVENT(evm[j], 0, ME_TEMPER_TYPE, + MERGE_CHANNEL_PORT(i), + val[7], (val[0] == 0x7f)); + j++; + } + num_events += j; + } + break; + case 0x0c: + SETMIDIEVENT(evm[0], 0, ME_USER_TEMPER_ENTRY, + 0, val[4], val[21]); + for (i = 0; i < val[21]; i++) { + SETMIDIEVENT(evm[i * 5 + 1], 0, ME_USER_TEMPER_ENTRY, + 1, val[i * 10 + 22], val[i * 10 + 23]); + SETMIDIEVENT(evm[i * 5 + 2], 0, ME_USER_TEMPER_ENTRY, + 2, val[i * 10 + 24], val[i * 10 + 25]); + SETMIDIEVENT(evm[i * 5 + 3], 0, ME_USER_TEMPER_ENTRY, + 3, val[i * 10 + 26], val[i * 10 + 27]); + SETMIDIEVENT(evm[i * 5 + 4], 0, ME_USER_TEMPER_ENTRY, + 4, val[i * 10 + 28], val[i * 10 + 29]); + SETMIDIEVENT(evm[i * 5 + 5], 0, ME_USER_TEMPER_ENTRY, + 5, val[i * 10 + 30], val[i * 10 + 31]); + } + num_events += val[21] * 5 + 1; + break; + } + break; + case 0x09: /* General MIDI Message */ + switch (val[3]) { + case 0x01: /* Channel Pressure */ + for (i = 5; i < len && val[i] != 0xf7; i += 2) { + switch (val[i]) { + case 0x00: /* Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x00); + num_events++; + break; + case 0x01: /* Filter Cutoff Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x01); + num_events++; + break; + case 0x02: /* Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x02); + num_events++; + break; + case 0x03: /* LFO Pitch Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x04); + num_events++; + break; + case 0x04: /* LFO Filter Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x05); + num_events++; + break; + case 0x05: /* LFO Amplitude Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x06); + num_events++; + break; + } + } + break; + } + break; + case 0x7b: /* End of File */ + case 0x7c: /* Handshaking Message: Wait */ + case 0x7d: /* Handshaking Message: Cancel */ + case 0x7e: /* Handshaking Message: NAK */ + case 0x7f: /* Handshaking Message: ACK */ + break; + } + + return(num_events); +} + + +int SysexConvert::parse_sysex_event(uint8_t *val, int32_t len, MidiEvent *ev, Instruments *instruments) +{ + uint16_t vol; + + if (len >= 10 && + val[0] == 0x41 && /* Roland ID */ + val[1] == 0x10 && /* Device ID */ + val[2] == 0x42 && /* GS Model ID */ + val[3] == 0x12) /* Data Set Command */ + { + /* Roland GS-Based Synthesizers. + * val[4..6] is address, val[7..len-2] is body. + * + * GS Channel part number + * 0 10 + * 1-9 1-9 + * 10-15 11-16 + */ + + int32_t addr, checksum, i; /* SysEx address */ + uint8_t *body; /* SysEx body */ + uint8_t p, gslen; /* Channel part number [0..15] */ + + /* check Checksum */ + checksum = 0; + for (gslen = 9; gslen < len; gslen++) + if (val[gslen] == 0xF7) + break; + for (i = 4; i < gslen - 1; i++) { + checksum += val[i]; + } + if (((128 - (checksum & 0x7F)) & 0x7F) != val[gslen - 1]) { + return 0; + } + + addr = (((int32_t)val[4]) << 16 | + ((int32_t)val[5]) << 8 | + (int32_t)val[6]); + body = val + 7; + p = (uint8_t)((addr >> 8) & 0xF); + if (p == 0) + p = 9; + else if (p <= 9) + p--; + p = MERGE_CHANNEL_PORT(p); + + if (val[4] == 0x50) { /* for double module mode */ + p += 16; + addr = (((int32_t)0x40) << 16 | + ((int32_t)val[5]) << 8 | + (int32_t)val[6]); + } + else { /* single module mode */ + addr = (((int32_t)val[4]) << 16 | + ((int32_t)val[5]) << 8 | + (int32_t)val[6]); + } + + if ((addr & 0xFFF0FF) == 0x401015) /* Rhythm Parts */ + { + /* GS drum part check from Masaaki Koyanagi's patch (GS_Drum_Part_Check()) */ + /* Modified by Masanao Izumo */ + SETMIDIEVENT(*ev, 0, ME_DRUMPART, p, *body, SYSEX_TAG); + return 1; + } + + if ((addr & 0xFFF0FF) == 0x401016) /* Key Shift */ + { + SETMIDIEVENT(*ev, 0, ME_KEYSHIFT, p, *body, SYSEX_TAG); + return 1; + } + + if (addr == 0x400000) /* Master Tune, not for SMF */ + { + uint16_t tune = ((body[1] & 0xF) << 8) | ((body[2] & 0xF) << 4) | (body[3] & 0xF); + + if (tune < 0x18) + tune = 0x18; + else if (tune > 0x7E8) + tune = 0x7E8; + SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune & 0xFF, (tune >> 8) & 0x7F); + return 1; + } + + if (addr == 0x400004) /* Master Volume */ + { + vol = gs_convert_master_vol(*body); + SETMIDIEVENT(*ev, 0, ME_MASTER_VOLUME, + 0, vol & 0xFF, (vol >> 8) & 0xFF); + return 1; + } + + if ((addr & 0xFFF0FF) == 0x401019) /* Volume on/off */ + { +#if 0 + SETMIDIEVENT(*ev, 0, ME_VOLUME_ONOFF, p, *body >= 64, SYSEX_TAG); +#endif + return 0; + } + + if ((addr & 0xFFF0FF) == 0x401002) /* Receive channel on/off */ + { +#if 0 + SETMIDIEVENT(*ev, 0, ME_RECEIVE_CHANNEL, (uint8_t)p, *body >= 64, SYSEX_TAG); +#endif + return 0; + } + + if (0x402000 <= addr && addr <= 0x402F5A) /* Controller Routing */ + return 0; + + if ((addr & 0xFFF0FF) == 0x401040) /* Alternate Scale Tunings */ + return 0; + + if ((addr & 0xFFFFF0) == 0x400130) /* Changing Effects */ + { +#if 0 + struct chorus_text_gs_t *chorus_text = &(reverb->chorus_status_gs.text); + switch (addr & 0xF) + { + case 0x8: /* macro */ + memcpy(chorus_text->macro, body, 3); + break; + case 0x9: /* PRE-LPF */ + memcpy(chorus_text->pre_lpf, body, 3); + break; + case 0xa: /* level */ + memcpy(chorus_text->level, body, 3); + break; + case 0xb: /* feed back */ + memcpy(chorus_text->feed_back, body, 3); + break; + case 0xc: /* delay */ + memcpy(chorus_text->delay, body, 3); + break; + case 0xd: /* rate */ + memcpy(chorus_text->rate, body, 3); + break; + case 0xe: /* depth */ + memcpy(chorus_text->depth, body, 3); + break; + case 0xf: /* send level */ + memcpy(chorus_text->send_level, body, 3); + break; + default: break; + } +#endif + return 0; + } + + if ((addr & 0xFFF0FF) == 0x401003) /* Rx Pitch-Bend */ + return 0; + + if (addr == 0x400110) /* Voice Reserve */ + { +#if 0 + if (len >= 25) + memcpy(reverb->chorus_status_gs.text.voice_reserve, body, 18); +#endif + return 0; + } + + if (addr == 0x40007F || /* GS Reset */ + addr == 0x00007F) /* SC-88 Single Module */ + { + SETMIDIEVENT(*ev, 0, ME_RESET, 0, GS_SYSTEM_MODE, SYSEX_TAG); + return 1; + } + return 0; + } + + if (len > 9 && + val[0] == 0x41 && /* Roland ID */ + val[1] == 0x10 && /* Device ID */ + val[2] == 0x45 && + val[3] == 0x12 && + val[4] == 0x10 && + val[5] == 0x00 && + val[6] == 0x00) + { + return 0; + } + + if (len > 9 && /* GS lcd event. by T.Nogami*/ + val[0] == 0x41 && /* Roland ID */ + val[1] == 0x10 && /* Device ID */ + val[2] == 0x45 && + val[3] == 0x12 && + val[4] == 0x10 && + val[5] == 0x01 && + val[6] == 0x00) + { + return 0; + } + + /* val[1] can have values other than 0x10 for the XG ON event, which + * work on real XG hardware. I have several midi that use 0x1f instead + * of 0x10. playmidi.h lists 0x10 - 0x13 as MU50/80/90/100. I don't + * know what real world Device Number 0x1f would correspond to, but the + * XG spec says the entire 0x1n range is valid, and 0x1f works on real + * hardware, so I have modified the check below to accept the entire + * 0x1n range. + * + * I think there are/were some hacks somewhere in playmidi.c (?) to work + * around non- 0x10 values, but this fixes the root of the problem, which + * allows the server mode to handle XG initialization properly as well. + */ + if (len >= 8 && + val[0] == 0x43 && + (val[1] >= 0x10 && val[1] <= 0x1f) && + val[2] == 0x4C) + { + int addr = (val[3] << 16) | (val[4] << 8) | val[5]; + + if (addr == 0x00007E) /* XG SYSTEM ON */ + { + SETMIDIEVENT(*ev, 0, ME_RESET, 0, XG_SYSTEM_MODE, SYSEX_TAG); + return 1; + } + else if (addr == 0x000000 && len >= 12) /* XG Master Tune */ + { + uint16_t tune = ((val[7] & 0xF) << 8) | ((val[8] & 0xF) << 4) | (val[9] & 0xF); + + if (tune > 0x7FF) + tune = 0x7FF; + SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune & 0xFF, (tune >> 8) & 0x7F); + return 1; + } + } + + if (len >= 7 && val[0] == 0x7F && val[1] == 0x7F) + { + if (val[2] == 0x04 && val[3] == 0x03) /* GM2 Master Fine Tune */ + { + uint16_t tune = (val[4] & 0x7F) | (val[5] << 7) | 0x4000; + + SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune & 0xFF, (tune >> 8) & 0x7F); + return 1; + } + if (val[2] == 0x04 && val[3] == 0x04) /* GM2 Master Coarse Tune */ + { + uint8_t tune = val[5]; + + if (tune < 0x28) + tune = 0x28; + else if (tune > 0x58) + tune = 0x58; + SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune, 0x80); + return 1; + } + } + + /* Non-RealTime / RealTime Universal SysEx messages + * 0 0x7e(Non-RealTime) / 0x7f(RealTime) + * 1 SysEx device ID. Could be from 0x00 to 0x7f. + * 0x7f means disregard device. + * 2 Sub ID + * ... + * E 0xf7 + */ + if (len > 4 && val[0] >= 0x7e) + switch (val[2]) { + case 0x01: /* Sample Dump header */ + case 0x02: /* Sample Dump packet */ + case 0x03: /* Dump Request */ + break; + case 0x04: /* MIDI Time Code Setup/Device Control */ + switch (val[3]) { + case 0x01: /* Master Volume */ + vol = gm_convert_master_vol(val[4], val[5]); + if (val[1] == 0x7f) { + SETMIDIEVENT(*ev, 0, ME_MASTER_VOLUME, 0, + vol & 0xff, vol >> 8 & 0xff); + } + else { + SETMIDIEVENT(*ev, 0, ME_MAINVOLUME, + MERGE_CHANNEL_PORT(val[1]), + vol >> 8 & 0xff, 0); + } + return 1; + } + break; + case 0x05: /* Sample Dump extensions */ + case 0x06: /* Inquiry Message */ + case 0x07: /* File Dump */ + break; + case 0x08: /* MIDI Tuning Standard */ + switch (val[3]) { + case 0x0a: + SETMIDIEVENT(*ev, 0, ME_TEMPER_KEYSIG, 0, + val[4] - 0x40 + val[5] * 16, (val[0] == 0x7f)); + return 1; + } + break; + case 0x09: /* General MIDI Message */ + /* GM System Enable/Disable */ + if (val[3] == 1) { + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "SysEx: GM System On"); + SETMIDIEVENT(*ev, 0, ME_RESET, 0, GM_SYSTEM_MODE, 0); + } + else if (val[3] == 3) { + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "SysEx: GM2 System On"); + SETMIDIEVENT(*ev, 0, ME_RESET, 0, GM2_SYSTEM_MODE, 0); + } + else { + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "SysEx: GM System Off"); + SETMIDIEVENT(*ev, 0, ME_RESET, 0, DEFAULT_SYSTEM_MODE, 0); + } + return 1; + case 0x7b: /* End of File */ + case 0x7c: /* Handshaking Message: Wait */ + case 0x7d: /* Handshaking Message: Cancel */ + case 0x7e: /* Handshaking Message: NAK */ + case 0x7f: /* Handshaking Message: ACK */ + break; + } + + return 0; +} +} \ No newline at end of file diff --git a/src/sound/timidity++/recache.cpp b/src/sound/timidity++/recache.cpp new file mode 100644 index 0000000000..c8893d6b90 --- /dev/null +++ b/src/sound/timidity++/recache.cpp @@ -0,0 +1,440 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + recache.c + + Code related to resample cache. +*/ + +#include +#include + +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "tables.h" +#include "mblock.h" +#include "recache.h" +#include "resample.h" + +namespace TimidityPlus +{ + +#define CACHE_DATA_LEN (allocate_cache_size / sizeof(sample_t)) + +inline uint32_t sp_hash(Sample *sp, int note) +{ + return ((uint32_t)(intptr_t)(sp)+(uint32_t)(note)); +} + + +#define RESAMPLATION_CACHE _x = do_resamplation(src, ofs, &resrc); \ + dest[i] = (int16_t) ((_x > 32767) ? 32767 \ + : ((_x < -32768) ? -32768 : _x)) + + + + +void Recache::free_cache_data(void) { + free(cache_data); + reuse_mblock(&hash_entry_pool); +} + +void Recache::resamp_cache_reset(void) +{ + if (cache_data == NULL) { + cache_data = (sample_t *) + safe_large_malloc((CACHE_DATA_LEN + 1) * sizeof(sample_t)); + memset(cache_data, 0, (CACHE_DATA_LEN + 1) * sizeof(sample_t)); + init_mblock(&hash_entry_pool); + } + cache_data_len = 0; + memset(cache_hash_table, 0, sizeof(cache_hash_table)); + memset(channel_note_table, 0, sizeof(channel_note_table)); + reuse_mblock(&hash_entry_pool); +} + +struct cache_hash *Recache::resamp_cache_fetch(Sample *sp, int note) +{ + unsigned int addr; + struct cache_hash *p; + + if (sp->vibrato_control_ratio || (sp->modes & MODES_PINGPONG) + || (sp->sample_rate == playback_rate + && sp->root_freq == get_note_freq(sp, sp->note_to_use))) + return NULL; + addr = sp_hash(sp, note) % HASH_TABLE_SIZE; + p = cache_hash_table[addr]; + while (p && (p->note != note || p->sp != sp)) + p = p->next; + if (p && p->resampled != NULL) + return p; + return NULL; +} + +void Recache::resamp_cache_refer_on(Voice *vp, int32_t sample_start) +{ + unsigned int addr; + struct cache_hash *p; + int note, ch; + + ch = vp->channel; + if (vp->vibrato_control_ratio || player->channel[ch].portamento + || (vp->sample->modes & MODES_PINGPONG) + || vp->orig_frequency != vp->frequency + || (vp->sample->sample_rate == playback_rate + && vp->sample->root_freq + == get_note_freq(vp->sample, vp->sample->note_to_use))) + return; + note = vp->note; + if (channel_note_table[ch].cache[note]) + resamp_cache_refer_off(ch, note, sample_start); + addr = sp_hash(vp->sample, note) % HASH_TABLE_SIZE; + p = cache_hash_table[addr]; + while (p && (p->note != note || p->sp != vp->sample)) + p = p->next; + if (! p) { + p = (struct cache_hash *) + new_segment(&hash_entry_pool, sizeof(struct cache_hash)); + p->cnt = 0; + p->note = vp->note; + p->sp = vp->sample; + p->resampled = NULL; + p->next = cache_hash_table[addr]; + cache_hash_table[addr] = p; + } + channel_note_table[ch].cache[note] = p; + channel_note_table[ch].on[note] = sample_start; +} + +void Recache::resamp_cache_refer_off(int ch, int note, int32_t sample_end) +{ + int32_t sample_start, len; + struct cache_hash *p; + Sample *sp; + + p = channel_note_table[ch].cache[note]; + if (p == NULL) + return; + sp = p->sp; + if (sp->sample_rate == playback_rate + && sp->root_freq == get_note_freq(sp, sp->note_to_use)) + return; + sample_start = channel_note_table[ch].on[note]; + len = sample_end - sample_start; + if (len < 0) { + channel_note_table[ch].cache[note] = NULL; + return; + } + if (! (sp->modes & MODES_LOOPING)) { + double a; + int32_t slen; + + a = ((double) sp->root_freq * playback_rate) + / ((double) sp->sample_rate * get_note_freq(sp, note)); + slen = (int32_t) ((sp->data_length >> FRACTION_BITS) * a); + if (len > slen) + len = slen; + } + p->cnt += len; + channel_note_table[ch].cache[note] = NULL; +} + +void Recache::resamp_cache_refer_alloff(int ch, int32_t sample_end) +{ + int i; + + for (i = 0; i < 128; i++) + resamp_cache_refer_off(ch, i, sample_end); +} + +void Recache::resamp_cache_create(void) +{ + int i, skip; + int32_t n, t1, t2, total; + struct cache_hash **array; + + /* It is NP completion that solve the best cache hit rate. + * So I thought better algorism O(n log n), but not a best solution. + * Follows implementation takes good hit rate, and it is fast. + */ + n = t1 = t2 = 0; + total = 0; + /* set size per count */ + for (i = 0; i < HASH_TABLE_SIZE; i++) { + struct cache_hash *p, *q; + + p = cache_hash_table[i], q = NULL; + while (p) { + struct cache_hash *tmp; + + t1 += p->cnt; + tmp = p, p = p->next; + if (tmp->cnt > 0) { + Sample *sp; + splen_t newlen; + + sp = tmp->sp; + sample_resamp_info(sp, tmp->note, NULL, NULL, &newlen); + if (newlen > 0) { + total += tmp->cnt; + tmp->r = (double) newlen / tmp->cnt; + tmp->next = q, q = tmp; + n++; + } + } + } + cache_hash_table[i] = q; + } + if (n == 0) { + ctl_cmsg(CMSG_INFO, VERB_VERBOSE, "No pre-resampling cache hit"); + return; + } + array = (struct cache_hash **) new_segment(&hash_entry_pool, + n * sizeof(struct cache_hash *)); + n = 0; + for (i = 0; i < HASH_TABLE_SIZE; i++) { + struct cache_hash *p; + + for (p = cache_hash_table[i]; p; p = p->next) + array[n++] = p; + } + if ((uint32_t)total > CACHE_DATA_LEN) + qsort_cache_array(array, 0, n - 1); + skip = 0; + for (i = 0; i < n; i++) { + if (array[i]->r != 0 + && cache_resampling(array[i]) == CACHE_RESAMPLING_OK) + t2 += array[i]->cnt; + else + skip++; + } + /* update cache_hash_table */ + if (skip) + for (i = 0; i < HASH_TABLE_SIZE; i++) { + struct cache_hash *p, *q; + + p = cache_hash_table[i], q = NULL; + while (p) { + struct cache_hash *tmp; + + tmp = p, p = p->next; + if (tmp->resampled) + tmp->next = q, q = tmp; + } + cache_hash_table[i] = q; + } +} + +double Recache::sample_resamp_info(Sample *sp, int note, + splen_t *loop_start, splen_t *loop_end, splen_t *data_length) +{ + splen_t xls, xle, ls, le, ll, newlen; + double a, xxls, xxle, xn; + + a = ((double) sp->sample_rate * get_note_freq(sp, note)) + / ((double) sp->root_freq * playback_rate); + a = TIM_FSCALENEG((double) (int32_t) TIM_FSCALE(a, FRACTION_BITS), + FRACTION_BITS); + xn = sp->data_length / a; + if (xn >= SPLEN_T_MAX) { + /* Ignore this sample */ + *data_length = 0; + return 0.0; + } + newlen = (splen_t) (TIM_FSCALENEG(xn, FRACTION_BITS) + 0.5); + ls = sp->loop_start; + le = sp->loop_end; + ll = le - ls; + xxls = ls / a + 0.5; + if (xxls >= SPLEN_T_MAX) { + /* Ignore this sample */ + *data_length = 0; + return 0.0; + } + xls = (splen_t) xxls; + xxle = le / a + 0.5; + if (xxle >= SPLEN_T_MAX) { + /* Ignore this sample */ + *data_length = 0; + return 0.0; + } + xle = (splen_t) xxle; + if ((sp->modes & MODES_LOOPING) + && ((xle - xls) >> FRACTION_BITS) < MIN_LOOPLEN) { + splen_t n; + splen_t newxle; + double xl; /* Resampled new loop length */ + double xnewxle; + + xl = ll / a; + if (xl >= SPLEN_T_MAX) { + /* Ignore this sample */ + *data_length = 0; + return 0.0; + } + n = (splen_t) (0.0001 + MIN_LOOPLEN + / TIM_FSCALENEG(xl, FRACTION_BITS)) + 1; + xnewxle = le / a + n * xl + 0.5; + if (xnewxle >= SPLEN_T_MAX) { + /* Ignore this sample */ + *data_length = 0; + return 0.0; + } + newxle = (splen_t) xnewxle; + newlen += (newxle - xle) >> FRACTION_BITS; + xle = newxle; + } + if (loop_start) + *loop_start = (splen_t) (xls & ~FRACTION_MASK); + if (loop_end) + *loop_end = (splen_t) (xle & ~FRACTION_MASK); + *data_length = newlen << FRACTION_BITS; + return a; +} + +void Recache::qsort_cache_array(struct cache_hash **a, int32_t first, int32_t last) +{ + int32_t i = first, j = last; + struct cache_hash *x, *t; + + if (j - i < SORT_THRESHOLD) { + insort_cache_array(a + i, j - i + 1); + return; + } + x = a[(first + last) / 2]; + for (;;) { + while (a[i]->r < x->r) + i++; + while (x->r < a[j]->r) + j--; + if (i >= j) + break; + t = a[i], a[i] = a[j], a[j] = t; + i++, j--; + } + if (first < i - 1) + qsort_cache_array(a, first, i - 1); + if (j + 1 < last) + qsort_cache_array(a, j + 1, last); +} + +void Recache::insort_cache_array(struct cache_hash **data, int32_t n) +{ + int32_t i, j; + struct cache_hash *x; + + for (i = 1; i < n; i++) { + x = data[i]; + for (j = i - 1; j >= 0 && x->r < data[j]->r; j--) + data[j + 1] = data[j]; + data[j + 1] = x; + } +} + +int Recache::cache_resampling(struct cache_hash *p) +{ + Sample *sp, *newsp; + sample_t *src, *dest; + splen_t newlen, ofs, le, ls, ll, xls, xle; + int32_t incr, _x; + resample_rec_t resrc; + double a; + int8_t note; + + sp = p->sp; + if (sp->note_to_use) + note = sp->note_to_use; + else + note = p->note; + a = sample_resamp_info(sp, note, &xls, &xle, &newlen); + if (newlen == 0) + return CACHE_RESAMPLING_NOTOK; + newlen >>= FRACTION_BITS; + if (cache_data_len + newlen + 1 > CACHE_DATA_LEN) + return CACHE_RESAMPLING_NOTOK; + resrc.loop_start = ls = sp->loop_start; + resrc.loop_end = le = sp->loop_end; + resrc.data_length = sp->data_length; + ll = sp->loop_end - sp->loop_start; + dest = cache_data + cache_data_len; + src = sp->data; + newsp = (Sample *) new_segment(&hash_entry_pool, sizeof(Sample)); + memcpy(newsp, sp, sizeof(Sample)); + newsp->data = dest; + ofs = 0; + incr = (splen_t) (TIM_FSCALE(a, FRACTION_BITS) + 0.5); + if (sp->modes & MODES_LOOPING) + for (splen_t i = 0; i < newlen; i++) { + if (ofs >= le) + ofs -= ll; + RESAMPLATION_CACHE; + ofs += incr; + } + else + for (splen_t i = 0; i < newlen; i++) { + RESAMPLATION_CACHE; + ofs += incr; + } + newsp->loop_start = xls; + newsp->loop_end = xle; + newsp->data_length = newlen << FRACTION_BITS; + if (sp->modes & MODES_LOOPING) + loop_connect(dest, (int32_t) (xls >> FRACTION_BITS), + (int32_t) (xle >> FRACTION_BITS)); + dest[xle >> FRACTION_BITS] = dest[xls >> FRACTION_BITS]; + newsp->root_freq = get_note_freq(newsp, note); + newsp->sample_rate = playback_rate; + p->resampled = newsp; + cache_data_len += newlen + 1; + return CACHE_RESAMPLING_OK; +} + +void Recache::loop_connect(sample_t *data, int32_t start, int32_t end) +{ + int i, mixlen; + int32_t t0, t1; + + mixlen = MIXLEN; + if (start < mixlen) + mixlen = start; + if (end - start < mixlen) + mixlen = end - start; + if (mixlen <= 0) + return; + t0 = start - mixlen; + t1 = end - mixlen; + for (i = 0; i < mixlen; i++) { + double x, b; + + b = i / (double) mixlen; /* 0 <= b < 1 */ + x = b * data[t0 + i] + (1.0 - b) * data[t1 + i]; + if (x < -32768) + data[t1 + i] = -32768; + else if (x > 32767) + data[t1 + i] = 32767; + else + data[t1 + i] = (sample_t) x; + } +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/recache.h b/src/sound/timidity++/recache.h new file mode 100644 index 0000000000..4e9d58e4c8 --- /dev/null +++ b/src/sound/timidity++/recache.h @@ -0,0 +1,106 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef ___RECACHE_H_ +#define ___RECACHE_H_ + +#include + +namespace TimidityPlus +{ + + +struct cache_hash +{ + /* cache key */ + int note; + Sample *sp; + + int32_t cnt; /* counter */ + double r; /* size/refcnt */ + Sample *resampled; + struct cache_hash *next; +}; + +class Player; + +class Recache +{ + Player *player; + enum + { + HASH_TABLE_SIZE = 251, + MIXLEN = 256, + + MIN_LOOPSTART = MIXLEN, + MIN_LOOPLEN = 1024, + MAX_EXPANDLEN = (1024 * 32), + + CACHE_RESAMPLING_OK = 0, + CACHE_RESAMPLING_NOTOK = 1, + SORT_THRESHOLD = 20 + }; + + struct CNote + { + int32_t on[128]; + struct cache_hash *cache[128]; + }; + + CNote channel_note_table[MAX_CHANNELS] = { 0 }; + sample_t *cache_data = NULL; + splen_t cache_data_len = 0; + struct cache_hash *cache_hash_table[HASH_TABLE_SIZE] = { 0 }; + MBlockList hash_entry_pool = { nullptr, 0 }; + + + void free_cache_data(void); + double sample_resamp_info(Sample *, int, splen_t *, splen_t *, splen_t *); + void qsort_cache_array(struct cache_hash **, int32_t, int32_t); + void insort_cache_array(struct cache_hash **, int32_t); + int cache_resampling(struct cache_hash *); + void loop_connect(sample_t *, int32_t, int32_t); + +public: + + Recache(Player *p) + { + player = p; + resamp_cache_reset(); + } + + ~Recache() + { + free_cache_data(); + } + + void resamp_cache_reset(void); + void resamp_cache_refer_on(Voice *vp, int32_t sample_start); + void resamp_cache_refer_off(int ch, int note, int32_t sample_end); + void resamp_cache_refer_alloff(int ch, int32_t sample_end); + void resamp_cache_create(void); + struct cache_hash *resamp_cache_fetch(Sample *sp, int note); + +}; + +const int32_t allocate_cache_size = DEFAULT_CACHE_DATA_SIZE; + +} +#endif /* ___RECACHE_H_ */ diff --git a/src/sound/timidity++/resample.cpp b/src/sound/timidity++/resample.cpp new file mode 100644 index 0000000000..d7dcb9e9a1 --- /dev/null +++ b/src/sound/timidity++/resample.cpp @@ -0,0 +1,979 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + resample.c +*/ + +#include +#include +#include + +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "tables.h" +#include "resample.h" +#include "recache.h" + +namespace TimidityPlus +{ + + +/* for start/end of samples */ +static float newt_coeffs[58][58]; +static int sample_bounds_min, sample_bounds_max; /* min/max bounds for sample data */ + +#define DEFAULT_GAUSS_ORDER 25 +static float *gauss_table[(1 << FRACTION_BITS)] = { 0 }; /* don't need doubles */ +static int gauss_n = DEFAULT_GAUSS_ORDER; + + +static void initialize_newton_coeffs() +{ + int i, j, n = 57; + int sign; + + 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; + } + } + for (i = 0; i <= n; i++) + for (j = 0, sign = pow(-1, i); j <= i; j++, sign *= -1) + newt_coeffs[i][j] *= sign; + +} + + + +/* Very fast and accurate table based interpolation. Better speed and higher + accuracy than Newton. This isn't *quite* true Gauss interpolation; it's + more a slightly modified Gauss interpolation that I accidently stumbled + upon. Rather than normalize all x values in the window to be in the range + [0 to 2*PI], it simply divides them all by 2*PI instead. I don't know why + this works, but it does. Gauss should only work on periodic data with the + window spanning exactly one period, so it is no surprise that regular Gauss + interpolation doesn't work too well on general audio data. But dividing + the x values by 2*PI magically does. Any other scaling produces degraded + results or total garbage. If anyone can work out the theory behind why + this works so well (at first glance, it shouldn't ??), please contact me + (Eric A. Welsh, ewelsh@ccb.wustl.edu), as I would really like to have some + mathematical justification for doing this. Despite the lack of any sound + theoretical basis, this method DOES result in highly accurate interpolation + (or possibly approximaton, not sure yet if it truly interpolates, but it + looks like it does). -N 34 is as high as it can go before errors start + appearing. But even at -N 34, it is more accurate than Newton at -N 57. + -N 34 has no problem running in realtime on my system, but -N 25 is the + default, since that is the optimal compromise between speed and accuracy. + I strongly recommend using Gauss interpolation. It is the highest + quality interpolation option available, and is much faster than using + Newton polynomials. */ + + +static resample_t resample_gauss(sample_t *src, splen_t ofs, resample_rec_t *rec) +{ + sample_t *sptr; + int32_t left, right, temp_n; + + left = (ofs >> FRACTION_BITS); + right = (rec->data_length >> FRACTION_BITS) - left - 1; + temp_n = (right << 1) - 1; + if (temp_n > (left << 1) + 1) + temp_n = (left << 1) + 1; + if (temp_n < gauss_n) { + int ii, jj; + float xd, y; + if (temp_n <= 0) + temp_n = 1; + xd = ofs & FRACTION_MASK; + xd /= (1L << FRACTION_BITS); + xd += temp_n >> 1; + y = 0; + sptr = src + (ofs >> FRACTION_BITS) - (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; + return ((y > sample_bounds_max) ? sample_bounds_max : + ((y < sample_bounds_min) ? sample_bounds_min : y)); + } + else { + float *gptr, *gend; + float y; + y = 0; + sptr = src + left - (gauss_n >> 1); + gptr = gauss_table[ofs&FRACTION_MASK]; + if (gauss_n == DEFAULT_GAUSS_ORDER) { + /* expanding the loop for the default case. + * this will allow intensive optimization when compiled + * with SSE2 capability. + */ +#define do_gauss y += *(sptr++) * *(gptr++); + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + y += *sptr * *gptr; +#undef do_gauss + } + else { + gend = gptr + gauss_n; + do { + y += *(sptr++) * *(gptr++); + } while (gptr <= gend); + } + return ((y > sample_bounds_max) ? sample_bounds_max : + ((y < sample_bounds_min) ? sample_bounds_min : y)); + } +} + + +#define RESAMPLATION *dest++ = resample_gauss(src, ofs, &resrc); + +/* exported for recache.c */ +resample_t do_resamplation(sample_t *src, splen_t ofs, resample_rec_t *rec) +{ + return resample_gauss(src, ofs, rec); +} + +#define PRECALC_LOOP_COUNT(start, end, incr) (int32_t)(((int64_t)((end) - (start) + (incr) - 1)) / (incr)) + +void initialize_gauss_table(int n) +{ + int m, i, k, n_half = (n >> 1); + double ck; + double x, x_inc, xz; + double z[35], zsin_[34 + 35], *zsin, xzsin[35]; + float *gptr; + + for (i = 0; i <= n; i++) + z[i] = i / (4 * M_PI); + zsin = &zsin_[34]; + for (i = -n; i <= n; i++) + zsin[i] = sin(i / (4 * M_PI)); + + x_inc = 1.0 / (1 << FRACTION_BITS); + gptr = (float*)safe_realloc(gauss_table[0], (n + 1) * sizeof(float)*(1 << FRACTION_BITS)); + for (m = 0, x = 0.0; m < (1 << FRACTION_BITS); m++, x += x_inc) + { + xz = (x + n_half) / (4 * M_PI); + for (i = 0; i <= n; i++) + xzsin[i] = sin(xz - z[i]); + gauss_table[m] = gptr; + + for (k = 0; k <= n; k++) + { + ck = 1.0; + + for (i = 0; i <= n; i++) + { + if (i == k) + continue; + + ck *= xzsin[i] / zsin[k - i]; + } + + *gptr++ = ck; + } + } +} + +void free_gauss_table(void) +{ + if (gauss_table[0] != 0) + free(gauss_table[0]); + gauss_table[0] = NULL; +} + +/* initialize the coefficients of the current resampling algorithm */ +void initialize_resampler_coeffs(void) +{ + // Only needs to be done once. + static bool done = false; + if (done) return; + done = true; + // atterm(free_gauss_table); + + initialize_newton_coeffs(); + initialize_gauss_table(gauss_n); + /* we don't have to initialize newton table any more */ + + /* bounds checking values for the appropriate sample types */ + /* this is as good a place as any to initialize them */ + if (play_mode->encoding & PE_24BIT) + { + sample_bounds_min = -8388608; + sample_bounds_max = 8388607; + } + else /* 16-bit */ + { + sample_bounds_min = -32768; + sample_bounds_max = 32767; + } +} + + +/*************** resampling with fixed increment *****************/ + +resample_t *Resampler::rs_plain_c(int v, int32_t *countptr) +{ + Voice *vp = &player->voice[v]; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int32_t ofs, count = *countptr, i, le; + + le = (int32_t)(vp->sample->loop_end >> FRACTION_BITS); + ofs = (int32_t)(vp->sample_offset >> FRACTION_BITS); + + i = ofs + count; + if (i > le) + i = le; + count = i - ofs; + + for (i = 0; i < count; i++) { + dest[i] = src[i + ofs]; + } + + ofs += count; + if (ofs == le) + { + vp->timeout = 1; + *countptr = count; + } + vp->sample_offset = ((splen_t)ofs << FRACTION_BITS); + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_plain(int v, int32_t *countptr) +{ + /* Play sample until end, then free the voice. */ + Voice *vp = &player->voice[v]; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + splen_t + ofs = vp->sample_offset, + ls = 0, + le = vp->sample->data_length; + resample_rec_t resrc; + int32_t count = *countptr, incr = vp->sample_increment; + int32_t i, j; + + if (vp->cache && incr == (1 << FRACTION_BITS)) + return rs_plain_c(v, countptr); + + resrc.loop_start = ls; + resrc.loop_end = le; + resrc.data_length = vp->sample->data_length; + if (incr < 0) incr = -incr; /* In case we're coming out of a bidir loop */ + + /* Precalc how many times we should go through the loop. + NOTE: Assumes that incr > 0 and that ofs <= le */ + i = PRECALC_LOOP_COUNT(ofs, le, incr); + + if (i > count) + { + i = count; + count = 0; + } + else count -= i; + + for (j = 0; j < i; j++) + { + RESAMPLATION; + ofs += incr; + } + + if (ofs >= le) + { + vp->timeout = 1; + *countptr -= count; + } + + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_loop_c(Voice *vp, int32_t count) +{ + int32_t + ofs = (int32_t)(vp->sample_offset >> FRACTION_BITS), + le = (int32_t)(vp->sample->loop_end >> FRACTION_BITS), + ll = le - (int32_t)(vp->sample->loop_start >> FRACTION_BITS); + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int32_t i, j; + + while (count) + { + while (ofs >= le) + ofs -= ll; + /* Precalc how many times we should go through the loop */ + i = le - ofs; + if (i > count) + i = count; + count -= i; + for (j = 0; j < i; j++) { + dest[j] = src[j + ofs]; + } + dest += i; + ofs += i; + } + vp->sample_offset = ((splen_t)ofs << FRACTION_BITS); + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_loop(Voice *vp, int32_t count) +{ + /* Play sample until end-of-loop, skip back and continue. */ + splen_t + ofs = vp->sample_offset, + ls, le, ll; + resample_rec_t resrc; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int32_t i, j; + int32_t incr = vp->sample_increment; + + if (vp->cache && incr == (1 << FRACTION_BITS)) + return rs_loop_c(vp, count); + + resrc.loop_start = ls = vp->sample->loop_start; + resrc.loop_end = le = vp->sample->loop_end; + ll = le - ls; + resrc.data_length = vp->sample->data_length; + + while (count) + { + while (ofs >= le) { ofs -= ll; } + /* Precalc how many times we should go through the loop */ + i = PRECALC_LOOP_COUNT(ofs, le, incr); + if (i > count) { + i = count; + count = 0; + } + else { count -= i; } + for (j = 0; j < i; j++) { + RESAMPLATION; + ofs += incr; + } + } + + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_bidir(Voice *vp, int32_t count) +{ + int32_t + ofs = vp->sample_offset, + le = vp->sample->loop_end, + ls = vp->sample->loop_start; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int32_t incr = vp->sample_increment; + resample_rec_t resrc; + + int32_t + le2 = le << 1, + ls2 = ls << 1; + int32_t i, j; + /* Play normally until inside the loop region */ + + resrc.loop_start = ls; + resrc.loop_end = le; + resrc.data_length = vp->sample->data_length; + + if (incr > 0 && ofs < ls) + { + /* NOTE: Assumes that incr > 0, which is NOT always the case + when doing bidirectional looping. I have yet to see a case + where both ofs <= ls AND incr < 0, however. */ + i = PRECALC_LOOP_COUNT(ofs, ls, incr); + if (i > count) + { + i = count; + count = 0; + } + else count -= i; + for (j = 0; j < i; j++) + { + RESAMPLATION; + ofs += incr; + } + } + + /* Then do the bidirectional looping */ + + while (count) + { + /* Precalc how many times we should go through the loop */ + i = PRECALC_LOOP_COUNT(ofs, incr > 0 ? le : ls, incr); + if (i > count) + { + i = count; + count = 0; + } + else count -= i; + for (j = 0; j < i; j++) + { + RESAMPLATION; + ofs += incr; + } + if (ofs >= 0 && ofs >= le) + { + /* fold the overshoot back in */ + ofs = le2 - ofs; + incr *= -1; + } + else if (ofs <= 0 || ofs <= ls) + { + ofs = ls2 - ofs; + incr *= -1; + } + } + vp->sample_increment = incr; + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer + resample_buffer_offset; +} + +/*********************** vibrato versions ***************************/ + +/* We only need to compute one half of the vibrato sine cycle */ +static int vib_phase_to_inc_ptr(int phase) +{ + if (phase < VIBRATO_SAMPLE_INCREMENTS / 2) + return VIBRATO_SAMPLE_INCREMENTS / 2 - 1 - phase; + else if (phase >= 3 * VIBRATO_SAMPLE_INCREMENTS / 2) + return 5 * VIBRATO_SAMPLE_INCREMENTS / 2 - 1 - phase; + else + return phase - VIBRATO_SAMPLE_INCREMENTS / 2; +} + +int32_t Resampler::update_vibrato(Voice *vp, int sign) +{ + int32_t depth; + int phase, pb; + double a; + int ch = vp->channel; + + if (vp->vibrato_delay > 0) + { + vp->vibrato_delay -= vp->vibrato_control_ratio; + if (vp->vibrato_delay > 0) + return vp->sample_increment; + } + + if (vp->vibrato_phase++ >= 2 * VIBRATO_SAMPLE_INCREMENTS - 1) + vp->vibrato_phase = 0; + phase = vib_phase_to_inc_ptr(vp->vibrato_phase); + + if (vp->vibrato_sample_increment[phase]) + { + if (sign) + return -vp->vibrato_sample_increment[phase]; + else + return vp->vibrato_sample_increment[phase]; + } + + /* Need to compute this sample increment. */ + + depth = vp->vibrato_depth; + depth <<= 7; + + if (vp->vibrato_sweep && !player->channel[ch].mod.val) + { + /* Need to update sweep */ + vp->vibrato_sweep_position += vp->vibrato_sweep; + if (vp->vibrato_sweep_position >= (1 << SWEEP_SHIFT)) + vp->vibrato_sweep = 0; + else + { + /* Adjust depth */ + depth *= vp->vibrato_sweep_position; + depth >>= SWEEP_SHIFT; + } + } + + if (vp->sample->inst_type == INST_SF2) { + pb = (int)((lookup_triangular(vp->vibrato_phase * + (SINE_CYCLE_LENGTH / (2 * VIBRATO_SAMPLE_INCREMENTS))) + * (double)(depth)* VIBRATO_AMPLITUDE_TUNING)); + } + else { + pb = (int)((lookup_sine(vp->vibrato_phase * + (SINE_CYCLE_LENGTH / (2 * VIBRATO_SAMPLE_INCREMENTS))) + * (double)(depth)* VIBRATO_AMPLITUDE_TUNING)); + } + + a = TIM_FSCALE(((double)(vp->sample->sample_rate) * + (double)(vp->frequency)) / + ((double)(vp->sample->root_freq) * + (double)(playback_rate)), + FRACTION_BITS); + + if (pb < 0) { + pb = -pb; + a /= bend_fine[(pb >> 5) & 0xFF] * bend_coarse[pb >> 13]; + pb = -pb; + } + else { + a *= bend_fine[(pb >> 5) & 0xFF] * bend_coarse[pb >> 13]; + } + a += 0.5; + + /* If the sweep's over, we can store the newly computed sample_increment */ + if (!vp->vibrato_sweep || player->channel[ch].mod.val) + vp->vibrato_sample_increment[phase] = (int32_t)a; + + if (sign) + a = -a; /* need to preserve the loop direction */ + + return (int32_t)a; +} + +resample_t *Resampler::rs_vib_plain(int v, int32_t *countptr) +{ + /* Play sample until end, then free the voice. */ + Voice *vp = &player->voice[v]; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + splen_t + ls = 0, + le = vp->sample->data_length, + ofs = vp->sample_offset; + resample_rec_t resrc; + + int32_t count = *countptr, incr = vp->sample_increment; + int cc = vp->vibrato_control_counter; + + resrc.loop_start = ls; + resrc.loop_end = le; + resrc.data_length = vp->sample->data_length; + /* This has never been tested */ + + if (incr < 0) incr = -incr; /* In case we're coming out of a bidir loop */ + + while (count--) + { + if (!cc--) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(vp, 0); + } + RESAMPLATION; + ofs += incr; + if (ofs >= le) + { + vp->timeout = 1; + *countptr -= count; + break; + } + } + + vp->vibrato_control_counter = cc; + vp->sample_increment = incr; + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_vib_loop(Voice *vp, int32_t count) +{ + /* Play sample until end-of-loop, skip back and continue. */ + splen_t + ofs = vp->sample_offset, + ls = vp->sample->loop_start, + le = vp->sample->loop_end, + ll = le - vp->sample->loop_start; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int cc = vp->vibrato_control_counter; + int32_t incr = vp->sample_increment; + resample_rec_t resrc; + int32_t i, j; + int vibflag = 0; + + resrc.loop_start = ls; + resrc.loop_end = le; + resrc.data_length = vp->sample->data_length; + + while (count) + { + /* Hopefully the loop is longer than an increment */ + while (ofs >= le) { ofs -= ll; } + /* Precalc how many times to go through the loop, taking + the vibrato control ratio into account this time. */ + i = PRECALC_LOOP_COUNT(ofs, le, incr); + if (i > count) { + i = count; + } + if (i > cc) { + i = cc; + vibflag = 1; + } + else { cc -= i; } + count -= i; + if (vibflag) { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(vp, 0); + vibflag = 0; + } + for (j = 0; j < i; j++) { + RESAMPLATION; + ofs += incr; + } + } + + vp->vibrato_control_counter = cc; + vp->sample_increment = incr; + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_vib_bidir(Voice *vp, int32_t count) +{ + int32_t + ofs = vp->sample_offset, + le = vp->sample->loop_end, + ls = vp->sample->loop_start; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int cc = vp->vibrato_control_counter; + int32_t incr = vp->sample_increment; + resample_rec_t resrc; + + + resrc.loop_start = ls; + resrc.loop_end = le; + resrc.data_length = vp->sample->data_length; + /* Play normally until inside the loop region */ + + if (ofs < ls) + { + while (count--) + { + if (!cc--) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(vp, 0); + } + RESAMPLATION; + ofs += incr; + if (ofs >= ls) + break; + } + } + + /* Then do the bidirectional looping */ + + if (count > 0) + while (count--) + { + if (!cc--) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(vp, (incr < 0)); + } + RESAMPLATION; + ofs += incr; + if (ofs >= le) + { + /* fold the overshoot back in */ + ofs = le - (ofs - le); + incr = -incr; + } + else if (ofs <= ls) + { + ofs = ls + (ls - ofs); + incr = -incr; + } + } + + /* Update changed values */ + vp->vibrato_control_counter = cc; + vp->sample_increment = incr; + vp->sample_offset = ofs; + return resample_buffer + resample_buffer_offset; +} + +/*********************** portamento versions ***************************/ + +int Resampler::rs_update_porta(int v) +{ + Voice *vp = &player->voice[v]; + int32_t d; + + d = vp->porta_dpb; + if (vp->porta_pb < 0) + { + if (d > -vp->porta_pb) + d = -vp->porta_pb; + } + else + { + if (d > vp->porta_pb) + d = -vp->porta_pb; + else + d = -d; + } + + vp->porta_pb += d; + if (vp->porta_pb == 0) + { + vp->porta_control_ratio = 0; + vp->porta_pb = 0; + } + player->recompute_freq(v); + return vp->porta_control_ratio; +} + +resample_t *Resampler::porta_resample_voice(int v, int32_t *countptr, int mode) +{ + Voice *vp = &player->voice[v]; + int32_t n = *countptr, i; + resample_t *(Resampler::*resampler)(int, int32_t *, int); + int cc = vp->porta_control_counter; + int loop; + + if (vp->vibrato_control_ratio) + resampler = &Resampler::vib_resample_voice; + else + resampler = &Resampler::normal_resample_voice; + if (mode != 1) + loop = 1; + else + loop = 0; + + vp->cache = NULL; + resample_buffer_offset = 0; + while (resample_buffer_offset < n) + { + if (cc == 0) + { + if ((cc = rs_update_porta(v)) == 0) + { + i = n - resample_buffer_offset; + (this->*resampler)(v, &i, mode); + resample_buffer_offset += i; + break; + } + } + + i = n - resample_buffer_offset; + if (i > cc) + i = cc; + (this->*resampler)(v, &i, mode); + resample_buffer_offset += i; + + if (!loop && (i == 0 || vp->status == VOICE_FREE)) + break; + cc -= i; + } + *countptr = resample_buffer_offset; + resample_buffer_offset = 0; + vp->porta_control_counter = cc; + return resample_buffer; +} + +/* interface function */ +resample_t *Resampler::vib_resample_voice(int v, int32_t *countptr, int mode) +{ + Voice *vp = &player->voice[v]; + + vp->cache = NULL; + if (mode == 0) + return rs_vib_loop(vp, *countptr); + if (mode == 1) + return rs_vib_plain(v, countptr); + return rs_vib_bidir(vp, *countptr); +} + +/* interface function */ +resample_t *Resampler::normal_resample_voice(int v, int32_t *countptr, int mode) +{ + Voice *vp = &player->voice[v]; + if (mode == 0) + return rs_loop(vp, *countptr); + if (mode == 1) + return rs_plain(v, countptr); + return rs_bidir(vp, *countptr); +} + +/* interface function */ +resample_t *Resampler::resample_voice(int v, int32_t *countptr) +{ + Voice *vp = &player->voice[v]; + int mode; + resample_t *result; + int32_t i; + + if (vp->sample->sample_rate == playback_rate && + vp->sample->root_freq == get_note_freq(vp->sample, vp->sample->note_to_use) && + vp->frequency == vp->orig_frequency) + { + int32_t ofs; + + /* Pre-resampled data -- just update the offset and check if + we're out of data. */ + ofs = (int32_t)(vp->sample_offset >> FRACTION_BITS); /* Kind of silly to use + FRACTION_BITS here... */ + if (*countptr >= (int32_t)((vp->sample->data_length >> FRACTION_BITS) - ofs)) + { + /* Note finished. Free the voice. */ + vp->timeout = 1; + + /* Let the caller know how much data we had left */ + *countptr = (int32_t)(vp->sample->data_length >> FRACTION_BITS) - ofs; + } + else + vp->sample_offset += *countptr << FRACTION_BITS; + + for (i = 0; i < *countptr; i++) { + resample_buffer[i] = vp->sample->data[i + ofs]; + } + return resample_buffer; + } + + mode = vp->sample->modes; + if ((mode & MODES_LOOPING) && + ((mode & MODES_ENVELOPE) || + (vp->status & (VOICE_ON | VOICE_SUSTAINED)))) + { + if (mode & MODES_PINGPONG) + { + vp->cache = NULL; + mode = 2; /* Bidir loop */ + } + else + mode = 0; /* loop */ + } + else + mode = 1; /* no loop */ + + if (vp->porta_control_ratio) + result = porta_resample_voice(v, countptr, mode); + else if (vp->vibrato_control_ratio) + result = vib_resample_voice(v, countptr, mode); + else + result = normal_resample_voice(v, countptr, mode); + + return result; +} + + +void pre_resample(Sample * sp) +{ + double a, b; + splen_t ofs, newlen; + sample_t *newdata, *dest, *src = (sample_t *)sp->data; + int32_t i, count, incr, f, x; + resample_rec_t resrc; + + f = get_note_freq(sp, sp->note_to_use); + a = b = ((double)(sp->root_freq) * playback_rate) / + ((double)(sp->sample_rate) * f); + if ((int64_t)sp->data_length * a >= 0x7fffffffL) + { + /* Too large to compute */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, " *** Can't pre-resampling for note %d", + sp->note_to_use); + return; + } + newlen = (splen_t)(sp->data_length * a); + count = (newlen >> FRACTION_BITS); + ofs = incr = (sp->data_length - 1) / (count - 1); + + if ((double)newlen + incr >= 0x7fffffffL) + { + /* Too large to compute */ + ctl_cmsg(CMSG_INFO, VERB_DEBUG, " *** Can't pre-resampling for note %d", + sp->note_to_use); + return; + } + + dest = newdata = (sample_t *)safe_malloc((int32_t)(newlen >> (FRACTION_BITS - 1)) + 2); + dest[newlen >> FRACTION_BITS] = 0; + + *dest++ = src[0]; + + resrc.loop_start = 0; + resrc.loop_end = sp->data_length; + resrc.data_length = sp->data_length; + + /* Since we're pre-processing and this doesn't have to be done in + real-time, we go ahead and do the higher order interpolation. */ + for (i = 1; i < count; i++) + { + x = resample_gauss(src, ofs, &resrc); + *dest++ = (int16_t)((x > 32767) ? 32767 : ((x < -32768) ? -32768 : x)); + ofs += incr; + } + + sp->data_length = newlen; + sp->loop_start = (splen_t)(sp->loop_start * b); + sp->loop_end = (splen_t)(sp->loop_end * b); + free(sp->data); + sp->data = (sample_t *)newdata; + sp->root_freq = f; + sp->sample_rate = playback_rate; + sp->low_freq = freq_table[0]; + sp->high_freq = freq_table[127]; +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/resample.h b/src/sound/timidity++/resample.h new file mode 100644 index 0000000000..8523cdc310 --- /dev/null +++ b/src/sound/timidity++/resample.h @@ -0,0 +1,88 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + resample.h +*/ + +#ifndef ___RESAMPLE_H_ +#define ___RESAMPLE_H_ + +#include +#include "sysdep.h" + +namespace TimidityPlus +{ + + +typedef int32_t resample_t; + +enum { + RESAMPLE_CSPLINE, + RESAMPLE_LAGRANGE, + RESAMPLE_GAUSS, + RESAMPLE_NEWTON, + RESAMPLE_LINEAR, + RESAMPLE_NONE +}; + +extern void initialize_resampler_coeffs(void); +extern void free_gauss_table(void); + +typedef struct resample_rec { + splen_t loop_start; + splen_t loop_end; + splen_t data_length; +} resample_rec_t; + +extern resample_t do_resamplation(sample_t *src, splen_t ofs, resample_rec_t *rec); + +extern void pre_resample(Sample *sp); +class Player; + +class Resampler // This is only here to put the buffer on the stack without changing all the code. +{ + Player *player; + resample_t resample_buffer[AUDIO_BUFFER_SIZE] = { 0 }; + int resample_buffer_offset = 0; + + resample_t *rs_plain_c(int v, int32_t *countptr); + resample_t *rs_plain(int v, int32_t *countptr); + resample_t *rs_loop_c(Voice *vp, int32_t count); + resample_t *rs_loop(Voice *vp, int32_t count); + resample_t *rs_bidir(Voice *vp, int32_t count); + resample_t *rs_vib_plain(int v, int32_t *countptr); + resample_t *rs_vib_loop(Voice *vp, int32_t count); + resample_t *rs_vib_bidir(Voice *vp, int32_t count); + resample_t *porta_resample_voice(int v, int32_t *countptr, int mode); + resample_t *normal_resample_voice(int v, int32_t *countptr, int mode); + resample_t *vib_resample_voice(int v, int32_t *countptr, int mode); + int rs_update_porta(int v); + int32_t update_vibrato(Voice *vp, int sign); + +public: + Resampler(Player *p) + { + player = p; + } + resample_t * resample_voice(int v, int32_t *countptr); +}; + +} + +#endif /* ___RESAMPLE_H_ */ diff --git a/src/sound/timidity++/reverb.cpp b/src/sound/timidity++/reverb.cpp new file mode 100644 index 0000000000..49ace61e9b --- /dev/null +++ b/src/sound/timidity++/reverb.cpp @@ -0,0 +1,4437 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * REVERB EFFECT FOR TIMIDITY++-1.X (Version 0.06e 1999/1/28) + * + * Copyright (C) 1997,1998,1999 Masaki Kiryu + * (http://w3mb.kcom.ne.jp/~mkiryu/) + * + * reverb.c -- main reverb engine. + * + */ + +#include +#include "timidity.h" +#include "tables.h" +#include "common.h" +#include "reverb.h" +#include "optcode.h" +#include +#include + +namespace TimidityPlus +{ + +#define SYS_EFFECT_PRE_LPF + + +const double reverb_predelay_factor = 1.0; +const double freeverb_scaleroom = 0.28; +const double freeverb_offsetroom = 0.7; + +#define MASTER_CHORUS_LEVEL 1.7 +#define MASTER_DELAY_LEVEL 3.25 + +/* */ +/* Dry Signal */ +/* */ + + +void Reverb::set_dry_signal(register int32_t *buf, int32_t n) +{ + int32_t i; + int32_t *dbuf = direct_buffer; + + for(i = n - 1; i >= 0; i--) + { + dbuf[i] += buf[i]; + } +} + +void Reverb::set_dry_signal_xg(register int32_t *sbuffer, int32_t n, int32_t level) +{ + register int32_t i; + register int32_t count = n; + if(!level) {return;} + double send_level = (double)level / 127.0; + + for(i = 0; i < count; i++) + { + direct_buffer[i] += int32_t(sbuffer[i] * send_level); + } +} + +void Reverb::mix_dry_signal(int32_t *buf, int32_t n) +{ + memcpy(buf, direct_buffer, sizeof(int32_t) * n); + memset(direct_buffer, 0, sizeof(int32_t) * n); +} + +/* */ +/* Effect Utilities */ +/* */ +static int isprime(int val) +{ + int i; + if (val == 2) {return 1;} + if (val & 1) + { + for (i = 3; i < (int)sqrt((double)val) + 1; i += 2) { + if ((val % i) == 0) {return 0;} + } + return 1; /* prime */ + } + else + { + return 0; + } /* even */ +} + +/*! delay */ +void Reverb::free_delay(simple_delay *delay) +{ + if(delay->buf != NULL) + { + free(delay->buf); + delay->buf = NULL; + } +} + +void Reverb::set_delay(simple_delay *delay, int32_t size) +{ + if(size < 1) {size = 1;} + free_delay(delay); + delay->buf = (int32_t *)safe_malloc(sizeof(int32_t) * size); + if(delay->buf == NULL) {return;} + delay->index = 0; + delay->size = size; + memset(delay->buf, 0, sizeof(int32_t) * delay->size); +} + +void Reverb::do_delay(int32_t *stream, int32_t *buf, int32_t size, int32_t *index) +{ + int32_t output; + output = buf[*index]; + buf[*index] = *stream; + if (++*index >= size) {*index = 0;} + *stream = output; +} + +/*! LFO (low frequency oscillator) */ +void Reverb::init_lfo(lfo *lfo, double freq, int type, double phase) +{ + int32_t i, cycle, diff; + + lfo->count = 0; + lfo->freq = freq; + if (lfo->freq < 0.05) {lfo->freq = 0.05;} + cycle = (double)playback_rate / lfo->freq; + if (cycle < 1) {cycle = 1;} + lfo->cycle = cycle; + lfo->icycle = TIM_FSCALE((SINE_CYCLE_LENGTH - 1) / (double)cycle, 24) - 0.5; + diff = SINE_CYCLE_LENGTH * phase / 360.0; + + if(lfo->type != type) { /* generate LFO waveform */ + switch(type) { + case LFO_SINE: + for(i = 0; i < SINE_CYCLE_LENGTH; i++) + lfo->buf[i] = TIM_FSCALE((lookup_sine(i + diff) + 1.0) / 2.0, 16); + break; + case LFO_TRIANGULAR: + for(i = 0; i < SINE_CYCLE_LENGTH; i++) + lfo->buf[i] = TIM_FSCALE((lookup_triangular(i + diff) + 1.0) / 2.0, 16); + break; + default: + for(i = 0; i < SINE_CYCLE_LENGTH; i++) {lfo->buf[i] = TIM_FSCALE(0.5, 16);} + break; + } + } + lfo->type = type; +} + +/* returned value is between 0 and (1 << 16) */ +int32_t Reverb::do_lfo(lfo *lfo) +{ + int32_t val; + val = lfo->buf[imuldiv24(lfo->count, lfo->icycle)]; + if(++lfo->count == lfo->cycle) {lfo->count = 0;} + return val; +} + +void Reverb::do_mod_delay(int32_t *stream, int32_t *buf, int32_t size, int32_t *rindex, int32_t *windex, + int32_t ndelay, int32_t depth, int32_t lfoval, int32_t *hist) +{ + int32_t t1, t2; + if (++*windex == size) {*windex = 0;} + t1 = buf[*rindex]; + t2 = imuldiv24(lfoval, depth); + *rindex = *windex - ndelay - (t2 >> 8); + if (*rindex < 0) {*rindex += size;} + t2 = 0xFF - (t2 & 0xFF); + *hist = t1 + imuldiv8(buf[*rindex] - *hist, t2); + buf[*windex] = *stream; + *stream = *hist; +} + +/*! modulated allpass filter with allpass interpolation (for Plate Reverberator,...) */ +void Reverb::free_mod_allpass(mod_allpass *delay) +{ + if(delay->buf != NULL) { + free(delay->buf); + delay->buf = NULL; + } +} + +void Reverb::set_mod_allpass(mod_allpass *delay, int32_t ndelay, int32_t depth, double feedback) +{ + int32_t size = ndelay + depth + 1; + free_mod_allpass(delay); + delay->buf = (int32_t *)safe_malloc(sizeof(int32_t) * size); + if(delay->buf == NULL) {return;} + delay->rindex = 0; + delay->windex = 0; + delay->hist = 0; + delay->ndelay = ndelay; + delay->depth = depth; + delay->size = size; + delay->feedback = feedback; + delay->feedbacki = TIM_FSCALE(feedback, 24); + memset(delay->buf, 0, sizeof(int32_t) * delay->size); +} + +void Reverb::do_mod_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *rindex, int32_t *windex, + int32_t ndelay, int32_t depth, int32_t lfoval, int32_t *hist, int32_t feedback) +{ + int t1, t2, t3; + if (++*windex == size) {*windex = 0;} + t3 = *stream + imuldiv24(*hist, feedback); + t1 = buf[*rindex]; + t2 = imuldiv24(lfoval, depth); + *rindex = *windex - ndelay - (t2 >> 8); + if (*rindex < 0) {*rindex += size;} + t2 = 0xFF - (t2 & 0xFF); + *hist = t1 + imuldiv8(buf[*rindex] - *hist, t2); + buf[*windex] = t3; + *stream = *hist - imuldiv24(t3, feedback); +} + +/* allpass filter */ +void Reverb::free_allpass(allpass *allpass) +{ + if(allpass->buf != NULL) { + free(allpass->buf); + allpass->buf = NULL; + } +} + +void Reverb::set_allpass(allpass *allpass, int32_t size, double feedback) +{ + if(allpass->buf != NULL) { + free(allpass->buf); + allpass->buf = NULL; + } + allpass->buf = (int32_t *)safe_malloc(sizeof(int32_t) * size); + if(allpass->buf == NULL) {return;} + allpass->index = 0; + allpass->size = size; + allpass->feedback = feedback; + allpass->feedbacki = TIM_FSCALE(feedback, 24); + memset(allpass->buf, 0, sizeof(int32_t) * allpass->size); +} + +void Reverb::do_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *index, int32_t feedback) +{ + int32_t bufout, output; + bufout = buf[*index]; + output = *stream - imuldiv24(bufout, feedback); + buf[*index] = output; + if (++*index >= size) {*index = 0;} + *stream = bufout + imuldiv24(output, feedback); +} + +void Reverb::init_filter_moog(filter_moog *svf) +{ + svf->b0 = svf->b1 = svf->b2 = svf->b3 = svf->b4 = 0; +} + +/*! calculate Moog VCF coefficients */ +void Reverb::calc_filter_moog(filter_moog *svf) +{ + double res, fr, p, q, f; + + if (svf->freq > playback_rate / 2) {svf->freq = playback_rate / 2;} + else if(svf->freq < 20) {svf->freq = 20;} + + if(svf->freq != svf->last_freq || svf->res_dB != svf->last_res_dB) { + if(svf->last_freq == 0) {init_filter_moog(svf);} + svf->last_freq = svf->freq, svf->last_res_dB = svf->res_dB; + + res = pow(10, (svf->res_dB - 96) / 20); + fr = 2.0 * (double)svf->freq / (double)playback_rate; + q = 1.0 - fr; + p = fr + 0.8 * fr * q; + f = p + p - 1.0; + q = res * (1.0 + 0.5 * q * (1.0 - q + 5.6 * q * q)); + svf->f = TIM_FSCALE(f, 24); + svf->p = TIM_FSCALE(p, 24); + svf->q = TIM_FSCALE(q, 24); + } +} + +void Reverb::do_filter_moog(int32_t *stream, int32_t *high, int32_t f, int32_t p, int32_t q, + int32_t *b0, int32_t *b1, int32_t *b2, int32_t *b3, int32_t *b4) +{ + int32_t t1, t2, t3, tb0 = *b0, tb1 = *b1, tb2 = *b2, tb3 = *b3, tb4 = *b4; + t3 = *stream - imuldiv24(q, tb4); + t1 = tb1; tb1 = imuldiv24(t3 + tb0, p) - imuldiv24(tb1, f); + t2 = tb2; tb2 = imuldiv24(tb1 + t1, p) - imuldiv24(tb2, f); + t1 = tb3; tb3 = imuldiv24(tb2 + t2, p) - imuldiv24(tb3, f); + *stream = tb4 = imuldiv24(tb3 + t1, p) - imuldiv24(tb4, f); + tb0 = t3; + *stream = tb4; + *high = t3 - tb4; + *b0 = tb0, *b1 = tb1, *b2 = tb2, *b3 = tb3, *b4 = tb4; +} + +void Reverb::init_filter_moog_dist(filter_moog_dist *svf) +{ + svf->b0 = svf->b1 = svf->b2 = svf->b3 = svf->b4 = 0.0; +} + +/*! calculate Moog VCF coefficients */ +void Reverb::calc_filter_moog_dist(filter_moog_dist *svf) +{ + double res, fr, p, q, f; + + if (svf->freq > playback_rate / 2) {svf->freq = playback_rate / 2;} + else if (svf->freq < 20) {svf->freq = 20;} + + if (svf->freq != svf->last_freq || svf->res_dB != svf->last_res_dB + || svf->dist != svf->last_dist) { + if (svf->last_freq == 0) {init_filter_moog_dist(svf);} + svf->last_freq = svf->freq, svf->last_res_dB = svf->res_dB, + svf->last_dist = svf->dist; + + res = pow(10.0, (svf->res_dB - 96.0) / 20.0); + fr = 2.0 * (double)svf->freq / (double)playback_rate; + q = 1.0 - fr; + p = fr + 0.8 * fr * q; + f = p + p - 1.0; + q = res * (1.0 + 0.5 * q * (1.0 - q + 5.6 * q * q)); + svf->f = f; + svf->p = p; + svf->q = q; + svf->d = 1.0 + svf->dist; + } +} + +void Reverb::do_filter_moog_dist(double *stream, double *high, double *band, double f, double p, double q, double d, + double *b0, double *b1, double *b2, double *b3, double *b4) +{ + double t1, t2, in, tb0 = *b0, tb1 = *b1, tb2 = *b2, tb3 = *b3, tb4 = *b4; + in = *stream; + in -= q * tb4; + t1 = tb1; tb1 = (in + tb0) * p - tb1 * f; + t2 = tb2; tb2 = (tb1 + t1) * p - tb2 * f; + t1 = tb3; tb3 = (tb2 + t2) * p - tb3 * f; + tb4 = (tb3 + t1) * p - tb4 * f; + tb4 *= d; + tb4 = tb4 - tb4 * tb4 * tb4 * 0.166667; + tb0 = in; + *stream = tb4; + *high = in - tb4; + *band = 3.0 * (tb3 - tb4); + *b0 = tb0, *b1 = tb1, *b2 = tb2, *b3 = tb3, *b4 = tb4; +} + +void Reverb::do_filter_moog_dist_band(double *stream, double f, double p, double q, double d, + double *b0, double *b1, double *b2, double *b3, double *b4) +{ + double t1, t2, in, tb0 = *b0, tb1 = *b1, tb2 = *b2, tb3 = *b3, tb4 = *b4; + in = *stream; + in -= q * tb4; + t1 = tb1; tb1 = (in + tb0) * p - tb1 * f; + t2 = tb2; tb2 = (tb1 + t1) * p - tb2 * f; + t1 = tb3; tb3 = (tb2 + t2) * p - tb3 * f; + tb4 = (tb3 + t1) * p - tb4 * f; + tb4 *= d; + tb4 = tb4 - tb4 * tb4 * tb4 * 0.166667; + tb0 = in; + *stream = 3.0 * (tb3 - tb4); + *b0 = tb0, *b1 = tb1, *b2 = tb2, *b3 = tb3, *b4 = tb4; +} + +void Reverb::init_filter_lpf18(filter_lpf18 *p) +{ + p->ay1 = p->ay2 = p->aout = p->lastin = 0; +} + +/*! calculate LPF18 coefficients */ +void Reverb::calc_filter_lpf18(filter_lpf18 *p) +{ + double kfcn, kp, kp1, kp1h, kres, value; + + if(p->freq != p->last_freq || p->dist != p->last_dist + || p->res != p->last_res) { + if(p->last_freq == 0) {init_filter_lpf18(p);} + p->last_freq = p->freq, p->last_dist = p->dist, p->last_res = p->res; + + kfcn = 2.0 * (double)p->freq / (double)playback_rate; + kp = ((-2.7528 * kfcn + 3.0429) * kfcn + 1.718) * kfcn - 0.9984; + kp1 = kp + 1.0; + kp1h = 0.5 * kp1; + kres = p->res * (((-2.7079 * kp1 + 10.963) * kp1 - 14.934) * kp1 + 8.4974); + value = 1.0 + (p->dist * (1.5 + 2.0 * kres * (1.0 - kfcn))); + + p->kp = kp, p->kp1h = kp1h, p->kres = kres, p->value = value; + } +} + +void Reverb::do_filter_lpf18(double *stream, double *ay1, double *ay2, double *aout, double *lastin, + double kres, double value, double kp, double kp1h) +{ + double ax1, ay11, ay31; + ax1 = *lastin; + ay11 = *ay1; + ay31 = *ay2; + *lastin = *stream - tanh(kres * *aout); + *ay1 = kp1h * (*lastin + ax1) - kp * *ay1; + *ay2 = kp1h * (*ay1 + ay11) - kp * *ay2; + *aout = kp1h * (*ay2 + ay31) - kp * *aout; + *stream = tanh(*aout * value); +} + +#define WS_AMP_MAX ((int32_t) 0x0fffffff) +#define WS_AMP_MIN ((int32_t)-0x0fffffff) + +void Reverb::do_hard_clipping(int32_t *stream, int32_t d) +{ + int32_t x; + x = imuldiv24(*stream, d); + x = (x > WS_AMP_MAX) ? WS_AMP_MAX + : (x < WS_AMP_MIN) ? WS_AMP_MIN : x; + *stream = x; +} + +void Reverb::do_soft_clipping1(int32_t *stream, int32_t d) +{ + int32_t x, ai = TIM_FSCALE(1.5, 24), bi = TIM_FSCALE(0.5, 24); + x = imuldiv24(*stream, d); + x = (x > WS_AMP_MAX) ? WS_AMP_MAX + : (x < WS_AMP_MIN) ? WS_AMP_MIN : x; + x = imuldiv24(x, ai) - imuldiv24(imuldiv28(imuldiv28(x, x), x), bi); + *stream = x; +} + +void Reverb::do_soft_clipping2(int32_t *stream, int32_t d) +{ + int32_t x; + x = imuldiv24(*stream, d); + x = (x > WS_AMP_MAX) ? WS_AMP_MAX + : (x < WS_AMP_MIN) ? WS_AMP_MIN : x; + x = signlong(x) * ((abs(x) << 1) - imuldiv28(x, x)); + *stream = x; +} + +/*! 1st order lowpass filter */ +void Reverb::init_filter_lowpass1(filter_lowpass1 *p) +{ + if (p->a > 1.0) {p->a = 1.0;} + p->x1l = p->x1r = 0; + p->ai = TIM_FSCALE(p->a, 24); + p->iai = TIM_FSCALE(1.0 - p->a, 24); +} + +void Reverb::do_filter_lowpass1(int32_t *stream, int32_t *x1, int32_t a, int32_t ia) +{ + *stream = *x1 = imuldiv24(*x1, ia) + imuldiv24(*stream, a); +} + +void Reverb::do_filter_lowpass1_stereo(int32_t *buf, int32_t count, filter_lowpass1 *p) +{ + int32_t i, a = p->ai, ia = p->iai, x1l = p->x1l, x1r = p->x1r; + + for(i = 0; i < count; i++) { + do_filter_lowpass1(&buf[i], &x1l, a, ia); + ++i; + do_filter_lowpass1(&buf[i], &x1r, a, ia); + } + p->x1l = x1l, p->x1r = x1r; +} + +void Reverb::init_filter_biquad(filter_biquad *p) +{ + p->x1l = 0, p->x2l = 0, p->y1l = 0, p->y2l = 0, p->x1r = 0, + p->x2r = 0, p->y1r = 0, p->y2r = 0; +} + +/*! biquad lowpass filter */ +void Reverb::calc_filter_biquad_low(filter_biquad *p) +{ + double a0, a1, a2, b1, b02, omega, sn, cs, alpha; + + if(p->freq != p->last_freq || p->q != p->last_q) { + if (p->last_freq == 0) {init_filter_biquad(p);} + p->last_freq = p->freq, p->last_q = p->q; + omega = 2.0 * M_PI * (double)p->freq / (double)playback_rate; + sn = sin(omega); + cs = cos(omega); + if (p->q == 0 || p->freq < 0 || p->freq > playback_rate / 2) { + p->b02 = TIM_FSCALE(1.0, 24); + p->a1 = p->a2 = p->b1 = 0; + return; + } else {alpha = sn / (2.0 * p->q);} + + a0 = 1.0 / (1.0 + alpha); + b02 = ((1.0 - cs) / 2.0) * a0; + b1 = (1.0 - cs) * a0; + a1 = (-2.0 * cs) * a0; + a2 = (1.0 - alpha) * a0; + + p->b1 = TIM_FSCALE(b1, 24); + p->a2 = TIM_FSCALE(a2, 24); + p->a1 = TIM_FSCALE(a1, 24); + p->b02 = TIM_FSCALE(b02, 24); + } +} + +/*! biquad highpass filter */ +void Reverb::calc_filter_biquad_high(filter_biquad *p) +{ + double a0, a1, a2, b1, b02, omega, sn, cs, alpha; + + if(p->freq != p->last_freq || p->q != p->last_q) { + if (p->last_freq == 0) {init_filter_biquad(p);} + p->last_freq = p->freq, p->last_q = p->q; + omega = 2.0 * M_PI * (double)p->freq / (double)playback_rate; + sn = sin(omega); + cs = cos(omega); + if (p->q == 0 || p->freq < 0 || p->freq > playback_rate / 2) { + p->b02 = TIM_FSCALE(1.0, 24); + p->a1 = p->a2 = p->b1 = 0; + return; + } else {alpha = sn / (2.0 * p->q);} + + a0 = 1.0 / (1.0 + alpha); + b02 = ((1.0 + cs) / 2.0) * a0; + b1 = (-(1.0 + cs)) * a0; + a1 = (-2.0 * cs) * a0; + a2 = (1.0 - alpha) * a0; + + p->b1 = TIM_FSCALE(b1, 24); + p->a2 = TIM_FSCALE(a2, 24); + p->a1 = TIM_FSCALE(a1, 24); + p->b02 = TIM_FSCALE(b02, 24); + } +} + +void Reverb::do_filter_biquad(int32_t *stream, int32_t a1, int32_t a2, int32_t b1, + int32_t b02, int32_t *x1, int32_t *x2, int32_t *y1, int32_t *y2) +{ + int32_t t1; + t1 = imuldiv24(*stream + *x2, b02) + imuldiv24(*x1, b1) - imuldiv24(*y1, a1) - imuldiv24(*y2, a2); + *x2 = *x1; + *x1 = *stream; + *y2 = *y1; + *y1 = t1; + *stream = t1; +} + +void Reverb::init_filter_shelving(filter_shelving *p) +{ + p->x1l = 0, p->x2l = 0, p->y1l = 0, p->y2l = 0, p->x1r = 0, + p->x2r = 0, p->y1r = 0, p->y2r = 0; +} + +/*! shelving filter */ +void Reverb::calc_filter_shelving_low(filter_shelving *p) +{ + double a0, a1, a2, b0, b1, b2, omega, sn, cs, A, beta; + + init_filter_shelving(p); + + A = pow(10, p->gain / 40); + omega = 2.0 * M_PI * (double)p->freq / (double)playback_rate; + sn = sin(omega); + cs = cos(omega); + if (p->freq < 0 || p->freq > playback_rate / 2) { + p->b0 = TIM_FSCALE(1.0, 24); + p->a1 = p->b1 = p->a2 = p->b2 = 0; + return; + } + if (p->q == 0) {beta = sqrt(A + A);} + else {beta = sqrt(A) / p->q;} + + a0 = 1.0 / ((A + 1) + (A - 1) * cs + beta * sn); + a1 = 2.0 * ((A - 1) + (A + 1) * cs); + a2 = -((A + 1) + (A - 1) * cs - beta * sn); + b0 = A * ((A + 1) - (A - 1) * cs + beta * sn); + b1 = 2.0 * A * ((A - 1) - (A + 1) * cs); + b2 = A * ((A + 1) - (A - 1) * cs - beta * sn); + + a1 *= a0; + a2 *= a0; + b1 *= a0; + b2 *= a0; + b0 *= a0; + + p->a1 = TIM_FSCALE(a1, 24); + p->a2 = TIM_FSCALE(a2, 24); + p->b0 = TIM_FSCALE(b0, 24); + p->b1 = TIM_FSCALE(b1, 24); + p->b2 = TIM_FSCALE(b2, 24); +} + +void Reverb::calc_filter_shelving_high(filter_shelving *p) +{ + double a0, a1, a2, b0, b1, b2, omega, sn, cs, A, beta; + + init_filter_shelving(p); + + A = pow(10, p->gain / 40); + omega = 2.0 * M_PI * (double)p->freq / (double)playback_rate; + sn = sin(omega); + cs = cos(omega); + if (p->freq < 0 || p->freq > playback_rate / 2) { + p->b0 = TIM_FSCALE(1.0, 24); + p->a1 = p->b1 = p->a2 = p->b2 = 0; + return; + } + if (p->q == 0) {beta = sqrt(A + A);} + else {beta = sqrt(A) / p->q;} + + a0 = 1.0 / ((A + 1) - (A - 1) * cs + beta * sn); + a1 = (-2 * ((A - 1) - (A + 1) * cs)); + a2 = -((A + 1) - (A - 1) * cs - beta * sn); + b0 = A * ((A + 1) + (A - 1) * cs + beta * sn); + b1 = -2 * A * ((A - 1) + (A + 1) * cs); + b2 = A * ((A + 1) + (A - 1) * cs - beta * sn); + + a1 *= a0; + a2 *= a0; + b0 *= a0; + b1 *= a0; + b2 *= a0; + + p->a1 = TIM_FSCALE(a1, 24); + p->a2 = TIM_FSCALE(a2, 24); + p->b0 = TIM_FSCALE(b0, 24); + p->b1 = TIM_FSCALE(b1, 24); + p->b2 = TIM_FSCALE(b2, 24); +} + +void Reverb::do_shelving_filter_stereo(int32_t* buf, int32_t count, filter_shelving *p) +{ + int32_t i; + int32_t x1l = p->x1l, x2l = p->x2l, y1l = p->y1l, y2l = p->y2l, + x1r = p->x1r, x2r = p->x2r, y1r = p->y1r, y2r = p->y2r, yout; + int32_t a1 = p->a1, a2 = p->a2, b0 = p->b0, b1 = p->b1, b2 = p->b2; + + for(i = 0; i < count; i++) { + yout = imuldiv24(buf[i], b0) + imuldiv24(x1l, b1) + imuldiv24(x2l, b2) + imuldiv24(y1l, a1) + imuldiv24(y2l, a2); + x2l = x1l; + x1l = buf[i]; + y2l = y1l; + y1l = yout; + buf[i] = yout; + + yout = imuldiv24(buf[++i], b0) + imuldiv24(x1r, b1) + imuldiv24(x2r, b2) + imuldiv24(y1r, a1) + imuldiv24(y2r, a2); + x2r = x1r; + x1r = buf[i]; + y2r = y1r; + y1r = yout; + buf[i] = yout; + } + p->x1l = x1l, p->x2l = x2l, p->y1l = y1l, p->y2l = y2l, + p->x1r = x1r, p->x2r = x2r, p->y1r = y1r, p->y2r = y2r; +} + +void Reverb::init_filter_peaking(filter_peaking *p) +{ + p->x1l = 0, p->x2l = 0, p->y1l = 0, p->y2l = 0, p->x1r = 0, + p->x2r = 0, p->y1r = 0, p->y2r = 0; +} + +/*! peaking filter */ +void Reverb::calc_filter_peaking(filter_peaking *p) +{ + double a0, ba1, a2, b0, b2, omega, sn, cs, A, alpha; + + init_filter_peaking(p); + + A = pow(10, p->gain / 40); + omega = 2.0 * M_PI * (double)p->freq / (double)playback_rate; + sn = sin(omega); + cs = cos(omega); + if (p->q == 0 || p->freq < 0 || p->freq > playback_rate / 2) { + p->b0 = TIM_FSCALE(1.0, 24); + p->ba1 = p->a2 = p->b2 = 0; + return; + } else {alpha = sn / (2.0 * p->q);} + + a0 = 1.0 / (1.0 + alpha / A); + ba1 = -2.0 * cs; + a2 = 1.0 - alpha / A; + b0 = 1.0 + alpha * A; + b2 = 1.0 - alpha * A; + + ba1 *= a0; + a2 *= a0; + b0 *= a0; + b2 *= a0; + + p->ba1 = TIM_FSCALE(ba1, 24); + p->a2 = TIM_FSCALE(a2, 24); + p->b0 = TIM_FSCALE(b0, 24); + p->b2 = TIM_FSCALE(b2, 24); +} + +void Reverb::do_peaking_filter_stereo(int32_t* buf, int32_t count, filter_peaking *p) +{ + int32_t i; + int32_t x1l = p->x1l, x2l = p->x2l, y1l = p->y1l, y2l = p->y2l, + x1r = p->x1r, x2r = p->x2r, y1r = p->y1r, y2r = p->y2r, yout; + int32_t ba1 = p->ba1, a2 = p->a2, b0 = p->b0, b2 = p->b2; + + for(i = 0; i < count; i++) { + yout = imuldiv24(buf[i], b0) + imuldiv24(x1l - y1l, ba1) + imuldiv24(x2l, b2) - imuldiv24(y2l, a2); + x2l = x1l; + x1l = buf[i]; + y2l = y1l; + y1l = yout; + buf[i] = yout; + + yout = imuldiv24(buf[++i], b0) + imuldiv24(x1r - y1r, ba1) + imuldiv24(x2r, b2) - imuldiv24(y2r, a2); + x2r = x1r; + x1r = buf[i]; + y2r = y1r; + y1r = yout; + buf[i] = yout; + } + p->x1l = x1l, p->x2l = x2l, p->y1l = y1l, p->y2l = y2l, + p->x1r = x1r, p->x2r = x2r, p->y1r = y1r, p->y2r = y2r; +} + +void Reverb::init_pink_noise(pink_noise *p) +{ + p->b0 = p->b1 = p->b2 = p->b3 = p->b4 = p->b5 = p->b6 = 0; +} + +float Reverb::get_pink_noise(pink_noise *p) +{ + float b0 = p->b0, b1 = p->b1, b2 = p->b2, b3 = p->b3, + b4 = p->b4, b5 = p->b5, b6 = p->b6, pink, white; + + white = (float)flt_rand() * 2.0 - 1.0; + b0 = 0.99886 * b0 + white * 0.0555179; + b1 = 0.99332 * b1 + white * 0.0750759; + b2 = 0.96900 * b2 + white * 0.1538520; + b3 = 0.86650 * b3 + white * 0.3104856; + b4 = 0.55000 * b4 + white * 0.5329522; + b5 = -0.7616 * b5 - white * 0.0168980; + pink = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; + b6 = white * 0.115926; + pink *= 0.22f; + pink = (pink > 1.0) ? 1.0 : (pink < -1.0) ? -1.0 : pink; + + p->b0 = b0, p->b1 = b1, p->b2 = b2, p->b3 = b3, + p->b4 = b4, p->b5 = b5, p->b6 = b6; + + return pink; +} + +float Reverb::get_pink_noise_light(pink_noise *p) +{ + float b0 = p->b0, b1 = p->b1, b2 = p->b2, pink, white; + + white = (float)flt_rand() * 2.0 - 1.0; + b0 = 0.99765 * b0 + white * 0.0990460; + b1 = 0.96300 * b1 + white * 0.2965164; + b2 = 0.57000 * b2 + white * 1.0526913; + pink = b0 + b1 + b2 + white * 0.1848; + pink *= 0.22f; + pink = (pink > 1.0) ? 1.0 : (pink < -1.0) ? -1.0 : pink; + + p->b0 = b0, p->b1 = b1, p->b2 = b2; + + return pink; +} + +/* */ +/* Standard Reverb Effect */ +/* */ +#define REV_VAL0 5.3 +#define REV_VAL1 10.5 +#define REV_VAL2 44.12 +#define REV_VAL3 21.0 + + +void Reverb::set_ch_reverb(register int32_t *sbuffer, int32_t n, int32_t level) +{ + register int32_t i; + if(!level) {return;} + double send_level = (double)level / 127.0 * REV_INP_LEV; + + for(i = 0; i < n; i++) + { + reverb_effect_buffer[i] += int32_t(sbuffer[i] * send_level); + } +} + +double Reverb::gs_revchar_to_roomsize(int character) +{ + double rs; + switch(character) { + case 0: rs = 1.0; break; /* Room 1 */ + case 1: rs = 0.94; break; /* Room 2 */ + case 2: rs = 0.97; break; /* Room 3 */ + case 3: rs = 0.90; break; /* Hall 1 */ + case 4: rs = 0.85; break; /* Hall 2 */ + default: rs = 1.0; break; /* Plate, Delay, Panning Delay */ + } + return rs; +} + +double Reverb::gs_revchar_to_level(int character) +{ + double level; + switch(character) { + case 0: level = 0.744025605; break; /* Room 1 */ + case 1: level = 1.224309745; break; /* Room 2 */ + case 2: level = 0.858592403; break; /* Room 3 */ + case 3: level = 1.0471802; break; /* Hall 1 */ + case 4: level = 1.0; break; /* Hall 2 */ + case 5: level = 0.865335496; break; /* Plate */ + default: level = 1.0; break; /* Delay, Panning Delay */ + } + return level; +} + +double Reverb::gs_revchar_to_rt(int character) +{ + double rt; + switch(character) { + case 0: rt = 0.516850262; break; /* Room 1 */ + case 1: rt = 1.004226004; break; /* Room 2 */ + case 2: rt = 0.691046825; break; /* Room 3 */ + case 3: rt = 0.893006004; break; /* Hall 1 */ + case 4: rt = 1.0; break; /* Hall 2 */ + case 5: rt = 0.538476488; break; /* Plate */ + default: rt = 1.0; break; /* Delay, Panning Delay */ + } + return rt; +} + +void Reverb::init_standard_reverb(InfoStandardReverb *info) +{ + double time; + info->ta = info->tb = 0; + info->HPFL = info->HPFR = info->LPFL = info->LPFR = info->EPFL = info->EPFR = 0; + info->spt0 = info->spt1 = info->spt2 = info->spt3 = 0; + time = reverb_time_table[reverb_status_gs.time] * gs_revchar_to_rt(reverb_status_gs.character) + / reverb_time_table[64] * 0.8; + info->rpt0 = REV_VAL0 * playback_rate / 1000.0 * time; + info->rpt1 = REV_VAL1 * playback_rate / 1000.0 * time; + info->rpt2 = REV_VAL2 * playback_rate / 1000.0 * time; + info->rpt3 = REV_VAL3 * playback_rate / 1000.0 * time; + while (!isprime(info->rpt0)) {info->rpt0++;} + while (!isprime(info->rpt1)) {info->rpt1++;} + while (!isprime(info->rpt2)) {info->rpt2++;} + while (!isprime(info->rpt3)) {info->rpt3++;} + set_delay(&(info->buf0_L), info->rpt0 + 1); + set_delay(&(info->buf0_R), info->rpt0 + 1); + set_delay(&(info->buf1_L), info->rpt1 + 1); + set_delay(&(info->buf1_R), info->rpt1 + 1); + set_delay(&(info->buf2_L), info->rpt2 + 1); + set_delay(&(info->buf2_R), info->rpt2 + 1); + set_delay(&(info->buf3_L), info->rpt3 + 1); + set_delay(&(info->buf3_R), info->rpt3 + 1); + info->fbklev = 0.12; + info->nmixlev = 0.7; + info->cmixlev = 0.9; + info->monolev = 0.7; + info->hpflev = 0.5; + info->lpflev = 0.45; + info->lpfinp = 0.55; + info->epflev = 0.4; + info->epfinp = 0.48; + info->width = 0.125; + info->wet = 2.0 * (double)reverb_status_gs.level / 127.0 * gs_revchar_to_level(reverb_status_gs.character); + info->fbklevi = TIM_FSCALE(info->fbklev, 24); + info->nmixlevi = TIM_FSCALE(info->nmixlev, 24); + info->cmixlevi = TIM_FSCALE(info->cmixlev, 24); + info->monolevi = TIM_FSCALE(info->monolev, 24); + info->hpflevi = TIM_FSCALE(info->hpflev, 24); + info->lpflevi = TIM_FSCALE(info->lpflev, 24); + info->lpfinpi = TIM_FSCALE(info->lpfinp, 24); + info->epflevi = TIM_FSCALE(info->epflev, 24); + info->epfinpi = TIM_FSCALE(info->epfinp, 24); + info->widthi = TIM_FSCALE(info->width, 24); + info->weti = TIM_FSCALE(info->wet, 24); +} + +void Reverb::free_standard_reverb(InfoStandardReverb *info) +{ + free_delay(&(info->buf0_L)); + free_delay(&(info->buf0_R)); + free_delay(&(info->buf1_L)); + free_delay(&(info->buf1_R)); + free_delay(&(info->buf2_L)); + free_delay(&(info->buf2_R)); + free_delay(&(info->buf3_L)); + free_delay(&(info->buf3_R)); +} + +/*! Standard Reverberator; this implementation is specialized for system effect. */ +void Reverb::do_ch_standard_reverb(int32_t *buf, int32_t count, InfoStandardReverb *info) +{ + int32_t i, fixp, s, t; + int32_t spt0 = info->spt0, spt1 = info->spt1, spt2 = info->spt2, spt3 = info->spt3, + ta = info->ta, tb = info->tb, HPFL = info->HPFL, HPFR = info->HPFR, + LPFL = info->LPFL, LPFR = info->LPFR, EPFL = info->EPFL, EPFR = info->EPFR; + int32_t *buf0_L = info->buf0_L.buf, *buf0_R = info->buf0_R.buf, + *buf1_L = info->buf1_L.buf, *buf1_R = info->buf1_R.buf, + *buf2_L = info->buf2_L.buf, *buf2_R = info->buf2_R.buf, + *buf3_L = info->buf3_L.buf, *buf3_R = info->buf3_R.buf; + double fbklev = info->fbklev, cmixlev = info->cmixlev, + hpflev = info->hpflev, lpflev = info->lpflev, lpfinp = info->lpfinp, + epflev = info->epflev, epfinp = info->epfinp, width = info->width, + rpt0 = info->rpt0, rpt1 = info->rpt1, rpt2 = info->rpt2, rpt3 = info->rpt3, wet = info->wet; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_standard_reverb(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_standard_reverb(info); + return; + } + + for (i = 0; i < count; i++) + { + /* L */ + fixp = reverb_effect_buffer[i]; + + LPFL = LPFL * lpflev + (buf2_L[spt2] + tb) * lpfinp + ta * width; + ta = buf3_L[spt3]; + s = buf3_L[spt3] = buf0_L[spt0]; + buf0_L[spt0] = -LPFL; + + t = (HPFL + fixp) * hpflev; + HPFL = t - fixp; + + buf2_L[spt2] = (s - fixp * fbklev) * cmixlev; + tb = buf1_L[spt1]; + buf1_L[spt1] = t; + + EPFL = EPFL * epflev + ta * epfinp; + buf[i] += int32_t((ta + EPFL) * wet); + + /* R */ + fixp = reverb_effect_buffer[++i]; + + LPFR = LPFR * lpflev + (buf2_R[spt2] + tb) * lpfinp + ta * width; + ta = buf3_R[spt3]; + s = buf3_R[spt3] = buf0_R[spt0]; + buf0_R[spt0] = LPFR; + + t = (HPFR + fixp) * hpflev; + HPFR = t - fixp; + + buf2_R[spt2] = (s - fixp * fbklev) * cmixlev; + tb = buf1_R[spt1]; + buf1_R[spt1] = t; + + EPFR = EPFR * epflev + ta * epfinp; + buf[i] += int32_t((ta + EPFR) * wet); + + if (++spt0 == rpt0) {spt0 = 0;} + if (++spt1 == rpt1) {spt1 = 0;} + if (++spt2 == rpt2) {spt2 = 0;} + if (++spt3 == rpt3) {spt3 = 0;} + } + memset(reverb_effect_buffer, 0, sizeof(int32_t) * count); + info->spt0 = spt0, info->spt1 = spt1, info->spt2 = spt2, info->spt3 = spt3, + info->ta = ta, info->tb = tb, info->HPFL = HPFL, info->HPFR = HPFR, + info->LPFL = LPFL, info->LPFR = LPFR, info->EPFL = EPFL, info->EPFR = EPFR; +} + +/*! Standard Monoral Reverberator; this implementation is specialized for system effect. */ +void Reverb::do_ch_standard_reverb_mono(int32_t *buf, int32_t count, InfoStandardReverb *info) +{ + int32_t i, fixp, s, t; + int32_t spt0 = info->spt0, spt1 = info->spt1, spt2 = info->spt2, spt3 = info->spt3, + ta = info->ta, tb = info->tb, HPFL = info->HPFL, HPFR = info->HPFR, + LPFL = info->LPFL, LPFR = info->LPFR, EPFL = info->EPFL, EPFR = info->EPFR; + int32_t *buf0_L = info->buf0_L.buf, *buf0_R = info->buf0_R.buf, + *buf1_L = info->buf1_L.buf, *buf1_R = info->buf1_R.buf, + *buf2_L = info->buf2_L.buf, *buf2_R = info->buf2_R.buf, + *buf3_L = info->buf3_L.buf, *buf3_R = info->buf3_R.buf; + double fbklev = info->fbklev, nmixlev = info->nmixlev, monolev = info->monolev, + hpflev = info->hpflev, lpflev = info->lpflev, lpfinp = info->lpfinp, + epflev = info->epflev, epfinp = info->epfinp, width = info->width, + rpt0 = info->rpt0, rpt1 = info->rpt1, rpt2 = info->rpt2, rpt3 = info->rpt3, wet = info->wet; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_standard_reverb(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_standard_reverb(info); + return; + } + + for (i = 0; i < count; i++) + { + /* L */ + fixp = buf[i] * monolev; + + LPFL = LPFL * lpflev + (buf2_L[spt2] + tb) * lpfinp + ta * width; + ta = buf3_L[spt3]; + s = buf3_L[spt3] = buf0_L[spt0]; + buf0_L[spt0] = -LPFL; + + t = (HPFL + fixp) * hpflev; + HPFL = t - fixp; + + buf2_L[spt2] = (s - fixp * fbklev) * nmixlev; + tb = buf1_L[spt1]; + buf1_L[spt1] = t; + + /* R */ + LPFR = LPFR * lpflev + (buf2_R[spt2] + tb) * lpfinp + ta * width; + ta = buf3_R[spt3]; + s = buf3_R[spt3] = buf0_R[spt0]; + buf0_R[spt0] = LPFR; + + t = (HPFR + fixp) * hpflev; + HPFR = t - fixp; + + buf2_R[spt2] = (s - fixp * fbklev) * nmixlev; + tb = buf1_R[spt1]; + buf1_R[spt1] = t; + + EPFR = EPFR * epflev + ta * epfinp; + buf[i] = (ta + EPFR) * wet + fixp; + + if (++spt0 == rpt0) {spt0 = 0;} + if (++spt1 == rpt1) {spt1 = 0;} + if (++spt2 == rpt2) {spt2 = 0;} + if (++spt3 == rpt3) {spt3 = 0;} + } + memset(reverb_effect_buffer, 0, sizeof(int32_t) * count); + info->spt0 = spt0, info->spt1 = spt1, info->spt2 = spt2, info->spt3 = spt3, + info->ta = ta, info->tb = tb, info->HPFL = HPFL, info->HPFR = HPFR, + info->LPFL = LPFL, info->LPFR = LPFR, info->EPFL = EPFL, info->EPFR = EPFR; +} + +/* */ +/* Freeverb */ +/* */ +void Reverb::set_freeverb_allpass(allpass *allpass, int32_t size) +{ + if(allpass->buf != NULL) { + free(allpass->buf); + allpass->buf = NULL; + } + allpass->buf = (int32_t *)safe_malloc(sizeof(int32_t) * size); + if(allpass->buf == NULL) {return;} + allpass->index = 0; + allpass->size = size; +} + +void Reverb::init_freeverb_allpass(allpass *allpass) +{ + memset(allpass->buf, 0, sizeof(int32_t) * allpass->size); +} + +void Reverb::set_freeverb_comb(comb *comb, int32_t size) +{ + if(comb->buf != NULL) { + free(comb->buf); + comb->buf = NULL; + } + comb->buf = (int32_t *)safe_malloc(sizeof(int32_t) * size); + if(comb->buf == NULL) {return;} + comb->index = 0; + comb->size = size; + comb->filterstore = 0; +} + +void Reverb::init_freeverb_comb(comb *comb) +{ + memset(comb->buf, 0, sizeof(int32_t) * comb->size); +} + +#define scalewet 0.06 +#define scaledamp 0.4 +#define initialroom 0.5 +#define initialdamp 0.5 +#define initialwet 1 / scalewet +#define initialdry 0 +#define initialwidth 0.5 +#define initialallpassfbk 0.65 +#define stereospread 23 +static const int combtunings[numcombs] = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617}; +static const int allpasstunings[numallpasses] = {225, 341, 441, 556}; +#define fixedgain 0.025 +#define combfbk 3.0 + +void Reverb::realloc_freeverb_buf(InfoFreeverb *rev) +{ + int i; + int32_t tmpL, tmpR; + double time, samplerate = playback_rate; + + time = reverb_time_table[reverb_status_gs.time] * gs_revchar_to_rt(reverb_status_gs.character) * combfbk + / (60 * combtunings[numcombs - 1] / (-20 * log10(rev->roomsize1) * 44100.0)); + + for(i = 0; i < numcombs; i++) + { + tmpL = combtunings[i] * samplerate * time / 44100.0; + tmpR = (combtunings[i] + stereospread) * samplerate * time / 44100.0; + if(tmpL < 10) tmpL = 10; + if(tmpR < 10) tmpR = 10; + while(!isprime(tmpL)) tmpL++; + while(!isprime(tmpR)) tmpR++; + rev->combL[i].size = tmpL; + rev->combR[i].size = tmpR; + set_freeverb_comb(&rev->combL[i], rev->combL[i].size); + set_freeverb_comb(&rev->combR[i], rev->combR[i].size); + } + + for(i = 0; i < numallpasses; i++) + { + tmpL = allpasstunings[i] * samplerate * time / 44100.0; + tmpR = (allpasstunings[i] + stereospread) * samplerate * time / 44100.0; + if(tmpL < 10) tmpL = 10; + if(tmpR < 10) tmpR = 10; + while(!isprime(tmpL)) tmpL++; + while(!isprime(tmpR)) tmpR++; + rev->allpassL[i].size = tmpL; + rev->allpassR[i].size = tmpR; + set_freeverb_allpass(&rev->allpassL[i], rev->allpassL[i].size); + set_freeverb_allpass(&rev->allpassR[i], rev->allpassR[i].size); + } +} + +void Reverb::update_freeverb(InfoFreeverb *rev) +{ + int i; + double allpassfbk = 0.55, rtbase, rt; + + rev->wet = (double)reverb_status_gs.level / 127.0 * gs_revchar_to_level(reverb_status_gs.character) * fixedgain; + rev->roomsize = gs_revchar_to_roomsize(reverb_status_gs.character) * freeverb_scaleroom + freeverb_offsetroom; + rev->width = 0.5; + + rev->wet1 = rev->width / 2.0 + 0.5; + rev->wet2 = (1.0 - rev->width) / 2.0; + rev->roomsize1 = rev->roomsize; + rev->damp1 = rev->damp; + + realloc_freeverb_buf(rev); + + rtbase = 1.0 / (44100.0 * reverb_time_table[reverb_status_gs.time] * gs_revchar_to_rt(reverb_status_gs.character)); + + for(i = 0; i < numcombs; i++) + { + rt = pow(10.0, -combfbk * (double)combtunings[i] * rtbase); + rev->combL[i].feedback = rt; + rev->combR[i].feedback = rt; + rev->combL[i].damp1 = rev->damp1; + rev->combR[i].damp1 = rev->damp1; + rev->combL[i].damp2 = 1 - rev->damp1; + rev->combR[i].damp2 = 1 - rev->damp1; + rev->combL[i].damp1i = TIM_FSCALE(rev->combL[i].damp1, 24); + rev->combR[i].damp1i = TIM_FSCALE(rev->combR[i].damp1, 24); + rev->combL[i].damp2i = TIM_FSCALE(rev->combL[i].damp2, 24); + rev->combR[i].damp2i = TIM_FSCALE(rev->combR[i].damp2, 24); + rev->combL[i].feedbacki = TIM_FSCALE(rev->combL[i].feedback, 24); + rev->combR[i].feedbacki = TIM_FSCALE(rev->combR[i].feedback, 24); + } + + for(i = 0; i < numallpasses; i++) + { + rev->allpassL[i].feedback = allpassfbk; + rev->allpassR[i].feedback = allpassfbk; + rev->allpassL[i].feedbacki = TIM_FSCALE(rev->allpassL[i].feedback, 24); + rev->allpassR[i].feedbacki = TIM_FSCALE(rev->allpassR[i].feedback, 24); + } + + rev->wet1i = TIM_FSCALE(rev->wet1, 24); + rev->wet2i = TIM_FSCALE(rev->wet2, 24); + + set_delay(&(rev->pdelay), (int32_t)((double)reverb_status_gs.pre_delay_time * reverb_predelay_factor * playback_rate / 1000.0)); +} + +void Reverb::init_freeverb(InfoFreeverb *rev) +{ + int i; + for(i = 0; i < numcombs; i++) { + init_freeverb_comb(&rev->combL[i]); + init_freeverb_comb(&rev->combR[i]); + } + for(i = 0; i < numallpasses; i++) { + init_freeverb_allpass(&rev->allpassL[i]); + init_freeverb_allpass(&rev->allpassR[i]); + } +} + +void Reverb::alloc_freeverb_buf(InfoFreeverb *rev) +{ + int i; + if(rev->alloc_flag) {return;} + for (i = 0; i < numcombs; i++) { + set_freeverb_comb(&rev->combL[i], combtunings[i]); + set_freeverb_comb(&rev->combR[i], combtunings[i] + stereospread); + } + for (i = 0; i < numallpasses; i++) { + set_freeverb_allpass(&rev->allpassL[i], allpasstunings[i]); + set_freeverb_allpass(&rev->allpassR[i], allpasstunings[i] + stereospread); + rev->allpassL[i].feedback = initialallpassfbk; + rev->allpassR[i].feedback = initialallpassfbk; + } + + rev->wet = initialwet * scalewet; + rev->damp = initialdamp * scaledamp; + rev->width = initialwidth; + rev->roomsize = initialroom * freeverb_scaleroom + freeverb_offsetroom; + + rev->alloc_flag = 1; +} + +void Reverb::free_freeverb_buf(InfoFreeverb *rev) +{ + int i; + + for(i = 0; i < numcombs; i++) + { + if(rev->combL[i].buf != NULL) { + free(rev->combL[i].buf); + rev->combL[i].buf = NULL; + } + if(rev->combR[i].buf != NULL) { + free(rev->combR[i].buf); + rev->combR[i].buf = NULL; + } + } + for(i = 0; i < numallpasses; i++) + { + if(rev->allpassL[i].buf != NULL) { + free(rev->allpassL[i].buf); + rev->allpassL[i].buf = NULL; + } + if(rev->allpassR[i].buf != NULL) { + free(rev->allpassR[i].buf); + rev->allpassR[i].buf = NULL; + } + } + free_delay(&(rev->pdelay)); +} + +void Reverb::do_freeverb_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *index, int32_t feedback) +{ + int32_t bufout, output; + bufout = buf[*index]; + output = -*stream + bufout; + buf[*index] = *stream + imuldiv24(bufout, feedback); + if (++*index >= size) {*index = 0;} + *stream = output; +} + +void Reverb::do_freeverb_comb(int32_t input, int32_t *stream, int32_t *buf, int32_t size, int32_t *index, + int32_t damp1, int32_t damp2, int32_t *fs, int32_t feedback) +{ + int32_t output; + output = buf[*index]; + *fs = imuldiv24(output, damp2) + imuldiv24(*fs, damp1); + buf[*index] = input + imuldiv24(*fs, feedback); + if (++*index >= size) {*index = 0;} + *stream += output; +} + +void Reverb::do_ch_freeverb(int32_t *buf, int32_t count, InfoFreeverb *rev) +{ + int32_t i, k = 0; + int32_t outl, outr, input; + comb *combL = rev->combL, *combR = rev->combR; + allpass *allpassL = rev->allpassL, *allpassR = rev->allpassR; + simple_delay *pdelay = &(rev->pdelay); + + if(count == MAGIC_INIT_EFFECT_INFO) { + alloc_freeverb_buf(rev); + update_freeverb(rev); + init_freeverb(rev); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_freeverb_buf(rev); + return; + } + + for (k = 0; k < count; k++) + { + input = reverb_effect_buffer[k] + reverb_effect_buffer[k + 1]; + outl = outr = reverb_effect_buffer[k] = reverb_effect_buffer[k + 1] = 0; + + do_delay(&input, pdelay->buf, pdelay->size, &pdelay->index); + + for (i = 0; i < numcombs; i++) { + do_freeverb_comb(input, &outl, combL[i].buf, combL[i].size, &combL[i].index, + combL[i].damp1i, combL[i].damp2i, &combL[i].filterstore, combL[i].feedbacki); + do_freeverb_comb(input, &outr, combR[i].buf, combR[i].size, &combR[i].index, + combR[i].damp1i, combR[i].damp2i, &combR[i].filterstore, combR[i].feedbacki); + } + for (i = 0; i < numallpasses; i++) { + do_freeverb_allpass(&outl, allpassL[i].buf, allpassL[i].size, &allpassL[i].index, allpassL[i].feedbacki); + do_freeverb_allpass(&outr, allpassR[i].buf, allpassR[i].size, &allpassR[i].index, allpassR[i].feedbacki); + } + buf[k] += imuldiv24(outl, rev->wet1i) + imuldiv24(outr, rev->wet2i); + buf[k + 1] += imuldiv24(outr, rev->wet1i) + imuldiv24(outl, rev->wet2i); + ++k; + } +} + +/* */ +/* Reverb: Delay & Panning Delay */ +/* */ +/*! initialize Reverb: Delay Effect; this implementation is specialized for system effect. */ +void Reverb::init_ch_reverb_delay(InfoDelay3 *info) +{ + int32_t x; + info->size[0] = (double)reverb_status_gs.time * 3.75 * playback_rate / 1000.0; + x = info->size[0] + 1; /* allowance */ + set_delay(&(info->delayL), x); + set_delay(&(info->delayR), x); + info->index[0] = x - info->size[0]; + info->level[0] = (double)reverb_status_gs.level * 1.82 / 127.0; + info->feedback = sqrt((double)reverb_status_gs.delay_feedback / 127.0) * 0.98; + info->leveli[0] = TIM_FSCALE(info->level[0], 24); + info->feedbacki = TIM_FSCALE(info->feedback, 24); +} + +void Reverb::free_ch_reverb_delay(InfoDelay3 *info) +{ + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); +} + +/*! Reverb: Panning Delay Effect; this implementation is specialized for system effect. */ +void Reverb::do_ch_reverb_panning_delay(int32_t *buf, int32_t count, InfoDelay3 *info) +{ + int32_t i, l, r; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], level0i = info->leveli[0], + feedbacki = info->feedbacki; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_ch_reverb_delay(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_ch_reverb_delay(info); + return; + } + + for (i = 0; i < count; i++) + { + bufL[buf_index] = reverb_effect_buffer[i] + imuldiv24(bufR[index0], feedbacki); + l = imuldiv24(bufL[index0], level0i); + bufR[buf_index] = reverb_effect_buffer[i + 1] + imuldiv24(bufL[index0], feedbacki); + r = imuldiv24(bufR[index0], level0i); + + buf[i] += r; + buf[++i] += l; + + if (++index0 == buf_size) {index0 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + memset(reverb_effect_buffer, 0, sizeof(int32_t) * count); + info->index[0] = index0; + delayL->index = delayR->index = buf_index; +} + +/*! Reverb: Normal Delay Effect; this implementation is specialized for system effect. */ +void Reverb::do_ch_reverb_normal_delay(int32_t *buf, int32_t count, InfoDelay3 *info) +{ + int32_t i; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], level0i = info->leveli[0], + feedbacki = info->feedbacki; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_ch_reverb_delay(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_ch_reverb_delay(info); + return; + } + + for (i = 0; i < count; i++) + { + bufL[buf_index] = reverb_effect_buffer[i] + imuldiv24(bufL[index0], feedbacki); + buf[i] += imuldiv24(bufL[index0], level0i); + + bufR[buf_index] = reverb_effect_buffer[++i] + imuldiv24(bufR[index0], feedbacki); + buf[i] += imuldiv24(bufR[index0], level0i); + + if (++index0 == buf_size) {index0 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + memset(reverb_effect_buffer, 0, sizeof(int32_t) * count); + info->index[0] = index0; + delayL->index = delayR->index = buf_index; +} + +/* */ +/* Plate Reverberator */ +/* */ +#define PLATE_SAMPLERATE 29761.0 +#define PLATE_DECAY 0.50 +#define PLATE_DECAY_DIFFUSION1 0.70 +#define PLATE_DECAY_DIFFUSION2 0.50 +#define PLATE_INPUT_DIFFUSION1 0.750 +#define PLATE_INPUT_DIFFUSION2 0.625 +#define PLATE_BANDWIDTH 0.9955 +#define PLATE_DAMPING 0.0005 +#define PLATE_WET 0.25 + +/*! calculate delay sample in current sample-rate */ +int32_t Reverb::get_plate_delay(double delay, double t) +{ + return (int32_t)(delay * playback_rate * t / PLATE_SAMPLERATE); +} + +/*! Plate Reverberator; this implementation is specialized for system effect. */ +void Reverb::do_ch_plate_reverb(int32_t *buf, int32_t count, InfoPlateReverb *info) +{ + int32_t i; + int32_t x, xd, val, outl, outr, temp1, temp2, temp3; + simple_delay *pd = &(info->pd), *od1l = &(info->od1l), *od2l = &(info->od2l), + *od3l = &(info->od3l), *od4l = &(info->od4l), *od5l = &(info->od5l), + *od6l = &(info->od6l), *od1r = &(info->od1r), *od2r = &(info->od2r), + *od3r = &(info->od3r), *od4r = &(info->od4r), *od5r = &(info->od5r), + *od7r = &(info->od7r), *od7l = &(info->od7l), *od6r = &(info->od6r), + *td1 = &(info->td1), *td2 = &(info->td2), *td1d = &(info->td1d), *td2d = &(info->td2d); + allpass *ap1 = &(info->ap1), *ap2 = &(info->ap2), *ap3 = &(info->ap3), + *ap4 = &(info->ap4), *ap6 = &(info->ap6), *ap6d = &(info->ap6d); + mod_allpass *ap5 = &(info->ap5), *ap5d = &(info->ap5d); + lfo *lfo1 = &(info->lfo1), *lfo1d = &(info->lfo1d); + filter_lowpass1 *lpf1 = &(info->lpf1), *lpf2 = &(info->lpf2); + int32_t t1 = info->t1, t1d = info->t1d; + int32_t decayi = info->decayi, ddif1i = info->ddif1i, ddif2i = info->ddif2i, + idif1i = info->idif1i, idif2i = info->idif2i; + double t; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_lfo(lfo1, 1.30, LFO_SINE, 0); + init_lfo(lfo1d, 1.30, LFO_SINE, 0); + t = reverb_time_table[reverb_status_gs.time] / reverb_time_table[64] - 1.0; + t = 1.0 + t / 2; + set_delay(pd, reverb_status_gs.pre_delay_time * playback_rate / 1000); + set_delay(td1, get_plate_delay(4453, t)), + set_delay(td1d, get_plate_delay(4217, t)); + set_delay(td2, get_plate_delay(3720, t)); + set_delay(td2d, get_plate_delay(3163, t)); + set_delay(od1l, get_plate_delay(266, t)); + set_delay(od2l, get_plate_delay(2974, t)); + set_delay(od3l, get_plate_delay(1913, t)); + set_delay(od4l, get_plate_delay(1996, t)); + set_delay(od5l, get_plate_delay(1990, t)); + set_delay(od6l, get_plate_delay(187, t)); + set_delay(od7l, get_plate_delay(1066, t)); + set_delay(od1r, get_plate_delay(353, t)); + set_delay(od2r, get_plate_delay(3627, t)); + set_delay(od3r, get_plate_delay(1228, t)); + set_delay(od4r, get_plate_delay(2673, t)); + set_delay(od5r, get_plate_delay(2111, t)); + set_delay(od6r, get_plate_delay(335, t)); + set_delay(od7r, get_plate_delay(121, t)); + set_allpass(ap1, get_plate_delay(142, t), PLATE_INPUT_DIFFUSION1); + set_allpass(ap2, get_plate_delay(107, t), PLATE_INPUT_DIFFUSION1); + set_allpass(ap3, get_plate_delay(379, t), PLATE_INPUT_DIFFUSION2); + set_allpass(ap4, get_plate_delay(277, t), PLATE_INPUT_DIFFUSION2); + set_allpass(ap6, get_plate_delay(1800, t), PLATE_DECAY_DIFFUSION2); + set_allpass(ap6d, get_plate_delay(2656, t), PLATE_DECAY_DIFFUSION2); + set_mod_allpass(ap5, get_plate_delay(672, t), get_plate_delay(16, t), PLATE_DECAY_DIFFUSION1); + set_mod_allpass(ap5d, get_plate_delay(908, t), get_plate_delay(16, t), PLATE_DECAY_DIFFUSION1); + lpf1->a = PLATE_BANDWIDTH, lpf2->a = 1.0 - PLATE_DAMPING; + init_filter_lowpass1(lpf1); + init_filter_lowpass1(lpf2); + info->t1 = info->t1d = 0; + info->decay = PLATE_DECAY; + info->decayi = TIM_FSCALE(info->decay, 24); + info->ddif1 = PLATE_DECAY_DIFFUSION1; + info->ddif1i = TIM_FSCALE(info->ddif1, 24); + info->ddif2 = PLATE_DECAY_DIFFUSION2; + info->ddif2i = TIM_FSCALE(info->ddif2, 24); + info->idif1 = PLATE_INPUT_DIFFUSION1; + info->idif1i = TIM_FSCALE(info->idif1, 24); + info->idif2 = PLATE_INPUT_DIFFUSION2; + info->idif2i = TIM_FSCALE(info->idif2, 24); + info->wet = PLATE_WET * (double)reverb_status_gs.level / 127.0; + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(pd); free_delay(td1); free_delay(td1d); free_delay(td2); + free_delay(td2d); free_delay(od1l); free_delay(od2l); free_delay(od3l); + free_delay(od4l); free_delay(od5l); free_delay(od6l); free_delay(od7l); + free_delay(od1r); free_delay(od2r); free_delay(od3r); free_delay(od4r); + free_delay(od5r); free_delay(od6r); free_delay(od7r); free_allpass(ap1); + free_allpass(ap2); free_allpass(ap3); free_allpass(ap4); free_allpass(ap6); + free_allpass(ap6d); free_mod_allpass(ap5); free_mod_allpass(ap5d); + return; + } + + for (i = 0; i < count; i++) + { + outr = outl = 0; + x = (reverb_effect_buffer[i] + reverb_effect_buffer[i + 1]) >> 1; + reverb_effect_buffer[i] = reverb_effect_buffer[i + 1] = 0; + + do_delay(&x, pd->buf, pd->size, &pd->index); + do_filter_lowpass1(&x, &lpf1->x1l, lpf1->ai, lpf1->iai); + do_allpass(&x, ap1->buf, ap1->size, &ap1->index, idif1i); + do_allpass(&x, ap2->buf, ap2->size, &ap2->index, idif1i); + do_allpass(&x, ap3->buf, ap3->size, &ap3->index, idif2i); + do_allpass(&x, ap4->buf, ap4->size, &ap4->index, idif2i); + + /* tank structure */ + xd = x; + x += imuldiv24(t1d, decayi); + val = do_lfo(lfo1); + do_mod_allpass(&x, ap5->buf, ap5->size, &ap5->rindex, &ap5->windex, + ap5->ndelay, ap5->depth, val, &ap5->hist, ddif1i); + temp1 = temp2 = temp3 = x; /* n_out_1 */ + do_delay(&temp1, od5l->buf, od5l->size, &od5l->index); + outl -= temp1; /* left output 5 */ + do_delay(&temp2, od1r->buf, od1r->size, &od1r->index); + outr += temp2; /* right output 1 */ + do_delay(&temp3, od2r->buf, od2r->size, &od2r->index); + outr += temp3; /* right output 2 */ + do_delay(&x, td1->buf, td1->size, &td1->index); + do_filter_lowpass1(&x, &lpf2->x1l, lpf2->ai, lpf2->iai); + temp1 = temp2 = x; /* n_out_2 */ + do_delay(&temp1, od6l->buf, od6l->size, &od6l->index); + outl -= temp1; /* left output 6 */ + do_delay(&temp2, od3r->buf, od3r->size, &od3r->index); + outr -= temp2; /* right output 3 */ + x = imuldiv24(x, decayi); + do_allpass(&x, ap6->buf, ap6->size, &ap6->index, ddif2i); + temp1 = temp2 = x; /* n_out_3 */ + do_delay(&temp1, od7l->buf, od7l->size, &od7l->index); + outl -= temp1; /* left output 7 */ + do_delay(&temp2, od4r->buf, od4r->size, &od4r->index); + outr += temp2; /* right output 4 */ + do_delay(&x, td2->buf, td2->size, &td2->index); + t1 = x; + + xd += imuldiv24(t1, decayi); + val = do_lfo(lfo1d); + do_mod_allpass(&x, ap5d->buf, ap5d->size, &ap5d->rindex, &ap5d->windex, + ap5d->ndelay, ap5d->depth, val, &ap5d->hist, ddif1i); + temp1 = temp2 = temp3 = xd; /* n_out_4 */ + do_delay(&temp1, od1l->buf, od1l->size, &od1l->index); + outl += temp1; /* left output 1 */ + do_delay(&temp2, od2l->buf, od2l->size, &od2l->index); + outl += temp2; /* left output 2 */ + do_delay(&temp3, od6r->buf, od6r->size, &od6r->index); + outr -= temp3; /* right output 6 */ + do_delay(&xd, td1d->buf, td1d->size, &td1d->index); + do_filter_lowpass1(&xd, &lpf2->x1r, lpf2->ai, lpf2->iai); + temp1 = temp2 = xd; /* n_out_5 */ + do_delay(&temp1, od3l->buf, od3l->size, &od3l->index); + outl -= temp1; /* left output 3 */ + do_delay(&temp2, od6r->buf, od6r->size, &od6r->index); + outr -= temp2; /* right output 6 */ + xd = imuldiv24(xd, decayi); + do_allpass(&xd, ap6d->buf, ap6d->size, &ap6d->index, ddif2i); + temp1 = temp2 = xd; /* n_out_6 */ + do_delay(&temp1, od4l->buf, od4l->size, &od4l->index); + outl += temp1; /* left output 4 */ + do_delay(&temp2, od7r->buf, od7r->size, &od7r->index); + outr -= temp2; /* right output 7 */ + do_delay(&xd, td2d->buf, td2d->size, &td2d->index); + t1d = xd; + + buf[i] += outl; + buf[i + 1] += outr; + + ++i; + } + info->t1 = t1, info->t1d = t1d; +} + +/*! initialize Reverb Effect */ +void Reverb::init_reverb(void) +{ + init_filter_lowpass1(&(reverb_status_gs.lpf)); + /* Only initialize freeverb if stereo output */ + /* Old non-freeverb must be initialized for mono reverb not to crash */ + if ( (opt_reverb_control == 3 || opt_reverb_control == 4 + || (opt_reverb_control < 0 && ! (opt_reverb_control & 0x100)))) { + switch(reverb_status_gs.character) { /* select reverb algorithm */ + case 5: /* Plate Reverb */ + do_ch_plate_reverb(NULL, MAGIC_INIT_EFFECT_INFO, &(reverb_status_gs.info_plate_reverb)); + REV_INP_LEV = reverb_status_gs.info_plate_reverb.wet; + break; + case 6: /* Delay */ + do_ch_reverb_normal_delay(NULL, MAGIC_INIT_EFFECT_INFO, &(reverb_status_gs.info_reverb_delay)); + REV_INP_LEV = 1.0; + break; + case 7: /* Panning Delay */ + do_ch_reverb_panning_delay(NULL, MAGIC_INIT_EFFECT_INFO, &(reverb_status_gs.info_reverb_delay)); + REV_INP_LEV = 1.0; + break; + default: /* Freeverb */ + do_ch_freeverb(NULL, MAGIC_INIT_EFFECT_INFO, &(reverb_status_gs.info_freeverb)); + REV_INP_LEV = reverb_status_gs.info_freeverb.wet; + break; + } + } else { /* Old Reverb */ + do_ch_standard_reverb(NULL, MAGIC_INIT_EFFECT_INFO, &(reverb_status_gs.info_standard_reverb)); + REV_INP_LEV = 1.0; + } + memset(reverb_effect_buffer, 0, reverb_effect_bufsize); + memset(direct_buffer, 0, direct_bufsize); +} + +void Reverb::do_ch_reverb(int32_t *buf, int32_t count) +{ +#ifdef SYS_EFFECT_PRE_LPF + if ((opt_reverb_control == 3 || opt_reverb_control == 4 + || (opt_reverb_control < 0 && ! (opt_reverb_control & 0x100))) && reverb_status_gs.pre_lpf) + do_filter_lowpass1_stereo(reverb_effect_buffer, count, &(reverb_status_gs.lpf)); +#endif /* SYS_EFFECT_PRE_LPF */ + if (opt_reverb_control == 3 || opt_reverb_control == 4 + || (opt_reverb_control < 0 && ! (opt_reverb_control & 0x100))) { + switch(reverb_status_gs.character) { /* select reverb algorithm */ + case 5: /* Plate Reverb */ + do_ch_plate_reverb(buf, count, &(reverb_status_gs.info_plate_reverb)); + REV_INP_LEV = reverb_status_gs.info_plate_reverb.wet; + break; + case 6: /* Delay */ + do_ch_reverb_normal_delay(buf, count, &(reverb_status_gs.info_reverb_delay)); + REV_INP_LEV = 1.0; + break; + case 7: /* Panning Delay */ + do_ch_reverb_panning_delay(buf, count, &(reverb_status_gs.info_reverb_delay)); + REV_INP_LEV = 1.0; + break; + default: /* Freeverb */ + do_ch_freeverb(buf, count, &(reverb_status_gs.info_freeverb)); + REV_INP_LEV = reverb_status_gs.info_freeverb.wet; + break; + } + } else { /* Old Reverb */ + do_ch_standard_reverb(buf, count, &(reverb_status_gs.info_standard_reverb)); + } +} + +/* */ +/* Delay Effect */ +/* */ + +void Reverb::init_ch_delay(void) +{ + memset(delay_effect_buffer, 0, sizeof(delay_effect_buffer)); + init_filter_lowpass1(&(delay_status_gs.lpf)); + do_ch_3tap_delay(NULL, MAGIC_INIT_EFFECT_INFO, &(delay_status_gs.info_delay)); +} + +void Reverb::do_ch_delay(int32_t *buf, int32_t count) +{ +#ifdef SYS_EFFECT_PRE_LPF + if ((opt_reverb_control == 3 || opt_reverb_control == 4 + || (opt_reverb_control < 0 && ! (opt_reverb_control & 0x100))) && delay_status_gs.pre_lpf) + do_filter_lowpass1_stereo(delay_effect_buffer, count, &(delay_status_gs.lpf)); +#endif /* SYS_EFFECT_PRE_LPF */ + switch (delay_status_gs.type) { + case 1: + do_ch_3tap_delay(buf, count, &(delay_status_gs.info_delay)); + break; + case 2: + do_ch_cross_delay(buf, count, &(delay_status_gs.info_delay)); + break; + default: + do_ch_normal_delay(buf, count, &(delay_status_gs.info_delay)); + break; + } +} + +void Reverb::set_ch_delay(register int32_t *sbuffer, int32_t n, int32_t level) +{ + register int32_t i; + if(!level) {return;} + double send_level = (double)level / 127.0; + + for(i = 0; i < n; i++) + { + delay_effect_buffer[i] += int32_t(sbuffer[i] * send_level); + } +} + +/*! initialize Delay Effect; this implementation is specialized for system effect. */ +void Reverb::init_ch_3tap_delay(InfoDelay3 *info) +{ + int32_t i, x; + + for (i = 0; i < 3; i++) { + info->size[i] = delay_status_gs.sample[i]; + } + x = info->size[0]; /* find maximum value */ + for (i = 1; i < 3; i++) { + if (info->size[i] > x) {x = info->size[i];} + } + x += 1; /* allowance */ + set_delay(&(info->delayL), x); + set_delay(&(info->delayR), x); + for (i = 0; i < 3; i++) { + info->index[i] = (x - info->size[i]) % x; /* set start-point */ + info->level[i] = delay_status_gs.level_ratio[i] * MASTER_DELAY_LEVEL; + info->leveli[i] = TIM_FSCALE(info->level[i], 24); + } + info->feedback = delay_status_gs.feedback_ratio; + info->send_reverb = delay_status_gs.send_reverb_ratio * REV_INP_LEV; + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->send_reverbi = TIM_FSCALE(info->send_reverb, 24); +} + +void Reverb::free_ch_3tap_delay(InfoDelay3 *info) +{ + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); +} + +/*! 3-Tap Stereo Delay Effect; this implementation is specialized for system effect. */ +void Reverb::do_ch_3tap_delay(int32_t *buf, int32_t count, InfoDelay3 *info) +{ + int32_t i, x; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], index1 = info->index[1], index2 = info->index[2]; + int32_t level0i = info->leveli[0], level1i = info->leveli[1], level2i = info->leveli[2], + feedbacki = info->feedbacki, send_reverbi = info->send_reverbi; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_ch_3tap_delay(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_ch_3tap_delay(info); + return; + } + + for (i = 0; i < count; i++) + { + bufL[buf_index] = delay_effect_buffer[i] + imuldiv24(bufL[index0], feedbacki); + x = imuldiv24(bufL[index0], level0i) + imuldiv24(bufL[index1] + bufR[index1], level1i); + buf[i] += x; + reverb_effect_buffer[i] += imuldiv24(x, send_reverbi); + + bufR[buf_index] = delay_effect_buffer[++i] + imuldiv24(bufR[index0], feedbacki); + x = imuldiv24(bufR[index0], level0i) + imuldiv24(bufL[index2] + bufR[index2], level2i); + buf[i] += x; + reverb_effect_buffer[i] += imuldiv24(x, send_reverbi); + + if (++index0 == buf_size) {index0 = 0;} + if (++index1 == buf_size) {index1 = 0;} + if (++index2 == buf_size) {index2 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + memset(delay_effect_buffer, 0, sizeof(int32_t) * count); + info->index[0] = index0, info->index[1] = index1, info->index[2] = index2; + delayL->index = delayR->index = buf_index; +} + +/*! Cross Delay Effect; this implementation is specialized for system effect. */ +void Reverb::do_ch_cross_delay(int32_t *buf, int32_t count, InfoDelay3 *info) +{ + int32_t i, l, r; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], level0i = info->leveli[0], + feedbacki = info->feedbacki, send_reverbi = info->send_reverbi; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_ch_3tap_delay(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_ch_3tap_delay(info); + return; + } + + for (i = 0; i < count; i++) + { + bufL[buf_index] = delay_effect_buffer[i] + imuldiv24(bufR[index0], feedbacki); + l = imuldiv24(bufL[index0], level0i); + bufR[buf_index] = delay_effect_buffer[i + 1] + imuldiv24(bufL[index0], feedbacki); + r = imuldiv24(bufR[index0], level0i); + + buf[i] += r; + reverb_effect_buffer[i] += imuldiv24(r, send_reverbi); + buf[++i] += l; + reverb_effect_buffer[i] += imuldiv24(l, send_reverbi); + + if (++index0 == buf_size) {index0 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + memset(delay_effect_buffer, 0, sizeof(int32_t) * count); + info->index[0] = index0; + delayL->index = delayR->index = buf_index; +} + +/*! Normal Delay Effect; this implementation is specialized for system effect. */ +void Reverb::do_ch_normal_delay(int32_t *buf, int32_t count, InfoDelay3 *info) +{ + int32_t i, x; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], level0i = info->leveli[0], + feedbacki = info->feedbacki, send_reverbi = info->send_reverbi; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_ch_3tap_delay(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_ch_3tap_delay(info); + return; + } + + for (i = 0; i < count; i++) + { + bufL[buf_index] = delay_effect_buffer[i] + imuldiv24(bufL[index0], feedbacki); + x = imuldiv24(bufL[index0], level0i); + buf[i] += x; + reverb_effect_buffer[i] += imuldiv24(x, send_reverbi); + + bufR[buf_index] = delay_effect_buffer[++i] + imuldiv24(bufR[index0], feedbacki); + x = imuldiv24(bufR[index0], level0i); + buf[i] += x; + reverb_effect_buffer[i] += imuldiv24(x, send_reverbi); + + if (++index0 == buf_size) {index0 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + memset(delay_effect_buffer, 0, sizeof(int32_t) * count); + info->index[0] = index0; + delayL->index = delayR->index = buf_index; +} + +/* */ +/* Chorus Effect */ +/* */ + +/*! Stereo Chorus; this implementation is specialized for system effect. */ +void Reverb::do_ch_stereo_chorus(int32_t *buf, int32_t count, InfoStereoChorus *info) +{ + int32_t i, output, f0, f1, v0, v1; + int32_t *bufL = info->delayL.buf, *bufR = info->delayR.buf, + *lfobufL = info->lfoL.buf, *lfobufR = info->lfoR.buf, + icycle = info->lfoL.icycle, cycle = info->lfoL.cycle, + leveli = info->leveli, feedbacki = info->feedbacki, + send_reverbi = info->send_reverbi, send_delayi = info->send_delayi, + depth = info->depth, pdelay = info->pdelay, rpt0 = info->rpt0; + int32_t wpt0 = info->wpt0, spt0 = info->spt0, spt1 = info->spt1, + hist0 = info->hist0, hist1 = info->hist1, lfocnt = info->lfoL.count; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_lfo(&(info->lfoL), (double)chorus_status_gs.rate * 0.122, LFO_TRIANGULAR, 0); + init_lfo(&(info->lfoR), (double)chorus_status_gs.rate * 0.122, LFO_TRIANGULAR, 90); + info->pdelay = chorus_delay_time_table[chorus_status_gs.delay] * (double)playback_rate / 1000.0; + info->depth = (double)(chorus_status_gs.depth + 1) / 3.2 * (double)playback_rate / 1000.0; + info->pdelay -= info->depth / 2; /* NOMINAL_DELAY to delay */ + if (info->pdelay < 1) {info->pdelay = 1;} + info->rpt0 = info->pdelay + info->depth + 2; /* allowance */ + set_delay(&(info->delayL), info->rpt0); + set_delay(&(info->delayR), info->rpt0); + info->feedback = (double)chorus_status_gs.feedback * 0.763 / 100.0; + info->level = (double)chorus_status_gs.level / 127.0 * MASTER_CHORUS_LEVEL; + info->send_reverb = (double)chorus_status_gs.send_reverb * 0.787 / 100.0 * REV_INP_LEV; + info->send_delay = (double)chorus_status_gs.send_delay * 0.787 / 100.0; + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->leveli = TIM_FSCALE(info->level, 24); + info->send_reverbi = TIM_FSCALE(info->send_reverb, 24); + info->send_delayi = TIM_FSCALE(info->send_delay, 24); + info->wpt0 = info->spt0 = info->spt1 = info->hist0 = info->hist1 = 0; + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + /* LFO */ + f0 = imuldiv24(lfobufL[imuldiv24(lfocnt, icycle)], depth); + spt0 = wpt0 - pdelay - (f0 >> 8); /* integral part of delay */ + f0 = 0xFF - (f0 & 0xFF); /* (1 - frac) * 256 */ + if(spt0 < 0) {spt0 += rpt0;} + f1 = imuldiv24(lfobufR[imuldiv24(lfocnt, icycle)], depth); + spt1 = wpt0 - pdelay - (f1 >> 8); /* integral part of delay */ + f1 = 0xFF - (f1 & 0xFF); /* (1 - frac) * 256 */ + if(spt1 < 0) {spt1 += rpt0;} + + for(i = 0; i < count; i++) { + v0 = bufL[spt0]; + v1 = bufR[spt1]; + + /* LFO */ + if(++wpt0 == rpt0) {wpt0 = 0;} + f0 = imuldiv24(lfobufL[imuldiv24(lfocnt, icycle)], depth); + spt0 = wpt0 - pdelay - (f0 >> 8); /* integral part of delay */ + f0 = 0xFF - (f0 & 0xFF); /* (1 - frac) * 256 */ + if(spt0 < 0) {spt0 += rpt0;} + f1 = imuldiv24(lfobufR[imuldiv24(lfocnt, icycle)], depth); + spt1 = wpt0 - pdelay - (f1 >> 8); /* integral part of delay */ + f1 = 0xFF - (f1 & 0xFF); /* (1 - frac) * 256 */ + if(spt1 < 0) {spt1 += rpt0;} + if(++lfocnt == cycle) {lfocnt = 0;} + + /* left */ + /* delay with all-pass interpolation */ + output = hist0 = v0 + imuldiv8(bufL[spt0] - hist0, f0); + bufL[wpt0] = chorus_effect_buffer[i] + imuldiv24(output, feedbacki); + output = imuldiv24(output, leveli); + buf[i] += output; + /* send to other system effects (it's peculiar to GS) */ + reverb_effect_buffer[i] += imuldiv24(output, send_reverbi); + delay_effect_buffer[i] += imuldiv24(output, send_delayi); + + /* right */ + /* delay with all-pass interpolation */ + output = hist1 = v1 + imuldiv8(bufR[spt1] - hist1, f1); + bufR[wpt0] = chorus_effect_buffer[++i] + imuldiv24(output, feedbacki); + output = imuldiv24(output, leveli); + buf[i] += output; + /* send to other system effects (it's peculiar to GS) */ + reverb_effect_buffer[i] += imuldiv24(output, send_reverbi); + delay_effect_buffer[i] += imuldiv24(output, send_delayi); + } + memset(chorus_effect_buffer, 0, sizeof(int32_t) * count); + info->wpt0 = wpt0, info->spt0 = spt0, info->spt1 = spt1, + info->hist0 = hist0, info->hist1 = hist1; + info->lfoL.count = info->lfoR.count = lfocnt; +} + +void Reverb::init_ch_chorus(void) +{ + /* clear delay-line of LPF */ + init_filter_lowpass1(&(chorus_status_gs.lpf)); + do_ch_stereo_chorus(NULL, MAGIC_INIT_EFFECT_INFO, &(chorus_status_gs.info_stereo_chorus)); + memset(chorus_effect_buffer, 0, sizeof(chorus_effect_buffer)); +} + +void Reverb::set_ch_chorus(register int32_t *sbuffer,int32_t n, int32_t level) +{ + register int32_t i; + register int32_t count = n; + if(!level) {return;} + double send_level = (double)level / 127.0; + + for(i = 0; i < count; i++) + { + chorus_effect_buffer[i] += int32_t(sbuffer[i] * send_level); + } +} + +void Reverb::do_ch_chorus(int32_t *buf, int32_t count) +{ +#ifdef SYS_EFFECT_PRE_LPF + if ((opt_reverb_control == 3 || opt_reverb_control == 4 + || (opt_reverb_control < 0 && ! (opt_reverb_control & 0x100))) && chorus_status_gs.pre_lpf) + do_filter_lowpass1_stereo(chorus_effect_buffer, count, &(chorus_status_gs.lpf)); +#endif /* SYS_EFFECT_PRE_LPF */ + + do_ch_stereo_chorus(buf, count, &(chorus_status_gs.info_stereo_chorus)); +} + +/* */ +/* EQ (Equalizer) */ +/* */ + +void Reverb::init_eq_gs() +{ + memset(eq_buffer, 0, sizeof(eq_buffer)); + calc_filter_shelving_low(&(eq_status_gs.lsf)); + calc_filter_shelving_high(&(eq_status_gs.hsf)); +} + +void Reverb::do_ch_eq_gs(int32_t* buf, int32_t count) +{ + register int32_t i; + + do_shelving_filter_stereo(eq_buffer, count, &(eq_status_gs.lsf)); + do_shelving_filter_stereo(eq_buffer, count, &(eq_status_gs.hsf)); + + for(i = 0; i < count; i++) { + buf[i] += eq_buffer[i]; + eq_buffer[i] = 0; + } +} + +void Reverb::do_ch_eq_xg(int32_t* buf, int32_t count, struct part_eq_xg *p) +{ + if(p->bass - 0x40 != 0) { + do_shelving_filter_stereo(buf, count, &(p->basss)); + } + if(p->treble - 0x40 != 0) { + do_shelving_filter_stereo(buf, count, &(p->trebles)); + } +} + +void Reverb::do_multi_eq_xg(int32_t* buf, int32_t count) +{ + if(multi_eq_xg.valid1) { + if(multi_eq_xg.shape1) { /* peaking */ + do_peaking_filter_stereo(buf, count, &(multi_eq_xg.eq1p)); + } else { /* shelving */ + do_shelving_filter_stereo(buf, count, &(multi_eq_xg.eq1s)); + } + } + if(multi_eq_xg.valid2) { + do_peaking_filter_stereo(buf, count, &(multi_eq_xg.eq2p)); + } + if(multi_eq_xg.valid3) { + do_peaking_filter_stereo(buf, count, &(multi_eq_xg.eq3p)); + } + if(multi_eq_xg.valid4) { + do_peaking_filter_stereo(buf, count, &(multi_eq_xg.eq4p)); + } + if(multi_eq_xg.valid5) { + if(multi_eq_xg.shape5) { /* peaking */ + do_peaking_filter_stereo(buf, count, &(multi_eq_xg.eq5p)); + } else { /* shelving */ + do_shelving_filter_stereo(buf, count, &(multi_eq_xg.eq5s)); + } + } +} + +void Reverb::set_ch_eq_gs(register int32_t *sbuffer, int32_t n) +{ + register int32_t i; + + for(i = 0; i < n; i++) + { + eq_buffer[i] += sbuffer[i]; + } +} + + +/* */ +/* Insertion and Variation Effect */ +/* */ +void Reverb::do_insertion_effect_gs(int32_t *buf, int32_t count) +{ + do_effect_list(buf, count, insertion_effect_gs.ef); +} + +void Reverb::do_insertion_effect_xg(int32_t *buf, int32_t count, struct effect_xg_t *st) +{ + do_effect_list(buf, count, st->ef); +} + +void Reverb::do_variation_effect1_xg(int32_t *buf, int32_t count) +{ + int32_t i, x; + int32_t send_reverbi = TIM_FSCALE((double)variation_effect_xg[0].send_reverb * (0.787 / 100.0 * REV_INP_LEV), 24), + send_chorusi = TIM_FSCALE((double)variation_effect_xg[0].send_chorus * (0.787 / 100.0), 24); + if (variation_effect_xg[0].connection == XG_CONN_SYSTEM) { + do_effect_list(delay_effect_buffer, count, variation_effect_xg[0].ef); + for (i = 0; i < count; i++) { + x = delay_effect_buffer[i]; + buf[i] += x; + reverb_effect_buffer[i] += imuldiv24(x, send_reverbi); + chorus_effect_buffer[i] += imuldiv24(x, send_chorusi); + } + } + memset(delay_effect_buffer, 0, sizeof(int32_t) * count); +} + +void Reverb::do_ch_chorus_xg(int32_t *buf, int32_t count) +{ + int32_t i; + int32_t send_reverbi = TIM_FSCALE((double)chorus_status_xg.send_reverb * (0.787 / 100.0 * REV_INP_LEV), 24); + + do_effect_list(chorus_effect_buffer, count, chorus_status_xg.ef); + for (i = 0; i < count; i++) { + buf[i] += chorus_effect_buffer[i]; + reverb_effect_buffer[i] += imuldiv24(chorus_effect_buffer[i], send_reverbi); + } + memset(chorus_effect_buffer, 0, sizeof(int32_t) * count); +} + +void Reverb::do_ch_reverb_xg(int32_t *buf, int32_t count) +{ + int32_t i; + + do_effect_list(reverb_effect_buffer, count, reverb_status_xg.ef); + for (i = 0; i < count; i++) { + buf[i] += reverb_effect_buffer[i]; + } + memset(reverb_effect_buffer, 0, sizeof(int32_t) * count); +} + +void Reverb::init_ch_effect_xg(void) +{ + memset(reverb_effect_buffer, 0, sizeof(reverb_effect_buffer)); + memset(chorus_effect_buffer, 0, sizeof(chorus_effect_buffer)); + memset(delay_effect_buffer, 0, sizeof(delay_effect_buffer)); +} + +void Reverb::alloc_effect(EffectList *ef) +{ + int i; + + ef->engine = NULL; + for(i = 0; effect_engine[i].type != -1; i++) { + if (effect_engine[i].type == ef->type) { + ef->engine = &(effect_engine[i]); + break; + } + } + if (ef->engine == NULL) {return;} + + if (ef->info != NULL) { + free(ef->info); + ef->info = NULL; + } + ef->info = safe_malloc(ef->engine->info_size); + memset(ef->info, 0, ef->engine->info_size); + +/* ctl_cmsg(CMSG_INFO, VERB_NOISY, "Effect Engine: %s", ef->engine->name); */ +} + +/*! allocate new effect item and add it into the tail of effect list. + EffectList *efc: pointer to the top of effect list. + int8_t type: type of new effect item. + void *info: pointer to infomation of new effect item. */ +EffectList *Reverb::push_effect(EffectList *efc, int type) +{ + EffectList *eft, *efn; + if (type == EFFECT_NONE) {return NULL;} + efn = (EffectList *)safe_malloc(sizeof(EffectList)); + memset(efn, 0, sizeof(EffectList)); + efn->type = type; + efn->next_ef = NULL; + efn->info = NULL; + alloc_effect(efn); + + if(efc == NULL) { + efc = efn; + } else { + eft = efc; + while(eft->next_ef != NULL) { + eft = eft->next_ef; + } + eft->next_ef = efn; + } + return efc; +} + +/*! process all items of effect list. */ +void Reverb::do_effect_list(int32_t *buf, int32_t count, EffectList *ef) +{ + EffectList *efc = ef; + if(ef == NULL) {return;} + while(efc != NULL && efc->engine->do_effect != NULL) + { + (this->*(efc->engine->do_effect))(buf, count, efc); + efc = efc->next_ef; + } +} + +/*! free all items of effect list. */ +void Reverb::free_effect_list(EffectList *ef) +{ + EffectList *efc, *efn; + efc = ef; + if (efc == NULL) {return;} + do { + efn = efc->next_ef; + if(efc->info != NULL) { + (this->*(efc->engine->do_effect))(NULL, MAGIC_FREE_EFFECT_INFO, efc); + free(efc->info); + efc->info = NULL; + } + efc->engine = NULL; + free(efc); + efc = NULL; + } while ((efc = efn) != NULL); +} + +/*! 2-Band EQ */ +void Reverb::do_eq2(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoEQ2 *eq = (InfoEQ2 *)ef->info; + if(count == MAGIC_INIT_EFFECT_INFO) { + eq->lsf.q = 0; + eq->lsf.freq = eq->low_freq; + eq->lsf.gain = eq->low_gain; + calc_filter_shelving_low(&(eq->lsf)); + eq->hsf.q = 0; + eq->hsf.freq = eq->high_freq; + eq->hsf.gain = eq->high_gain; + calc_filter_shelving_high(&(eq->hsf)); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + if(eq->low_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->lsf)); + } + if(eq->high_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->hsf)); + } +} + +/*! panning (pan = [0, 127]) */ +int32_t Reverb::do_left_panning(int32_t sample, int32_t pan) +{ + return imuldiv8(sample, 256 - pan - pan); +} + +int32_t Reverb::do_right_panning(int32_t sample, int32_t pan) +{ + return imuldiv8(sample, pan + pan); +} + +#define OD_BITS 28 +#define OD_MAX_NEG (1.0 / (double)(1L << OD_BITS)) +#define OD_DRIVE_GS 4.0 +#define OD_LEVEL_GS 0.5 +#define STEREO_OD_BITS 27 +#define STEREO_OD_MAX_NEG (1.0 / (double)(1L << STEREO_OD_BITS)) +#define OVERDRIVE_DIST 4.0 +#define OVERDRIVE_RES 0.1 +#define OVERDRIVE_LEVEL 1.0 +#define OVERDRIVE_OFFSET 0 +#define DISTORTION_DIST 40.0 +#define DISTORTION_RES 0.2 +#define DISTORTION_LEVEL 0.2 +#define DISTORTION_OFFSET 0 + +double Reverb::calc_gs_drive(int val) +{ + return (OD_DRIVE_GS * (double)val / 127.0 + 1.0); +} + +/*! GS 0x0110: Overdrive 1 */ +void Reverb::do_overdrive1(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoOverdrive1 *info = (InfoOverdrive1 *)ef->info; + filter_moog *svf = &(info->svf); + filter_biquad *lpf1 = &(info->lpf1); + void (Reverb::*do_amp_sim)(int32_t *, int32_t) = info->amp_sim; + int32_t i, input, high, leveli = info->leveli, di = info->di, + pan = info->pan, asdi = TIM_FSCALE(1.0, 24); + + if(count == MAGIC_INIT_EFFECT_INFO) { + /* decompositor */ + svf->freq = 500; + svf->res_dB = 0; + calc_filter_moog(svf); + init_filter_moog(svf); + /* amp simulator */ + info->amp_sim = &Reverb::do_dummy_clipping; + if (info->amp_sw == 1) { + if (info->amp_type <= 3) {info->amp_sim = &Reverb::do_soft_clipping2;} + } + /* waveshaper */ + info->di = TIM_FSCALE(calc_gs_drive(info->drive), 24); + info->leveli = TIM_FSCALE(info->level * OD_LEVEL_GS, 24); + /* anti-aliasing */ + lpf1->freq = 8000.0; + lpf1->q = 1.0; + calc_filter_biquad_low(lpf1); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + for(i = 0; i < count; i++) { + input = (buf[i] + buf[i + 1]) >> 1; + /* amp simulation */ + (this->*do_amp_sim)(&input, asdi); + /* decomposition */ + do_filter_moog(&input, &high, svf->f, svf->p, svf->q, + &svf->b0, &svf->b1, &svf->b2, &svf->b3, &svf->b4); + /* waveshaping */ + do_soft_clipping1(&high, di); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1l, &lpf1->x2l, &lpf1->y1l, &lpf1->y2l); + /* mixing */ + input = imuldiv24(high + input, leveli); + buf[i] = do_left_panning(input, pan); + buf[i + 1] = do_right_panning(input, pan); + ++i; + } +} + +/*! GS 0x0111: Distortion 1 */ +void Reverb::do_distortion1(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoOverdrive1 *info = (InfoOverdrive1 *)ef->info; + filter_moog *svf = &(info->svf); + filter_biquad *lpf1 = &(info->lpf1); + void (Reverb::*do_amp_sim)(int32_t *, int32_t) = info->amp_sim; + int32_t i, input, high, leveli = info->leveli, di = info->di, + pan = info->pan, asdi = TIM_FSCALE(1.0, 24); + + if(count == MAGIC_INIT_EFFECT_INFO) { + /* decompositor */ + svf->freq = 500; + svf->res_dB = 0; + calc_filter_moog(svf); + init_filter_moog(svf); + /* amp simulator */ + info->amp_sim = &Reverb::do_dummy_clipping; + if (info->amp_sw == 1) { + if (info->amp_type <= 3) {info->amp_sim = &Reverb::do_soft_clipping2;} + } + /* waveshaper */ + info->di = TIM_FSCALE(calc_gs_drive(info->drive), 24); + info->leveli = TIM_FSCALE(info->level * OD_LEVEL_GS, 24); + /* anti-aliasing */ + lpf1->freq = 8000.0; + lpf1->q = 1.0; + calc_filter_biquad_low(lpf1); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + for(i = 0; i < count; i++) { + input = (buf[i] + buf[i + 1]) >> 1; + /* amp simulation */ + (this->*do_amp_sim)(&input, asdi); + /* decomposition */ + do_filter_moog(&input, &high, svf->f, svf->p, svf->q, + &svf->b0, &svf->b1, &svf->b2, &svf->b3, &svf->b4); + /* waveshaping */ + do_hard_clipping(&high, di); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1l, &lpf1->x2l, &lpf1->y1l, &lpf1->y2l); + /* mixing */ + input = imuldiv24(high + input, leveli); + buf[i] = do_left_panning(input, pan); + buf[i + 1] = do_right_panning(input, pan); + ++i; + } +} + +/*! GS 0x1103: OD1 / OD2 */ +void Reverb::do_dual_od(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoOD1OD2 *info = (InfoOD1OD2 *)ef->info; + filter_moog *svfl = &(info->svfl), *svfr = &(info->svfr); + filter_biquad *lpf1 = &(info->lpf1); + void (Reverb::*do_amp_siml)(int32_t *, int32_t) = info->amp_siml, + (Reverb::*do_odl)(int32_t *, int32_t) = info->odl, + (Reverb::*do_odr)(int32_t *, int32_t) = info->odr; + int32_t i, inputl, inputr, high, levelli = info->levelli, levelri = info->levelri, + dli = info->dli, dri = info->dri, panl = info->panl, panr = info->panr, asdi = TIM_FSCALE(1.0, 24); + + if(count == MAGIC_INIT_EFFECT_INFO) { + /* left */ + /* decompositor */ + svfl->freq = 500; + svfl->res_dB = 0; + calc_filter_moog(svfl); + init_filter_moog(svfl); + /* amp simulator */ + info->amp_siml = &Reverb::do_dummy_clipping; + if (info->amp_swl == 1) { + if (info->amp_typel <= 3) {info->amp_siml = &Reverb::do_soft_clipping2;} + } + /* waveshaper */ + if(info->typel == 0) {info->odl = &Reverb::do_soft_clipping1;} + else {info->odl = &Reverb::do_hard_clipping;} + info->dli = TIM_FSCALE(calc_gs_drive(info->drivel), 24); + info->levelli = TIM_FSCALE(info->levell * OD_LEVEL_GS, 24); + /* right */ + /* decompositor */ + svfr->freq = 500; + svfr->res_dB = 0; + calc_filter_moog(svfr); + init_filter_moog(svfr); + /* amp simulator */ + info->amp_simr = &Reverb::do_dummy_clipping; + if (info->amp_swr == 1) { + if (info->amp_typer <= 3) {info->amp_simr = &Reverb::do_soft_clipping2;} + } + /* waveshaper */ + if(info->typer == 0) {info->odr = &Reverb::do_soft_clipping1;} + else {info->odr = &Reverb::do_hard_clipping;} + info->dri = TIM_FSCALE(calc_gs_drive(info->driver), 24); + info->levelri = TIM_FSCALE(info->levelr * OD_LEVEL_GS, 24); + /* anti-aliasing */ + lpf1->freq = 8000.0; + lpf1->q = 1.0; + calc_filter_biquad_low(lpf1); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + for(i = 0; i < count; i++) { + /* left */ + inputl = buf[i]; + /* amp simulation */ + (this->*do_amp_siml)(&inputl, asdi); + /* decomposition */ + do_filter_moog(&inputl, &high, svfl->f, svfl->p, svfl->q, + &svfl->b0, &svfl->b1, &svfl->b2, &svfl->b3, &svfl->b4); + /* waveshaping */ + (this->*do_odl)(&high, dli); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1l, &lpf1->x2l, &lpf1->y1l, &lpf1->y2l); + inputl = imuldiv24(high + inputl, levelli); + + /* right */ + inputr = buf[++i]; + /* amp simulation */ + (this->*do_amp_siml)(&inputr, asdi); + /* decomposition */ + do_filter_moog(&inputr, &high, svfr->f, svfr->p, svfr->q, + &svfr->b0, &svfr->b1, &svfr->b2, &svfr->b3, &svfr->b4); + /* waveshaping */ + (this->*do_odr)(&high, dri); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1r, &lpf1->x2r, &lpf1->y1r, &lpf1->y2r); + inputr = imuldiv24(high + inputr, levelri); + + /* panning */ + buf[i - 1] = do_left_panning(inputl, panl) + do_left_panning(inputr, panr); + buf[i] = do_right_panning(inputl, panl) + do_right_panning(inputr, panr); + } +} + +#define HEXA_CHORUS_WET_LEVEL 0.2 +#define HEXA_CHORUS_DEPTH_DEV (1.0 / (20.0 + 1.0)) +#define HEXA_CHORUS_DELAY_DEV (1.0 / (20.0 * 3.0)) + +/*! GS 0x0140: HEXA-CHORUS */ +void Reverb::do_hexa_chorus(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoHexaChorus *info = (InfoHexaChorus *)ef->info; + lfo *lfo = &(info->lfo0); + simple_delay *buf0 = &(info->buf0); + int32_t *ebuf = buf0->buf, size = buf0->size, index = buf0->index; + int32_t spt0 = info->spt0, spt1 = info->spt1, spt2 = info->spt2, + spt3 = info->spt3, spt4 = info->spt4, spt5 = info->spt5, + hist0 = info->hist0, hist1 = info->hist1, hist2 = info->hist2, + hist3 = info->hist3, hist4 = info->hist4, hist5 = info->hist5; + int32_t dryi = info->dryi, weti = info->weti; + int32_t pan0 = info->pan0, pan1 = info->pan1, pan2 = info->pan2, + pan3 = info->pan3, pan4 = info->pan4, pan5 = info->pan5; + int32_t depth0 = info->depth0, depth1 = info->depth1, depth2 = info->depth2, + depth3 = info->depth3, depth4 = info->depth4, depth5 = info->depth5, + pdelay0 = info->pdelay0, pdelay1 = info->pdelay1, pdelay2 = info->pdelay2, + pdelay3 = info->pdelay3, pdelay4 = info->pdelay4, pdelay5 = info->pdelay5; + int32_t i, lfo_val, + v0, v1, v2, v3, v4, v5, f0, f1, f2, f3, f4, f5; + + if(count == MAGIC_INIT_EFFECT_INFO) { + set_delay(buf0, (int32_t)(9600.0 * playback_rate / 44100.0)); + init_lfo(lfo, lfo->freq, LFO_TRIANGULAR, 0); + info->dryi = TIM_FSCALE(info->level * info->dry, 24); + info->weti = TIM_FSCALE(info->level * info->wet * HEXA_CHORUS_WET_LEVEL, 24); + v0 = info->depth * ((double)info->depth_dev * HEXA_CHORUS_DEPTH_DEV); + info->depth0 = info->depth - v0; + info->depth1 = info->depth; + info->depth2 = info->depth + v0; + info->depth3 = info->depth + v0; + info->depth4 = info->depth; + info->depth5 = info->depth - v0; + v0 = info->pdelay * ((double)info->pdelay_dev * HEXA_CHORUS_DELAY_DEV); + info->pdelay0 = info->pdelay + v0; + info->pdelay1 = info->pdelay + v0 * 2; + info->pdelay2 = info->pdelay + v0 * 3; + info->pdelay3 = info->pdelay + v0 * 3; + info->pdelay4 = info->pdelay + v0 * 2; + info->pdelay5 = info->pdelay + v0; + /* in this part, validation check may be necessary. */ + info->pan0 = 64 - info->pan_dev * 3; + info->pan1 = 64 - info->pan_dev * 2; + info->pan2 = 64 - info->pan_dev; + info->pan3 = 64 + info->pan_dev; + info->pan4 = 64 + info->pan_dev * 2; + info->pan5 = 64 + info->pan_dev * 3; + info->hist0 = info->hist1 = info->hist2 + = info->hist3 = info->hist4 = info->hist5 = 0; + info->spt0 = info->spt1 = info->spt2 + = info->spt3 = info->spt4 = info->spt5 = 0; + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(buf0); + return; + } + + /* LFO */ + lfo_val = lfo->buf[imuldiv24(lfo->count, lfo->icycle)]; + f0 = imuldiv24(lfo_val, depth0); + spt0 = index - pdelay0 - (f0 >> 8); /* integral part of delay */ + if(spt0 < 0) {spt0 += size;} + f1 = imuldiv24(lfo_val, depth1); + spt1 = index - pdelay1 - (f1 >> 8); /* integral part of delay */ + if(spt1 < 0) {spt1 += size;} + f2 = imuldiv24(lfo_val, depth2); + spt2 = index - pdelay2 - (f2 >> 8); /* integral part of delay */ + if(spt2 < 0) {spt2 += size;} + f3 = imuldiv24(lfo_val, depth3); + spt3 = index - pdelay3 - (f3 >> 8); /* integral part of delay */ + if(spt3 < 0) {spt3 += size;} + f4 = imuldiv24(lfo_val, depth4); + spt4 = index - pdelay4 - (f4 >> 8); /* integral part of delay */ + if(spt4 < 0) {spt4 += size;} + f5 = imuldiv24(lfo_val, depth5); + spt5 = index - pdelay5 - (f5 >> 8); /* integral part of delay */ + if(spt5 < 0) {spt5 += size;} + + for(i = 0; i < count; i++) { + v0 = ebuf[spt0], v1 = ebuf[spt1], v2 = ebuf[spt2], + v3 = ebuf[spt3], v4 = ebuf[spt4], v5 = ebuf[spt5]; + + /* LFO */ + if(++index == size) {index = 0;} + lfo_val = do_lfo(lfo); + f0 = imuldiv24(lfo_val, depth0); + spt0 = index - pdelay0 - (f0 >> 8); /* integral part of delay */ + f0 = 0xFF - (f0 & 0xFF); /* (1 - frac) * 256 */ + if(spt0 < 0) {spt0 += size;} + f1 = imuldiv24(lfo_val, depth1); + spt1 = index - pdelay1 - (f1 >> 8); /* integral part of delay */ + f1 = 0xFF - (f1 & 0xFF); /* (1 - frac) * 256 */ + if(spt1 < 0) {spt1 += size;} + f2 = imuldiv24(lfo_val, depth2); + spt2 = index - pdelay2 - (f2 >> 8); /* integral part of delay */ + f2 = 0xFF - (f2 & 0xFF); /* (1 - frac) * 256 */ + if(spt2 < 0) {spt2 += size;} + f3 = imuldiv24(lfo_val, depth3); + spt3 = index - pdelay3 - (f3 >> 8); /* integral part of delay */ + f3 = 0xFF - (f3 & 0xFF); /* (1 - frac) * 256 */ + if(spt3 < 0) {spt3 += size;} + f4 = imuldiv24(lfo_val, depth4); + spt4 = index - pdelay4 - (f4 >> 8); /* integral part of delay */ + f4 = 0xFF - (f4 & 0xFF); /* (1 - frac) * 256 */ + if(spt4 < 0) {spt4 += size;} + f5 = imuldiv24(lfo_val, depth5); + spt5 = index - pdelay5 - (f5 >> 8); /* integral part of delay */ + f5 = 0xFF - (f5 & 0xFF); /* (1 - frac) * 256 */ + if(spt5 < 0) {spt5 += size;} + + /* chorus effect */ + /* all-pass interpolation */ + hist0 = v0 + imuldiv8(ebuf[spt0] - hist0, f0); + hist1 = v1 + imuldiv8(ebuf[spt1] - hist1, f1); + hist2 = v2 + imuldiv8(ebuf[spt2] - hist2, f2); + hist3 = v3 + imuldiv8(ebuf[spt3] - hist3, f3); + hist4 = v4 + imuldiv8(ebuf[spt4] - hist4, f4); + hist5 = v5 + imuldiv8(ebuf[spt5] - hist5, f5); + ebuf[index] = imuldiv24(buf[i] + buf[i + 1], weti); + + /* mixing */ + buf[i] = do_left_panning(hist0, pan0) + do_left_panning(hist1, pan1) + + do_left_panning(hist2, pan2) + do_left_panning(hist3, pan3) + + do_left_panning(hist4, pan4) + do_left_panning(hist5, pan5) + + imuldiv24(buf[i], dryi); + buf[i + 1] = do_right_panning(hist0, pan0) + do_right_panning(hist1, pan1) + + do_right_panning(hist2, pan2) + do_right_panning(hist3, pan3) + + do_right_panning(hist4, pan4) + do_right_panning(hist5, pan5) + + imuldiv24(buf[i + 1], dryi); + + ++i; + } + buf0->size = size, buf0->index = index; + info->spt0 = spt0, info->spt1 = spt1, info->spt2 = spt2, + info->spt3 = spt3, info->spt4 = spt4, info->spt5 = spt5, + info->hist0 = hist0, info->hist1 = hist1, info->hist2 = hist2, + info->hist3 = hist3, info->hist4 = hist4, info->hist5 = hist5; +} + +void Reverb::free_effect_xg(struct effect_xg_t *st) +{ + free_effect_list(st->ef); + st->ef = NULL; +} + +void Reverb::free_effect_buffers(void) +{ + int i; + /* free GM/GS/GM2 effects */ + do_ch_standard_reverb(NULL, MAGIC_FREE_EFFECT_INFO, &(reverb_status_gs.info_standard_reverb)); + do_ch_freeverb(NULL, MAGIC_FREE_EFFECT_INFO, &(reverb_status_gs.info_freeverb)); + do_ch_plate_reverb(NULL, MAGIC_FREE_EFFECT_INFO, &(reverb_status_gs.info_plate_reverb)); + do_ch_reverb_normal_delay(NULL, MAGIC_FREE_EFFECT_INFO, &(reverb_status_gs.info_reverb_delay)); + do_ch_stereo_chorus(NULL, MAGIC_FREE_EFFECT_INFO, &(chorus_status_gs.info_stereo_chorus)); + do_ch_3tap_delay(NULL, MAGIC_FREE_EFFECT_INFO, &(delay_status_gs.info_delay)); + free_effect_list(insertion_effect_gs.ef); + insertion_effect_gs.ef = NULL; + /* free XG effects */ + free_effect_xg(&reverb_status_xg); + free_effect_xg(&chorus_status_xg); + for (i = 0; i < XG_VARIATION_EFFECT_NUM; i++) { + free_effect_xg(&variation_effect_xg[i]); + } + for (i = 0; i < XG_INSERTION_EFFECT_NUM; i++) { + free_effect_xg(&insertion_effect_xg[i]); + } +} + +int Reverb::clip_int(int val, int min, int max) +{ + return ((val > max) ? max : (val < min) ? min : val); +} + +void Reverb::conv_gs_eq2(struct insertion_effect_gs_t *ieffect, EffectList *ef) +{ + InfoEQ2 *eq = (InfoEQ2 *)ef->info; + + eq->high_freq = 4000; + eq->high_gain = clip_int(ieffect->parameter[16] - 0x40, -12, 12); + eq->low_freq = 400; + eq->low_gain = clip_int(ieffect->parameter[17] - 0x40, -12, 12); +} + +void Reverb::conv_gs_overdrive1(struct insertion_effect_gs_t *ieffect, EffectList *ef) +{ + InfoOverdrive1 *od = (InfoOverdrive1 *)ef->info; + + od->drive = ieffect->parameter[0]; + od->amp_type = ieffect->parameter[1]; + od->amp_sw = ieffect->parameter[2]; + od->pan = ieffect->parameter[18]; + od->level = (double)ieffect->parameter[19] / 127.0; +} + +void Reverb::conv_gs_dual_od(struct insertion_effect_gs_t *ieffect, EffectList *ef) +{ + InfoOD1OD2 *od = (InfoOD1OD2 *)ef->info; + + od->typel = ieffect->parameter[0]; + od->drivel = ieffect->parameter[1]; + od->amp_typel = ieffect->parameter[2]; + od->amp_swl = ieffect->parameter[3]; + od->typer = ieffect->parameter[5]; + od->driver = ieffect->parameter[6]; + od->amp_typer = ieffect->parameter[7]; + od->amp_swr = ieffect->parameter[8]; + od->panl = ieffect->parameter[15]; + od->levell = (double)ieffect->parameter[16] / 127.0; + od->panr = ieffect->parameter[17]; + od->levelr = (double)ieffect->parameter[18] / 127.0; + od->level = (double)ieffect->parameter[19] / 127.0; +} + +double Reverb::calc_dry_gs(int val) +{ + return ((double)(127 - val) / 127.0); +} + +double Reverb::calc_wet_gs(int val) +{ + return ((double)val / 127.0); +} + +void Reverb::conv_gs_hexa_chorus(struct insertion_effect_gs_t *ieffect, EffectList *ef) +{ + InfoHexaChorus *info = (InfoHexaChorus *)ef->info; + + info->level = (double)ieffect->parameter[19] / 127.0; + info->pdelay = pre_delay_time_table[ieffect->parameter[0]] * (double)playback_rate / 1000.0; + info->depth = (double)(ieffect->parameter[2] + 1) / 3.2 * (double)playback_rate / 1000.0; + info->pdelay -= info->depth / 2; + if(info->pdelay <= 1) {info->pdelay = 1;} + info->lfo0.freq = rate1_table[ieffect->parameter[1]]; + info->pdelay_dev = ieffect->parameter[3]; + info->depth_dev = ieffect->parameter[4] - 64; + info->pan_dev = ieffect->parameter[5]; + info->dry = calc_dry_gs(ieffect->parameter[15]); + info->wet = calc_wet_gs(ieffect->parameter[15]); +} + +double Reverb::calc_dry_xg(int val, struct effect_xg_t *st) +{ + if (st->connection) {return 0.0;} + else {return ((double)(127 - val) / 127.0);} +} + +double Reverb::calc_wet_xg(int val, struct effect_xg_t *st) +{ + switch(st->connection) { + case XG_CONN_SYSTEM: + return ((double)st->ret / 127.0); + case XG_CONN_SYSTEM_CHORUS: + return ((double)st->ret / 127.0); + case XG_CONN_SYSTEM_REVERB: + return ((double)st->ret / 127.0); + default: + return ((double)val / 127.0); + } +} + +/*! 3-Band EQ */ +void Reverb::do_eq3(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoEQ3 *eq = (InfoEQ3 *)ef->info; + if (count == MAGIC_INIT_EFFECT_INFO) { + eq->lsf.q = 0; + eq->lsf.freq = eq->low_freq; + eq->lsf.gain = eq->low_gain; + calc_filter_shelving_low(&(eq->lsf)); + eq->hsf.q = 0; + eq->hsf.freq = eq->high_freq; + eq->hsf.gain = eq->high_gain; + calc_filter_shelving_high(&(eq->hsf)); + eq->peak.q = 1.0 / eq->mid_width; + eq->peak.freq = eq->mid_freq; + eq->peak.gain = eq->mid_gain; + calc_filter_peaking(&(eq->peak)); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + if (eq->low_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->lsf)); + } + if (eq->high_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->hsf)); + } + if (eq->mid_gain != 0) { + do_peaking_filter_stereo(buf, count, &(eq->peak)); + } +} + +/*! Stereo EQ */ +void Reverb::do_stereo_eq(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoStereoEQ *eq = (InfoStereoEQ *)ef->info; + int32_t i, leveli = eq->leveli; + if (count == MAGIC_INIT_EFFECT_INFO) { + eq->lsf.q = 0; + eq->lsf.freq = eq->low_freq; + eq->lsf.gain = eq->low_gain; + calc_filter_shelving_low(&(eq->lsf)); + eq->hsf.q = 0; + eq->hsf.freq = eq->high_freq; + eq->hsf.gain = eq->high_gain; + calc_filter_shelving_high(&(eq->hsf)); + eq->m1.q = eq->m1_q; + eq->m1.freq = eq->m1_freq; + eq->m1.gain = eq->m1_gain; + calc_filter_peaking(&(eq->m1)); + eq->m2.q = eq->m2_q; + eq->m2.freq = eq->m2_freq; + eq->m2.gain = eq->m2_gain; + calc_filter_peaking(&(eq->m2)); + eq->leveli = TIM_FSCALE(eq->level, 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + if (eq->level != 1.0) { + for (i = 0; i < count; i++) { + buf[i] = imuldiv24(buf[i], leveli); + } + } + if (eq->low_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->lsf)); + } + if (eq->high_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->hsf)); + } + if (eq->m1_gain != 0) { + do_peaking_filter_stereo(buf, count, &(eq->m1)); + } + if (eq->m2_gain != 0) { + do_peaking_filter_stereo(buf, count, &(eq->m2)); + } +} + +void Reverb::conv_xg_eq2(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ2 *info = (InfoEQ2 *)ef->info; + + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[0], 4, 40)]; + info->low_gain = clip_int(st->param_lsb[1] - 64, -12, 12); + info->high_freq = eq_freq_table_xg[clip_int(st->param_lsb[2], 28, 58)]; + info->high_gain = clip_int(st->param_lsb[3] - 64, -12, 12); +} + +void Reverb::conv_xg_eq3(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ3 *info = (InfoEQ3 *)ef->info; + + info->low_gain = clip_int(st->param_lsb[0] - 64, -12, 12); + info->mid_freq = eq_freq_table_xg[clip_int(st->param_lsb[1], 14, 54)]; + info->mid_gain = clip_int(st->param_lsb[2] - 64, -12, 12); + info->mid_width = (double)clip_int(st->param_lsb[3], 10, 120) / 10.0; + info->high_gain = clip_int(st->param_lsb[4] - 64, -12, 12); + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[5], 4, 40)]; + info->high_freq = eq_freq_table_xg[clip_int(st->param_lsb[6], 28, 58)]; +} + +static const float eq_q_table_gs[] = +{ + 0.5, 1.0, 2.0, 4.0, 9.0, +}; + +void Reverb::conv_gs_stereo_eq(struct insertion_effect_gs_t *st, EffectList *ef) +{ + InfoStereoEQ *info = (InfoStereoEQ *)ef->info; + + info->low_freq = (st->parameter[0] == 0) ? 200 : 400; + info->low_gain = clip_int(st->parameter[1] - 64, -12, 12); + info->high_freq = (st->parameter[2] == 0) ? 4000 : 8000; + info->high_gain = clip_int(st->parameter[3] - 64, -12, 12); + info->m1_freq = eq_freq_table_gs[st->parameter[4]]; + info->m1_q = eq_q_table_gs[clip_int(st->parameter[5], 0, 4)]; + info->m1_gain = clip_int(st->parameter[6] - 64, -12, 12); + info->m2_freq = eq_freq_table_gs[st->parameter[7]]; + info->m2_q = eq_q_table_gs[clip_int(st->parameter[8], 0, 4)]; + info->m2_gain = clip_int(st->parameter[9] - 64, -12, 12); + info->level = (double)st->parameter[19] / 127.0; +} + +void Reverb::conv_xg_chorus_eq3(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ3 *info = (InfoEQ3 *)ef->info; + + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[5], 4, 40)]; + info->low_gain = clip_int(st->param_lsb[6] - 64, -12, 12); + info->high_freq = eq_freq_table_xg[clip_int(st->param_lsb[7], 28, 58)]; + info->high_gain = clip_int(st->param_lsb[8] - 64, -12, 12); + info->mid_freq = eq_freq_table_xg[clip_int(st->param_lsb[10], 14, 54)]; + info->mid_gain = clip_int(st->param_lsb[11] - 64, -12, 12); + info->mid_width = (double)clip_int(st->param_lsb[12], 10, 120) / 10.0; +} + +void Reverb::conv_xg_chorus(struct effect_xg_t *st, EffectList *ef) +{ + InfoChorus *info = (InfoChorus *)ef->info; + + info->rate = lfo_freq_table_xg[st->param_lsb[0]]; + info->depth_ms = (double)(st->param_lsb[1] + 1) / 3.2 / 2.0; + info->feedback = (double)(st->param_lsb[2] - 64) * (0.763 * 2.0 / 100.0); + info->pdelay_ms = mod_delay_offset_table_xg[st->param_lsb[3]]; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); + info->phase_diff = 90.0; +} + +void Reverb::conv_xg_flanger(struct effect_xg_t *st, EffectList *ef) +{ + InfoChorus *info = (InfoChorus *)ef->info; + + info->rate = lfo_freq_table_xg[st->param_lsb[0]]; + info->depth_ms = (double)(st->param_lsb[1] + 1) / 3.2 / 2.0; + info->feedback = (double)(st->param_lsb[2] - 64) * (0.763 * 2.0 / 100.0); + info->pdelay_ms = mod_delay_offset_table_xg[st->param_lsb[2]]; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); + info->phase_diff = (double)(clip_int(st->param_lsb[13], 4, 124) - 64) * 3.0; +} + +void Reverb::conv_xg_symphonic(struct effect_xg_t *st, EffectList *ef) +{ + InfoChorus *info = (InfoChorus *)ef->info; + + info->rate = lfo_freq_table_xg[st->param_lsb[0]]; + info->depth_ms = (double)(st->param_lsb[1] + 1) / 3.2 / 2.0; + info->feedback = 0.0; + info->pdelay_ms = mod_delay_offset_table_xg[st->param_lsb[3]]; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); + info->phase_diff = 90.0; +} + +void Reverb::do_chorus(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoChorus *info = (InfoChorus *)ef->info; + int32_t i, output, f0, f1, v0, v1; + int32_t *bufL = info->delayL.buf, *bufR = info->delayR.buf, + *lfobufL = info->lfoL.buf, *lfobufR = info->lfoR.buf, + icycle = info->lfoL.icycle, cycle = info->lfoL.cycle, + dryi = info->dryi, weti = info->weti, feedbacki = info->feedbacki, + depth = info->depth, pdelay = info->pdelay, rpt0 = info->rpt0; + int32_t wpt0 = info->wpt0, spt0 = info->spt0, spt1 = info->spt1, + hist0 = info->hist0, hist1 = info->hist1, lfocnt = info->lfoL.count; + + if (count == MAGIC_INIT_EFFECT_INFO) { + init_lfo(&(info->lfoL), info->rate, LFO_TRIANGULAR, 0); + init_lfo(&(info->lfoR), info->rate, LFO_TRIANGULAR, info->phase_diff); + info->pdelay = info->pdelay_ms * (double)playback_rate / 1000.0; + info->depth = info->depth_ms * (double)playback_rate / 1000.0; + info->pdelay -= info->depth / 2; /* NOMINAL_DELAY to delay */ + if (info->pdelay < 1) {info->pdelay = 1;} + info->rpt0 = info->pdelay + info->depth + 2; /* allowance */ + set_delay(&(info->delayL), info->rpt0); + set_delay(&(info->delayR), info->rpt0); + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + info->wpt0 = info->spt0 = info->spt1 = info->hist0 = info->hist1 = 0; + return; + } else if (count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + /* LFO */ + f0 = imuldiv24(lfobufL[imuldiv24(lfocnt, icycle)], depth); + spt0 = wpt0 - pdelay - (f0 >> 8); /* integral part of delay */ + f0 = 0xFF - (f0 & 0xFF); /* (1 - frac) * 256 */ + if (spt0 < 0) {spt0 += rpt0;} + f1 = imuldiv24(lfobufR[imuldiv24(lfocnt, icycle)], depth); + spt1 = wpt0 - pdelay - (f1 >> 8); /* integral part of delay */ + f1 = 0xFF - (f1 & 0xFF); /* (1 - frac) * 256 */ + if (spt1 < 0) {spt1 += rpt0;} + + for (i = 0; i < count; i++) { + v0 = bufL[spt0]; + v1 = bufR[spt1]; + + /* LFO */ + if(++wpt0 == rpt0) {wpt0 = 0;} + f0 = imuldiv24(lfobufL[imuldiv24(lfocnt, icycle)], depth); + spt0 = wpt0 - pdelay - (f0 >> 8); /* integral part of delay */ + f0 = 0xFF - (f0 & 0xFF); /* (1 - frac) * 256 */ + if(spt0 < 0) {spt0 += rpt0;} + f1 = imuldiv24(lfobufR[imuldiv24(lfocnt, icycle)], depth); + spt1 = wpt0 - pdelay - (f1 >> 8); /* integral part of delay */ + f1 = 0xFF - (f1 & 0xFF); /* (1 - frac) * 256 */ + if(spt1 < 0) {spt1 += rpt0;} + if(++lfocnt == cycle) {lfocnt = 0;} + + /* left */ + /* delay with all-pass interpolation */ + output = hist0 = v0 + imuldiv8(bufL[spt0] - hist0, f0); + bufL[wpt0] = buf[i] + imuldiv24(output, feedbacki); + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(output, weti); + + /* right */ + /* delay with all-pass interpolation */ + output = hist1 = v1 + imuldiv8(bufR[spt1] - hist1, f1); + bufR[wpt0] = buf[++i] + imuldiv24(output, feedbacki); + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(output, weti); + } + info->wpt0 = wpt0, info->spt0 = spt0, info->spt1 = spt1, + info->hist0 = hist0, info->hist1 = hist1; + info->lfoL.count = info->lfoR.count = lfocnt; +} + +void Reverb::conv_xg_od_eq3(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ3 *info = (InfoEQ3 *)ef->info; + + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[1], 4, 40)]; + info->low_gain = clip_int(st->param_lsb[2] - 64, -12, 12); + info->mid_freq = eq_freq_table_xg[clip_int(st->param_lsb[6], 14, 54)]; + info->mid_gain = clip_int(st->param_lsb[7] - 64, -12, 12); + info->mid_width = (double)clip_int(st->param_lsb[8], 10, 120) / 10.0; + info->high_freq = 0; + info->high_gain = 0; +} + +void Reverb::conv_xg_overdrive(struct effect_xg_t *st, EffectList *ef) +{ + InfoStereoOD *info = (InfoStereoOD *)ef->info; + + info->od = &Reverb::do_soft_clipping1; + info->drive = (double)st->param_lsb[0] / 127.0; + info->cutoff = eq_freq_table_xg[clip_int(st->param_lsb[3], 34, 60)]; + info->level = (double)st->param_lsb[4] / 127.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::conv_xg_distortion(struct effect_xg_t *st, EffectList *ef) +{ + InfoStereoOD *info = (InfoStereoOD *)ef->info; + + info->od = &Reverb::do_hard_clipping; + info->drive = (double)st->param_lsb[0] / 127.0; + info->cutoff = eq_freq_table_xg[clip_int(st->param_lsb[3], 34, 60)]; + info->level = (double)st->param_lsb[4] / 127.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::conv_xg_amp_simulator(struct effect_xg_t *st, EffectList *ef) +{ + InfoStereoOD *info = (InfoStereoOD *)ef->info; + + info->od = &Reverb::do_soft_clipping2; + info->drive = (double)st->param_lsb[0] / 127.0; + info->cutoff = eq_freq_table_xg[clip_int(st->param_lsb[2], 34, 60)]; + info->level = (double)st->param_lsb[3] / 127.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::do_stereo_od(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoStereoOD *info = (InfoStereoOD *)ef->info; + filter_moog *svfl = &(info->svfl), *svfr = &(info->svfr); + filter_biquad *lpf1 = &(info->lpf1); + void (Reverb::*do_od)(int32_t *, int32_t) = info->od; + int32_t i, inputl, inputr, high, weti = info->weti, dryi = info->dryi, di = info->di; + + if(count == MAGIC_INIT_EFFECT_INFO) { + /* decompositor */ + svfl->freq = 500; + svfl->res_dB = 0; + calc_filter_moog(svfl); + init_filter_moog(svfl); + svfr->freq = 500; + svfr->res_dB = 0; + calc_filter_moog(svfr); + init_filter_moog(svfr); + /* anti-aliasing */ + lpf1->freq = info->cutoff; + lpf1->q = 1.0; + calc_filter_biquad_low(lpf1); + info->weti = TIM_FSCALE(info->wet * info->level, 24); + info->dryi = TIM_FSCALE(info->dry * info->level, 24); + info->di = TIM_FSCALE(calc_gs_drive(info->drive), 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + for(i = 0; i < count; i++) { + /* left */ + inputl = buf[i]; + /* decomposition */ + do_filter_moog(&inputl, &high, svfl->f, svfl->p, svfl->q, + &svfl->b0, &svfl->b1, &svfl->b2, &svfl->b3, &svfl->b4); + /* waveshaping */ + (this->*do_od)(&high, di); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1l, &lpf1->x2l, &lpf1->y1l, &lpf1->y2l); + buf[i] = imuldiv24(high + inputl, weti) + imuldiv24(buf[i], dryi); + + /* right */ + inputr = buf[++i]; + /* decomposition */ + do_filter_moog(&inputr, &high, svfr->f, svfr->p, svfr->q, + &svfr->b0, &svfr->b1, &svfr->b2, &svfr->b3, &svfr->b4); + /* waveshaping */ + (this->*do_od)(&high, di); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1r, &lpf1->x2r, &lpf1->y1r, &lpf1->y2r); + buf[i] = imuldiv24(high + inputr, weti) + imuldiv24(buf[i], dryi); + } +} + +void Reverb::do_delay_lcr(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x; + InfoDelayLCR *info = (InfoDelayLCR *)ef->info; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + filter_lowpass1 *lpf = &(info->lpf); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], index1 = info->index[1], index2 = info->index[2], + x1l = lpf->x1l, x1r = lpf->x1r; + int32_t cleveli = info->cleveli, feedbacki = info->feedbacki, + dryi = info->dryi, weti = info->weti, ai = lpf->ai, iai = lpf->iai; + + if(count == MAGIC_INIT_EFFECT_INFO) { + info->size[0] = info->ldelay * playback_rate / 1000.0; + info->size[1] = info->cdelay * playback_rate / 1000.0; + info->size[2] = info->rdelay * playback_rate / 1000.0; + x = info->fdelay * playback_rate / 1000.0; + for (i = 0; i < 3; i++) { + if (info->size[i] > x) {info->size[i] = x;} + } + x += 1; /* allowance */ + set_delay(&(info->delayL), x); + set_delay(&(info->delayR), x); + for (i = 0; i < 3; i++) { /* set start-point */ + info->index[i] = x - info->size[i]; + } + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->cleveli = TIM_FSCALE(info->clevel, 24); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + lpf->a = (1.0 - info->high_damp) * 44100.0 / playback_rate; + init_filter_lowpass1(lpf); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + for (i = 0; i < count; i++) + { + x = imuldiv24(bufL[buf_index], feedbacki); + do_filter_lowpass1(&x, &x1l, ai, iai); + bufL[buf_index] = buf[i] + x; + x = bufL[index0] + imuldiv24(bufL[index1], cleveli); + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(x, weti); + + x = imuldiv24(bufR[buf_index], feedbacki); + do_filter_lowpass1(&x, &x1r, ai, iai); + bufR[buf_index] = buf[++i] + x; + x = bufR[index2] + imuldiv24(bufR[index1], cleveli); + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(x, weti); + + if (++index0 == buf_size) {index0 = 0;} + if (++index1 == buf_size) {index1 = 0;} + if (++index2 == buf_size) {index2 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + info->index[0] = index0, info->index[1] = index1, info->index[2] = index2; + lpf->x1l = x1l, lpf->x1r = x1r; + delayL->index = delayR->index = buf_index; +} + +void Reverb::conv_xg_delay_eq2(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ2 *info = (InfoEQ2 *)ef->info; + + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[12], 4, 40)]; + info->low_gain = clip_int(st->param_lsb[13] - 64, -12, 12); + info->high_freq = eq_freq_table_xg[clip_int(st->param_lsb[14], 28, 58)]; + info->high_gain = clip_int(st->param_lsb[15] - 64, -12, 12); +} + +void Reverb::conv_xg_delay_lcr(struct effect_xg_t *st, EffectList *ef) +{ + InfoDelayLCR *info = (InfoDelayLCR *)ef->info; + + info->ldelay = (double)clip_int(st->param_msb[0] * 128 + st->param_lsb[0], 1, 14860) / 10.0; + info->rdelay = (double)clip_int(st->param_msb[1] * 128 + st->param_lsb[1], 1, 14860) / 10.0; + info->cdelay = (double)clip_int(st->param_msb[2] * 128 + st->param_lsb[2], 1, 14860) / 10.0; + info->fdelay = (double)clip_int(st->param_msb[3] * 128 + st->param_lsb[3], 1, 14860) / 10.0; + info->feedback = (double)(st->param_lsb[4] - 64) * (0.763 * 2.0 / 100.0); + info->clevel = (double)st->param_lsb[5] / 127.0; + info->high_damp = (double)clip_int(st->param_lsb[6], 1, 10) / 10.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::conv_xg_delay_lr(struct effect_xg_t *st, EffectList *ef) +{ + InfoDelayLR *info = (InfoDelayLR *)ef->info; + + info->ldelay = (double)clip_int(st->param_msb[0] * 128 + st->param_lsb[0], 1, 14860) / 10.0; + info->rdelay = (double)clip_int(st->param_msb[1] * 128 + st->param_lsb[1], 1, 14860) / 10.0; + info->fdelay1 = (double)clip_int(st->param_msb[2] * 128 + st->param_lsb[2], 1, 14860) / 10.0; + info->fdelay2 = (double)clip_int(st->param_msb[3] * 128 + st->param_lsb[3], 1, 14860) / 10.0; + info->feedback = (double)(st->param_lsb[4] - 64) * (0.763 * 2.0 / 100.0); + info->high_damp = (double)clip_int(st->param_lsb[5], 1, 10) / 10.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::do_delay_lr(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x; + InfoDelayLR *info = (InfoDelayLR *)ef->info; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + filter_lowpass1 *lpf = &(info->lpf); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t indexl = delayL->index, sizel = delayL->size, + indexr = delayR->index, sizer = delayR->size; + int32_t index0 = info->index[0], index1 = info->index[1], + x1l = lpf->x1l, x1r = lpf->x1r; + int32_t feedbacki = info->feedbacki, + dryi = info->dryi, weti = info->weti, ai = lpf->ai, iai = lpf->iai; + + if(count == MAGIC_INIT_EFFECT_INFO) { + info->size[0] = info->ldelay * playback_rate / 1000.0; + x = info->fdelay1 * playback_rate / 1000.0; + if (info->size[0] > x) {info->size[0] = x;} + x++; + set_delay(&(info->delayL), x); + info->index[0] = x - info->size[0]; + info->size[1] = info->rdelay * playback_rate / 1000.0; + x = info->fdelay2 * playback_rate / 1000.0; + if (info->size[1] > x) {info->size[1] = x;} + x++; + set_delay(&(info->delayR), x); + info->index[1] = x - info->size[1]; + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + lpf->a = (1.0 - info->high_damp) * 44100.0 / playback_rate; + init_filter_lowpass1(lpf); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + for (i = 0; i < count; i++) + { + x = imuldiv24(bufL[indexl], feedbacki); + do_filter_lowpass1(&x, &x1l, ai, iai); + bufL[indexl] = buf[i] + x; + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(bufL[index0], weti); + + x = imuldiv24(bufR[indexr], feedbacki); + do_filter_lowpass1(&x, &x1r, ai, iai); + bufR[indexr] = buf[++i] + x; + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(bufR[index1], weti); + + if (++index0 == sizel) {index0 = 0;} + if (++index1 == sizer) {index1 = 0;} + if (++indexl == sizel) {indexl = 0;} + if (++indexr == sizer) {indexr = 0;} + } + info->index[0] = index0, info->index[1] = index1; + lpf->x1l = x1l, lpf->x1r = x1r; + delayL->index = indexl, delayR->index = indexr; +} + +void Reverb::conv_xg_echo(struct effect_xg_t *st, EffectList *ef) +{ + InfoEcho *info = (InfoEcho *)ef->info; + + info->ldelay1 = (double)clip_int(st->param_msb[0] * 128 + st->param_lsb[0], 1, 7430) / 10.0; + info->lfeedback = (double)(st->param_lsb[1] - 64) * (0.763 * 2.0 / 100.0); + info->rdelay1 = (double)clip_int(st->param_msb[2] * 128 + st->param_lsb[2], 1, 7430) / 10.0; + info->rfeedback = (double)(st->param_lsb[3] - 64) * (0.763 * 2.0 / 100.0); + info->high_damp = (double)clip_int(st->param_lsb[4], 1, 10) / 10.0; + info->ldelay2 = (double)clip_int(st->param_msb[5] * 128 + st->param_lsb[5], 1, 7430) / 10.0; + info->rdelay2 = (double)clip_int(st->param_msb[6] * 128 + st->param_lsb[6], 1, 7430) / 10.0; + info->level = (double)st->param_lsb[7] / 127.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::do_echo(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x, y; + InfoEcho *info = (InfoEcho *)ef->info; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + filter_lowpass1 *lpf = &(info->lpf); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t indexl = delayL->index, sizel = delayL->size, + indexr = delayR->index, sizer = delayR->size; + int32_t index0 = info->index[0], index1 = info->index[1], + x1l = lpf->x1l, x1r = lpf->x1r; + int32_t lfeedbacki = info->lfeedbacki, rfeedbacki = info->rfeedbacki, leveli = info->leveli, + dryi = info->dryi, weti = info->weti, ai = lpf->ai, iai = lpf->iai; + + if(count == MAGIC_INIT_EFFECT_INFO) { + info->size[0] = info->ldelay2 * playback_rate / 1000.0; + x = info->ldelay1 * playback_rate / 1000.0; + if (info->size[0] > x) {info->size[0] = x;} + x++; + set_delay(&(info->delayL), x); + info->index[0] = x - info->size[0]; + info->size[1] = info->rdelay2 * playback_rate / 1000.0; + x = info->rdelay1 * playback_rate / 1000.0; + if (info->size[1] > x) {info->size[1] = x;} + x++; + set_delay(&(info->delayR), x); + info->index[1] = x - info->size[1]; + info->lfeedbacki = TIM_FSCALE(info->lfeedback, 24); + info->rfeedbacki = TIM_FSCALE(info->rfeedback, 24); + info->leveli = TIM_FSCALE(info->level, 24); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + lpf->a = (1.0 - info->high_damp) * 44100.0 / playback_rate; + init_filter_lowpass1(lpf); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + for (i = 0; i < count; i++) + { + y = bufL[indexl] + imuldiv24(bufL[index0], leveli); + x = imuldiv24(bufL[indexl], lfeedbacki); + do_filter_lowpass1(&x, &x1l, ai, iai); + bufL[indexl] = buf[i] + x; + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(y, weti); + + y = bufR[indexr] + imuldiv24(bufR[index1], leveli); + x = imuldiv24(bufR[indexr], rfeedbacki); + do_filter_lowpass1(&x, &x1r, ai, iai); + bufR[indexr] = buf[++i] + x; + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(y, weti); + + if (++index0 == sizel) {index0 = 0;} + if (++index1 == sizer) {index1 = 0;} + if (++indexl == sizel) {indexl = 0;} + if (++indexr == sizer) {indexr = 0;} + } + info->index[0] = index0, info->index[1] = index1; + lpf->x1l = x1l, lpf->x1r = x1r; + delayL->index = indexl, delayR->index = indexr; +} + +void Reverb::conv_xg_cross_delay(struct effect_xg_t *st, EffectList *ef) +{ + InfoCrossDelay *info = (InfoCrossDelay *)ef->info; + + info->lrdelay = (double)clip_int(st->param_msb[0] * 128 + st->param_lsb[0], 1, 7430) / 10.0; + info->rldelay = (double)clip_int(st->param_msb[1] * 128 + st->param_lsb[1], 1, 7430) / 10.0; + info->feedback = (double)(st->param_lsb[2] - 64) * (0.763 * 2.0 / 100.0); + info->input_select = st->param_lsb[3]; + info->high_damp = (double)clip_int(st->param_lsb[4], 1, 10) / 10.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::do_cross_delay(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, lfb, rfb, lout, rout; + InfoCrossDelay *info = (InfoCrossDelay *)ef->info; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + filter_lowpass1 *lpf = &(info->lpf); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t indexl = delayL->index, sizel = delayL->size, + indexr = delayR->index, sizer = delayR->size, + x1l = lpf->x1l, x1r = lpf->x1r; + int32_t feedbacki = info->feedbacki, + dryi = info->dryi, weti = info->weti, ai = lpf->ai, iai = lpf->iai; + + if(count == MAGIC_INIT_EFFECT_INFO) { + set_delay(&(info->delayL), (int32_t)(info->lrdelay * playback_rate / 1000.0)); + set_delay(&(info->delayR), (int32_t)(info->rldelay * playback_rate / 1000.0)); + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + lpf->a = (1.0 - info->high_damp) * 44100.0 / playback_rate; + init_filter_lowpass1(lpf); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + for (i = 0; i < count; i++) + { + lfb = imuldiv24(bufL[indexl], feedbacki); + do_filter_lowpass1(&lfb, &x1l, ai, iai); + lout = imuldiv24(buf[i], dryi) + imuldiv24(bufL[indexl], weti); + rfb = imuldiv24(bufR[indexr], feedbacki); + do_filter_lowpass1(&rfb, &x1r, ai, iai); + rout = imuldiv24(buf[i + 1], dryi) + imuldiv24(bufR[indexr], weti); + bufL[indexl] = buf[i] + rfb; + buf[i] = lout; + bufR[indexr] = buf[++i] + lfb; + buf[i] = rout; + + if (++indexl == sizel) {indexl = 0;} + if (++indexr == sizer) {indexr = 0;} + } + lpf->x1l = x1l, lpf->x1r = x1r; + delayL->index = indexl, delayR->index = indexr; +} + +void Reverb::conv_gs_lofi1(struct insertion_effect_gs_t *st, EffectList *ef) +{ + InfoLoFi1 *info = (InfoLoFi1 *)ef->info; + + info->pre_filter = st->parameter[0]; + info->lofi_type = 1 + clip_int(st->parameter[1], 0, 8); + info->post_filter = st->parameter[2]; + info->dry = calc_dry_gs(st->parameter[15] & 0x7F); + info->wet = calc_wet_gs(st->parameter[15] & 0x7F); + info->pan = st->parameter[18]; + info->level = (st->parameter[19] & 0x7F) / 127.0; +} + +int32_t Reverb::apply_lofi(int32_t input, int32_t bit_mask, int32_t level_shift) +{ + return (input + level_shift) & bit_mask; +} + +void Reverb::do_lofi1(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x, y; + InfoLoFi1 *info = (InfoLoFi1 *)ef->info; + int32_t bit_mask = info->bit_mask, dryi = info->dryi, weti = info->weti; + const int32_t level_shift = info->level_shift; + + if(count == MAGIC_INIT_EFFECT_INFO) { + info->bit_mask = ~0L << (info->lofi_type * 2); + info->level_shift = ~info->bit_mask >> 1; + info->dryi = TIM_FSCALE(info->dry * info->level, 24); + info->weti = TIM_FSCALE(info->wet * info->level, 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + + for (i = 0; i < count; i++) + { + x = buf[i]; + y = apply_lofi(x, bit_mask, level_shift); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + + x = buf[++i]; + y = apply_lofi(x, bit_mask, level_shift); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + } +} + +void Reverb::conv_gs_lofi2(struct insertion_effect_gs_t *st, EffectList *ef) +{ + InfoLoFi2 *info = (InfoLoFi2 *)ef->info; + + info->lofi_type = 1 + clip_int(st->parameter[0], 0, 5); + info->fil_type = clip_int(st->parameter[1], 0, 2); + info->fil.freq = cutoff_freq_table_gs[st->parameter[2]]; + info->rdetune = st->parameter[3]; + info->rnz_lev = (double)st->parameter[4] / 127.0; + info->wp_sel = clip_int(st->parameter[5], 0, 1); + info->wp_lpf.freq = lpf_table_gs[st->parameter[6]]; + info->wp_level = (double)st->parameter[7] / 127.0; + info->disc_type = clip_int(st->parameter[8], 0, 3); + info->disc_lpf.freq = lpf_table_gs[st->parameter[9]]; + info->discnz_lev = (double)st->parameter[10] / 127.0; + info->hum_type = clip_int(st->parameter[11], 0, 1); + info->hum_lpf.freq = lpf_table_gs[st->parameter[12]]; + info->hum_level = (double)st->parameter[13] / 127.0; + info->ms = clip_int(st->parameter[14], 0, 1); + info->dry = calc_dry_gs(st->parameter[15] & 0x7F); + info->wet = calc_wet_gs(st->parameter[15] & 0x7F); + info->pan = st->parameter[18]; + info->level = (st->parameter[19] & 0x7F) / 127.0; +} + +void Reverb::do_lofi2(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x, y; + InfoLoFi2 *info = (InfoLoFi2 *)ef->info; + filter_biquad *fil = &(info->fil); + int32_t bit_mask = info->bit_mask, dryi = info->dryi, weti = info->weti; + const int32_t level_shift = info->level_shift; + + if(count == MAGIC_INIT_EFFECT_INFO) { + fil->q = 1.0; + if (info->fil_type == 1) {calc_filter_biquad_low(fil);} + else if (info->fil_type == 2) {calc_filter_biquad_high(fil);} + else { + fil->freq = -1; /* bypass */ + calc_filter_biquad_low(fil); + } + info->bit_mask = ~0L << (info->lofi_type * 2); + info->level_shift = ~info->bit_mask >> 1; + info->dryi = TIM_FSCALE(info->dry * info->level, 24); + info->weti = TIM_FSCALE(info->wet * info->level, 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + for (i = 0; i < count; i++) + { + x = buf[i]; + y = apply_lofi(x, bit_mask, level_shift); + do_filter_biquad(&y, fil->a1, fil->a2, fil->b1, fil->b02, &fil->x1l, &fil->x2l, &fil->y1l, &fil->y2l); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + + x = buf[++i]; + y = apply_lofi(x, bit_mask, level_shift); + do_filter_biquad(&y, fil->a1, fil->a2, fil->b1, fil->b02, &fil->x1r, &fil->x2r, &fil->y1r, &fil->y2r); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + } +} + +void Reverb::conv_xg_lofi(struct effect_xg_t *st, EffectList *ef) +{ + InfoLoFi *info = (InfoLoFi *)ef->info; + + info->srf.freq = lofi_sampling_freq_table_xg[st->param_lsb[0]] / 2.0; + info->word_length = st->param_lsb[1]; + info->output_gain = clip_int(st->param_lsb[2], 0, 18); + info->lpf.freq = eq_freq_table_xg[clip_int(st->param_lsb[3], 10, 80)]; + info->filter_type = st->param_lsb[4]; + info->lpf.q = (double)clip_int(st->param_lsb[5], 10, 120) / 10.0; + info->bit_assign = clip_int(st->param_lsb[6], 0, 6); + info->emphasis = st->param_lsb[7]; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::do_lofi(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x, y; + InfoLoFi *info = (InfoLoFi *)ef->info; + filter_biquad *lpf = &(info->lpf), *srf = &(info->srf); + int32_t bit_mask = info->bit_mask, dryi = info->dryi, weti = info->weti; + const int32_t level_shift = info->level_shift; + + if(count == MAGIC_INIT_EFFECT_INFO) { + srf->q = 1.0; + calc_filter_biquad_low(srf); + calc_filter_biquad_low(lpf); + info->bit_mask = ~((1L << (info->bit_assign + 22 - GUARD_BITS)) - 1L); + info->level_shift = ~info->bit_mask >> 1; + info->dryi = TIM_FSCALE(info->dry * pow(10.0, (double)info->output_gain / 20.0), 24); + info->weti = TIM_FSCALE(info->wet * pow(10.0, (double)info->output_gain / 20.0), 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + + for (i = 0; i < count; i++) + { + x = buf[i]; + y = apply_lofi(x, bit_mask, level_shift); + do_filter_biquad(&y, srf->a1, srf->a2, srf->b1, srf->b02, &srf->x1l, &srf->x2l, &srf->y1l, &srf->y2l); + do_filter_biquad(&y, lpf->a1, lpf->a2, lpf->b1, lpf->b02, &lpf->x1l, &lpf->x2l, &lpf->y1l, &lpf->y2l); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + + x = buf[++i]; + y = apply_lofi(x, bit_mask, level_shift); + do_filter_biquad(&y, srf->a1, srf->a2, srf->b1, srf->b02, &srf->x1r, &srf->x2r, &srf->y1r, &srf->y2r); + do_filter_biquad(&y, lpf->a1, lpf->a2, lpf->b1, lpf->b02, &lpf->x1r, &lpf->x2r, &lpf->y1r, &lpf->y2r); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + } +} + +void Reverb::conv_xg_auto_wah_od(struct effect_xg_t *st, EffectList *ef) +{ + InfoXGAutoWahOD *info = (InfoXGAutoWahOD *)ef->info; + + info->lpf.freq = eq_freq_table_xg[clip_int(st->param_lsb[13], 34, 80)]; + info->level = (double)st->param_lsb[14] / 127.0; +} + +void Reverb::conv_xg_auto_wah_od_eq3(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ3 *info = (InfoEQ3 *)ef->info; + + info->low_freq = eq_freq_table_xg[24]; + info->low_gain = clip_int(st->param_lsb[11] - 64, -12, 12); + info->mid_freq = eq_freq_table_xg[41]; + info->mid_gain = clip_int(st->param_lsb[12] - 64, -12, 12); + info->mid_width = 1.0; + info->high_freq = 0; + info->high_gain = 0; +} + +void Reverb::conv_xg_auto_wah_eq2(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ2 *info = (InfoEQ2 *)ef->info; + + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[5], 4, 40)]; + info->low_gain = clip_int(st->param_lsb[6] - 64, -12, 12); + info->high_freq = eq_freq_table_xg[clip_int(st->param_lsb[7], 28, 58)]; + info->high_gain = clip_int(st->param_lsb[8] - 64, -12, 12); +} + +void Reverb::conv_xg_auto_wah(struct effect_xg_t *st, EffectList *ef) +{ + InfoXGAutoWah *info = (InfoXGAutoWah *)ef->info; + + info->lfo_freq = lfo_freq_table_xg[st->param_lsb[0]]; + info->lfo_depth = st->param_lsb[1]; + info->offset_freq = (double)(st->param_lsb[2]) * 3900.0 / 127.0 + 100.0; + info->resonance = (double)clip_int(st->param_lsb[3], 10, 120) / 10.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); + info->drive = st->param_lsb[10]; +} + +double Reverb::calc_xg_auto_wah_freq(int32_t lfo_val, double offset_freq, int8_t depth) +{ + double freq; + int32_t fine; + fine = ((lfo_val - (1L << 15)) * depth) >> 7; /* max: +-2^8 fine */ + if (fine >= 0) { + freq = offset_freq * bend_fine[fine & 0xff] + * bend_coarse[fine >> 8 & 0x7f]; + } else { + freq = offset_freq / (bend_fine[(-fine) & 0xff] + * bend_coarse[(-fine) >> 8 & 0x7f]); + } + return freq; +} + +#define XG_AUTO_WAH_BITS (32 - GUARD_BITS) +#define XG_AUTO_WAH_MAX_NEG (1.0 / (double)(1L << XG_AUTO_WAH_BITS)) + +void Reverb::do_xg_auto_wah(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x, y, val; + InfoXGAutoWah *info = (InfoXGAutoWah *)ef->info; + filter_moog_dist *fil0 = &(info->fil0), *fil1 = &(info->fil1); + lfo *lfo = &(info->lfo); + int32_t dryi = info->dryi, weti = info->weti, fil_cycle = info->fil_cycle; + int8_t lfo_depth = info->lfo_depth; + double yf, offset_freq = info->offset_freq; + int32_t fil_count = info->fil_count; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_lfo(lfo, info->lfo_freq, LFO_TRIANGULAR, 0); + fil0->res_dB = fil1->res_dB = (info->resonance - 1.0) * 12.0 / 11.0; + fil0->dist = fil1->dist = 4.0 * sqrt((double)info->drive / 127.0); + val = do_lfo(lfo); + fil0->freq = fil1->freq = calc_xg_auto_wah_freq(val, info->offset_freq, info->lfo_depth); + calc_filter_moog_dist(fil0); + init_filter_moog_dist(fil0); + calc_filter_moog_dist(fil1); + init_filter_moog_dist(fil1); + info->fil_count = 0; + info->fil_cycle = (int32_t)(44.0 * playback_rate / 44100.0); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + + for (i = 0; i < count; i++) + { + x = y = buf[i]; + yf = (double)y * XG_AUTO_WAH_MAX_NEG; + do_filter_moog_dist_band(&yf, fil0->f, fil0->p, fil0->q, fil0->d, + &fil0->b0, &fil0->b1, &fil0->b2, &fil0->b3, &fil0->b4); + y = TIM_FSCALE(yf, XG_AUTO_WAH_BITS); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + + x = y = buf[++i]; + yf = (double)y * XG_AUTO_WAH_MAX_NEG; + do_filter_moog_dist_band(&yf, fil0->f, fil0->p, fil0->q, fil0->d, + &fil1->b0, &fil1->b1, &fil1->b2, &fil1->b3, &fil1->b4); + y = TIM_FSCALE(yf, XG_AUTO_WAH_BITS); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + + val = do_lfo(lfo); + + if (++fil_count == fil_cycle) { + fil_count = 0; + fil0->freq = calc_xg_auto_wah_freq(val, offset_freq, lfo_depth); + calc_filter_moog_dist(fil0); + } + } + info->fil_count = fil_count; +} + +void Reverb::do_xg_auto_wah_od(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x; + InfoXGAutoWahOD *info = (InfoXGAutoWahOD *)ef->info; + filter_biquad *lpf = &(info->lpf); + int32_t leveli = info->leveli; + + if(count == MAGIC_INIT_EFFECT_INFO) { + lpf->q = 1.0; + calc_filter_biquad_low(lpf); + info->leveli = TIM_FSCALE(info->level, 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + + for (i = 0; i < count; i++) + { + x = buf[i]; + do_filter_biquad(&x, lpf->a1, lpf->a2, lpf->b1, lpf->b02, &lpf->x1l, &lpf->x2l, &lpf->y1l, &lpf->y2l); + buf[i] = imuldiv24(x, leveli); + + x = buf[++i]; + do_filter_biquad(&x, lpf->a1, lpf->a2, lpf->b1, lpf->b02, &lpf->x1r, &lpf->x2r, &lpf->y1r, &lpf->y2r); + buf[i] = imuldiv24(x, leveli); + } +} + +enum { /* width * length * height */ + ER_SMALL_ROOM, /* 5m * 5m * 3m */ + ER_MEDIUM_ROOM, /* 10m * 10m * 5m */ + ER_LARGE_ROOM, /* 12m * 12m * 8m */ + ER_MEDIUM_HALL, /* 15m * 20m * 10m */ + ER_LARGE_HALL, /* 20m * 40m * 18m */ + ER_EOF, +}; + + +const struct _EffectEngine Reverb::effect_engine[] = { +{ EFFECT_NONE, "None", NULL, NULL, NULL, 0,}, +{ EFFECT_STEREO_EQ, "Stereo-EQ", &Reverb::do_stereo_eq, &Reverb::conv_gs_stereo_eq, NULL, sizeof(InfoStereoEQ),}, +{ EFFECT_EQ2, "2-Band EQ", &Reverb::do_eq2, &Reverb::conv_gs_eq2, &Reverb::conv_xg_eq2, sizeof(InfoEQ2),}, +{ EFFECT_EQ3, "3-Band EQ", &Reverb::do_eq3, NULL, &Reverb::conv_xg_eq3, sizeof(InfoEQ3),}, +{ EFFECT_OVERDRIVE1, "Overdrive", &Reverb::do_overdrive1, &Reverb::conv_gs_overdrive1, NULL, sizeof(InfoOverdrive1),}, +{ EFFECT_DISTORTION1, "Distortion", &Reverb::do_distortion1, &Reverb::conv_gs_overdrive1, NULL, sizeof(InfoOverdrive1),}, +{ EFFECT_OD1OD2, "OD1/OD2", &Reverb::do_dual_od, &Reverb::conv_gs_dual_od, NULL, sizeof(InfoOD1OD2),}, +{ EFFECT_HEXA_CHORUS, "Hexa-Chorus", &Reverb::do_hexa_chorus, &Reverb::conv_gs_hexa_chorus, NULL, sizeof(InfoHexaChorus),}, +{ EFFECT_CHORUS, "Chorus", &Reverb::do_chorus, NULL, &Reverb::conv_xg_chorus, sizeof(InfoChorus),}, +{ EFFECT_FLANGER, "Flanger", &Reverb::do_chorus, NULL, &Reverb::conv_xg_flanger, sizeof(InfoChorus),}, +{ EFFECT_SYMPHONIC, "Symphonic", &Reverb::do_chorus, NULL, &Reverb::conv_xg_symphonic, sizeof(InfoChorus),}, +{ EFFECT_CHORUS_EQ3, "3-Band EQ (XG Chorus built-in)", &Reverb::do_eq3, NULL, &Reverb::conv_xg_chorus_eq3, sizeof(InfoEQ3),}, +{ EFFECT_STEREO_OVERDRIVE, "Stereo Overdrive", &Reverb::do_stereo_od, NULL, &Reverb::conv_xg_overdrive, sizeof(InfoStereoOD),}, +{ EFFECT_STEREO_DISTORTION, "Stereo Distortion", &Reverb::do_stereo_od, NULL, &Reverb::conv_xg_distortion, sizeof(InfoStereoOD),}, +{ EFFECT_STEREO_AMP_SIMULATOR, "Amp Simulator", &Reverb::do_stereo_od, NULL, &Reverb::conv_xg_amp_simulator, sizeof(InfoStereoOD),}, +{ EFFECT_OD_EQ3, "2-Band EQ (XG OD built-in)", &Reverb::do_eq3, NULL, &Reverb::conv_xg_od_eq3, sizeof(InfoEQ3),}, +{ EFFECT_DELAY_LCR, "Delay L,C,R", &Reverb::do_delay_lcr, NULL, &Reverb::conv_xg_delay_lcr, sizeof(InfoDelayLCR),}, +{ EFFECT_DELAY_LR, "Delay L,R", &Reverb::do_delay_lr, NULL, &Reverb::conv_xg_delay_lr, sizeof(InfoDelayLR),}, +{ EFFECT_ECHO, "Echo", &Reverb::do_echo, NULL, &Reverb::conv_xg_echo, sizeof(InfoEcho),}, +{ EFFECT_CROSS_DELAY, "Cross Delay", &Reverb::do_cross_delay, NULL, &Reverb::conv_xg_cross_delay, sizeof(InfoCrossDelay),}, +{ EFFECT_DELAY_EQ2, "2-Band EQ (XG Delay built-in)", &Reverb::do_eq2, NULL, &Reverb::conv_xg_delay_eq2, sizeof(InfoEQ2),}, +{ EFFECT_LOFI, "Lo-Fi", &Reverb::do_lofi, NULL, &Reverb::conv_xg_lofi, sizeof(InfoLoFi),}, +{ EFFECT_LOFI1, "Lo-Fi 1", &Reverb::do_lofi1, &Reverb::conv_gs_lofi1, NULL, sizeof(InfoLoFi1),}, +{ EFFECT_LOFI2, "Lo-Fi 2", &Reverb::do_lofi2, &Reverb::conv_gs_lofi2, NULL, sizeof(InfoLoFi2),}, +{ EFFECT_XG_AUTO_WAH, "Auto Wah", &Reverb::do_xg_auto_wah, NULL, &Reverb::conv_xg_auto_wah, sizeof(InfoXGAutoWah),}, +{ EFFECT_XG_AUTO_WAH_EQ2, "2-Band EQ (Auto Wah built-in)", &Reverb::do_eq2, NULL, &Reverb::conv_xg_auto_wah_eq2, sizeof(InfoEQ2),}, +{ EFFECT_XG_AUTO_WAH_OD, "OD (Auto Wah built-in)", &Reverb::do_xg_auto_wah_od, NULL, &Reverb::conv_xg_auto_wah_od, sizeof(InfoXGAutoWahOD),}, +{ EFFECT_XG_AUTO_WAH_OD_EQ3, "2-Band EQ (Auto Wah OD built-in)", &Reverb::do_eq3, NULL, &Reverb::conv_xg_auto_wah_od_eq3, sizeof(InfoEQ3),}, +{ -1, "EOF", NULL, NULL, NULL, 0, }, +}; + +const struct effect_parameter_xg_t Reverb::effect_parameter_xg[] = { +{ 0, 0, "NO EFFECT", + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, 0, }, + +{ 0x05, 0, "DELAY L,C,R", { 0x1A, 0x0D, 0x27, 0x27, 0, 0, 0, 0, 0, 0,},{ + 0x05, 0x03, 0x08, 0x08, 74, 100, 10, 0, 0, 32, 0, 0, 28, 64, 46, 64,}, 9,}, + +{ 0x06, 0, "DELAY L,R", { 0x13, 0x1D, 0x1D, 0x1D, 0, 0, 0, 0, 0, 0,},{ + 0x44, 0x26, 0x28, 0x26, 87, 10, 0, 0, 0, 32, 0, 0, 28, 64, 46, 64, },9,}, + +{ 0x07, 0, "ECHO", { 0x0D, 0, 0x0D, 0, 0, 0x0D, 0x0D, 0, 0, 0,},{ + 0x24, 80, 0x74, 80, 10, 0x24, 0x74, 0, 0, 40, 0, 0, 28, 64, 46, 64,}, 9,}, + +{ 0x08, 0, "CROSS DELAY", { 0x0D, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 0x24, 0x56, 111, 1, 10, 0, 0, 0, 0, 32, 0, 0, 28, 64, 46, 64,}, 9,}, + +{ 0x41, 0, "CHORUS 1", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 6, 54, 77, 106, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x01, "CHORUS 2",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 8, 63, 64, 30, 0, 28, 62, 42, 58, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x02, "CHORUS 3",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 4, 44, 64, 110, 0, 28, 64, 46, 66, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x03, "GM CHORUS 1",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 9, 10, 64, 109, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x04, "GM CHORUS 2", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 28, 34, 67, 105, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x05, "GM CHORUS 3", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 9, 34, 69, 105, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x06, "GM CHORUS 4", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 26, 29, 75, 102, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x07, "FB CHORUS", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 6, 43, 107, 111, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x08, "CHORUS 4", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 9, 32, 69, 104, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 1, 0,}, 9,}, + +{ 0x42, 0, "CELESTE 1", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 12, 32, 64, 0, 0, 28, 64, 46, 64, 127, 40, 68, 10, 0, 0, 0,}, 9,}, + +{ 0x42, 0x01, "CELESTE 2", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 28, 18, 90, 2, 0, 28, 62, 42, 60, 84, 40, 68, 10, 0, 0, 0,}, 9,}, + +{ 0x42, 0x02, "CELESTE 3", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 4, 63, 44, 2, 0, 28, 64, 46, 68, 127, 40, 68, 10, 0, 0,0,}, 9,}, + +{ 0x42, 0x08, "CELESTE 4", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 8, 29, 64, 0, 0, 28, 64, 51, 66, 127, 40, 68, 10, 0, 1, 0,}, 9,}, + +{ 0x43, 0, "FLANGER 1", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 14, 14, 104, 2, 0, 28, 64, 46, 64, 96, 40, 64, 10, 4, 0, 0,}, 9, }, + +{ 0x43, 0x01, "FLANGER 2", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 32, 17, 26, 2, 0, 28, 64, 46, 60, 96, 40, 64, 10, 4, 0, 0,}, 9, }, + +{ 0x43, 0x07, "GM FLANGER", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 3, 21, 120, 1, 0, 28, 64, 46, 64, 96, 40, 64, 10, 4, 0, 0,}, 9, }, + +{ 0x43, 0x08, "FLANGER 3", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 4, 109, 109, 2, 0, 28, 64, 46, 64, 127, 40, 64, 10, 4, 0, 0,}, 9, }, + +{ 0x44, 0, "SYMPHONIC", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 12, 25, 16, 0, 0, 28, 64, 46, 64, 127, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x49, 0, "DISTORTION", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 40, 20, 72, 53, 48, 0, 43, 74, 10, 127, 120, 0, 0, 0, 0,0,}, 0,}, + +{ 0x49, 0x08, "STEREO DISTORTION", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 18, 27, 71, 48, 84, 0, 32, 66, 10, 127, 105, 0, 0, 0, 0, 0,}, 0,}, + +{ 0x4A, 0, "OVERDRIVE", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 29, 24, 68, 45, 55, 0, 41, 72, 10, 127, 104, 0, 0, 0, 0, 0,}, 0,}, + +{ 0x4A, 0x08, "STEREO OVERDRIVE", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 10, 24, 69, 46, 105, 0, 41, 66, 10, 127, 104, 0, 0, 0, 0, 0,}, 0,}, + +{ 0x4B, 0, "AMP SIMULATOR", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 39, 1, 48, 55, 0, 0, 0, 0, 0, 127, 112, 0, 0, 0, 0, 0,}, 0,}, + +{ 0x4B, 0x08, "STEREO AMP SIMULATOR", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 16, 2, 46, 119, 0, 0, 0, 0, 0, 127, 106, 0, 0, 0, 0, 0,}, 0,}, + +{ 0x4C, 0, "3-BAND EQ", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 70, 34, 60, 10, 70, 28, 46, 0, 0, 127, 0, 0, 0, 0, 0, 0,}, -1,}, + +{ 0x4D, 0, "2-BAND EQ", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 28, 70, 46, 70, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0,}, -1,}, + +{ 0x4E, 0, "AUTO WAH", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 70, 56, 39, 25, 0, 28, 66, 46, 64, 127, 0, 0, 0, 0, 0, 0,}, 2, }, + +{ 0x4E, 0x01, "AUTO WAH+DISTORTION", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 40, 73, 26, 29, 0, 28, 66, 46, 64, 127, 30, 72, 74, 53, 48, 0,}, 2, }, + +{ 0x4E, 0x02, "AUTO WAH+OVERDRIVE", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 48, 64, 32, 23, 0, 28, 66, 46, 64, 127, 29, 68, 72, 45, 55, 0,}, 2, }, + +{ 0x5E, 0, "LO-FI",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 2, 60, 6, 54, 5, 10, 1, 1, 0, 127, 0, 0, 0, 0, 1, 0,}, 9, }, + +{ -1, -1, "EOF",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, 0,}, +}; + +const struct effect_parameter_gs_t Reverb::effect_parameter_gs[] = { +{ 0, 0, "None", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, 0, 0,}, +{ 0x01, 0x00, "Stereo-EQ", { 1, 0x45, 1, 0x34, 0x48, 0, 0x48, 0x38, 0, 0x48, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 127,}, 19, -1,}, +{ 0x01, 0x10, "Overdrive",{ 48, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0x40, 0x40, 0x40, 96,}, 0, 18,}, +{ 0x01, 0x11, "Distrotion",{ 76, 3, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0x40, 0x38, 0x40, 84,}, 0, 18, }, +{ 0x11, 0x03, "OD1/OD2",{ 0, 48, 1, 1, 0, 1, 76, 3, 1, 0, + 0, 0, 0, 0, 0, 0x40, 96, 0x40, 84, 127,}, 1, 6, }, +{ 0x01, 0x40, "Hexa Chorus",{ 0x18, 0x08, 127, 5, 66, 16, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0x40, 0x40, 64, 112,}, 1, 15, }, +{ 0x01, 0x72, "Lo-Fi 1",{ 2, 6, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 127, 0x40, 0x40, 64, 127,}, 15, 18, }, +{ 0x01, 0x73, "Lo-Fi 2",{ 2, 1, 0x20, 0, 64, 1, 127, 0, 0, 127, + 0, 0, 127, 0, 1, 127, 0x40, 0x40, 64, 127,}, 3, 15, }, +{ -1, -1, "EOF",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, 0, 0,}, +}; + + +////////////////////////////////// from readmidi + +/*! initialize Delay Effect (GS) */ +void Reverb::init_delay_status_gs(void) +{ + struct delay_status_gs_t *p = &delay_status_gs; + p->type = 0; + p->level = 0x40; + p->level_center = 0x7F; + p->level_left = 0; + p->level_right = 0; + p->time_c = 0x61; + p->time_l = 0x01; + p->time_r = 0x01; + p->feedback = 0x50; + p->pre_lpf = 0; + recompute_delay_status_gs(); +} + +/*! recompute Delay Effect (GS) */ +void Reverb::recompute_delay_status_gs(void) +{ + struct delay_status_gs_t *p = &delay_status_gs; + p->time_center = delay_time_center_table[p->time_c > 0x73 ? 0x73 : p->time_c]; + p->time_ratio_left = (double)p->time_l / 24; + p->time_ratio_right = (double)p->time_r / 24; + p->sample[0] = p->time_center * playback_rate / 1000.0f; + p->sample[1] = p->sample[0] * p->time_ratio_left; + p->sample[2] = p->sample[0] * p->time_ratio_right; + p->level_ratio[0] = p->level * p->level_center / (127.0f * 127.0f); + p->level_ratio[1] = p->level * p->level_left / (127.0f * 127.0f); + p->level_ratio[2] = p->level * p->level_right / (127.0f * 127.0f); + p->feedback_ratio = (double)(p->feedback - 64) * (0.763f * 2.0f / 100.0f); + p->send_reverb_ratio = (double)p->send_reverb * (0.787f / 100.0f); + + if (p->level_left != 0 || (p->level_right != 0 && p->type == 0)) { + p->type = 1; /* it needs 3-tap delay effect. */ + } + + if (p->pre_lpf) { + p->lpf.a = 2.0 * ((double)(7 - p->pre_lpf) / 7.0f * 16000.0f + 200.0f) / playback_rate; + init_filter_lowpass1(&(p->lpf)); + } +} + +/*! Delay Macro (GS) */ +void Reverb::set_delay_macro_gs(int macro) +{ + struct delay_status_gs_t *p = &delay_status_gs; + if (macro >= 4) { p->type = 2; } /* cross delay */ + macro *= 10; + p->time_center = delay_time_center_table[delay_macro_presets[macro + 1]]; + p->time_ratio_left = (double)delay_macro_presets[macro + 2] / 24; + p->time_ratio_right = (double)delay_macro_presets[macro + 3] / 24; + p->level_center = delay_macro_presets[macro + 4]; + p->level_left = delay_macro_presets[macro + 5]; + p->level_right = delay_macro_presets[macro + 6]; + p->level = delay_macro_presets[macro + 7]; + p->feedback = delay_macro_presets[macro + 8]; +} + +/*! initialize Reverb Effect (GS) */ +void Reverb::init_reverb_status_gs(void) +{ + struct reverb_status_gs_t *p = &reverb_status_gs; + p->character = 0x04; + p->pre_lpf = 0; + p->level = 0x40; + p->time = 0x40; + p->delay_feedback = 0; + p->pre_delay_time = 0; + recompute_reverb_status_gs(); + init_reverb(); +} + +/*! recompute Reverb Effect (GS) */ +void Reverb::recompute_reverb_status_gs(void) +{ + struct reverb_status_gs_t *p = &reverb_status_gs; + + if (p->pre_lpf) { + p->lpf.a = 2.0 * ((double)(7 - p->pre_lpf) / 7.0f * 16000.0f + 200.0f) / playback_rate; + init_filter_lowpass1(&(p->lpf)); + } +} + +/*! Reverb Type (GM2) */ +void Reverb::set_reverb_macro_gm2(int macro) +{ + struct reverb_status_gs_t *p = &reverb_status_gs; + int type = macro; + if (macro == 8) { macro = 5; } + macro *= 6; + p->character = reverb_macro_presets[macro]; + p->pre_lpf = reverb_macro_presets[macro + 1]; + p->level = reverb_macro_presets[macro + 2]; + p->time = reverb_macro_presets[macro + 3]; + p->delay_feedback = reverb_macro_presets[macro + 4]; + p->pre_delay_time = reverb_macro_presets[macro + 5]; + + switch (type) { /* override GS macro's parameter */ + case 0: /* Small Room */ + p->time = 44; + break; + case 1: /* Medium Room */ + case 8: /* Plate */ + p->time = 50; + break; + case 2: /* Large Room */ + p->time = 56; + break; + case 3: /* Medium Hall */ + case 4: /* Large Hall */ + p->time = 64; + break; + } +} + +/*! Reverb Macro (GS) */ +void Reverb::set_reverb_macro_gs(int macro) +{ + struct reverb_status_gs_t *p = &reverb_status_gs; + macro *= 6; + p->character = reverb_macro_presets[macro]; + p->pre_lpf = reverb_macro_presets[macro + 1]; + p->level = reverb_macro_presets[macro + 2]; + p->time = reverb_macro_presets[macro + 3]; + p->delay_feedback = reverb_macro_presets[macro + 4]; + p->pre_delay_time = reverb_macro_presets[macro + 5]; +} + +/*! initialize Chorus Effect (GS) */ +void Reverb::init_chorus_status_gs(void) +{ + struct chorus_status_gs_t *p = &chorus_status_gs; + p->macro = 0; + p->pre_lpf = 0; + p->level = 0x40; + p->feedback = 0x08; + p->delay = 0x50; + p->rate = 0x03; + p->depth = 0x13; + p->send_reverb = 0; + p->send_delay = 0; + recompute_chorus_status_gs(); +} + +/*! recompute Chorus Effect (GS) */ +void Reverb::recompute_chorus_status_gs() +{ + struct chorus_status_gs_t *p = &chorus_status_gs; + + if (p->pre_lpf) { + p->lpf.a = 2.0 * ((double)(7 - p->pre_lpf) / 7.0f * 16000.0f + 200.0f) / playback_rate; + init_filter_lowpass1(&(p->lpf)); + } +} + +/*! Chorus Macro (GS), Chorus Type (GM2) */ +void Reverb::set_chorus_macro_gs(int macro) +{ + struct chorus_status_gs_t *p = &chorus_status_gs; + macro *= 8; + p->pre_lpf = chorus_macro_presets[macro]; + p->level = chorus_macro_presets[macro + 1]; + p->feedback = chorus_macro_presets[macro + 2]; + p->delay = chorus_macro_presets[macro + 3]; + p->rate = chorus_macro_presets[macro + 4]; + p->depth = chorus_macro_presets[macro + 5]; + p->send_reverb = chorus_macro_presets[macro + 6]; + p->send_delay = chorus_macro_presets[macro + 7]; +} + +/*! initialize EQ (GS) */ +void Reverb::init_eq_status_gs(void) +{ + struct eq_status_gs_t *p = &eq_status_gs; + p->low_freq = 0; + p->low_gain = 0x40; + p->high_freq = 0; + p->high_gain = 0x40; + recompute_eq_status_gs(); +} + +/*! recompute EQ (GS) */ +void Reverb::recompute_eq_status_gs(void) +{ + double freq, dbGain; + struct eq_status_gs_t *p = &eq_status_gs; + + /* Lowpass Shelving Filter */ + if (p->low_freq == 0) { freq = 200; } + else { freq = 400; } + dbGain = p->low_gain - 0x40; + if (freq < playback_rate / 2) { + p->lsf.q = 0; + p->lsf.freq = freq; + p->lsf.gain = dbGain; + calc_filter_shelving_low(&(p->lsf)); + } + + /* Highpass Shelving Filter */ + if (p->high_freq == 0) { freq = 3000; } + else { freq = 6000; } + dbGain = p->high_gain - 0x40; + if (freq < playback_rate / 2) { + p->hsf.q = 0; + p->hsf.freq = freq; + p->hsf.gain = dbGain; + calc_filter_shelving_high(&(p->hsf)); + } +} + +/*! initialize Multi EQ (XG) */ +void Reverb::init_multi_eq_xg(void) +{ + multi_eq_xg.valid = 0; + set_multi_eq_type_xg(0); + recompute_multi_eq_xg(); +} + +/*! set Multi EQ type (XG) */ +void Reverb::set_multi_eq_type_xg(int type) +{ + struct multi_eq_xg_t *p = &multi_eq_xg; + type *= 20; + p->gain1 = multi_eq_block_table_xg[type]; + p->freq1 = multi_eq_block_table_xg[type + 1]; + p->q1 = multi_eq_block_table_xg[type + 2]; + p->shape1 = multi_eq_block_table_xg[type + 3]; + p->gain2 = multi_eq_block_table_xg[type + 4]; + p->freq2 = multi_eq_block_table_xg[type + 5]; + p->q2 = multi_eq_block_table_xg[type + 6]; + p->gain3 = multi_eq_block_table_xg[type + 8]; + p->freq3 = multi_eq_block_table_xg[type + 9]; + p->q3 = multi_eq_block_table_xg[type + 10]; + p->gain4 = multi_eq_block_table_xg[type + 12]; + p->freq4 = multi_eq_block_table_xg[type + 13]; + p->q4 = multi_eq_block_table_xg[type + 14]; + p->gain5 = multi_eq_block_table_xg[type + 16]; + p->freq5 = multi_eq_block_table_xg[type + 17]; + p->q5 = multi_eq_block_table_xg[type + 18]; + p->shape5 = multi_eq_block_table_xg[type + 19]; +} + +/*! recompute Multi EQ (XG) */ +void Reverb::recompute_multi_eq_xg(void) +{ + struct multi_eq_xg_t *p = &multi_eq_xg; + + if (p->freq1 != 0 && p->freq1 < 60 && p->gain1 != 0x40) { + p->valid1 = 1; + if (p->shape1) { /* peaking */ + p->eq1p.q = (double)p->q1 / 10.0; + p->eq1p.freq = eq_freq_table_xg[p->freq1]; + p->eq1p.gain = p->gain1 - 0x40; + calc_filter_peaking(&(p->eq1p)); + } + else { /* shelving */ + p->eq1s.q = (double)p->q1 / 10.0; + p->eq1s.freq = eq_freq_table_xg[p->freq1]; + p->eq1s.gain = p->gain1 - 0x40; + calc_filter_shelving_low(&(p->eq1s)); + } + } + else { p->valid1 = 0; } + if (p->freq2 != 0 && p->freq2 < 60 && p->gain2 != 0x40) { + p->valid2 = 1; + p->eq2p.q = (double)p->q2 / 10.0; + p->eq2p.freq = eq_freq_table_xg[p->freq2]; + p->eq2p.gain = p->gain2 - 0x40; + calc_filter_peaking(&(p->eq2p)); + } + else { p->valid2 = 0; } + if (p->freq3 != 0 && p->freq3 < 60 && p->gain3 != 0x40) { + p->valid3 = 1; + p->eq3p.q = (double)p->q3 / 10.0; + p->eq4p.freq = eq_freq_table_xg[p->freq3]; + p->eq4p.gain = p->gain3 - 0x40; + calc_filter_peaking(&(p->eq3p)); + } + else { p->valid3 = 0; } + if (p->freq4 != 0 && p->freq4 < 60 && p->gain4 != 0x40) { + p->valid4 = 1; + p->eq4p.q = (double)p->q4 / 10.0; + p->eq4p.freq = eq_freq_table_xg[p->freq4]; + p->eq4p.gain = p->gain4 - 0x40; + calc_filter_peaking(&(p->eq4p)); + } + else { p->valid4 = 0; } + if (p->freq5 != 0 && p->freq5 < 60 && p->gain5 != 0x40) { + p->valid5 = 1; + if (p->shape5) { /* peaking */ + p->eq5p.q = (double)p->q5 / 10.0; + p->eq5p.freq = eq_freq_table_xg[p->freq5]; + p->eq5p.gain = p->gain5 - 0x40; + calc_filter_peaking(&(p->eq5p)); + } + else { /* shelving */ + p->eq5s.q = (double)p->q5 / 10.0; + p->eq5s.freq = eq_freq_table_xg[p->freq5]; + p->eq5s.gain = p->gain5 - 0x40; + calc_filter_shelving_high(&(p->eq5s)); + } + } + else { p->valid5 = 0; } + p->valid = p->valid1 || p->valid2 || p->valid3 || p->valid4 || p->valid5; +} + +void Reverb::set_effect_param_xg(struct effect_xg_t *st, int type_msb, int type_lsb) +{ + int i, j; + for (i = 0; effect_parameter_xg[i].type_msb != -1 + && effect_parameter_xg[i].type_lsb != -1; i++) { + if (type_msb == effect_parameter_xg[i].type_msb + && type_lsb == effect_parameter_xg[i].type_lsb) { + for (j = 0; j < 16; j++) { + st->param_lsb[j] = effect_parameter_xg[i].param_lsb[j]; + } + for (j = 0; j < 10; j++) { + st->param_msb[j] = effect_parameter_xg[i].param_msb[j]; + } + ctl_cmsg(CMSG_INFO, VERB_NOISY, "XG EFX: %s", effect_parameter_xg[i].name); + return; + } + } + if (type_msb != 0) { + for (i = 0; effect_parameter_xg[i].type_msb != -1 + && effect_parameter_xg[i].type_lsb != -1; i++) { + if (type_lsb == effect_parameter_xg[i].type_lsb) { + for (j = 0; j < 16; j++) { + st->param_lsb[j] = effect_parameter_xg[i].param_lsb[j]; + } + for (j = 0; j < 10; j++) { + st->param_msb[j] = effect_parameter_xg[i].param_msb[j]; + } + ctl_cmsg(CMSG_INFO, VERB_NOISY, "XG EFX: %s", effect_parameter_xg[i].name); + return; + } + } + } +} + +/*! recompute XG effect parameters. */ +void Reverb::recompute_effect_xg(struct effect_xg_t *st) +{ + EffectList *efc = st->ef; + + if (efc == NULL) { return; } + while (efc != NULL && efc->info != NULL) + { + (this->*(efc->engine->conv_xg))(st, efc); + (this->*(efc->engine->do_effect))(NULL, MAGIC_INIT_EFFECT_INFO, efc); + efc = efc->next_ef; + } +} + +void Reverb::realloc_effect_xg(struct effect_xg_t *st) +{ + int type_msb = st->type_msb, type_lsb = st->type_lsb; + + free_effect_list(st->ef); + st->ef = NULL; + st->use_msb = 0; + + switch (type_msb) { + case 0x05: + st->use_msb = 1; + st->ef = push_effect(st->ef, EFFECT_DELAY_LCR); + st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2); + break; + case 0x06: + st->use_msb = 1; + st->ef = push_effect(st->ef, EFFECT_DELAY_LR); + st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2); + break; + case 0x07: + st->use_msb = 1; + st->ef = push_effect(st->ef, EFFECT_ECHO); + st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2); + break; + case 0x08: + st->use_msb = 1; + st->ef = push_effect(st->ef, EFFECT_CROSS_DELAY); + st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2); + break; + case 0x41: + case 0x42: + st->ef = push_effect(st->ef, EFFECT_CHORUS); + st->ef = push_effect(st->ef, EFFECT_CHORUS_EQ3); + break; + case 0x43: + st->ef = push_effect(st->ef, EFFECT_FLANGER); + st->ef = push_effect(st->ef, EFFECT_CHORUS_EQ3); + break; + case 0x44: + st->ef = push_effect(st->ef, EFFECT_SYMPHONIC); + st->ef = push_effect(st->ef, EFFECT_CHORUS_EQ3); + break; + case 0x49: + st->ef = push_effect(st->ef, EFFECT_STEREO_DISTORTION); + st->ef = push_effect(st->ef, EFFECT_OD_EQ3); + break; + case 0x4A: + st->ef = push_effect(st->ef, EFFECT_STEREO_OVERDRIVE); + st->ef = push_effect(st->ef, EFFECT_OD_EQ3); + break; + case 0x4B: + st->ef = push_effect(st->ef, EFFECT_STEREO_AMP_SIMULATOR); + break; + case 0x4C: + st->ef = push_effect(st->ef, EFFECT_EQ3); + break; + case 0x4D: + st->ef = push_effect(st->ef, EFFECT_EQ2); + break; + case 0x4E: + if (type_lsb == 0x01 || type_lsb == 0x02) { + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH); + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_EQ2); + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_OD); + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_OD_EQ3); + } + else { + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH); + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_EQ2); + } + break; + case 0x5E: + st->ef = push_effect(st->ef, EFFECT_LOFI); + break; + default: /* Not Supported */ + type_msb = type_lsb = 0; + break; + } + set_effect_param_xg(st, type_msb, type_lsb); + recompute_effect_xg(st); +} + +void Reverb::init_effect_xg(struct effect_xg_t *st) +{ + int i; + + free_effect_list(st->ef); + st->ef = NULL; + + st->use_msb = 0; + st->type_msb = st->type_lsb = st->connection = + st->send_reverb = st->send_chorus = 0; + st->part = 0x7f; + st->ret = st->pan = st->mw_depth = st->bend_depth = st->cat_depth = + st->ac1_depth = st->ac2_depth = st->cbc1_depth = st->cbc2_depth = 0x40; + for (i = 0; i < 16; i++) { st->param_lsb[i] = 0; } + for (i = 0; i < 10; i++) { st->param_msb[i] = 0; } +} + +/*! initialize XG effect parameters */ +void Reverb::init_all_effect_xg(void) +{ + int i; + init_effect_xg(&reverb_status_xg); + reverb_status_xg.type_msb = 0x01; + reverb_status_xg.connection = XG_CONN_SYSTEM_REVERB; + realloc_effect_xg(&reverb_status_xg); + init_effect_xg(&chorus_status_xg); + chorus_status_xg.type_msb = 0x41; + chorus_status_xg.connection = XG_CONN_SYSTEM_CHORUS; + realloc_effect_xg(&chorus_status_xg); + for (i = 0; i < XG_VARIATION_EFFECT_NUM; i++) { + init_effect_xg(&variation_effect_xg[i]); + variation_effect_xg[i].type_msb = 0x05; + realloc_effect_xg(&variation_effect_xg[i]); + } + for (i = 0; i < XG_INSERTION_EFFECT_NUM; i++) { + init_effect_xg(&insertion_effect_xg[i]); + insertion_effect_xg[i].type_msb = 0x49; + realloc_effect_xg(&insertion_effect_xg[i]); + } + init_ch_effect_xg(); +} + +/*! initialize GS insertion effect parameters */ +void Reverb::init_insertion_effect_gs(void) +{ + int i; + struct insertion_effect_gs_t *st = &insertion_effect_gs; + + free_effect_list(st->ef); + st->ef = NULL; + + for (i = 0; i < 20; i++) { st->parameter[i] = 0; } + + st->type = 0; + st->type_lsb = 0; + st->type_msb = 0; + st->send_reverb = 0x28; + st->send_chorus = 0; + st->send_delay = 0; + st->control_source1 = 0; + st->control_depth1 = 0x40; + st->control_source2 = 0; + st->control_depth2 = 0x40; + st->send_eq_switch = 0x01; +} + +void Reverb::set_effect_param_gs(struct insertion_effect_gs_t *st, int msb, int lsb) +{ + int i, j; + for (i = 0; effect_parameter_gs[i].type_msb != -1 + && effect_parameter_gs[i].type_lsb != -1; i++) { + if (msb == effect_parameter_gs[i].type_msb + && lsb == effect_parameter_gs[i].type_lsb) { + for (j = 0; j < 20; j++) { + st->parameter[j] = effect_parameter_gs[i].param[j]; + } + ctl_cmsg(CMSG_INFO, VERB_NOISY, "GS EFX: %s", effect_parameter_gs[i].name); + break; + } + } +} + +/*! recompute GS insertion effect parameters. */ +void Reverb::recompute_insertion_effect_gs(void) +{ + struct insertion_effect_gs_t *st = &insertion_effect_gs; + EffectList *efc = st->ef; + + if (st->ef == NULL) { return; } + while (efc != NULL && efc->info != NULL) + { + (this->*(efc->engine->conv_gs))(st, efc); + (this->*(efc->engine->do_effect))(NULL, MAGIC_INIT_EFFECT_INFO, efc); + efc = efc->next_ef; + } +} + +/*! re-allocate GS insertion effect parameters. */ +void Reverb::realloc_insertion_effect_gs(void) +{ + struct insertion_effect_gs_t *st = &insertion_effect_gs; + int type_msb = st->type_msb, type_lsb = st->type_lsb; + + free_effect_list(st->ef); + st->ef = NULL; + + switch (type_msb) { + case 0x01: + switch (type_lsb) { + case 0x00: /* Stereo-EQ */ + st->ef = push_effect(st->ef, EFFECT_STEREO_EQ); + break; + case 0x10: /* Overdrive */ + st->ef = push_effect(st->ef, EFFECT_EQ2); + st->ef = push_effect(st->ef, EFFECT_OVERDRIVE1); + break; + case 0x11: /* Distortion */ + st->ef = push_effect(st->ef, EFFECT_EQ2); + st->ef = push_effect(st->ef, EFFECT_DISTORTION1); + break; + case 0x40: /* Hexa Chorus */ + st->ef = push_effect(st->ef, EFFECT_EQ2); + st->ef = push_effect(st->ef, EFFECT_HEXA_CHORUS); + break; + case 0x72: /* Lo-Fi 1 */ + st->ef = push_effect(st->ef, EFFECT_EQ2); + st->ef = push_effect(st->ef, EFFECT_LOFI1); + break; + case 0x73: /* Lo-Fi 2 */ + st->ef = push_effect(st->ef, EFFECT_EQ2); + st->ef = push_effect(st->ef, EFFECT_LOFI2); + break; + default: break; + } + break; + case 0x11: + switch (type_lsb) { + case 0x03: /* OD1 / OD2 */ + st->ef = push_effect(st->ef, EFFECT_OD1OD2); + break; + default: break; + } + break; + default: break; + } + + set_effect_param_gs(st, type_msb, type_lsb); + + recompute_insertion_effect_gs(); +} + +void Reverb::init_effect_status(int play_system_mode) +{ + free_effect_buffers(); + init_reverb_status_gs(); + init_delay_status_gs(); + init_chorus_status_gs(); + init_eq_status_gs(); + init_insertion_effect_gs(); + init_multi_eq_xg(); + if (play_system_mode == XG_SYSTEM_MODE) { init_all_effect_xg(); } +} + +///////////////////////////////////////////////////////////////////// +} \ No newline at end of file diff --git a/src/sound/timidity++/reverb.h b/src/sound/timidity++/reverb.h new file mode 100644 index 0000000000..b84475e6cd --- /dev/null +++ b/src/sound/timidity++/reverb.h @@ -0,0 +1,814 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * REVERB EFFECT FOR TIMIDITY++-1.X (Version 0.06e 1999/1/28) + * + * Copyright (C) 1997,1998,1999 Masaki Kiryu + * (http://w3mb.kcom.ne.jp/~mkiryu/) + * + * reverb.h + * + */ +#ifndef ___REVERB_H_ +#define ___REVERB_H_ + +#include + +namespace TimidityPlus +{ + + +#define DEFAULT_REVERB_SEND_LEVEL 40 + + +/* */ +/* Effect Utitities */ +/* */ +/*! simple delay */ +typedef struct { + int32_t *buf, size, index; +} simple_delay; + +/*! Pink Noise Generator */ +typedef struct { + float b0, b1, b2, b3, b4, b5, b6; +} pink_noise; + + +#ifndef SINE_CYCLE_LENGTH +#define SINE_CYCLE_LENGTH 1024 +#endif + +/*! LFO */ +typedef struct { + int32_t buf[SINE_CYCLE_LENGTH]; + int32_t count, cycle; /* in samples */ + int32_t icycle; /* proportional to (SINE_CYCLE_LENGTH / cycle) */ + int type; /* current content of its buffer */ + double freq; /* in Hz */ +} lfo; + +enum { + LFO_NONE = 0, + LFO_SINE, + LFO_TRIANGULAR, +}; + +/*! modulated delay with allpass interpolation */ +typedef struct { + int32_t *buf, size, rindex, windex, hist; + int32_t ndelay, depth; /* in samples */ +} mod_delay; + +/*! modulated allpass filter with allpass interpolation */ +typedef struct { + int32_t *buf, size, rindex, windex, hist; + int32_t ndelay, depth; /* in samples */ + double feedback; + int32_t feedbacki; +} mod_allpass; + +/*! Moog VCF (resonant IIR state variable filter) */ +typedef struct { + int16_t freq, last_freq; /* in Hz */ + double res_dB, last_res_dB; /* in dB */ + int32_t f, q, p; /* coefficients in fixed-point */ + int32_t b0, b1, b2, b3, b4; +} filter_moog; + +/*! Moog VCF (resonant IIR state variable filter with distortion) */ +typedef struct { + int16_t freq, last_freq; /* in Hz */ + double res_dB, last_res_dB; /* in dB */ + double dist, last_dist, f, q, p, d, b0, b1, b2, b3, b4; +} filter_moog_dist; + +/*! LPF18 (resonant IIR lowpass filter with waveshaping) */ +typedef struct { + int16_t freq, last_freq; /* in Hz */ + double dist, res, last_dist, last_res; /* in linear */ + double ay1, ay2, aout, lastin, kres, value, kp, kp1h; +} filter_lpf18; + +/*! 1st order lowpass filter */ +typedef struct { + double a; + int32_t ai, iai; /* coefficients in fixed-point */ + int32_t x1l, x1r; +} filter_lowpass1; + +/*! lowpass / highpass filter */ +typedef struct { + double freq, q, last_freq, last_q; + int32_t x1l, x2l, y1l, y2l, x1r, x2r, y1r, y2r; + int32_t a1, a2, b1, b02; +} filter_biquad; + +#ifndef PART_EQ_XG +#define PART_EQ_XG +/*! shelving filter */ +typedef struct { + double freq, gain, q; + int32_t x1l, x2l, y1l, y2l, x1r, x2r, y1r, y2r; + int32_t a1, a2, b0, b1, b2; +} filter_shelving; + +struct part_eq_xg { + int8_t bass, treble, bass_freq, treble_freq; + filter_shelving basss, trebles; + int8_t valid; +}; +#endif /* PART_EQ_XG */ + + +/*! peaking filter */ +typedef struct { + double freq, gain, q; + int32_t x1l, x2l, y1l, y2l, x1r, x2r, y1r, y2r; + int32_t ba1, a2, b0, b2; +} filter_peaking; + + +/*! allpass filter */ +typedef struct _allpass { + int32_t *buf, size, index; + double feedback; + int32_t feedbacki; +} allpass; + +/*! comb filter */ +typedef struct _comb { + int32_t *buf, filterstore, size, index; + double feedback, damp1, damp2; + int32_t feedbacki, damp1i, damp2i; +} comb; + +/* */ +/* Insertion and Variation Effect */ +/* */ +struct effect_xg_t { + int8_t use_msb, type_msb, type_lsb, param_lsb[16], param_msb[10], + ret, pan, send_reverb, send_chorus, connection, part, + mw_depth, bend_depth, cat_depth, ac1_depth, ac2_depth, cbc1_depth, + cbc2_depth; + struct _EffectList *ef; +}; + + +enum { + EFFECT_NONE, + EFFECT_EQ2, + EFFECT_EQ3, + EFFECT_STEREO_EQ, + EFFECT_OVERDRIVE1, + EFFECT_DISTORTION1, + EFFECT_OD1OD2, + EFFECT_CHORUS, + EFFECT_FLANGER, + EFFECT_SYMPHONIC, + EFFECT_CHORUS_EQ3, + EFFECT_STEREO_OVERDRIVE, + EFFECT_STEREO_DISTORTION, + EFFECT_STEREO_AMP_SIMULATOR, + EFFECT_OD_EQ3, + EFFECT_HEXA_CHORUS, + EFFECT_DELAY_LCR, + EFFECT_DELAY_LR, + EFFECT_ECHO, + EFFECT_CROSS_DELAY, + EFFECT_DELAY_EQ2, + EFFECT_LOFI, + EFFECT_LOFI1, + EFFECT_LOFI2, + EFFECT_XG_AUTO_WAH, + EFFECT_XG_AUTO_WAH_EQ2, + EFFECT_XG_AUTO_WAH_OD, + EFFECT_XG_AUTO_WAH_OD_EQ3, +}; + +#define MAGIC_INIT_EFFECT_INFO -1 +#define MAGIC_FREE_EFFECT_INFO -2 + +class Reverb; + +struct insertion_effect_gs_t { + int32_t type; + int8_t type_lsb, type_msb, parameter[20], send_reverb, + send_chorus, send_delay, control_source1, control_depth1, + control_source2, control_depth2, send_eq_switch; + struct _EffectList *ef; +}; + +enum { + XG_CONN_INSERTION = 0, + XG_CONN_SYSTEM = 1, + XG_CONN_SYSTEM_CHORUS, + XG_CONN_SYSTEM_REVERB, +}; + +#define XG_INSERTION_EFFECT_NUM 2 +#define XG_VARIATION_EFFECT_NUM 1 + +typedef struct _EffectList { + int type; + void *info; + const struct _EffectEngine *engine; + struct _EffectList *next_ef; +} EffectList; + +struct _EffectEngine { + int type; + const char *name; + void (Reverb::*do_effect)(int32_t *, int32_t, struct _EffectList *); + void (Reverb::*conv_gs)(struct insertion_effect_gs_t *, struct _EffectList *); + void (Reverb::*conv_xg)(struct effect_xg_t *, struct _EffectList *); + int info_size; +}; + + +struct effect_parameter_gs_t { + int8_t type_msb, type_lsb; + const char *name; + int8_t param[20]; + int8_t control1, control2; +}; + + +struct effect_parameter_xg_t { + int8_t type_msb, type_lsb; + const char *name; + int8_t param_msb[10], param_lsb[16]; + int8_t control; +}; + + +/*! 2-Band EQ */ +typedef struct { + int16_t low_freq, high_freq; /* in Hz */ + int16_t low_gain, high_gain; /* in dB */ + filter_shelving hsf, lsf; +} InfoEQ2; + +/*! 3-Band EQ */ +typedef struct { + int16_t low_freq, high_freq, mid_freq; /* in Hz */ + int16_t low_gain, high_gain, mid_gain; /* in dB */ + double mid_width; + filter_shelving hsf, lsf; + filter_peaking peak; +} InfoEQ3; + +/*! Stereo EQ */ +typedef struct { + int16_t low_freq, high_freq, m1_freq, m2_freq; /* in Hz */ + int16_t low_gain, high_gain, m1_gain, m2_gain; /* in dB */ + double m1_q, m2_q, level; + int32_t leveli; + filter_shelving hsf, lsf; + filter_peaking m1, m2; +} InfoStereoEQ; + +/*! Overdrive 1 / Distortion 1 */ +typedef struct { + double level; + int32_t leveli, di; /* in fixed-point */ + int8_t drive, pan, amp_sw, amp_type; + filter_moog svf; + filter_biquad lpf1; + void (Reverb::*amp_sim)(int32_t *, int32_t); +} InfoOverdrive1; + +/*! OD1 / OD2 */ +typedef struct { + double level, levell, levelr; + int32_t levelli, levelri, dli, dri; /* in fixed-point */ + int8_t drivel, driver, panl, panr, typel, typer, amp_swl, amp_swr, amp_typel, amp_typer; + filter_moog svfl, svfr; + filter_biquad lpf1; + void (Reverb::*amp_siml)(int32_t *, int32_t), (Reverb::*amp_simr)(int32_t *, int32_t); + void (Reverb::*odl)(int32_t *, int32_t), (Reverb::*odr)(int32_t *, int32_t); +} InfoOD1OD2; + +/*! HEXA-CHORUS */ +typedef struct { + simple_delay buf0; + lfo lfo0; + double dry, wet, level; + int32_t pdelay, depth; /* in samples */ + int8_t pdelay_dev, depth_dev, pan_dev; + int32_t dryi, weti; /* in fixed-point */ + int32_t pan0, pan1, pan2, pan3, pan4, pan5; + int32_t depth0, depth1, depth2, depth3, depth4, depth5, + pdelay0, pdelay1, pdelay2, pdelay3, pdelay4, pdelay5; + int32_t spt0, spt1, spt2, spt3, spt4, spt5, + hist0, hist1, hist2, hist3, hist4, hist5; +} InfoHexaChorus; + +/*! Plate Reverb */ +typedef struct { + simple_delay pd, od1l, od2l, od3l, od4l, od5l, od6l, od7l, + od1r, od2r, od3r, od4r, od5r, od6r, od7r, + td1, td2, td1d, td2d; + lfo lfo1, lfo1d; + allpass ap1, ap2, ap3, ap4, ap6, ap6d; + mod_allpass ap5, ap5d; + filter_lowpass1 lpf1, lpf2; + int32_t t1, t1d; + double decay, ddif1, ddif2, idif1, idif2, dry, wet; + int32_t decayi, ddif1i, ddif2i, idif1i, idif2i, dryi, weti; +} InfoPlateReverb; + +/*! Standard Reverb */ +typedef struct { + int32_t spt0, spt1, spt2, spt3, rpt0, rpt1, rpt2, rpt3; + int32_t ta, tb, HPFL, HPFR, LPFL, LPFR, EPFL, EPFR; + simple_delay buf0_L, buf0_R, buf1_L, buf1_R, buf2_L, buf2_R, buf3_L, buf3_R; + double fbklev, nmixlev, cmixlev, monolev, hpflev, lpflev, lpfinp, epflev, epfinp, width, wet; + int32_t fbklevi, nmixlevi, cmixlevi, monolevi, hpflevi, lpflevi, lpfinpi, epflevi, epfinpi, widthi, weti; +} InfoStandardReverb; + +/*! Freeverb */ +#define numcombs 8 +#define numallpasses 4 + +typedef struct { + simple_delay pdelay; + double roomsize, roomsize1, damp, damp1, wet, wet1, wet2, width; + comb combL[numcombs], combR[numcombs]; + allpass allpassL[numallpasses], allpassR[numallpasses]; + int32_t wet1i, wet2i; + int8_t alloc_flag; +} InfoFreeverb; + +/*! 3-Tap Stereo Delay Effect */ +typedef struct { + simple_delay delayL, delayR; + int32_t size[3], index[3]; + double level[3], feedback, send_reverb; + int32_t leveli[3], feedbacki, send_reverbi; +} InfoDelay3; + +/*! Stereo Chorus Effect */ +typedef struct { + simple_delay delayL, delayR; + lfo lfoL, lfoR; + int32_t wpt0, spt0, spt1, hist0, hist1; + int32_t rpt0, depth, pdelay; + double level, feedback, send_reverb, send_delay; + int32_t leveli, feedbacki, send_reverbi, send_delayi; +} InfoStereoChorus; + +/*! Chorus */ +typedef struct { + simple_delay delayL, delayR; + lfo lfoL, lfoR; + int32_t wpt0, spt0, spt1, hist0, hist1; + int32_t rpt0, depth, pdelay; + double dry, wet, feedback, pdelay_ms, depth_ms, rate, phase_diff; + int32_t dryi, weti, feedbacki; +} InfoChorus; + + +/*! Stereo Overdrive / Distortion */ +typedef struct { + double level, dry, wet, drive, cutoff; + int32_t dryi, weti, di; + filter_moog svfl, svfr; + filter_biquad lpf1; + void (Reverb::*od)(int32_t *, int32_t); +} InfoStereoOD; + +/*! Delay L,C,R */ +typedef struct { + simple_delay delayL, delayR; + int32_t index[3], size[3]; /* L,C,R */ + double rdelay, ldelay, cdelay, fdelay; /* in ms */ + double dry, wet, feedback, clevel, high_damp; + int32_t dryi, weti, feedbacki, cleveli; + filter_lowpass1 lpf; +} InfoDelayLCR; + +/*! Delay L,R */ +typedef struct { + simple_delay delayL, delayR; + int32_t index[2], size[2]; /* L,R */ + double rdelay, ldelay, fdelay1, fdelay2; /* in ms */ + double dry, wet, feedback, high_damp; + int32_t dryi, weti, feedbacki; + filter_lowpass1 lpf; +} InfoDelayLR; + +/*! Echo */ +typedef struct { + simple_delay delayL, delayR; + int32_t index[2], size[2]; /* L1,R1 */ + double rdelay1, ldelay1, rdelay2, ldelay2; /* in ms */ + double dry, wet, lfeedback, rfeedback, high_damp, level; + int32_t dryi, weti, lfeedbacki, rfeedbacki, leveli; + filter_lowpass1 lpf; +} InfoEcho; + +/*! Cross Delay */ +typedef struct { + simple_delay delayL, delayR; + double lrdelay, rldelay; /* in ms */ + double dry, wet, feedback, high_damp; + int32_t dryi, weti, feedbacki, input_select; + filter_lowpass1 lpf; +} InfoCrossDelay; + +/*! Lo-Fi 1 */ +typedef struct { + int8_t lofi_type, pan, pre_filter, post_filter; + double level, dry, wet; + int32_t bit_mask, level_shift, dryi, weti; + filter_biquad pre_fil, post_fil; +} InfoLoFi1; + +/*! Lo-Fi 2 */ +typedef struct { + int8_t wp_sel, disc_type, hum_type, ms, pan, rdetune, lofi_type, fil_type; + double wp_level, rnz_lev, discnz_lev, hum_level, dry, wet, level; + int32_t bit_mask, level_shift, wp_leveli, rnz_levi, discnz_levi, hum_keveki, dryi, weti; + filter_biquad fil, wp_lpf, hum_lpf, disc_lpf; +} InfoLoFi2; + +/*! LO-FI */ +typedef struct { + int8_t output_gain, word_length, filter_type, bit_assign, emphasis; + double dry, wet; + int32_t bit_mask, level_shift, dryi, weti; + filter_biquad lpf, srf; +} InfoLoFi; + +/*! XG: Auto Wah */ +typedef struct { + int8_t lfo_depth, drive; + double resonance, lfo_freq, offset_freq, dry, wet; + int32_t dryi, weti, fil_count, fil_cycle; + lfo lfo; + filter_moog_dist fil0, fil1; +} InfoXGAutoWah; + +typedef struct { + double level; + int32_t leveli; + filter_biquad lpf; +} InfoXGAutoWahOD; + + +/* GS parameters of reverb effect */ +struct reverb_status_gs_t +{ + /* GS parameters */ + int8_t character, pre_lpf, level, time, delay_feedback, pre_delay_time; + + InfoStandardReverb info_standard_reverb; + InfoPlateReverb info_plate_reverb; + InfoFreeverb info_freeverb; + InfoDelay3 info_reverb_delay; + filter_lowpass1 lpf; +}; + +struct chorus_text_gs_t +{ + int status; + uint8_t voice_reserve[18], macro[3], pre_lpf[3], level[3], feed_back[3], + delay[3], rate[3], depth[3], send_level[3]; +}; + +/* GS parameters of chorus effect */ +struct chorus_status_gs_t +{ + /* GS parameters */ + int8_t macro, pre_lpf, level, feedback, delay, rate, depth, send_reverb, send_delay; + + //struct chorus_text_gs_t text; + + InfoStereoChorus info_stereo_chorus; + filter_lowpass1 lpf; +}; + +/* GS parameters of delay effect */ +struct delay_status_gs_t +{ + /* GS parameters */ + int8_t type, level, level_center, level_left, level_right, + feedback, pre_lpf, send_reverb, time_c, time_l, time_r; + double time_center; /* in ms */ + double time_ratio_left, time_ratio_right; /* in pct */ + + /* for pre-calculation */ + int32_t sample[3]; /* center, left, right */ + double level_ratio[3]; /* center, left, right */ + double feedback_ratio, send_reverb_ratio; + + filter_lowpass1 lpf; + InfoDelay3 info_delay; +}; + +/* GS parameters of channel EQ */ +struct eq_status_gs_t +{ + /* GS parameters */ + int8_t low_freq, high_freq, low_gain, high_gain; + + filter_shelving hsf, lsf; +}; + +/* XG parameters of Multi EQ */ +struct multi_eq_xg_t +{ + /* XG parameters */ + int8_t type, gain1, gain2, gain3, gain4, gain5, + freq1, freq2, freq3, freq4, freq5, + q1, q2, q3, q4, q5, shape1, shape5; + + int8_t valid, valid1, valid2, valid3, valid4, valid5; + filter_shelving eq1s, eq5s; + filter_peaking eq1p, eq2p, eq3p, eq4p, eq5p; +}; + + + +class Reverb +{ + double REV_INP_LEV; + + int32_t direct_buffer[AUDIO_BUFFER_SIZE * 2]; + int32_t direct_bufsize; + + int32_t reverb_effect_buffer[AUDIO_BUFFER_SIZE * 2]; + int32_t reverb_effect_bufsize; + + int32_t delay_effect_buffer[AUDIO_BUFFER_SIZE * 2]; + int32_t chorus_effect_buffer[AUDIO_BUFFER_SIZE * 2]; + int32_t eq_buffer[AUDIO_BUFFER_SIZE * 2]; + + + static const struct _EffectEngine effect_engine[]; + + void free_delay(simple_delay *delay); + void set_delay(simple_delay *delay, int32_t size); + void do_delay(int32_t *stream, int32_t *buf, int32_t size, int32_t *index); + void init_lfo(lfo *lfo, double freq, int type, double phase); + int32_t do_lfo(lfo *lfo); + void do_mod_delay(int32_t *stream, int32_t *buf, int32_t size, int32_t *rindex, int32_t *windex, int32_t ndelay, int32_t depth, int32_t lfoval, int32_t *hist); + void free_mod_allpass(mod_allpass *delay); + void set_mod_allpass(mod_allpass *delay, int32_t ndelay, int32_t depth, double feedback); + void do_mod_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *rindex, int32_t *windex, int32_t ndelay, int32_t depth, int32_t lfoval, int32_t *hist, int32_t feedback); + void free_allpass(allpass *allpass); + void set_allpass(allpass *allpass, int32_t size, double feedback); + void do_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *index, int32_t feedback); + void init_filter_moog(filter_moog *svf); + void calc_filter_moog(filter_moog *svf); + void do_filter_moog(int32_t *stream, int32_t *high, int32_t f, int32_t p, int32_t q, int32_t *b0, int32_t *b1, int32_t *b2, int32_t *b3, int32_t *b4); + void init_filter_moog_dist(filter_moog_dist *svf); + void calc_filter_moog_dist(filter_moog_dist *svf); + void do_filter_moog_dist(double *stream, double *high, double *band, double f, double p, double q, double d, double *b0, double *b1, double *b2, double *b3, double *b4); + void do_filter_moog_dist_band(double *stream, double f, double p, double q, double d, double *b0, double *b1, double *b2, double *b3, double *b4); + void init_filter_lpf18(filter_lpf18 *p); + void calc_filter_lpf18(filter_lpf18 *p); + void do_filter_lpf18(double *stream, double *ay1, double *ay2, double *aout, double *lastin, double kres, double value, double kp, double kp1h); + void do_dummy_clipping(int32_t *stream, int32_t d) {} + void do_hard_clipping(int32_t *stream, int32_t d); + void do_soft_clipping1(int32_t *stream, int32_t d); + void do_soft_clipping2(int32_t *stream, int32_t d); + void do_filter_lowpass1(int32_t *stream, int32_t *x1, int32_t a, int32_t ia); + void do_filter_lowpass1_stereo(int32_t *buf, int32_t count, filter_lowpass1 *p); + void init_filter_biquad(filter_biquad *p); + void calc_filter_biquad_low(filter_biquad *p); + void calc_filter_biquad_high(filter_biquad *p); + void do_filter_biquad(int32_t *stream, int32_t a1, int32_t a2, int32_t b1, int32_t b02, int32_t *x1, int32_t *x2, int32_t *y1, int32_t *y2); + void init_filter_shelving(filter_shelving *p); + void do_shelving_filter_stereo(int32_t* buf, int32_t count, filter_shelving *p); + void do_peaking_filter_stereo(int32_t* buf, int32_t count, filter_peaking *p); + double gs_revchar_to_roomsize(int character); + double gs_revchar_to_level(int character); + double gs_revchar_to_rt(int character); + void init_filter_peaking(filter_peaking *p); + void init_standard_reverb(InfoStandardReverb *info); + void free_standard_reverb(InfoStandardReverb *info); + void do_ch_standard_reverb(int32_t *buf, int32_t count, InfoStandardReverb *info); + void do_ch_standard_reverb_mono(int32_t *buf, int32_t count, InfoStandardReverb *info); + void set_freeverb_allpass(allpass *allpass, int32_t size); + void init_freeverb_allpass(allpass *allpass); + void set_freeverb_comb(comb *comb, int32_t size); + void init_freeverb_comb(comb *comb); + void realloc_freeverb_buf(InfoFreeverb *rev); + void update_freeverb(InfoFreeverb *rev); + void init_freeverb(InfoFreeverb *rev); + void alloc_freeverb_buf(InfoFreeverb *rev); + void free_freeverb_buf(InfoFreeverb *rev); + void do_freeverb_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *index, int32_t feedback); + void do_freeverb_comb(int32_t input, int32_t *stream, int32_t *buf, int32_t size, int32_t *index, int32_t damp1, int32_t damp2, int32_t *fs, int32_t feedback); + void do_ch_freeverb(int32_t *buf, int32_t count, InfoFreeverb *rev); + void init_ch_reverb_delay(InfoDelay3 *info); + void free_ch_reverb_delay(InfoDelay3 *info); + void do_ch_reverb_panning_delay(int32_t *buf, int32_t count, InfoDelay3 *info); + void do_ch_reverb_normal_delay(int32_t *buf, int32_t count, InfoDelay3 *info); + int32_t get_plate_delay(double delay, double t); + void do_ch_plate_reverb(int32_t *buf, int32_t count, InfoPlateReverb *info); + void init_ch_3tap_delay(InfoDelay3 *info); + void free_ch_3tap_delay(InfoDelay3 *info); + void do_ch_3tap_delay(int32_t *buf, int32_t count, InfoDelay3 *info); + void do_ch_cross_delay(int32_t *buf, int32_t count, InfoDelay3 *info); + void do_ch_normal_delay(int32_t *buf, int32_t count, InfoDelay3 *info); + void do_ch_stereo_chorus(int32_t *buf, int32_t count, InfoStereoChorus *info); + void alloc_effect(EffectList *ef); + void do_eq2(int32_t *buf, int32_t count, EffectList *ef); + int32_t do_left_panning(int32_t sample, int32_t pan); + int32_t do_right_panning(int32_t sample, int32_t pan); + double calc_gs_drive(int val); + void do_overdrive1(int32_t *buf, int32_t count, EffectList *ef); + void do_distortion1(int32_t *buf, int32_t count, EffectList *ef); + void do_dual_od(int32_t *buf, int32_t count, EffectList *ef); + void do_hexa_chorus(int32_t *buf, int32_t count, EffectList *ef); + void free_effect_xg(struct effect_xg_t *st); + int clip_int(int val, int min, int max); + void conv_gs_eq2(struct insertion_effect_gs_t *ieffect, EffectList *ef); + void conv_gs_overdrive1(struct insertion_effect_gs_t *ieffect, EffectList *ef); + void conv_gs_dual_od(struct insertion_effect_gs_t *ieffect, EffectList *ef); + double calc_dry_gs(int val); + double calc_wet_gs(int val); + void conv_gs_hexa_chorus(struct insertion_effect_gs_t *ieffect, EffectList *ef); + double calc_dry_xg(int val, struct effect_xg_t *st); + double calc_wet_xg(int val, struct effect_xg_t *st); + void do_eq3(int32_t *buf, int32_t count, EffectList *ef); + void do_stereo_eq(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_eq2(struct effect_xg_t *st, EffectList *ef); + void conv_xg_eq3(struct effect_xg_t *st, EffectList *ef); + void conv_gs_stereo_eq(struct insertion_effect_gs_t *st, EffectList *ef); + void conv_xg_chorus_eq3(struct effect_xg_t *st, EffectList *ef); + void conv_xg_chorus(struct effect_xg_t *st, EffectList *ef); + void conv_xg_flanger(struct effect_xg_t *st, EffectList *ef); + void conv_xg_symphonic(struct effect_xg_t *st, EffectList *ef); + void do_chorus(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_od_eq3(struct effect_xg_t *st, EffectList *ef); + void conv_xg_overdrive(struct effect_xg_t *st, EffectList *ef); + void conv_xg_distortion(struct effect_xg_t *st, EffectList *ef); + void conv_xg_amp_simulator(struct effect_xg_t *st, EffectList *ef); + void do_stereo_od(int32_t *buf, int32_t count, EffectList *ef); + void do_delay_lcr(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_delay_eq2(struct effect_xg_t *st, EffectList *ef); + void conv_xg_delay_lcr(struct effect_xg_t *st, EffectList *ef); + void conv_xg_delay_lr(struct effect_xg_t *st, EffectList *ef); + void do_delay_lr(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_echo(struct effect_xg_t *st, EffectList *ef); + void do_echo(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_cross_delay(struct effect_xg_t *st, EffectList *ef); + void do_cross_delay(int32_t *buf, int32_t count, EffectList *ef); + void conv_gs_lofi1(struct insertion_effect_gs_t *st, EffectList *ef); + inline int32_t apply_lofi(int32_t input, int32_t bit_mask, int32_t level_shift); + void do_lofi1(int32_t *buf, int32_t count, EffectList *ef); + void conv_gs_lofi2(struct insertion_effect_gs_t *st, EffectList *ef); + void do_lofi2(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_lofi(struct effect_xg_t *st, EffectList *ef); + void do_lofi(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_auto_wah_od(struct effect_xg_t *st, EffectList *ef); + void conv_xg_auto_wah_od_eq3(struct effect_xg_t *st, EffectList *ef); + void conv_xg_auto_wah_eq2(struct effect_xg_t *st, EffectList *ef); + void conv_xg_auto_wah(struct effect_xg_t *st, EffectList *ef); + double calc_xg_auto_wah_freq(int32_t lfo_val, double offset_freq, int8_t depth); + void do_xg_auto_wah(int32_t *buf, int32_t count, EffectList *ef); + void do_xg_auto_wah_od(int32_t *buf, int32_t count, EffectList *ef); + +public: + Reverb() + { + // Make sure that this starts out with all zeros. + memset(this, 0, sizeof(*this)); + REV_INP_LEV = 1.0; + direct_bufsize = sizeof(direct_buffer); + reverb_effect_bufsize = sizeof(reverb_effect_buffer); + + } + + ~Reverb() + { + free_effect_buffers(); + } + + void set_dry_signal(int32_t *, int32_t); + void set_dry_signal_xg(int32_t *, int32_t, int32_t); + void mix_dry_signal(int32_t *, int32_t); + void free_effect_buffers(void); + void init_pink_noise(pink_noise *); + float get_pink_noise(pink_noise *); + float get_pink_noise_light(pink_noise *); + void calc_filter_shelving_high(filter_shelving *); + void calc_filter_shelving_low(filter_shelving *); + void calc_filter_peaking(filter_peaking *); + void do_insertion_effect_gs(int32_t*, int32_t); + void do_insertion_effect_xg(int32_t*, int32_t, struct effect_xg_t *); + void do_variation_effect1_xg(int32_t*, int32_t); + void init_ch_effect_xg(void); + EffectList *push_effect(EffectList *, int); + void do_effect_list(int32_t *, int32_t, EffectList *); + void free_effect_list(EffectList *); + void init_filter_lowpass1(filter_lowpass1 *p); + + /* */ + /* System Effect */ + /* */ + /* Reverb Effect */ + void do_ch_reverb(int32_t *, int32_t); + void set_ch_reverb(int32_t *, int32_t, int32_t); + void init_reverb(void); + void do_ch_reverb_xg(int32_t *, int32_t); + + /* Chorus Effect */ + void do_ch_chorus(int32_t *, int32_t); + void set_ch_chorus(int32_t *, int32_t, int32_t); + void init_ch_chorus(void); + void do_ch_chorus_xg(int32_t *, int32_t); + + /* Delay (Celeste) Effect */ + void do_ch_delay(int32_t *, int32_t); + void set_ch_delay(int32_t *, int32_t, int32_t); + void init_ch_delay(void); + + /* EQ */ + void init_eq_gs(void); + void set_ch_eq_gs(int32_t *, int32_t); + void do_ch_eq_gs(int32_t *, int32_t); + void do_ch_eq_xg(int32_t *, int32_t, struct part_eq_xg *); + void do_multi_eq_xg(int32_t *, int32_t); + + // These get accessed directly by the player. + struct multi_eq_xg_t multi_eq_xg; + pink_noise global_pink_noise_light; + struct insertion_effect_gs_t insertion_effect_gs; + struct effect_xg_t insertion_effect_xg[XG_INSERTION_EFFECT_NUM], + variation_effect_xg[XG_VARIATION_EFFECT_NUM], reverb_status_xg, chorus_status_xg; + + static const struct effect_parameter_gs_t effect_parameter_gs[]; + static const struct effect_parameter_xg_t effect_parameter_xg[]; + + void init_for_effect() + { + init_pink_noise(&global_pink_noise_light); + init_reverb(); + init_ch_delay(); + init_ch_chorus(); + init_eq_gs(); + } + + + struct reverb_status_gs_t reverb_status_gs; + //struct chorus_text_gs_t chorus_text_gs; + struct chorus_status_gs_t chorus_status_gs; + struct delay_status_gs_t delay_status_gs; + struct eq_status_gs_t eq_status_gs; + + + ////////////////////////////////// from readmidi + + void init_delay_status_gs(void); + void recompute_delay_status_gs(void); + void set_delay_macro_gs(int macro); + void init_reverb_status_gs(void); + void recompute_reverb_status_gs(void); + void set_reverb_macro_gm2(int macro); + void set_reverb_macro_gs(int macro); + void init_chorus_status_gs(void); + void recompute_chorus_status_gs(); + void set_chorus_macro_gs(int macro); + void init_eq_status_gs(void); + void recompute_eq_status_gs(void); + void init_multi_eq_xg(void); + void set_multi_eq_type_xg(int type); + void recompute_multi_eq_xg(void); + void set_effect_param_xg(struct effect_xg_t *st, int type_msb, int type_lsb); + void recompute_effect_xg(struct effect_xg_t *st); + void realloc_effect_xg(struct effect_xg_t *st); + void init_effect_xg(struct effect_xg_t *st); + void init_all_effect_xg(void); + void init_insertion_effect_gs(void); + void set_effect_param_gs(struct insertion_effect_gs_t *st, int msb, int lsb); + void recompute_insertion_effect_gs(void); + void realloc_insertion_effect_gs(void); + void init_effect_status(int play_system_mode); + +}; + +} +#endif /* ___REVERB_H_ */ diff --git a/src/sound/timidity++/sbkconv.cpp b/src/sound/timidity++/sbkconv.cpp new file mode 100644 index 0000000000..e2a81bcec4 --- /dev/null +++ b/src/sound/timidity++/sbkconv.cpp @@ -0,0 +1,210 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*================================================================ + * SBK --> SF2 Conversion + * + * Copyright (C) 1996,1997 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *================================================================*/ + +#include +#include +#include "timidity.h" +#include "common.h" +#include "sffile.h" +#include "sfitem.h" + + +namespace TimidityPlus +{ + +/*---------------------------------------------------------------- + * prototypes + *----------------------------------------------------------------*/ + +static int sbk_cutoff(int gen, int val); +static int sbk_filterQ(int gen, int val); +static int sbk_tenpct(int gen, int val); +static int sbk_panpos(int gen, int val); +static int sbk_atten(int gen, int val); +static int sbk_scale(int gen, int val); +static int sbk_time(int gen, int val); +static int sbk_tm_key(int gen, int val); +static int sbk_freq(int gen, int val); +static int sbk_pshift(int gen, int val); +static int sbk_cshift(int gen, int val); +static int sbk_tremolo(int gen, int val); +static int sbk_volsust(int gen, int val); +static int sbk_modsust(int gen, int val); + +/*---------------------------------------------------------------- + * convertor function table + *----------------------------------------------------------------*/ + +static SBKConv sbk_convertors[T_EOT] = { + NULL, NULL, NULL, NULL, NULL, + + sbk_cutoff, sbk_filterQ, sbk_tenpct, sbk_panpos, sbk_atten, sbk_scale, + + sbk_time, sbk_tm_key, sbk_freq, sbk_pshift, sbk_cshift, + sbk_tremolo, sbk_modsust, sbk_volsust, +}; + + +/*---------------------------------------------------------------- + * sbk --> sf2 conversion + *----------------------------------------------------------------*/ + +int sbk_to_sf2(int oper, int amount, const LayerItem *layer_items) +{ + const LayerItem *item = &layer_items[oper]; + if (item->type < 0 || item->type >= T_EOT) { + fprintf(stderr, "illegal gen item type %d\n", item->type); + return amount; + } + if (sbk_convertors[item->type]) + return sbk_convertors[item->type](oper, amount); + return amount; +} + +/*---------------------------------------------------------------- + * conversion rules for each type + *----------------------------------------------------------------*/ + +/* initial cutoff */ +static int sbk_cutoff(int gen, int val) +{ + if (val == 127) + return 14400; + else + return 59 * val + 4366; + /*return 50 * val + 4721;*/ +} + +/* initial resonance */ +static int sbk_filterQ(int gen, int val) +{ + return val * 3 / 2; +} + +/* chorus/reverb */ +static int sbk_tenpct(int gen, int val) +{ + return val * 1000 / 256; +} + +/* pan position */ +static int sbk_panpos(int gen, int val) +{ + return val * 1000 / 127 - 500; +} + +/* initial attenuation */ +static int sbk_atten(int gen, int val) +{ + if (val == 0) + return 1000; + return (int)(-200.0 * log10((double)val / 127.0) * 10); +} + +/* scale tuning */ +static int sbk_scale(int gen, int val) +{ + return (val ? 50 : 100); +} + +/* env/lfo time parameter */ +static int sbk_time(int gen, int val) +{ + if (val <= 0) val = 1; + return (int)(log((double)val / 1000.0) / log(2.0) * 1200.0); +} + +/* time change per key */ +static int sbk_tm_key(int gen, int val) +{ + return (int)(val * 5.55); +} + +/* lfo frequency */ +static int sbk_freq(int gen, int val) +{ + if (val == 0) { + if (gen == SF_freqLfo1) return -725; + else /* SF_freqLfo2*/ return -15600; + } + /*return (int)(3986.0 * log10((double)val) - 7925.0);*/ + return (int)(1200 * log10((double)val) / log10(2.0) - 7925.0); + +} + +/* lfo/env pitch shift */ +static int sbk_pshift(int gen, int val) +{ + return (1200 * val / 64 + 1) / 2; +} + +/* lfo/env cutoff freq shift */ +static int sbk_cshift(int gen, int val) +{ + if (gen == SF_lfo1ToFilterFc) + return (1200 * 3 * val) / 64; + else + return (1200 * 6 * val) / 64; +} + +/* lfo volume shift */ +static int sbk_tremolo(int gen, int val) +{ + return (120 * val) / 64; +} + +/* mod env sustain */ +static int sbk_modsust(int gen, int val) +{ + if (val < 96) + return 1000 * (96 - val) / 96; + else + return 0; +} + +/* vol env sustain */ +static int sbk_volsust(int gen, int val) +{ + if (val < 96) + return (2000 - 21 * val) / 2; + else + return 0; +} +} \ No newline at end of file diff --git a/src/sound/timidity++/sffile.cpp b/src/sound/timidity++/sffile.cpp new file mode 100644 index 0000000000..f8d739e002 --- /dev/null +++ b/src/sound/timidity++/sffile.cpp @@ -0,0 +1,736 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*================================================================ + * sffile.c + * read SoundFont file (SBK/SF2) and store the layer lists + * + * Copyright (C) 1996,1997 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *================================================================*/ + +/* + * Modified by Masanao Izumo + */ + +#include +#include +#include +#include "timidity.h" +#include "common.h" +#include "instrum.h" + +namespace TimidityPlus +{ + +/*================================================================ + * preset / instrument bag record + *================================================================*/ + + + /*---------------------------------------------------------------- + * function prototypes + *----------------------------------------------------------------*/ + +#define NEW(type,nums) (type*)safe_malloc(sizeof(type) * (nums)) + +static int READCHUNK(SFChunk *vp, struct timidity_file *tf) +{ + if (tf_read(vp, 8, 1, tf) != 1) + return -1; + vp->size = LE_LONG(vp->size); + return 1; +} + +static int READDW(uint32_t *vp, struct timidity_file *tf) +{ + if (tf_read(vp, 4, 1, tf) != 1) + return -1; + *vp = LE_LONG(*vp); + return 1; +} + +static int READW(uint16_t *vp, struct timidity_file *tf) +{ + if (tf_read(vp, 2, 1, tf) != 1) + return -1; + *vp = LE_SHORT(*vp); + return 1; +} + +static int READSTR(char *str, struct timidity_file *tf) +{ + int n; + + if (tf_read(str, 20, 1, tf) != 1) + return -1; + str[19] = '\0'; + n = (int)strlen(str); + while (n > 0 && str[n - 1] == ' ') + n--; + str[n] = '\0'; + return n; +} + +#define READID(var,tf) tf_read(var, 4, 1, tf) +#define READB(var,tf) tf_read(&var, 1, 1, tf) +#define SKIPB(tf) skip(tf, 1) +#define SKIPW(tf) skip(tf, 2) +#define SKIPDW(tf) skip(tf, 4) + +#define FSKIP(size,tf) skip(tf, size) + + +/*----------------------------------------------------------------*/ + +/*---------------------------------------------------------------- + * id numbers + *----------------------------------------------------------------*/ + +enum { + /* level 0; chunk */ + UNKN_ID, RIFF_ID, LIST_ID, SFBK_ID, + /* level 1; id only */ + INFO_ID, SDTA_ID, PDTA_ID, + /* info stuff; chunk */ + IFIL_ID, ISNG_ID, IROM_ID, INAM_ID, IVER_ID, IPRD_ID, ICOP_ID, + ICRD_ID, IENG_ID, ISFT_ID, ICMT_ID, + /* sample data stuff; chunk */ + SNAM_ID, SMPL_ID, + /* preset stuff; chunk */ + PHDR_ID, PBAG_ID, PMOD_ID, PGEN_ID, + /* inst stuff; chunk */ + INST_ID, IBAG_ID, IMOD_ID, IGEN_ID, + /* sample header; chunk */ + SHDR_ID +}; + + +/*================================================================ + * load a soundfont file + *================================================================*/ + +int Instruments::load_soundfont(SFInfo *sf, struct timidity_file *fd) +{ + SFChunk chunk; + + sf->preset = NULL; + sf->sample = NULL; + sf->inst = NULL; + sf->sf_name = NULL; + + prbags.bag = inbags.bag = NULL; + prbags.gen = inbags.gen = NULL; + prbags.nbags = inbags.nbags = + prbags.ngens = inbags.ngens = 0; + + /* check RIFF file header */ + READCHUNK(&chunk, fd); + if (chunkid(chunk.id) != RIFF_ID) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: *** not a RIFF file", fd->filename.c_str()); + return -1; + } + /* check file id */ + READID(chunk.id, fd); + if (chunkid(chunk.id) != SFBK_ID) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "%s: *** not a SoundFont file", fd->filename.c_str()); + return -1; + } + + for (;;) { + if (READCHUNK(&chunk, fd) <= 0) + break; + else if (chunkid(chunk.id) == LIST_ID) { + if (process_list(chunk.size, sf, fd)) + break; + } + else { + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, + "%s: *** illegal id in level 0: %4.4s %4d", + fd->filename.c_str(), chunk.id, chunk.size); + FSKIP(chunk.size, fd); + } + } + + /* parse layer structure */ + convert_layers(sf); + + /* free private tables */ + if (prbags.bag) free(prbags.bag); + if (prbags.gen) free(prbags.gen); + if (inbags.bag) free(inbags.bag); + if (inbags.gen) free(inbags.gen); + + return 0; +} + + +/*================================================================ + * free buffer + *================================================================*/ + +void Instruments::free_soundfont(SFInfo *sf) +{ + int i; + if (sf->preset) { + for (i = 0; i < sf->npresets; i++) + free_layer(&sf->preset[i].hdr); + free(sf->preset); + } + if (sf->inst) { + for (i = 0; i < sf->ninsts; i++) + free_layer(&sf->inst[i].hdr); + free(sf->inst); + } + if (sf->sample) free(sf->sample); + if (sf->sf_name) free(sf->sf_name); +} + + +/*---------------------------------------------------------------- + * get id value from 4bytes ID string + *----------------------------------------------------------------*/ + +int Instruments::chunkid(char *id) +{ + static struct idstring { + const char *str; + int id; + } idlist[] = { + {"RIFF", RIFF_ID}, + {"LIST", LIST_ID}, + {"sfbk", SFBK_ID}, + {"INFO", INFO_ID}, + {"sdta", SDTA_ID}, + {"snam", SNAM_ID}, + {"smpl", SMPL_ID}, + {"pdta", PDTA_ID}, + {"phdr", PHDR_ID}, + {"pbag", PBAG_ID}, + {"pmod", PMOD_ID}, + {"pgen", PGEN_ID}, + {"inst", INST_ID}, + {"ibag", IBAG_ID}, + {"imod", IMOD_ID}, + {"igen", IGEN_ID}, + {"shdr", SHDR_ID}, + {"ifil", IFIL_ID}, + {"isng", ISNG_ID}, + {"irom", IROM_ID}, + {"iver", IVER_ID}, + {"INAM", INAM_ID}, + {"IPRD", IPRD_ID}, + {"ICOP", ICOP_ID}, + {"ICRD", ICRD_ID}, + {"IENG", IENG_ID}, + {"ISFT", ISFT_ID}, + {"ICMT", ICMT_ID}, + }; + + int i; + + for (i = 0; i < sizeof(idlist) / sizeof(idlist[0]); i++) { + if (strncmp(id, idlist[i].str, 4) == 0) + return idlist[i].id; + } + + return UNKN_ID; +} + + +/*================================================================ + * process a list chunk + *================================================================*/ + +int Instruments::process_list(int size, SFInfo *sf, struct timidity_file *fd) +{ + SFChunk chunk; + + /* read the following id string */ + READID(chunk.id, fd); size -= 4; + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "%c%c%c%c:", + chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]); + switch (chunkid(chunk.id)) { + case INFO_ID: + return process_info(size, sf, fd); + case SDTA_ID: + return process_sdta(size, sf, fd); + case PDTA_ID: + return process_pdta(size, sf, fd); + default: + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, + "%s: *** illegal id in level 1: %4.4s", + fd->filename.c_str(), chunk.id); + FSKIP(size, fd); /* skip it */ + return 0; + } +} + + +/*================================================================ + * process info list + *================================================================*/ + +int Instruments::process_info(int size, SFInfo *sf, struct timidity_file *fd) +{ + sf->infopos = tf_tell(fd); + sf->infosize = size; + + /* parse the buffer */ + while (size > 0) { + SFChunk chunk; + + /* read a sub chunk */ + if (READCHUNK(&chunk, fd) <= 0) + return -1; + size -= 8; + + ctl_cmsg(CMSG_INFO, VERB_DEBUG, " %c%c%c%c:", + chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]); + switch (chunkid(chunk.id)) { + case IFIL_ID: + /* soundfont file version */ + READW(&sf->version, fd); + READW(&sf->minorversion, fd); + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + " version %d, minor %d", + sf->version, sf->minorversion); + break; + case INAM_ID: + /* name of the font */ + sf->sf_name = (char*)safe_malloc(chunk.size + 1); + tf_read(sf->sf_name, 1, chunk.size, fd); + sf->sf_name[chunk.size] = 0; + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + " name %s", sf->sf_name); + break; + + default: + break; + } + size -= chunk.size; + } + return 0; +} + + +/*================================================================ + * process sample data list + *================================================================*/ + +int Instruments::process_sdta(int size, SFInfo *sf, struct timidity_file *fd) +{ + while (size > 0) { + SFChunk chunk; + + /* read a sub chunk */ + if (READCHUNK(&chunk, fd) <= 0) + return -1; + size -= 8; + + ctl_cmsg(CMSG_INFO, VERB_DEBUG, " %c%c%c%c:", + chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]); + switch (chunkid(chunk.id)) { + case SNAM_ID: + /* sample name list */ + load_sample_names(chunk.size, sf, fd); + break; + case SMPL_ID: + /* sample data starts from here */ + sf->samplepos = tf_tell(fd); + sf->samplesize = chunk.size; + FSKIP(chunk.size, fd); + break; + default: + FSKIP(chunk.size, fd); + break; + } + size -= chunk.size; + } + return 0; +} + + +/*================================================================ + * process preset data list + *================================================================*/ + +int Instruments::process_pdta(int size, SFInfo *sf, struct timidity_file *fd) +{ + while (size > 0) { + SFChunk chunk; + + /* read a subchunk */ + if (READCHUNK(&chunk, fd) <= 0) + return -1; + size -= 8; + + ctl_cmsg(CMSG_INFO, VERB_DEBUG, " %c%c%c%c:", + chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]); + switch (chunkid(chunk.id)) { + case PHDR_ID: + load_preset_header(chunk.size, sf, fd); + break; + case PBAG_ID: + load_bag(chunk.size, &prbags, fd); + break; + case PGEN_ID: + load_gen(chunk.size, &prbags, fd); + break; + case INST_ID: + load_inst_header(chunk.size, sf, fd); + break; + case IBAG_ID: + load_bag(chunk.size, &inbags, fd); + break; + case IGEN_ID: + load_gen(chunk.size, &inbags, fd); + break; + case SHDR_ID: + load_sample_info(chunk.size, sf, fd); + break; + case PMOD_ID: /* ignored */ + case IMOD_ID: /* ingored */ + default: + FSKIP(chunk.size, fd); + break; + } + size -= chunk.size; + } + return 0; +} + + +/*---------------------------------------------------------------- + * store sample name list; sf1 only + *----------------------------------------------------------------*/ + +void Instruments::load_sample_names(int size, SFInfo *sf, struct timidity_file *fd) +{ + int i, nsamples; + if (sf->version > 1) { + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, + "%s: *** version 2 has obsolete format??", + fd->filename.c_str()); + FSKIP(size, fd); + return; + } + + /* each sample name has a fixed lentgh (20 bytes) */ + nsamples = size / 20; + if (sf->sample == NULL) { + sf->nsamples = nsamples; + sf->sample = NEW(SFSampleInfo, sf->nsamples); + } + else if (sf->nsamples != nsamples) { + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, + "%s: *** different # of samples ?? (%d : %d)\n", + fd->filename.c_str(), sf->nsamples, nsamples); + FSKIP(size, fd); + return; + } + + /* read each name from file */ + for (i = 0; i < sf->nsamples; i++) { + READSTR(sf->sample[i].name, fd); + } +} + + +/*---------------------------------------------------------------- + * preset header list + *----------------------------------------------------------------*/ + +void Instruments::load_preset_header(int size, SFInfo *sf, struct timidity_file *fd) +{ + int i; + + sf->npresets = size / 38; + sf->preset = NEW(SFPresetHdr, sf->npresets); + for (i = 0; i < sf->npresets; i++) { + READSTR(sf->preset[i].hdr.name, fd); + READW(&sf->preset[i].preset, fd); + READW(&sf->preset[i].bank, fd); + READW(&sf->preset[i].hdr.bagNdx, fd); + SKIPDW(fd); /* lib; ignored*/ + SKIPDW(fd); /* genre; ignored */ + SKIPDW(fd); /* morph; ignored */ + /* initialize layer table; it'll be parsed later */ + sf->preset[i].hdr.nlayers = 0; + sf->preset[i].hdr.layer = NULL; + } +} + + +/*---------------------------------------------------------------- + * instrument header list + *----------------------------------------------------------------*/ + +void Instruments::load_inst_header(int size, SFInfo *sf, struct timidity_file *fd) +{ + int i; + + sf->ninsts = size / 22; + sf->inst = NEW(SFInstHdr, sf->ninsts); + for (i = 0; i < sf->ninsts; i++) { + READSTR(sf->inst[i].hdr.name, fd); + READW(&sf->inst[i].hdr.bagNdx, fd); + /* iniitialize layer table; it'll be parsed later */ + sf->inst[i].hdr.nlayers = 0; + sf->inst[i].hdr.layer = NULL; + + ctl_cmsg(CMSG_INFO, VERB_DEBUG, + " InstHdr %d (%s) bagNdx=%d", + i, sf->inst[i].hdr.name, sf->inst[i].hdr.bagNdx); + } +} + + +/*---------------------------------------------------------------- + * load preset/instrument bag list on the private table + *----------------------------------------------------------------*/ + +void Instruments::load_bag(int size, SFBags *bagp, struct timidity_file *fd) +{ + int i; + + size /= 4; + bagp->bag = NEW(uint16_t, size); + for (i = 0; i < size; i++) { + READW(&bagp->bag[i], fd); + SKIPW(fd); /* mod; ignored */ + } + bagp->nbags = size; +} + + +/*---------------------------------------------------------------- + * load preset/instrument generator list on the private table + *----------------------------------------------------------------*/ + +void Instruments::load_gen(int size, SFBags *bagp, struct timidity_file *fd) +{ + int i; + + size /= 4; + bagp->gen = NEW(SFGenRec, size); + for (i = 0; i < size; i++) { + READW((uint16_t *)&bagp->gen[i].oper, fd); + READW((uint16_t *)&bagp->gen[i].amount, fd); + } + bagp->ngens = size; +} + + +/*---------------------------------------------------------------- + * load sample info list + *----------------------------------------------------------------*/ + +void Instruments::load_sample_info(int size, SFInfo *sf, struct timidity_file *fd) +{ + int i; + int in_rom; + + /* the record size depends on the soundfont version */ + if (sf->version > 1) { + /* SF2 includes sample name and other infos */ + sf->nsamples = size / 46; + sf->sample = NEW(SFSampleInfo, sf->nsamples); + } + else { + /* SBK; sample name may be read already */ + int nsamples = size / 16; + if (sf->sample == NULL) { + sf->nsamples = nsamples; + sf->sample = NEW(SFSampleInfo, sf->nsamples); + } + else if (sf->nsamples != nsamples) { + /* overwrite it */ + sf->nsamples = nsamples; + } + } + + in_rom = 1; /* data may start from ROM samples */ + for (i = 0; i < sf->nsamples; i++) { + if (sf->version > 1) /* SF2 only */ + READSTR(sf->sample[i].name, fd); + READDW((uint32_t *)&sf->sample[i].startsample, fd); + READDW((uint32_t *)&sf->sample[i].endsample, fd); + READDW((uint32_t *)&sf->sample[i].startloop, fd); + READDW((uint32_t *)&sf->sample[i].endloop, fd); + if (sf->version > 1) { /* SF2 only */ + READDW((uint32_t *)&sf->sample[i].samplerate, fd); + READB(sf->sample[i].originalPitch, fd); + READB(sf->sample[i].pitchCorrection, fd); + READW(&sf->sample[i].samplelink, fd); + READW(&sf->sample[i].sampletype, fd); + } + else { /* for SBK; set missing infos */ + sf->sample[i].samplerate = 44100; + sf->sample[i].originalPitch = 60; + sf->sample[i].pitchCorrection = 0; + sf->sample[i].samplelink = 0; + /* the first RAM data starts from address 0 */ + if (sf->sample[i].startsample == 0) + in_rom = 0; + if (in_rom) + sf->sample[i].sampletype = 0x8001; + else + sf->sample[i].sampletype = 1; + } + } +} + + +/*================================================================ + * convert from bags to layers + *================================================================*/ + +void Instruments::convert_layers(SFInfo *sf) +{ + int i; + + if (prbags.bag == NULL || prbags.gen == NULL || + inbags.bag == NULL || inbags.gen == NULL) { + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, + "%s: *** illegal bags / gens", sf->sf_name); + return; + } + + for (i = 0; i < sf->npresets - 1; i++) { + generate_layers(&sf->preset[i].hdr, + &sf->preset[i + 1].hdr, + &prbags); + } + for (i = 0; i < sf->ninsts - 1; i++) { + generate_layers(&sf->inst[i].hdr, + &sf->inst[i + 1].hdr, + &inbags); + } +} + + +/*---------------------------------------------------------------- + * generate layer lists from stored bags + *----------------------------------------------------------------*/ + +void Instruments::generate_layers(SFHeader *hdr, SFHeader *next, SFBags *bags) +{ + int i; + SFGenLayer *layp; + + hdr->nlayers = next->bagNdx - hdr->bagNdx; + if (hdr->nlayers < 0) { + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, + "%s: illegal layer numbers %d", + "", hdr->nlayers); + return; + } + if (hdr->nlayers == 0) + return; + hdr->layer = (SFGenLayer*)safe_malloc(sizeof(SFGenLayer) * hdr->nlayers); + layp = hdr->layer; + for (layp = hdr->layer, i = hdr->bagNdx; i < next->bagNdx; layp++, i++) { + int genNdx = bags->bag[i]; + layp->nlists = bags->bag[i + 1] - genNdx; + if (layp->nlists < 0) { + ctl_cmsg(CMSG_WARNING, VERB_NORMAL, + "%s: illegal list numbers %d", + "", layp->nlists); + return; + } + layp->list = (SFGenRec*)safe_malloc(sizeof(SFGenRec) * layp->nlists); + memcpy(layp->list, &bags->gen[genNdx], + sizeof(SFGenRec) * layp->nlists); + } +} + +/*---------------------------------------------------------------- + * free a layer + *----------------------------------------------------------------*/ + +void Instruments::free_layer(SFHeader *hdr) +{ + int i; + for (i = 0; i < hdr->nlayers; i++) { + SFGenLayer *layp = &hdr->layer[i]; + if (layp->nlists >= 0) + free(layp->list); + } + if (hdr->nlayers > 0) + free(hdr->layer); +} + +/* add blank loop for each data */ +static const int auto_add_blank = 0; +void Instruments::correct_samples(SFInfo *sf) +{ + int i; + SFSampleInfo *sp; + int prev_end; + + prev_end = 0; + for (sp = sf->sample, i = 0; i < sf->nsamples; i++, sp++) { + /* correct sample positions for SBK file */ + if (sf->version == 1) { + sp->startloop++; + sp->endloop += 2; + } + + /* calculate sample data size */ + if (sp->sampletype & 0x8000) + sp->size = 0; + else if (sp->startsample < prev_end && sp->startsample != 0) + sp->size = 0; + else { + sp->size = -1; + if (!auto_add_blank && i != sf->nsamples - 1) + sp->size = sp[1].startsample - sp->startsample; + if (sp->size < 0) + sp->size = sp->endsample - sp->startsample + 48; + } + prev_end = sp->endsample; + + /* calculate short-shot loop size */ + if (auto_add_blank || i == sf->nsamples - 1) + sp->loopshot = 48; + else { + sp->loopshot = sp[1].startsample - sp->endsample; + if (sp->loopshot < 0 || sp->loopshot > 48) + sp->loopshot = 48; + } + } +} +} \ No newline at end of file diff --git a/src/sound/timidity++/sffile.h b/src/sound/timidity++/sffile.h new file mode 100644 index 0000000000..ceec1b4d46 --- /dev/null +++ b/src/sound/timidity++/sffile.h @@ -0,0 +1,144 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*================================================================ + * sffile.h + * SoundFont file (SBK/SF2) format defintions + * + * Copyright (C) 1996,1997 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *================================================================*/ + +/* + * Modified by Masanao Izumo + */ + +#ifndef SFFILE_H_DEF +#define SFFILE_H_DEF + +namespace TimidityPlus +{ + +/* chunk record header */ +typedef struct _SFChunk { + char id[4]; + int32_t size; +} SFChunk; + +/* generator record */ +typedef struct _SFGenRec { + int16_t oper; + int16_t amount; +} SFGenRec; + +/* layered generators record */ +typedef struct _SFGenLayer { + int nlists; + SFGenRec *list; +} SFGenLayer; + +/* header record */ +typedef struct _SFHeader { + char name[20]; + uint16_t bagNdx; + /* layered stuff */ + int nlayers; + SFGenLayer *layer; +} SFHeader; + +/* preset header record */ +typedef struct _SFPresetHdr { + SFHeader hdr; + uint16_t preset, bank; + /*int32_t lib, genre, morphology;*/ /* not used */ +} SFPresetHdr; + +/* instrument header record */ +typedef struct _SFInstHdr { + SFHeader hdr; +} SFInstHdr; + +/* sample info record */ +typedef struct _SFSampleInfo { + char name[20]; + int32_t startsample, endsample; + int32_t startloop, endloop; + /* ver.2 additional info */ + int32_t samplerate; + uint8_t originalPitch; + int8_t pitchCorrection; + uint16_t samplelink; + uint16_t sampletype; /*1=mono, 2=right, 4=left, 8=linked, $8000=ROM*/ + /* optional info */ + int32_t size; /* sample size */ + int32_t loopshot; /* short-shot loop size */ +} SFSampleInfo; + + +/*---------------------------------------------------------------- + * soundfont file info record + *----------------------------------------------------------------*/ + +typedef struct _SFInfo { + /* file name */ + char *sf_name; + + /* version of this file */ + uint16_t version, minorversion; + /* sample position (from origin) & total size (in bytes) */ + int32_t samplepos; + int32_t samplesize; + + /* raw INFO chunk list */ + int32_t infopos, infosize; + + /* preset headers */ + int npresets; + SFPresetHdr *preset; + + /* sample infos */ + int nsamples; + SFSampleInfo *sample; + + /* instrument headers */ + int ninsts; + SFInstHdr *inst; + +} SFInfo; + + +/*---------------------------------------------------------------- + * functions + *----------------------------------------------------------------*/ +} +#endif diff --git a/src/sound/timidity++/sfitem.cpp b/src/sound/timidity++/sfitem.cpp new file mode 100644 index 0000000000..1e2173c1d5 --- /dev/null +++ b/src/sound/timidity++/sfitem.cpp @@ -0,0 +1,98 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*================================================================ + * sfitem.c + * soundfont generator table definition + *================================================================*/ + +#include +#include "timidity.h" +#include "common.h" +#include "sflayer.h" +#include "sfitem.h" + +namespace TimidityPlus +{ + +/* layer type definitions */ +const LayerItem static_layer_items[SF_EOF] = { + {L_INHRT, T_OFFSET, 0, 0, 0}, /* startAddrs */ + {L_INHRT, T_OFFSET, 0, 0, 0}, /* endAddrs */ + {L_INHRT, T_OFFSET, 0, 0, 0}, /* startloopAddrs */ + {L_INHRT, T_OFFSET, 0, 0, 0}, /* endloopAddrs */ + {L_INHRT, T_HI_OFF, 0, 0, 0}, /* startAddrsHi */ + {L_INHRT, T_PSHIFT, -12000, 12000, 0}, /* lfo1ToPitch */ + {L_INHRT, T_PSHIFT, -12000, 12000, 0}, /* lfo2ToPitch */ + {L_INHRT, T_PSHIFT, -12000, 12000, 0}, /* env1ToPitch */ + {L_INHRT, T_CUTOFF, 1500, 13500, 13500}, /* initialFilterFc */ + {L_INHRT, T_FILTERQ, 0, 960, 0}, /* initialFilterQ */ + {L_INHRT, T_CSHIFT, -12000, 12000, 0}, /* lfo1ToFilterFc */ + {L_INHRT, T_CSHIFT, -12000, 12000, 0}, /* env1ToFilterFc */ + {L_INHRT, T_HI_OFF, 0, 0, 0}, /* endAddrsHi */ + {L_INHRT, T_TREMOLO, -960, 960, 0}, /* lfo1ToVolume */ + {L_INHRT, T_NOP, 0, 0, 0}, /* env2ToVolume / unused1 */ + {L_INHRT, T_TENPCT, 0, 1000, 0}, /* chorusEffectsSend */ + {L_INHRT, T_TENPCT, 0, 1000, 0}, /* reverbEffectsSend */ + {L_INHRT, T_PANPOS, 0, 1000, 0}, /* panEffectsSend */ + {L_INHRT, T_NOP, 0, 0, 0}, /* unused */ + {L_INHRT, T_NOP, 0, 0, 0}, /* sampleVolume / unused */ + {L_INHRT, T_NOP, 0, 0, 0}, /* unused3 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* delayLfo1 */ + {L_INHRT, T_FREQ, -16000, 4500, 0}, /* freqLfo1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* delayLfo2 */ + {L_INHRT, T_FREQ, -16000, 4500, 0}, /* freqLfo2 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* delayEnv1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* attackEnv1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* holdEnv1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* decayEnv1 */ + {L_INHRT, T_MODSUST, 0, 1000, 0}, /* sustainEnv1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* releaseEnv1 */ + {L_INHRT, T_TM_KEY, -1200, 1200, 0}, /* autoHoldEnv1 */ + {L_INHRT, T_TM_KEY, -1200, 1200, 0}, /* autoDecayEnv1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* delayEnv2 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* attackEnv2 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* holdEnv2 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* decayEnv2 */ + {L_INHRT, T_VOLSUST, 0, 1440, 0}, /* sustainEnv2 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* releaseEnv2 */ + {L_INHRT, T_TM_KEY, -1200, 1200, 0}, /* autoHoldEnv2 */ + {L_INHRT, T_TM_KEY, -1200, 1200, 0}, /* autoDecayEnv2 */ + {L_PRSET, T_NOCONV, 0, 0, 0}, /* instrument */ + {L_INHRT, T_NOP, 0, 0, 0}, /* nop */ + {L_RANGE, T_RANGE, 0, 0, RANGE(0,127)}, /* keyRange */ + {L_RANGE, T_RANGE, 0, 0, RANGE(0,127)}, /* velRange */ + {L_INHRT, T_HI_OFF, 0, 0, 0}, /* startloopAddrsHi */ + {L_OVWRT, T_NOCONV, 0, 127, -1}, /* keynum */ + {L_OVWRT, T_NOCONV, 0, 127, -1}, /* velocity */ + {L_INHRT, T_ATTEN, 0, 1440, 0}, /* initialAttenuation */ + {L_INHRT, T_NOP, 0, 0, 0}, /* keyTuning */ + {L_INHRT, T_HI_OFF, 0, 0, 0}, /* endloopAddrsHi */ + {L_INHRT, T_NOCONV, -120, 120, 0}, /* coarseTune */ + {L_INHRT, T_NOCONV, -99, 99, 0}, /* fineTune */ + {L_INSTR, T_NOCONV, 0, 0, 0}, /* sampleId */ + {L_OVWRT, T_NOCONV, 0, 3, 0}, /* sampleFlags */ + {L_OVWRT, T_NOCONV, 0, 0, 0}, /* samplePitch (only in SBK) */ + {L_INHRT, T_SCALE, 0, 1200, 100}, /* scaleTuning */ + {L_OVWRT, T_NOCONV, 0, 127, 0}, /* keyExclusiveClass */ + {L_OVWRT, T_NOCONV, 0, 127, -1}, /* rootKey */ +}; + +} \ No newline at end of file diff --git a/src/sound/timidity++/sfitem.h b/src/sound/timidity++/sfitem.h new file mode 100644 index 0000000000..ef6b4144db --- /dev/null +++ b/src/sound/timidity++/sfitem.h @@ -0,0 +1,94 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*================================================================ + * sfitem.h + * soundfont generator conversion table + *================================================================*/ + +#ifndef SFITEM_H_DEF +#define SFITEM_H_DEF + +#include "sflayer.h" +#include "sffile.h" + +namespace TimidityPlus +{ + + +typedef struct _LayerItem { + int copy; /* copy policy */ + int type; /* conversion type */ + int minv; /* minimum value */ + int maxv; /* maximum value */ + int defv; /* default value */ +} LayerItem; + +/* copy policy */ +enum { + L_INHRT, /* add to global */ + L_OVWRT, /* overwrite on global */ + L_RANGE, /* range */ + L_PRSET, /* preset only */ + L_INSTR /* instrument only */ +}; + +/* data type */ +enum { + T_NOP, /* nothing */ + T_NOCONV, /* no conversion */ + T_OFFSET, /* address offset */ + T_HI_OFF, /* address coarse offset (32k) */ + T_RANGE, /* range; composite values (0-127/0-127) */ + + T_CUTOFF, /* initial cutoff */ + T_FILTERQ, /* initial resonance */ + T_TENPCT, /* effects send */ + T_PANPOS, /* panning position */ + T_ATTEN, /* initial attenuation */ + T_SCALE, /* scale tuning */ + + T_TIME, /* envelope/LFO time */ + T_TM_KEY, /* time change per key */ + T_FREQ, /* LFO frequency */ + T_PSHIFT, /* env/LFO pitch shift */ + T_CSHIFT, /* env/LFO cutoff shift */ + T_TREMOLO, /* LFO tremolo shift */ + T_MODSUST, /* modulation env sustain level */ + T_VOLSUST, /* volume env sustain level */ + + T_EOT /* end of type */ +}; + +/* sbk->sf2 convertor function */ +typedef int (*SBKConv)(int gen, int amount); + +/* macros for range operation */ +#define RANGE(lo,hi) (((hi)&0xff) << 8 | ((lo)&0xff)) +#define LOWNUM(val) ((val) & 0xff) +#define HIGHNUM(val) (((val) >> 8) & 0xff) + +/* layer type definitions */ +extern const LayerItem static_layer_items[SF_EOF]; + +extern int sbk_to_sf2(int oper, int amount, const LayerItem *); + +} +#endif diff --git a/src/sound/timidity++/sflayer.h b/src/sound/timidity++/sflayer.h new file mode 100644 index 0000000000..4119cf480f --- /dev/null +++ b/src/sound/timidity++/sflayer.h @@ -0,0 +1,107 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef ___SFLAYER_H_ +#define ___SFLAYER_H_ + +namespace TimidityPlus +{ + +/*================================================================ + * sflayer.h + * SoundFont layer structure + *================================================================*/ + +enum { + SF_startAddrs, /* 0 sample start address -4 (0to*0xffffff)*/ + SF_endAddrs, /* 1 */ + SF_startloopAddrs, /* 2 loop start address -4 (0 to * 0xffffff) */ + SF_endloopAddrs, /* 3 loop end address -3 (0 to * 0xffffff) */ + SF_startAddrsHi, /* 4 high word of startAddrs */ + SF_lfo1ToPitch, /* 5 main fm: lfo1-> pitch */ + SF_lfo2ToPitch, /* 6 aux fm: lfo2-> pitch */ + SF_env1ToPitch, /* 7 pitch env: env1(aux)-> pitch */ + SF_initialFilterFc, /* 8 initial filter cutoff */ + SF_initialFilterQ, /* 9 filter Q */ + SF_lfo1ToFilterFc, /* 10 filter modulation: lfo1->filter*cutoff */ + SF_env1ToFilterFc, /* 11 filter env: env1(aux)->filter * cutoff */ + SF_endAddrsHi, /* 12 high word of endAddrs */ + SF_lfo1ToVolume, /* 13 tremolo: lfo1-> volume */ + SF_env2ToVolume, /* 14 Env2Depth: env2-> volume */ + SF_chorusEffectsSend, /* 15 chorus */ + SF_reverbEffectsSend, /* 16 reverb */ + SF_panEffectsSend, /* 17 pan */ + SF_auxEffectsSend, /* 18 pan auxdata (internal) */ + SF_sampleVolume, /* 19 used internally */ + SF_unused3, /* 20 */ + SF_delayLfo1, /* 21 delay 0x8000-n*(725us) */ + SF_freqLfo1, /* 22 frequency */ + SF_delayLfo2, /* 23 delay 0x8000-n*(725us) */ + SF_freqLfo2, /* 24 frequency */ + SF_delayEnv1, /* 25 delay 0x8000 - n(725us) */ + SF_attackEnv1, /* 26 attack */ + SF_holdEnv1, /* 27 hold */ + SF_decayEnv1, /* 28 decay */ + SF_sustainEnv1, /* 29 sustain */ + SF_releaseEnv1, /* 30 release */ + SF_autoHoldEnv1, /* 31 */ + SF_autoDecayEnv1, /* 32 */ + SF_delayEnv2, /* 33 delay 0x8000 - n(725us) */ + SF_attackEnv2, /* 34 attack */ + SF_holdEnv2, /* 35 hold */ + SF_decayEnv2, /* 36 decay */ + SF_sustainEnv2, /* 37 sustain */ + SF_releaseEnv2, /* 38 release */ + SF_autoHoldEnv2, /* 39 */ + SF_autoDecayEnv2, /* 40 */ + SF_instrument, /* 41 */ + SF_nop, /* 42 */ + SF_keyRange, /* 43 */ + SF_velRange, /* 44 */ + SF_startloopAddrsHi, /* 45 high word of startloopAddrs */ + SF_keynum, /* 46 */ + SF_velocity, /* 47 */ + SF_initAtten, /* 48 */ + SF_keyTuning, /* 49 */ + SF_endloopAddrsHi, /* 50 high word of endloopAddrs */ + SF_coarseTune, /* 51 */ + SF_fineTune, /* 52 */ + SF_sampleId, /* 53 */ + SF_sampleFlags, /* 54 */ + SF_samplePitch, /* 55 SF1 only */ + SF_scaleTuning, /* 56 */ + SF_keyExclusiveClass, /* 57 */ + SF_rootKey, /* 58 */ + SF_EOF /* 59 */ +}; + + +/*---------------------------------------------------------------- + * layer value table + *----------------------------------------------------------------*/ + +typedef struct _LayerTable { + short val[SF_EOF]; + char set[SF_EOF]; +} LayerTable; + + +} +#endif /* ___SFLAYER_H_ */ diff --git a/src/sound/timidity++/smplfile.cpp b/src/sound/timidity++/smplfile.cpp new file mode 100644 index 0000000000..cc556bf4c5 --- /dev/null +++ b/src/sound/timidity++/smplfile.cpp @@ -0,0 +1,1228 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + smplfile.c + + core and WAVE,AIFF/AIFF-C importer by Kentaro Sato +*/ + +#include +#include + +#include + +#include "timidity.h" +#include "common.h" +#include "filter.h" +#include "instrum.h" +#include "playmidi.h" +#include "resample.h" +#include "tables.h" + +namespace TimidityPlus +{ + +typedef int (Instruments::*SampleImporterDiscriminateProc)(char *sample_file); + /* returns 0 if file may be loadable */ +typedef int (Instruments::*SampleImporterSampleLoaderProc)(char *sample_file, Instrument *inst); + /* sets inst->samples, inst->sample and returns 0 if loaded */ + /* inst is pre-allocated, and is freed by caller if loading failed */ + /* -1 to let caller give up testing other importers */ + +struct SampleImporter { + const char *extension; /* file extension excluding '.' */ + SampleImporterDiscriminateProc discriminant; + SampleImporterSampleLoaderProc load; + /* either extension or discriminant may be NULL */ + int added; /* for get_importers()'s internal use */ +}; + +static double ConvertFromIeeeExtended(const char *); + +Instrument *Instruments::extract_sample_file(char *sample_file) +{ + Instrument *inst; + SampleImporter *importers[10], *importer; + int i, j, count, result; + Sample *sample; + + if ((count = get_importers(sample_file, sizeof importers / sizeof importers[0], importers)) == 0) + return NULL; + inst = (Instrument *)safe_malloc(sizeof(Instrument)); + inst->type = INST_PCM; + inst->instname = NULL; + inst->samples = 0; + inst->sample = NULL; + i = 0; + importer = NULL; + while ((i = get_next_importer(sample_file, i, count, importers)) < count) + { + if ((result = (this->*(importers[i]->load))(sample_file, inst)) == 0) + { + importer = importers[i]; + break; + } + if (result == -1) /* importer told to give up test */ + break; + j = inst->samples; + while(j > 0) + { + if (inst->sample[--j].data_alloced) + free(inst->sample[j].data); + } + inst->samples = 0; + free(inst->sample); + inst->sample = NULL; + i++; /* try next */ + } + if (importer == NULL) + { + free_instrument(inst); + return NULL; + } + /* post-process */ + if (inst->instname == NULL) + { + const char *name; + + name = strrchr(sample_file, '/'); + if (name == NULL) + name = sample_file - 1; + inst->instname = strdup(name + 1); + } + for(i = 0; i < inst->samples; i++) + { + sample = &inst->sample[i]; + /* If necessary do some anti-aliasing filtering */ + if (antialiasing_allowed) + antialiasing((int16_t *)sample->data, + sample->data_length >> FRACTION_BITS, + sample->sample_rate, playback_rate); + /* resample it if possible */ + if (sample->note_to_use && !(sample->modes & MODES_LOOPING)) + pre_resample(sample); + } + return inst; +} + +#define ADD_IMPORTER importer->added = 1; \ + importers[count++] = importer; + +/* returns number of importers which may be suitable for the file */ +int Instruments::get_importers(const char *sample_file, int limit, SampleImporter **importers) +{ + static SampleImporter sample_importers[] = { + {"wav", &Instruments::import_wave_discriminant, &Instruments::import_wave_load}, + {"aiff", &Instruments::import_aiff_discriminant, &Instruments::import_aiff_load}, + {NULL, NULL, NULL}, + }; + + SampleImporter *importer; + int count; + const char *extension; + + count = 0; + importer = sample_importers; + while(importer->load != NULL && count < limit) + { + importer->added = 0; + importer++; + } + /* first, extension matched importers */ + extension = strrchr(sample_file, '/'); + if (extension != NULL && (extension = strrchr(extension, '.')) != NULL) + { + extension++; + /* ones which have discriminant first */ + importer = sample_importers; + while(importer->load != NULL && count < limit) + { + if (!importer->added && importer->extension != NULL && importer->discriminant != NULL + && strcasecmp(extension, importer->extension) == 0) + {ADD_IMPORTER} + importer++; + } + /* then ones which don't have discriminant */ + importer = sample_importers; + while(importer->load != NULL && count < limit) + { + if (!importer->added && importer->extension != NULL + && importer->discriminant == NULL + && strcasecmp(extension, importer->extension) == 0) + {ADD_IMPORTER} + importer++; + } + } + /* lastly, ones which has discriminant */ + importer = sample_importers; + while(importer->load != NULL && count < limit) + { + if (!importer->added && importer->discriminant != NULL) + {ADD_IMPORTER} + importer++; + } + return count; +} + +/* returns importer index for the file */ +/* returns count if no importer available */ +int Instruments::get_next_importer(char *sample_file, int start, int count, SampleImporter **importers) +{ + int i; + + for(i = start; i < count; i++) + { + if (importers[i]->discriminant != NULL) + { + if ((this->*(importers[i]->discriminant))(sample_file) != 0) + continue; + } + return i; + } + return i; +} + +/*************** Sample Importers ***************/ + +#define MAX_SAMPLE_CHANNELS 16 + +/* from instrum.c */ +#define READ_CHAR(thing) \ + if (1 != tf_read(&tmpchar, 1, 1, tf)) goto fail; \ + thing = tmpchar; + +#define READ_SHORT_LE(thing) \ + if (1 != tf_read(&tmpshort, 2, 1, tf)) goto fail; \ + thing = LE_SHORT(tmpshort); +#define READ_LONG_LE(thing) \ + if (1 != tf_read(&tmplong, 4, 1, tf)) goto fail; \ + thing = LE_LONG(tmplong); +#define READ_SHORT_BE(thing) \ + if (1 != tf_read(&tmpshort, 2, 1, tf)) goto fail; \ + thing = BE_SHORT(tmpshort); +#define READ_LONG_BE(thing) \ + if (1 != tf_read(&tmplong, 4, 1, tf)) goto fail; \ + thing = BE_LONG(tmplong); + +const uint8_t pan_mono[] = {64}; /* center */ +const uint8_t pan_stereo[] = {1,127}; /* left,right */ +const uint8_t pan_3ch[] = {1,127,64}; /* left,right,center*/ +/* pannings below are set by guess */ +/*const uint8_t pan_quad[] = {1,127,16,112};*/ /* front-left?,front-right?,rear-left?,rear-right? */ +const uint8_t pan_4ch[] = {1,64,127,64}; /* left,center,right,surround?*/ +const uint8_t pan_6ch[] = {1,32,64,127,95,64}; /* left,left-center?,center,right,right-center?,surround? */ +const uint8_t *const gen_pan_list[6] = { + pan_mono, pan_stereo, pan_3ch, + pan_4ch, NULL, pan_6ch, +}; + +typedef struct { + uint8_t baseNote; + int8_t detune; + uint8_t lowNote; + uint8_t highNote; + uint8_t lowVelocity; + uint8_t highVelocity; + int16_t gain; +} GeneralInstrumentInfo; + +static void apply_GeneralInstrumentInfo(int samples, Sample *sample, const GeneralInstrumentInfo *info); + +/* read_sample_data() flags */ +#define SAMPLE_BIG_ENDIAN (1 << 0) +#define SAMPLE_8BIT_UNSIGNED (1 << 1) + +static int read_sample_data(int32_t flags, struct timidity_file *tf, int bits, int samples, int frames, sample_t **sdata); + +/*************** WAV Importer ***************/ + +typedef struct { + int16_t wFormatTag; + uint16_t wChannels; + uint32_t dwSamplesPerSec; + uint32_t dwAvgBytesPerSec; + uint16_t wBlockAlign; + uint16_t wBitsPerSample; +} WAVFormatChunk; + +typedef struct { + int32_t dwSamplePeriod; + int32_t dwMIDIUnityNote; + uint32_t dwMIDIPitchFraction; + int hasLoop, loopType; + int32_t loop_dwStart, loop_dwEnd, loop_dwFraction; +} WAVSamplerChunk; + +static int read_WAVFormatChunk(struct timidity_file *tf, WAVFormatChunk *fmt, int psize); +static int read_WAVSamplerChunk(struct timidity_file *tf, WAVSamplerChunk *smpl, int psize); +static int read_WAVInstrumentChunk(struct timidity_file *tf, GeneralInstrumentInfo *inst, int psize); + +int Instruments::import_wave_discriminant(char *sample_file) +{ + struct timidity_file *tf; + char buf[12]; + + if ((tf = open_file(sample_file, 1, OF_NORMAL, pathlist)) == NULL) + return 1; + if (tf_read(buf, 12, 1, tf) != 1 + || memcmp(&buf[0], "RIFF", 4) != 0 || memcmp(&buf[8], "WAVE", 4) != 0) + { + close_file(tf); + return 1; + } + close_file(tf); + return 0; +} + +#define WAVE_CHUNKFLAG_SAMPLER (1 << 0) +#define WAVE_CHUNKFLAG_INSTRUMENT (1 << 1) + +int Instruments::import_wave_load(char *sample_file, Instrument *inst) +{ + struct timidity_file *tf; + union { + int32_t i[3]; + char c[12]; + } xbuf; + char *buf = xbuf.c; + int state; /* initial > fmt_read > data_read */ + int i, chunk_size, type_index, type_size, samples = 0; + int32_t chunk_flags; + Sample *sample; + WAVFormatChunk format = {0,}; + WAVSamplerChunk samplerc = {0,}; + GeneralInstrumentInfo instc; + + if ((tf = open_file(sample_file, 1, OF_NORMAL, pathlist)) == NULL) + return 1; + if (tf_read(buf, 12, 1, tf) != 1 + || memcmp(&buf[0], "RIFF", 4) != 0 || memcmp(&buf[8], "WAVE", 4) != 0) + { + close_file(tf); + return 1; + } + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Loading WAV: %s", sample_file); + state = chunk_flags = 0; + type_index = 4, type_size = 8; + for(;;) { + if (tf_read(&buf[type_index], type_size, 1, tf) != 1) + break; + chunk_size = LE_LONG(xbuf.i[2]); + if (memcmp(&buf[4 + 0], "fmt ", 4) == 0) + { + if (state != 0 /* only one format chunk is required */ + || chunk_size < 0x10) /* too small */ + break; + if (!read_WAVFormatChunk(tf, &format, chunk_size)) + break; + if (format.wChannels < 1 /* invalid range */ + || format.wChannels > MAX_SAMPLE_CHANNELS + || format.wFormatTag != 1 /* compressed */ + || format.wBitsPerSample & 0x7 /* padding not supported */ + || format.wBitsPerSample > 16) /* more than 16-bit is not supported */ + break; + state++; + } + else if (memcmp(&buf[4 + 0], "data", 4) == 0) + { + int frames; + sample_t *sdata[MAX_SAMPLE_CHANNELS]; + + if (state != 1) + break; + frames = chunk_size / format.wBlockAlign; + inst->samples = samples = format.wChannels; + inst->sample = (Sample *)safe_malloc(sizeof(Sample) * samples); + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Format: %d-bits %dHz %dch, %d frames", + format.wBitsPerSample, format.dwSamplesPerSec, samples, frames); + initialize_sample(inst, frames, format.wBitsPerSample, format.dwSamplesPerSec); + /* load waveform data */ + for(i = 0; i < samples; i++) + { + inst->sample[i].data = sdata[i] = (sample_t *)safe_malloc(sizeof(sample_t) * frames); + inst->sample[i].data_alloced = 1; + } + if (!read_sample_data(SAMPLE_8BIT_UNSIGNED, tf, format.wBitsPerSample, samples, frames, sdata)) + break; + state++; + } + else if (!(chunk_flags & WAVE_CHUNKFLAG_SAMPLER) && memcmp(&buf[4 + 0], "smpl", 4) == 0) + { + if (!read_WAVSamplerChunk(tf, &samplerc, chunk_size)) + break; + chunk_flags |= WAVE_CHUNKFLAG_SAMPLER; + } + else if (!(chunk_flags & WAVE_CHUNKFLAG_INSTRUMENT) && memcmp(&buf[4 + 0], "inst", 4) == 0) + { + if (!read_WAVInstrumentChunk(tf, &instc, chunk_size)) + break; + chunk_flags |= WAVE_CHUNKFLAG_INSTRUMENT; + } + else if (tf_seek(tf, chunk_size, SEEK_CUR) == -1) + break; + type_index = 4 - (chunk_size & 1); + type_size = 8 + (chunk_size & 1); + } + close_file(tf); + if (chunk_flags & WAVE_CHUNKFLAG_SAMPLER) + { + uint8_t modes; + int32_t sample_rate, root_freq; + uint32_t loopStart = 0, loopEnd = 0; + + sample_rate = samplerc.dwSamplePeriod == 0 ? 0 : 1000000000 / samplerc.dwSamplePeriod; + root_freq = freq_table[samplerc.dwMIDIUnityNote]; + if (samplerc.dwMIDIPitchFraction != 0 + && samplerc.dwMIDIUnityNote != 127) /* no table data */ + { + int32_t diff; + + diff = freq_table[samplerc.dwMIDIUnityNote + 1] - root_freq; + root_freq += int32_t((double)samplerc.dwMIDIPitchFraction * diff / 0xFFFFFFFF); + } + if (samplerc.hasLoop) + { + const uint8_t loopModes[] = {MODES_LOOPING, MODES_LOOPING | MODES_PINGPONG, MODES_LOOPING | MODES_REVERSE}; + + modes = loopModes[samplerc.loopType]; + loopStart = samplerc.loop_dwStart << FRACTION_BITS; + loopEnd = samplerc.loop_dwEnd << FRACTION_BITS; + } + else + modes = 0; + for(i = 0; i < samples; i++) + { + sample = &inst->sample[i]; + if (sample_rate != 0) + sample->sample_rate = sample_rate; + sample->root_freq = root_freq; + if (modes != 0) + { + sample->loop_start = loopStart; + sample->loop_end = loopEnd; + } + sample->modes |= modes; + } + } + if (chunk_flags & WAVE_CHUNKFLAG_INSTRUMENT) + apply_GeneralInstrumentInfo(samples, inst->sample, &instc); + return (state != 2); +} + +static int read_WAVFormatChunk(struct timidity_file *tf, WAVFormatChunk *fmt, int csize) +{ + int32_t tmplong; + int16_t tmpshort; + + READ_SHORT_LE(fmt->wFormatTag); + READ_SHORT_LE(fmt->wChannels); + READ_LONG_LE(fmt->dwSamplesPerSec); + READ_LONG_LE(fmt->dwAvgBytesPerSec); + READ_SHORT_LE(fmt->wBlockAlign); + READ_SHORT_LE(fmt->wBitsPerSample); + if (tf_seek(tf, csize - 0x10, SEEK_CUR) == -1) + goto fail; + return 1; + fail: + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Unable to read format chunk"); + return 0; +} + +static int read_WAVSamplerChunk(struct timidity_file *tf, WAVSamplerChunk *smpl, int psize) +{ + int32_t tmplong; + int i, loopCount, cbSamplerData, dwPlayCount; + unsigned int loopType; + + smpl->hasLoop = 0; + /* skip dwManufacturer, dwProduct */ + if (tf_seek(tf, 4 + 4, SEEK_CUR) == -1) + goto fail; + READ_LONG_LE(smpl->dwSamplePeriod); + READ_LONG_LE(smpl->dwMIDIUnityNote); + READ_LONG_LE(smpl->dwMIDIPitchFraction); + /* skip dwSMPTEFormat, dwSMPTEOffset */ + if (tf_seek(tf, 4 + 4, SEEK_CUR) == -1) + goto fail; + READ_LONG_LE(loopCount); + READ_LONG_LE(cbSamplerData); + psize -= 4 * 9 + loopCount * 4 * 6; + for(i = 0; i < loopCount; i++) + { + /* skip dwIdentifier */ + if (tf_seek(tf, 4, SEEK_CUR) == -1) + goto fail; + READ_LONG_LE(loopType); /* dwType */ + if (!smpl->hasLoop && loopType <= 2) + { + smpl->loopType = loopType; + READ_LONG_LE(smpl->loop_dwStart); + READ_LONG_LE(smpl->loop_dwEnd); + READ_LONG_LE(smpl->loop_dwFraction); + READ_LONG_LE(dwPlayCount); + if (dwPlayCount == 0) /* infinite loop */ + smpl->hasLoop = 1; + } + else + { + if (tf_seek(tf, 4 * 4, SEEK_CUR) == -1) + goto fail; + } + } + if (psize != cbSamplerData) + ctl_cmsg(CMSG_WARNING, VERB_NOISY, "Bad sampler chunk length"); + if (tf_seek(tf, psize, SEEK_CUR) == -1) + goto fail; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Sampler: %dns/frame, note=%d, loops=%d", + smpl->dwSamplePeriod, smpl->dwMIDIUnityNote, loopCount); + return 1; + fail: + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Unable to read sampler chunk"); + return 0; +} + +static int read_WAVInstrumentChunk(struct timidity_file *tf, GeneralInstrumentInfo *inst, int psize) +{ + int8_t tmpchar; + + if (psize != 7) + goto fail; + READ_CHAR(inst->baseNote); + READ_CHAR(inst->detune); + READ_CHAR(inst->gain); + READ_CHAR(inst->lowNote); + READ_CHAR(inst->highNote); + READ_CHAR(inst->lowVelocity); + READ_CHAR(inst->highVelocity); + ctl_cmsg(CMSG_INFO, VERB_VERBOSE, "Instrument: note=%d (%d-%d), gain=%ddb, velocity=%d-%d", + inst->baseNote, inst->lowNote, inst->highNote, inst->gain, + inst->lowVelocity, inst->highVelocity); + return 1; + fail: + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Unable to read instrument chunk"); + return 0; +} + +/*************** AIFF importer ***************/ + +struct AIFFCommonChunk { + uint16_t numChannels; + uint32_t numSampleFrames; + uint16_t sampleSize; + double sampleRate; +}; + +struct AIFFSoundDataChunk { + uint32_t position; + Instrument *inst; + AIFFCommonChunk *common; +}; + +typedef struct { + uint16_t mode; + int16_t beginID, endID; +} AIFFLoopInfo; + +typedef struct { + int16_t id; + uint32_t position; +} AIFFMarkerData; + +static int read_AIFFInstumentChunk(struct timidity_file *tf, GeneralInstrumentInfo *inst, AIFFLoopInfo *loop, int csize); +static int read_AIFFMarkerChunk(struct timidity_file *tf, AIFFMarkerData **markers, int csize); +static int AIFFGetMarkerPosition(int16_t id, const AIFFMarkerData *markers, uint32_t *position); + +int Instruments::import_aiff_discriminant(char *sample_file) +{ + struct timidity_file *tf; + char buf[12]; + + if ((tf = open_file(sample_file, 1, OF_NORMAL, pathlist)) == NULL) + return 1; + if (tf_read(buf, 12, 1, tf) != 1 + || memcmp(&buf[0], "FORM", 4) != 0 || memcmp(&buf[8], "AIF", 3) != 0 + || (buf[8 + 3] != 'F' && buf[8 + 3] != 'C')) + { + close_file(tf); + return 1; + } + close_file(tf); + return 0; +} + +#define AIFF_CHUNKFLAG_COMMON (1 << 0) +#define AIFF_CHUNKFLAG_SOUND (1 << 1) +#define AIFF_CHUNKFLAG_INSTRUMENT (1 << 2) +#define AIFF_CHUNKFLAG_MARKER (1 << 3) +#define AIFF_CHUNKFLAG_SOUNDREAD (1 << 29) +#define AIFF_CHUNKFLAG_READERR (1 << 30) +#define AIFF_CHUNKFLAG_DUPCHUNK (1 << 31) +#define AIFF_CHUNKFLAG_REQUIRED (AIFF_CHUNKFLAG_COMMON | AIFF_CHUNKFLAG_SOUND) +#define AIFF_CHUNKFLAG_FAILED (AIFF_CHUNKFLAG_READERR | AIFF_CHUNKFLAG_DUPCHUNK) + +int Instruments::import_aiff_load(char *sample_file, Instrument *inst) +{ + struct timidity_file *tf; + union { + int32_t i[3]; + char c[12]; + } xbuf; + char *buf = xbuf.c; + int chunk_size, type_index, type_size; + int compressed; + int32_t chunk_flags; + AIFFCommonChunk common; + AIFFSoundDataChunk sound; + GeneralInstrumentInfo inst_info; + AIFFLoopInfo loop_info = {0,0,0}; + AIFFMarkerData *marker_data; + + if ((tf = open_file(sample_file, 1, OF_NORMAL, pathlist)) == NULL) + return 1; + if (tf_read(buf, 12, 1, tf) != 1 + || memcmp(&buf[0], "FORM", 4) != 0 || memcmp(&buf[8], "AIF", 3) != 0 + || (buf[8 + 3] != 'F' && buf[8 + 3] != 'C')) + { + close_file(tf); + return 1; + } + compressed = buf[8 + 3] == 'C'; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Loading AIFF: %s", sample_file); + type_index = 4, type_size = 8; + chunk_flags = 0; + sound.inst = inst; + sound.common = &common; + marker_data = NULL; + for(;;) { + if (tf_read(&buf[type_index], type_size, 1, tf) != 1) + break; + chunk_size = BE_LONG(xbuf.i[2]); + if (memcmp(&buf[4 + 0], "COMM", 4) == 0) + { + if (chunk_flags & AIFF_CHUNKFLAG_COMMON) + { + chunk_flags |= AIFF_CHUNKFLAG_DUPCHUNK; + break; + } + if (chunk_size < 18) /* too small */ + break; + if (!read_AIFFCommonChunk(tf, &common, chunk_size, compressed)) + break; + chunk_flags |= AIFF_CHUNKFLAG_COMMON; + } + else if (memcmp(&buf[4 + 0], "SSND", 4) == 0) + { + if (chunk_flags & AIFF_CHUNKFLAG_SOUND) + { + chunk_flags |= AIFF_CHUNKFLAG_DUPCHUNK; + break; + } + if (chunk_flags & AIFF_CHUNKFLAG_COMMON) + { + if (!read_AIFFSoundDataChunk(tf, &sound, chunk_size, 0)) + break; + chunk_flags |= AIFF_CHUNKFLAG_SOUNDREAD; + } + else if (!read_AIFFSoundDataChunk(tf, &sound, chunk_size, 1)) + break; + chunk_flags |= AIFF_CHUNKFLAG_SOUND; + } + else if (memcmp(&buf[4 + 0], "INST", 4) == 0) + { + if (chunk_flags & AIFF_CHUNKFLAG_INSTRUMENT) + { + chunk_flags |= AIFF_CHUNKFLAG_DUPCHUNK; + break; + } + else if (!read_AIFFInstumentChunk(tf, &inst_info, &loop_info, chunk_size)) + break; + chunk_flags |= AIFF_CHUNKFLAG_INSTRUMENT; + } + else if (memcmp(&buf[4 + 0], "MARK", 4) == 0) + { + if (chunk_flags & AIFF_CHUNKFLAG_MARKER) + { + chunk_flags |= AIFF_CHUNKFLAG_DUPCHUNK; + break; + } + else if (chunk_size < 2 || !read_AIFFMarkerChunk(tf, &marker_data, chunk_size)) + break; + chunk_flags |= AIFF_CHUNKFLAG_MARKER; + } + else if (inst->instname == NULL && memcmp(&buf[4 + 0], "NAME", 4) == 0) + { + inst->instname = (char*)malloc(chunk_size + 1); + if (tf_read(inst->instname, chunk_size, 1, tf) != 1) + { + chunk_flags |= AIFF_CHUNKFLAG_READERR; + break; + } + inst->instname[chunk_size] = '\0'; + } + else if (tf_seek(tf, chunk_size, SEEK_CUR) == -1) + break; + /* no need to check format version chunk */ + type_index = 4 - (chunk_size & 1); + type_size = 8 + (chunk_size & 1); + } + if (chunk_flags & AIFF_CHUNKFLAG_FAILED + || (chunk_flags & AIFF_CHUNKFLAG_REQUIRED) != AIFF_CHUNKFLAG_REQUIRED) + { + if (marker_data != NULL) + free(marker_data); + close_file(tf); + return -1; + } + if (!(chunk_flags & AIFF_CHUNKFLAG_SOUNDREAD)) + { + if (!read_AIFFSoundDataChunk(tf, &sound, 0, 2)) + { + if (marker_data != NULL) + free(marker_data); + close_file(tf); + return 1; + } + } + if (chunk_flags & AIFF_CHUNKFLAG_INSTRUMENT) + { + apply_GeneralInstrumentInfo(inst->samples, inst->sample, &inst_info); + if ((loop_info.mode == 1 || loop_info.mode == 2) + && chunk_flags & AIFF_CHUNKFLAG_MARKER && marker_data != NULL) + { + Sample *sample; + int i; + uint32_t loopStart, loopEnd; + uint8_t loopMode; + + if (AIFFGetMarkerPosition(loop_info.beginID, marker_data, &loopStart) + && AIFFGetMarkerPosition(loop_info.endID, marker_data, &loopEnd)) + { + loopMode = (loop_info.mode == 1) ? MODES_LOOPING : (MODES_LOOPING | MODES_PINGPONG); + loopStart <<= FRACTION_BITS; + loopEnd <<= FRACTION_BITS; + if (loopStart <= loopEnd) + { + for(i = 0; i < inst->samples; i++) + { + sample = &inst->sample[i]; + sample->loop_start = loopStart; + sample->loop_end = loopEnd; + sample->modes |= loopMode; + } + } + } + } + } + if (marker_data != NULL) + free(marker_data); + close_file(tf); + return 0; +} + + int Instruments::read_AIFFCommonChunk(struct timidity_file *tf, AIFFCommonChunk *comm, int csize, int compressed) +{ + int32_t tmplong; + int16_t tmpshort; + int8_t tmpchar; + char sampleRate[10]; + uint32_t compressionType; + + READ_SHORT_BE(comm->numChannels); + READ_LONG_BE(comm->numSampleFrames); + READ_SHORT_BE(comm->sampleSize); + if (tf_read(sampleRate, 10, 1, tf) != 1) + goto fail; + comm->sampleRate = ConvertFromIeeeExtended(sampleRate); + csize -= 8 + 10; + ctl_cmsg(CMSG_INFO, VERB_NOISY, "Format: %d-bits %dHz %dch, %d frames", + comm->sampleSize, (int)comm->sampleRate, comm->numChannels, comm->numSampleFrames); + if (compressed) + { + READ_LONG_BE(compressionType); + if (compressionType != BE_LONG(0x4E4F4E45) /* NONE */) + { + char compressionName[256]; + uint8_t compressionNameLength; + + READ_CHAR(compressionNameLength); + if (tf_read(compressionName, compressionNameLength, 1, tf) != 1) + goto fail; + compressionName[compressionNameLength] = '\0'; + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "AIFF-C unknown compression type: %s", compressionName); + goto fail; + } + csize -= 4; + /* ignore compressionName and its padding */ + } + if (tf_seek(tf, csize, SEEK_CUR) == -1) + goto fail; + return 1; + fail: + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Unable to read common chunk"); + return 0; +} + +int Instruments::read_AIFFSoundDataChunk(struct timidity_file *tf, AIFFSoundDataChunk *sound, int csize, int mode) +{ + int32_t tmplong; + uint32_t offset, blockSize; + + if (mode == 0 || mode == 1) + { + READ_LONG_BE(offset); + READ_LONG_BE(blockSize); + if (blockSize != 0) /* not implemented */ + goto fail; + if (mode == 0) /* read both information and data */ + return read_AIFFSoundData(tf, sound->inst, sound->common); + /* read information only */ + if ((sound->position = tf_tell(tf)) == -1) + goto fail; + sound->position += offset; + csize -= 8; + if (tf_seek(tf, csize, SEEK_CUR) == -1) + goto fail; + return 1; + } + else if (mode == 2) /* read data using information previously read */ + { + if (tf_seek(tf, sound->position, SEEK_SET) == -1) + goto fail; + return read_AIFFSoundData(tf, sound->inst, sound->common); + } + fail: + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Unable to read sound data chunk"); + return 0; +} + +int Instruments::read_AIFFSoundData(struct timidity_file *tf, Instrument *inst, AIFFCommonChunk *common) +{ + int i, samples; + Sample *sample; + sample_t *sdata[MAX_SAMPLE_CHANNELS]; + + if ((samples = common->numChannels) > MAX_SAMPLE_CHANNELS) + goto fail; + inst->samples = samples; + inst->sample = sample = (Sample *)safe_malloc(sizeof(Sample) * samples); + initialize_sample(inst, common->numSampleFrames, common->sampleSize, (int)common->sampleRate); + /* load samples */ + for(i = 0; i < samples; i++) + { + sample[i].data = sdata[i] = (sample_t *)safe_malloc(sizeof(sample_t) * common->numSampleFrames); + sample[i].data_alloced = 1; + } + if (!read_sample_data(SAMPLE_BIG_ENDIAN, tf, common->sampleSize, samples, common->numSampleFrames, sdata)) + goto fail; + return 1; + fail: + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Unable to read sound data"); + return 0; +} + +static int read_AIFFInstumentChunk(struct timidity_file *tf, GeneralInstrumentInfo *inst, AIFFLoopInfo *loop, int csize) +{ + int8_t tmpchar; + int16_t tmpshort; + + if (csize != 20) + { + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Bad instrument chunk length"); + if (tf_seek(tf, csize, SEEK_CUR) == -1) + goto fail; + return 1; + } + READ_CHAR(inst->baseNote); + READ_CHAR(inst->detune); + READ_CHAR(inst->lowNote); + READ_CHAR(inst->highNote); + READ_CHAR(inst->lowVelocity); + READ_CHAR(inst->highVelocity); + READ_SHORT_BE(inst->gain); + READ_SHORT_BE(loop->mode); /* sustain loop */ + READ_SHORT_BE(loop->beginID); + READ_SHORT_BE(loop->endID); + if (tf_seek(tf, 2 + 2 + 2, SEEK_CUR) == -1) /* release loop */ + goto fail; + ctl_cmsg(CMSG_INFO, VERB_VERBOSE, "Instrument: note=%d (%d-%d), gain=%ddb, velocity=%d-%d", + inst->baseNote, inst->lowNote, inst->highNote, inst->gain, + inst->lowVelocity, inst->highVelocity); + return 1; + fail: + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Unable to read instrument chunk"); + return 0; +} + +static int read_AIFFMarkerChunk(struct timidity_file *tf, AIFFMarkerData **markers, int csize) +{ + int32_t tmplong; + int16_t tmpshort; + int16_t markerCount; + int i, dest; + AIFFMarkerData *m; + + m = NULL; + READ_SHORT_BE(markerCount) + if (csize != 2 + markerCount * (2 + 4)) + { + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Bad marker chunk length"); + if (tf_seek(tf, csize, SEEK_CUR) == -1) + goto fail; + return 1; + } + if ((m = (AIFFMarkerData*)malloc(sizeof(AIFFMarkerData) * (markerCount + 1))) == NULL) + goto fail; + for(i = dest = 0; i < markerCount; i++) + { + READ_SHORT_BE(m[dest].id); + READ_LONG_BE(m[dest].position); + if (m[dest].id > 0) + dest++; + } + m[dest].id = 0; + *markers = m; + return 1; + fail: + if (m != NULL) + free(m); + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Unable to read marker chunk"); + return 0; +} + +static int AIFFGetMarkerPosition(int16_t id, const AIFFMarkerData *markers, uint32_t *position) +{ + const AIFFMarkerData *marker; + + marker = markers; + while(marker->id != 0) + { + if (marker->id == id) + { + *position = marker->position; + return 1; + } + marker++; + } + return 0; +} + +/******************************/ + +#define WAVE_BUF_SIZE (1 << 11) /* should be power of 2 */ +#define READ_WAVE_SAMPLE(dest, b, s) \ + if (tf_read(dest, (b) * (s), 1, tf) != 1) \ + goto fail +#define READ_WAVE_FRAME(dest, b, f) \ + READ_WAVE_SAMPLE(dest, b, (f) * channels) +#define BITS_S8_TO_16(n) ((uint16_t)((n) << 8) | ((n) ^ 0x80)) +#define BITS_U8_TO_16(n) ((uint16_t)(((n) ^ 0x80) << 8) | (n)) + +#define BLOCK_READ_BEGIN(stype, sbyte, fch) { /* sbyte may be sizeof(stype) */ \ + stype data[WAVE_BUF_SIZE / sizeof(stype)]; \ + int j; \ + for(block_frame_count = (sizeof data / sbyte / fch); block_frame_count != 0; block_frame_count >>= 1) { \ + while(i <= frames - block_frame_count) { \ + READ_WAVE_FRAME(data, sbyte, block_frame_count); \ + for(j = 0; j < (block_frame_count * (fch)); i++) +#define BLOCK_READ_END } } } + +static int read_sample_data(int32_t flags, struct timidity_file *tf, int bits, int channels, int frames, sample_t **sdata) +{ + int i, block_frame_count; + + i = 0; + if (bits == 16) + { + if (channels == 1) + { + READ_WAVE_SAMPLE(sdata[0], 2, frames); + if (flags & SAMPLE_BIG_ENDIAN) { + #ifndef _BIG_ENDIAN_ + for(i = 0; i < frames; i++) + sdata[0][i] = BE_SHORT(sdata[0][i]); + #endif + } else { + #ifdef _BIG_ENDIAN_ + for(i = 0; i < frames; i++) + sdata[0][i] = LE_SHORT(sdata[0][i]); + #endif + } + } else { + if (flags & SAMPLE_BIG_ENDIAN) { + BLOCK_READ_BEGIN(uint16_t, 2, channels) + { + int c; + for(c = 0; c < channels; c++, j++) + sdata[c][i] = BE_SHORT(data[j]); + } + BLOCK_READ_END + } else { + BLOCK_READ_BEGIN(uint16_t, 2, channels) + { + int c; + for(c = 0; c < channels; c++, j++) + sdata[c][i] = LE_SHORT(data[j]); + } + BLOCK_READ_END + } + } + } + else + { + if (channels == 1) + { + if (flags & SAMPLE_8BIT_UNSIGNED) { + BLOCK_READ_BEGIN(uint8_t, 1, 1) + { + sdata[0][i] = BITS_U8_TO_16(data[j]); j++; + } + BLOCK_READ_END + } else { + BLOCK_READ_BEGIN(uint8_t, 1, 1) + { + sdata[0][i] = BITS_S8_TO_16(data[j]); j++; + } + BLOCK_READ_END + } + } else { + if (flags & SAMPLE_8BIT_UNSIGNED) { + BLOCK_READ_BEGIN(uint8_t, 1, channels) + { + int c; + for(c = 0; c < channels; c++, j++) + sdata[c][i] = BITS_U8_TO_16(data[j]); + } + BLOCK_READ_END + } else { + BLOCK_READ_BEGIN(uint8_t, 1, channels) + { + int c; + for(c = 0; c < channels; c++, j++) + sdata[c][i] = BITS_S8_TO_16(data[j]); + } + BLOCK_READ_END + } + } + } + return 1; + fail: + ctl_cmsg(CMSG_WARNING, VERB_VERBOSE, "Unable to read sample data"); + return 0; +} + +/* from instrum.c */ +int32_t Instruments::convert_envelope_rate_s(uint8_t rate) +{ + int32_t r; + + r = 3 - ((rate >> 6) & 0x3); + r *= 3; + r = (int32_t)(rate & 0x3f) << r; /* 6.9 fixed point */ + + /* 15.15 fixed point. */ + return (((r * 44100) / playback_rate) * control_ratio) + << ((fast_decay) ? 10 : 9); +} + + +void Instruments::initialize_sample(Instrument *inst, int frames, int sample_bits, int sample_rate) +{ + int i, j, samples; + Sample *sample; + const uint8_t *panning; + + samples = inst->samples; + for(i = 0; i < samples; i++) + { + sample = &inst->sample[i]; + sample->data_alloced = 0; + sample->loop_start = 0; + sample->loop_end = sample->data_length = frames << FRACTION_BITS; + sample->sample_rate = sample_rate; + sample->low_freq = freq_table[0]; + sample->high_freq = freq_table[127]; + sample->root_freq = freq_table[60]; + sample->panning = 64; + sample->note_to_use = 0; + sample->volume = 1.0; + sample->modes = MODES_16BIT; + sample->low_vel = 0; + sample->high_vel = 127; + sample->tremolo_sweep_increment = + sample->tremolo_phase_increment = sample->tremolo_depth = + sample->vibrato_sweep_increment = sample->vibrato_control_ratio = sample->vibrato_depth = 0; + sample->cutoff_freq = sample->resonance = sample->tremolo_to_pitch = + sample->tremolo_to_fc = sample->modenv_to_pitch = sample->modenv_to_fc = + sample->vel_to_fc = sample->key_to_fc = sample->vel_to_resonance = 0; + sample->envelope_velf_bpo = sample->modenv_velf_bpo = + sample->vel_to_fc_threshold = 64; + sample->key_to_fc_bpo = 60; + sample->scale_freq = 60; + sample->scale_factor = 1024; + memset(sample->envelope_velf, 0, sizeof(sample->envelope_velf)); + memset(sample->envelope_keyf, 0, sizeof(sample->envelope_keyf)); + memset(sample->modenv_velf, 0, sizeof(sample->modenv_velf)); + memset(sample->modenv_keyf, 0, sizeof(sample->modenv_keyf)); + memset(sample->modenv_rate, 0, sizeof(sample->modenv_rate)); + memset(sample->modenv_offset, 0, sizeof(sample->modenv_offset)); + sample->envelope_delay = sample->modenv_delay = + sample->tremolo_delay = sample->vibrato_delay = 0; + sample->inst_type = INST_PCM; + sample->sample_type = SF_SAMPLETYPE_MONO; + sample->sf_sample_link = -1; + sample->sf_sample_index = 0; + } + if (samples <= 6 && (panning = gen_pan_list[samples - 1]) != NULL) + { + for(i = 0; i < samples; i++) + inst->sample[i].panning = panning[i]; + } + for(i = 0; i < 6; i++) + { + int32_t envelope_rate, envelope_offset; + + envelope_rate = convert_envelope_rate_s(63); /* wav2pat.c */ + envelope_offset = convert_envelope_offset(240); /* wav2pat.c */ + for(j = 0; j < samples; j++) + { + sample = &inst->sample[j]; + sample->envelope_rate[i] = envelope_rate; + sample->envelope_offset[i] = envelope_offset; + } + } +} + +static void apply_GeneralInstrumentInfo(int samples, Sample *sample, const GeneralInstrumentInfo *info) +{ + int32_t root_freq; + double gain; + int i; + + root_freq = freq_table[info->baseNote]; + if (info->detune < 0) + { + if (info->baseNote != 0) /* no table data */ + root_freq += (root_freq - freq_table[info->baseNote - 1]) * 50 / info->detune; + } + else if (info->detune > 0) + { + if (info->baseNote != 127) /* no table data */ + root_freq += (freq_table[info->baseNote + 1] - root_freq) * 50 / info->detune; + } + gain = pow(10, info->gain / 20.0); + for(i = 0; i < samples; i++) + { + sample[i].low_freq = freq_table[info->lowNote]; + sample[i].high_freq = freq_table[info->highNote]; + sample[i].root_freq = root_freq; + sample[i].volume *= gain; + sample[i].low_vel = info->lowVelocity; + sample[i].high_vel = info->highVelocity; + } +} + + +/* Copyright (C) 1989-1991 Ken Turkowski. + * + * All rights reserved. + * + * Warranty Information + * Even though I have reviewed this software, I make no warranty + * or representation, either express or implied, with respect to this + * software, its quality, accuracy, merchantability, or fitness for a + * particular purpose. As a result, this software is provided "as is," + * and you, its user, are assuming the entire risk as to its quality + * and accuracy. + * + * This code may be used and freely distributed as long as it includes + * this copyright notice and the above warranty information. + * + * Machine-independent I/O routines for IEEE floating-point numbers. + * + * NaN's and infinities are converted to HUGE_VAL or HUGE, which + * happens to be infinity on IEEE machines. Unfortunately, it is + * impossible to preserve NaN's in a machine-independent way. + * Infinities are, however, preserved on IEEE machines. + * + * These routines have been tested on the following machines: + * Apple Macintosh, MPW 3.1 C compiler + * Apple Macintosh, THINK C compiler + * Silicon Graphics IRIS, MIPS compiler + * Cray X/MP and Y/MP + * Digital Equipment VAX + * Sequent Balance (Multiprocesor 386) + * NeXT + * + * + * Implemented by Malcolm Slaney and Ken Turkowski. + * + * Malcolm Slaney contributions during 1988-1990 include big- and little- + * endian file I/O, conversion to and from Motorola's extended 80-bit + * floating-point format, and conversions to and from IEEE single- + * precision floating-point format. + * + * In 1991, Ken Turkowski implemented the conversions to and from + * IEEE double-precision format, added more precision to the extended + * conversions, and accommodated conversions involving +/- infinity, + * NaN's, and denormalized numbers. + */ + +/**************************************************************** + * Extended precision IEEE floating-point conversion routines. + * Extended is an 80-bit number as defined by Motorola, + * with a sign bit, 15 bits of exponent (offset 16383?), + * and a 64-bit mantissa, with no hidden bit. + ****************************************************************/ + +static double ConvertFromIeeeExtended(const char *bytes) +{ + double f; + int32_t expon; + uint32_t hiMant, loMant; + + expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); + hiMant = ((uint32_t)(bytes[2] & 0xFF) << 24) + | ((uint32_t)(bytes[3] & 0xFF) << 16) + | ((uint32_t)(bytes[4] & 0xFF) << 8) + | ((uint32_t)(bytes[5] & 0xFF)); + loMant = ((uint32_t)(bytes[6] & 0xFF) << 24) + | ((uint32_t)(bytes[7] & 0xFF) << 16) + | ((uint32_t)(bytes[8] & 0xFF) << 8) + | ((uint32_t)(bytes[9] & 0xFF)); + + if (expon == 0 && hiMant == 0 && loMant == 0) { + f = 0; + } + else { + if (expon == 0x7FFF) { /* Infinity or NaN */ + f = HUGE_VAL; + } + else { + expon -= 16383; + f = ldexp(hiMant, expon-=31); + f += ldexp(loMant, expon-=32); + } + } + + if (bytes[0] & 0x80) + return -f; + else + return f; +} +} \ No newline at end of file diff --git a/src/sound/timidity++/sndfont.cpp b/src/sound/timidity++/sndfont.cpp new file mode 100644 index 0000000000..75326fe132 --- /dev/null +++ b/src/sound/timidity++/sndfont.cpp @@ -0,0 +1,1514 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2005 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + This code from awesfx + Modified by Masanao Izumo + + ================================================================ + parsesf.c + parse SoundFont layers and convert it to AWE driver patch + + Copyright (C) 1996,1997 Takashi Iwai + ================================================================ +*/ + +#include +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "tables.h" +#include "instrum.h" +#include "playmidi.h" +#include "sffile.h" +#include "sflayer.h" +#include "sfitem.h" +#include "filter.h" +#include "freq.h" +#include "resample.h" + +namespace TimidityPlus +{ + +#define SFMalloc(rec, count) new_segment(&(rec)->pool, count) +#define SFStrdup(rec, s) strdup_mblock(&(rec)->pool, s) + +/*---------------------------------------------------------------- + * compile flags + *----------------------------------------------------------------*/ + + +/* return value */ +#define AWE_RET_OK 0 /* successfully loaded */ +#define AWE_RET_ERR 1 /* some fatal error occurs */ +#define AWE_RET_SKIP 2 /* some fonts are skipped */ +#define AWE_RET_NOMEM 3 /* out or memory; not all fonts loaded */ +#define AWE_RET_NOT_FOUND 4 /* the file is not found */ + +/*---------------------------------------------------------------- + * local parameters + *----------------------------------------------------------------*/ + +struct SFPatchRec +{ + int preset, bank, keynote; /* -1 = matches all */ +}; + +struct SampleList +{ + Sample v; + SampleList *next; + int32_t start; + int32_t len; + int32_t cutoff_freq; + int16_t resonance; + int16_t root, tune; + char low, high; /* key note range */ + int8_t reverb_send, chorus_send; + + /* Depend on playback_rate */ + int32_t vibrato_freq; + int32_t attack; + int32_t hold; + int32_t sustain; + int32_t decay; + int32_t release; + + int32_t modattack; + int32_t modhold; + int32_t modsustain; + int32_t moddecay; + int32_t modrelease; + + int bank, keynote; /* for drum instruments */ +}; + +struct InstList { + SFPatchRec pat; + int pr_idx; + int samples; + int order; + SampleList *slist; + InstList *next; +}; + +struct SFExclude { + SFPatchRec pat; + SFExclude *next; +}; + +struct SFOrder { + SFPatchRec pat; + int order; + SFOrder *next; +}; + +#define INSTHASHSIZE 127 +#define INSTHASH(bank, preset, keynote) \ + ((int)(((unsigned)bank ^ (unsigned)preset ^ (unsigned)keynote) % INSTHASHSIZE)) + +struct SFInsts { + struct timidity_file *tf; + char *fname; + int8_t def_order, def_cutoff_allowed, def_resonance_allowed; + uint16_t version, minorversion; + int32_t samplepos, samplesize; + InstList *instlist[INSTHASHSIZE]; + char **inst_namebuf; + SFExclude *sfexclude; + SFOrder *sforder; + SFInsts *next; + double amptune; + MBlockList pool; +}; + +/*----------------------------------------------------------------*/ + +/* prototypes */ + +#define P_GLOBAL 1 +#define P_LAYER 2 +#define def_drum_inst 0 + + +/*----------------------------------------------------------------*/ + + +SFInsts *Instruments::find_soundfont(char *sf_file) +{ + SFInsts *sf; + + for(sf = sfrecs; sf != NULL; sf = sf->next) + if(sf->fname != NULL && strcmp(sf->fname, sf_file) == 0) + return sf; + return NULL; +} + +SFInsts *Instruments::new_soundfont(char *sf_file) +{ + SFInsts *sf, *prev; + + for(sf = sfrecs, prev = NULL; sf != NULL; prev = sf, sf = sf->next) + { + if(sf->fname == NULL) + { + /* remove the record from the chain to reuse */ + if (prev != NULL) + prev->next = sf->next; + else if (sfrecs == sf) + sfrecs = sf->next; + break; + } + } + if(sf == NULL) + sf = (SFInsts *)safe_malloc(sizeof(SFInsts)); + memset(sf, 0, sizeof(SFInsts)); + init_mblock(&sf->pool); + sf->fname = SFStrdup(sf, sf_file); + sf->def_order = DEFAULT_SOUNDFONT_ORDER; + sf->amptune = 1.0; + return sf; +} + +void Instruments::add_soundfont(char *sf_file, int sf_order, int sf_cutoff, int sf_resonance, int amp) +{ + SFInsts *sf; + + if((sf = find_soundfont(sf_file)) == NULL) + { + sf = new_soundfont(sf_file); + sf->next = sfrecs; + sfrecs = sf; + } + + if(sf_order >= 0) + sf->def_order = sf_order; + if(sf_cutoff >= 0) + sf->def_cutoff_allowed = sf_cutoff; + if(sf_resonance >= 0) + sf->def_resonance_allowed = sf_resonance; + if(amp >= 0) + sf->amptune = (double)amp * 0.01; + current_sfrec = sf; +} + +void Instruments::remove_soundfont(char *sf_file) +{ + SFInsts *sf; + + if((sf = find_soundfont(sf_file)) != NULL) + end_soundfont(sf); +} + +void Instruments::free_soundfonts() +{ + SFInsts *sf, *next; + + for (sf = sfrecs; sf != NULL; sf = next) { + if ((sf->tf != NULL) && (sf->tf->url != NULL)) + free(sf->tf->url); + if (sf->tf != NULL) + free(sf->tf); + reuse_mblock(&sf->pool); + next = sf->next; + free(sf); + } +} + +char *Instruments::soundfont_preset_name(int bank, int preset, int keynote, + char **sndfile) +{ + SFInsts *rec; + if(sndfile != NULL) + *sndfile = NULL; + for(rec = sfrecs; rec != NULL; rec = rec->next) + if(rec->fname != NULL) + { + int addr; + InstList *ip; + + addr = INSTHASH(bank, preset, keynote); + for(ip = rec->instlist[addr]; ip; ip = ip->next) + if(ip->pat.bank == bank && ip->pat.preset == preset && + (keynote < 0 || keynote == ip->pat.keynote)) + break; + if(ip != NULL) + { + if(sndfile != NULL) + *sndfile = rec->fname; + return rec->inst_namebuf[ip->pr_idx]; + } + } + return NULL; +} + +void Instruments::init_sf(SFInsts *rec) +{ + SFInfo sfinfo; + int i; + + if ((rec->tf = open_file(rec->fname, 1, OF_VERBOSE, pathlist)) == NULL) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "Can't open soundfont file %s", rec->fname); + end_soundfont(rec); + return; + } + + if(load_soundfont(&sfinfo, rec->tf)) + { + end_soundfont(rec); + return; + } + + correct_samples(&sfinfo); + current_sfrec = rec; + for (i = 0; i < sfinfo.npresets; i++) { + int bank = sfinfo.preset[i].bank; + int preset = sfinfo.preset[i].preset; + + if (bank == 128) + /* FIXME: why not allow exclusion of drumsets? */ + alloc_instrument_bank(1, preset); + else { + if (is_excluded(rec, bank, preset, -1)) + continue; + alloc_instrument_bank(0, bank); + } + load_font(&sfinfo, i); + } + + /* copy header info */ + rec->version = sfinfo.version; + rec->minorversion = sfinfo.minorversion; + rec->samplepos = sfinfo.samplepos; + rec->samplesize = sfinfo.samplesize; + rec->inst_namebuf = + (char **)SFMalloc(rec, sfinfo.npresets * sizeof(char *)); + for(i = 0; i < sfinfo.npresets; i++) + rec->inst_namebuf[i] = + (char *)SFStrdup(rec, sfinfo.preset[i].hdr.name); + free_soundfont(&sfinfo); + + if (opt_sf_close_each_file) { + close_file(rec->tf); + rec->tf = NULL; + } +} + +void Instruments::init_load_soundfont(void) +{ + SFInsts *rec; + for(rec = sfrecs; rec != NULL; rec = rec->next) + if(rec->fname != NULL) + init_sf(rec); +} + +void Instruments::end_soundfont(SFInsts *rec) +{ + if (rec->tf) { + close_file(rec->tf); + rec->tf = NULL; + } + + rec->fname = NULL; + rec->inst_namebuf = NULL; + rec->sfexclude = NULL; + rec->sforder = NULL; + reuse_mblock(&rec->pool); +} + +Instrument *Instruments::extract_soundfont(char *sf_file, int bank, int preset,int keynote) +{ + SFInsts *sf; + + if((sf = find_soundfont(sf_file)) != NULL) + return try_load_soundfont(sf, -1, bank, preset, keynote); + sf = new_soundfont(sf_file); + sf->next = sfrecs; + sf->def_order = 2; + sfrecs = sf; + init_sf(sf); + return try_load_soundfont(sf, -1, bank, preset, keynote); +} + +/*---------------------------------------------------------------- + * get converted instrument info and load the wave data from file + *----------------------------------------------------------------*/ + +Instrument *Instruments::try_load_soundfont(SFInsts *rec, int order, int bank,int preset, int keynote) +{ + InstList *ip; + Instrument *inst = NULL; + int addr; + + if (rec->tf == NULL) { + if (rec->fname == NULL) + return NULL; + if ((rec->tf = open_file(rec->fname, 1, OF_VERBOSE, pathlist)) == NULL) { + ctl_cmsg(CMSG_ERROR, VERB_NORMAL, + "Can't open soundfont file %s", rec->fname); + end_soundfont(rec); + return NULL; + } + } + + addr = INSTHASH(bank, preset, keynote); + for (ip = rec->instlist[addr]; ip; ip = ip->next) { + if (ip->pat.bank == bank && ip->pat.preset == preset && + (keynote < 0 || ip->pat.keynote == keynote) && + (order < 0 || ip->order == order)) + break; + } + + if (ip && ip->samples) + inst = load_from_file(rec, ip); + + if (opt_sf_close_each_file) { + close_file(rec->tf); + rec->tf = NULL; + } + + return inst; +} + +Instrument *Instruments::load_soundfont_inst(int order, int bank, int preset, int keynote) +{ + SFInsts *rec; + Instrument *ip; + /* + * Search through all ordered soundfonts + */ + int o = order; + + for(rec = sfrecs; rec != NULL; rec = rec->next) + { + if(rec->fname != NULL) + { + ip = try_load_soundfont(rec, o, bank, preset, keynote); + if(ip != NULL) + return ip; + if (o > 0) o++; + } + } + return NULL; +} + +/*----------------------------------------------------------------*/ +#define TO_MHZ(abscents) (int32_t)(8176.0 * pow(2.0,(double)(abscents)/1200.0)) +#define TO_VOLUME(level) (uint8_t)(255.0 - (level) * (255.0/1000.0)) + +double Instruments::calc_volume(LayerTable *tbl) +{ + int v; + + if(!tbl->set[SF_initAtten] || (int)tbl->val[SF_initAtten] == 0) + return (double)1.0; + + v = (int)tbl->val[SF_initAtten]; + if(v < 0) {v = 0;} + else if(v > 960) {v = 960;} + return cb_to_amp_table[v]; +} + +/* convert from 16bit value to fractional offset (15.15) */ +int32_t Instruments::to_offset(int32_t offset) +{ + return offset << 14; +} + +#define SF_ENVRATE_MAX (0x3FFFFFFFL) +#define SF_ENVRATE_MIN (1L) + +/* calculate ramp rate in fractional unit; + * diff = 16bit, time = msec + */ +int32_t Instruments::calc_rate(int32_t diff, double msec) +{ + double rate; + + if(msec == 0) {return (int32_t)SF_ENVRATE_MAX + 1;} + if(diff <= 0) {diff = 1;} + diff <<= 14; + rate = ((double)diff / playback_rate) * control_ratio * 1000.0 / msec; + if(fast_decay) {rate *= 2;} + if(rate > SF_ENVRATE_MAX) {rate = SF_ENVRATE_MAX;} + else if(rate < SF_ENVRATE_MIN) {rate = SF_ENVRATE_MIN;} + return (int32_t)rate; +} + +/* calculate ramp rate in fractional unit; + * diff = 16bit, timecent + */ +int32_t Instruments::to_rate(int32_t diff, int timecent) +{ + double rate; + + if(timecent == -12000) /* instantaneous attack */ + {return (int32_t)SF_ENVRATE_MAX + 1;} + if(diff <= 0) {diff = 1;} + diff <<= 14; + rate = (double)diff * control_ratio / playback_rate / pow(2.0, (double)timecent / 1200.0); + if(fast_decay) {rate *= 2;} + if(rate > SF_ENVRATE_MAX) {rate = SF_ENVRATE_MAX;} + else if(rate < SF_ENVRATE_MIN) {rate = SF_ENVRATE_MIN;} + return (int32_t)rate; +} + +/* + * convert timecents to sec + */ +double Instruments::to_msec(int timecent) +{ + return timecent == -12000 ? 0 : 1000.0 * pow(2.0, (double)timecent / 1200.0); +} + +/* + * Sustain level + * sf: centibels + * parm: 0x7f - sustain_level(dB) * 0.75 + */ +int32_t Instruments::calc_sustain(int sust_cB) +{ + if(sust_cB <= 0) {return 65533;} + else if(sust_cB >= 1000) {return 0;} + else {return (1000 - sust_cB) * 65533 / 1000;} +} + +Instrument *Instruments::load_from_file(SFInsts *rec, InstList *ip) +{ + SampleList *sp; + Instrument *inst; + int i; + int32_t len; + + inst = (Instrument *)safe_malloc(sizeof(Instrument)); + inst->instname = rec->inst_namebuf[ip->pr_idx]; + inst->type = INST_SF2; + inst->samples = ip->samples; + inst->sample = (Sample *)safe_malloc(sizeof(Sample) * ip->samples); + memset(inst->sample, 0, sizeof(Sample) * ip->samples); + for (i = 0, sp = ip->slist; i < ip->samples && sp; i++, sp = sp->next) { + Sample *sample = inst->sample + i; + int32_t j; +#ifdef _BIG_ENDIAN_ + int32_t k; + int16_t *tmp, s; +#endif + memcpy(sample, &sp->v, sizeof(Sample)); + sample->data = NULL; + sample->data_alloced = 0; + + if(i > 0 && (!sample->note_to_use || + (sample->modes & MODES_LOOPING))) + { + SampleList *sps; + Sample *found, *s; + + found = NULL; + for(j = 0, sps = ip->slist, s = inst->sample; j < i && sps; + j++, sps = sps->next, s++) + { + if(s->data == NULL) + break; + if(sp->start == sps->start) + { + if(antialiasing_allowed) + { + if(sample->data_length != s->data_length || + sample->sample_rate != s->sample_rate) + continue; + } + if(s->note_to_use && !(s->modes & MODES_LOOPING)) + continue; + found = s; + break; + } + } + if(found) + { + sample->data = found->data; + sample->data_alloced = 0; + continue; + } + } + + sample->data = (sample_t *)safe_large_malloc(sp->len + 2 * 3); + sample->data_alloced = 1; + + tf_seek(rec->tf, sp->start, SEEK_SET); + tf_read(sample->data, sp->len, 1, rec->tf); + +#ifdef _BIG_ENDIAN_ + tmp = (int16_t*)sample->data; + k = sp->len / 2; + for (j = 0; j < k; j++) { + s = LE_SHORT(*tmp); + *tmp++ = s; + } +#endif + /* set a small blank loop at the tail for avoiding abnormal loop. */ + len = sp->len / 2; + sample->data[len] = sample->data[len + 1] = sample->data[len + 2] = 0; + + if (antialiasing_allowed) + antialiasing((int16_t *)sample->data, + sample->data_length >> FRACTION_BITS, + sample->sample_rate, + playback_rate); + + /* resample it if possible */ + if (sample->note_to_use && !(sample->modes & MODES_LOOPING)) + pre_resample(sample); + + /* do pitch detection on drums if surround chorus is used */ + if (ip->pat.bank == 128 && opt_surround_chorus) + { + Freq freq; + sample->chord = -1; + sample->root_freq_detected = + freq.freq_fourier(sample, &(sample->chord)); + sample->transpose_detected = + assign_pitch_to_freq(sample->root_freq_detected) - + assign_pitch_to_freq(sample->root_freq / 1024.0); + } + } + + return inst; +} + + +/*---------------------------------------------------------------- + * excluded samples + *----------------------------------------------------------------*/ + +int Instruments::exclude_soundfont(int bank, int preset, int keynote) +{ + SFExclude *exc; + if(current_sfrec == NULL) + return 1; + exc = (SFExclude*)SFMalloc(current_sfrec , sizeof(SFExclude)); + exc->pat.bank = bank; + exc->pat.preset = preset; + exc->pat.keynote = keynote; + exc->next = current_sfrec->sfexclude; + current_sfrec->sfexclude = exc; + return 0; +} + +/* check the instrument is specified to be excluded */ +int Instruments::is_excluded(SFInsts *rec, int bank, int preset, int keynote) +{ + SFExclude *p; + for (p = rec->sfexclude; p; p = p->next) { + if (p->pat.bank == bank && + (p->pat.preset < 0 || p->pat.preset == preset) && + (p->pat.keynote < 0 || p->pat.keynote == keynote)) + return 1; + } + return 0; +} + + +/*---------------------------------------------------------------- + * ordered samples + *----------------------------------------------------------------*/ + +int Instruments::order_soundfont(int bank, int preset, int keynote, int order) +{ + SFOrder *p; + if(current_sfrec == NULL) + return 1; + p = (SFOrder*)SFMalloc(current_sfrec, sizeof(SFOrder)); + p->pat.bank = bank; + p->pat.preset = preset; + p->pat.keynote = keynote; + p->order = order; + p->next = current_sfrec->sforder; + current_sfrec->sforder = p; + return 0; +} + +/* check the instrument is specified to be ordered */ +int Instruments::is_ordered(SFInsts *rec, int bank, int preset, int keynote) +{ + SFOrder *p; + for (p = rec->sforder; p; p = p->next) { + if (p->pat.bank == bank && + (p->pat.preset < 0 || p->pat.preset == preset) && + (p->pat.keynote < 0 || p->pat.keynote == keynote)) + return p->order; + } + return -1; +} + + +/*----------------------------------------------------------------*/ + +int Instruments::load_font(SFInfo *sf, int pridx) +{ + SFPresetHdr *preset = &sf->preset[pridx]; + int rc, j, nlayers; + SFGenLayer *layp, *globalp; + + /* if layer is empty, skip it */ + if ((nlayers = preset->hdr.nlayers) <= 0 || + (layp = preset->hdr.layer) == NULL) + return AWE_RET_SKIP; + /* check global layer */ + globalp = NULL; + if (is_global(layp)) { + globalp = layp; + layp++; + nlayers--; + } + /* parse for each preset layer */ + for (j = 0; j < nlayers; j++, layp++) { + LayerTable tbl; + + /* set up table */ + clear_table(&tbl); + if (globalp) + set_to_table(sf, &tbl, globalp, P_GLOBAL); + set_to_table(sf, &tbl, layp, P_LAYER); + + /* parse the instrument */ + rc = parse_layer(sf, pridx, &tbl, 0); + if(rc == AWE_RET_ERR || rc == AWE_RET_NOMEM) + return rc; + } + + return AWE_RET_OK; +} + + +/*----------------------------------------------------------------*/ + +/* parse a preset layer and convert it to the patch structure */ +int Instruments::parse_layer(SFInfo *sf, int pridx, LayerTable *tbl, int level) +{ + SFInstHdr *inst; + int rc, i, nlayers; + SFGenLayer *lay, *globalp; + + if (level >= 2) { + fprintf(stderr, "parse_layer: too deep instrument level\n"); + return AWE_RET_ERR; + } + + /* instrument must be defined */ + if (!tbl->set[SF_instrument]) + return AWE_RET_SKIP; + + inst = &sf->inst[tbl->val[SF_instrument]]; + + /* if layer is empty, skip it */ + if ((nlayers = inst->hdr.nlayers) <= 0 || + (lay = inst->hdr.layer) == NULL) + return AWE_RET_SKIP; + + reset_last_sample_info(); + + /* check global layer */ + globalp = NULL; + if (is_global(lay)) { + globalp = lay; + lay++; + nlayers--; + } + + /* parse for each layer */ + for (i = 0; i < nlayers; i++, lay++) { + LayerTable ctbl; + clear_table(&ctbl); + if (globalp) + set_to_table(sf, &ctbl, globalp, P_GLOBAL); + set_to_table(sf, &ctbl, lay, P_LAYER); + + if (!ctbl.set[SF_sampleId]) { + /* recursive loading */ + merge_table(sf, &ctbl, tbl); + if (! sanity_range(&ctbl)) + continue; + rc = parse_layer(sf, pridx, &ctbl, level+1); + if (rc != AWE_RET_OK && rc != AWE_RET_SKIP) + return rc; + + reset_last_sample_info(); + } else { + init_and_merge_table(sf, &ctbl, tbl); + if (! sanity_range(&ctbl)) + continue; + + /* load the info data */ + if ((rc = make_patch(sf, pridx, &ctbl)) == AWE_RET_ERR) + return rc; + } + } + return AWE_RET_OK; +} + + +int Instruments::is_global(SFGenLayer *layer) +{ + int i; + for (i = 0; i < layer->nlists; i++) { + if (layer->list[i].oper == SF_instrument || + layer->list[i].oper == SF_sampleId) + return 0; + } + return 1; +} + + +/*---------------------------------------------------------------- + * layer table handlers + *----------------------------------------------------------------*/ + +/* initialize layer table */ +void Instruments::clear_table(LayerTable *tbl) +{ + memset(tbl->val, 0, sizeof(tbl->val)); + memset(tbl->set, 0, sizeof(tbl->set)); +} + +/* set items in a layer to the table */ +void Instruments::set_to_table(SFInfo *sf, LayerTable *tbl, SFGenLayer *lay, int level) +{ + int i; + for (i = 0; i < lay->nlists; i++) { + SFGenRec *gen = &lay->list[i]; + /* copy the value regardless of its copy policy */ + tbl->val[gen->oper] = gen->amount; + tbl->set[gen->oper] = level; + } +} + +/* add an item to the table */ +void Instruments::add_item_to_table(LayerTable *tbl, int oper, int amount, int level) +{ + LayerItem *item = &layer_items[oper]; + int o_lo, o_hi, lo, hi; + + switch (item->copy) { + case L_INHRT: + tbl->val[oper] += amount; + break; + case L_OVWRT: + tbl->val[oper] = amount; + break; + case L_PRSET: + case L_INSTR: + /* do not overwrite */ + if (!tbl->set[oper]) + tbl->val[oper] = amount; + break; + case L_RANGE: + if (!tbl->set[oper]) { + tbl->val[oper] = amount; + } else { + o_lo = LOWNUM(tbl->val[oper]); + o_hi = HIGHNUM(tbl->val[oper]); + lo = LOWNUM(amount); + hi = HIGHNUM(amount); + if (lo < o_lo) lo = o_lo; + if (hi > o_hi) hi = o_hi; + tbl->val[oper] = RANGE(lo, hi); + } + break; + } +} + +/* merge two tables */ +void Instruments::merge_table(SFInfo *sf, LayerTable *dst, LayerTable *src) +{ + int i; + for (i = 0; i < SF_EOF; i++) { + if (src->set[i]) { + if (sf->version == 1) { + if (!dst->set[i] || + i == SF_keyRange || i == SF_velRange) + /* just copy it */ + dst->val[i] = src->val[i]; + } + else + add_item_to_table(dst, i, src->val[i], P_GLOBAL); + dst->set[i] = P_GLOBAL; + } + } +} + +/* merge and set default values */ +void Instruments::init_and_merge_table(SFInfo *sf, LayerTable *dst, LayerTable *src) +{ + int i; + + /* default value is not zero */ + if (sf->version == 1) { + layer_items[SF_sustainEnv1].defv = 1000; + layer_items[SF_sustainEnv2].defv = 1000; + layer_items[SF_freqLfo1].defv = -725; + layer_items[SF_freqLfo2].defv = -15600; + } else { + layer_items[SF_sustainEnv1].defv = 0; + layer_items[SF_sustainEnv2].defv = 0; + layer_items[SF_freqLfo1].defv = 0; + layer_items[SF_freqLfo2].defv = 0; + } + + /* set default */ + for (i = 0; i < SF_EOF; i++) { + if (!dst->set[i]) + dst->val[i] = layer_items[i].defv; + } + merge_table(sf, dst, src); + /* convert from SBK to SF2 */ + if (sf->version == 1) { + for (i = 0; i < SF_EOF; i++) { + if (dst->set[i]) + dst->val[i] = sbk_to_sf2(i, dst->val[i], layer_items); + } + } +} + + +/*---------------------------------------------------------------- + * check key and velocity range + *----------------------------------------------------------------*/ + +int Instruments::sanity_range(LayerTable *tbl) +{ + int lo, hi; + + lo = LOWNUM(tbl->val[SF_keyRange]); + hi = HIGHNUM(tbl->val[SF_keyRange]); + if (lo < 0 || lo > 127 || hi < 0 || hi > 127 || hi < lo) + return 0; + + lo = LOWNUM(tbl->val[SF_velRange]); + hi = HIGHNUM(tbl->val[SF_velRange]); + if (lo < 0 || lo > 127 || hi < 0 || hi > 127 || hi < lo) + return 0; + + return 1; +} + + +/*---------------------------------------------------------------- + * create patch record from the stored data table + *----------------------------------------------------------------*/ + +int Instruments::make_patch(SFInfo *sf, int pridx, LayerTable *tbl) +{ + int bank, preset, keynote; + int keynote_from, keynote_to, done; + int addr, order; + InstList *ip; + SFSampleInfo *sample; + SampleList *sp; + + sample = &sf->sample[tbl->val[SF_sampleId]]; + + if(sample->sampletype & SF_SAMPLETYPE_ROM) /* is ROM sample? */ + { + ctl_cmsg(CMSG_INFO, VERB_DEBUG, "preset %d is ROM sample: 0x%x", + pridx, sample->sampletype); + return AWE_RET_SKIP; + } + + bank = sf->preset[pridx].bank; + preset = sf->preset[pridx].preset; + if(bank == 128){ + keynote_from = LOWNUM(tbl->val[SF_keyRange]); + keynote_to = HIGHNUM(tbl->val[SF_keyRange]); + } else + keynote_from = keynote_to = -1; + + done = 0; + for(keynote=keynote_from;keynote<=keynote_to;keynote++){ + + if(is_excluded(current_sfrec, bank, preset, keynote)) + { + continue; + } else + done++; + + order = is_ordered(current_sfrec, bank, preset, keynote); + if(order < 0) + order = current_sfrec->def_order; + + addr = INSTHASH(bank, preset, keynote); + + for(ip = current_sfrec->instlist[addr]; ip; ip = ip->next) + { + if(ip->pat.bank == bank && ip->pat.preset == preset && + (keynote < 0 || keynote == ip->pat.keynote)) + break; + } + + if(ip == NULL) + { + ip = (InstList*)SFMalloc(current_sfrec, sizeof(InstList)); + memset(ip, 0, sizeof(InstList)); + ip->pr_idx = pridx; + ip->pat.bank = bank; + ip->pat.preset = preset; + ip->pat.keynote = keynote; + ip->order = order; + ip->samples = 0; + ip->slist = NULL; + ip->next = current_sfrec->instlist[addr]; + current_sfrec->instlist[addr] = ip; + } + + /* new sample */ + sp = (SampleList *)SFMalloc(current_sfrec, sizeof(SampleList)); + memset(sp, 0, sizeof(SampleList)); + + sp->bank = bank; + sp->keynote = keynote; + + if(tbl->set[SF_keynum]) { + sp->v.note_to_use = (int)tbl->val[SF_keynum]; + } else if(bank == 128) { + sp->v.note_to_use = keynote; + } + make_info(sf, sp, tbl); + + /* add a sample */ + if(ip->slist == NULL) + ip->slist = sp; + else + { + SampleList *cur, *prev; + int32_t start; + + /* Insert sample */ + start = sp->start; + cur = ip->slist; + prev = NULL; + while(cur && cur->start <= start) + { + prev = cur; + cur = cur->next; + } + if(prev == NULL) + { + sp->next = ip->slist; + ip->slist = sp; + } + else + { + prev->next = sp; + sp->next = cur; + } + } + ip->samples++; + + } /* for(;;) */ + + + if(done==0) + return AWE_RET_SKIP; + else + return AWE_RET_OK; +} + +/*---------------------------------------------------------------- + * + * Modified for TiMidity + */ + +/* conver to Sample parameter */ +void Instruments::make_info(SFInfo *sf, SampleList *vp, LayerTable *tbl) +{ + set_sample_info(sf, vp, tbl); + set_init_info(sf, vp, tbl); + set_rootkey(sf, vp, tbl); + set_rootfreq(vp); + + /* tremolo & vibrato */ + convert_tremolo(vp, tbl); + convert_vibrato(vp, tbl); +} + +void Instruments::set_envelope_parameters(SampleList *vp) +{ + /* convert envelope parameters */ + vp->v.envelope_offset[0] = to_offset(65535); + vp->v.envelope_rate[0] = vp->attack; + + vp->v.envelope_offset[1] = to_offset(65534); + vp->v.envelope_rate[1] = vp->hold; + + vp->v.envelope_offset[2] = to_offset(vp->sustain); + vp->v.envelope_rate[2] = vp->decay; + + vp->v.envelope_offset[3] = 0; + vp->v.envelope_rate[3] = vp->release; + + vp->v.envelope_offset[4] = 0; + vp->v.envelope_rate[4] = vp->release; + + vp->v.envelope_offset[5] = 0; + vp->v.envelope_rate[5] = vp->release; + + /* convert modulation envelope parameters */ + vp->v.modenv_offset[0] = to_offset(65535); + vp->v.modenv_rate[0] = vp->modattack; + + vp->v.modenv_offset[1] = to_offset(65534); + vp->v.modenv_rate[1] = vp->modhold; + + vp->v.modenv_offset[2] = to_offset(vp->modsustain); + vp->v.modenv_rate[2] = vp->moddecay; + + vp->v.modenv_offset[3] = 0; + vp->v.modenv_rate[3] = vp->modrelease; + + vp->v.modenv_offset[4] = 0; + vp->v.modenv_rate[4] = vp->modrelease; + + vp->v.modenv_offset[5] = 0; + vp->v.modenv_rate[5] = vp->modrelease; +} + +/* set sample address */ +void Instruments::set_sample_info(SFInfo *sf, SampleList *vp, LayerTable *tbl) +{ + SFSampleInfo *sp = &sf->sample[tbl->val[SF_sampleId]]; + + /* set sample position */ + vp->start = (tbl->val[SF_startAddrsHi] << 15) + + tbl->val[SF_startAddrs] + + sp->startsample; + vp->len = (tbl->val[SF_endAddrsHi] << 15) + + tbl->val[SF_endAddrs] + + sp->endsample - vp->start; + + vp->start = abs(vp->start); + vp->len = abs(vp->len); + + /* set loop position */ + vp->v.loop_start = (tbl->val[SF_startloopAddrsHi] << 15) + + tbl->val[SF_startloopAddrs] + + sp->startloop - vp->start; + vp->v.loop_end = (tbl->val[SF_endloopAddrsHi] << 15) + + tbl->val[SF_endloopAddrs] + + sp->endloop - vp->start; + + /* set data length */ + vp->v.data_length = vp->len + 1; + + /* fix loop position */ + if (vp->v.loop_end > (splen_t)vp->len + 1) + vp->v.loop_end = vp->len + 1; + if (vp->v.loop_start > (splen_t)vp->len) + vp->v.loop_start = vp->len; + if (vp->v.loop_start >= vp->v.loop_end) + { + vp->v.loop_start = vp->len; + vp->v.loop_end = vp->len + 1; + } + + /* Sample rate */ + if(sp->samplerate > 50000) {sp->samplerate = 50000;} + else if(sp->samplerate < 400) {sp->samplerate = 400;} + vp->v.sample_rate = sp->samplerate; + + /* sample mode */ + vp->v.modes = MODES_16BIT; + + /* volume envelope & total volume */ + vp->v.volume = calc_volume(tbl) * current_sfrec->amptune; + + convert_volume_envelope(vp, tbl); + set_envelope_parameters(vp); + + if(tbl->val[SF_sampleFlags] == 1 || tbl->val[SF_sampleFlags] == 3) + { + /* looping */ + vp->v.modes |= MODES_LOOPING | MODES_SUSTAIN; + if(tbl->val[SF_sampleFlags] == 3) + vp->v.data_length = vp->v.loop_end; /* strip the tail */ + } + else + { + /* set a small blank loop at the tail for avoiding abnormal loop. */ + vp->v.loop_start = vp->len; + vp->v.loop_end = vp->len + 1; + } + + /* convert to fractional samples */ + vp->v.data_length <<= FRACTION_BITS; + vp->v.loop_start <<= FRACTION_BITS; + vp->v.loop_end <<= FRACTION_BITS; + + /* point to the file position */ + vp->start = vp->start * 2 + sf->samplepos; + vp->len *= 2; + + vp->v.vel_to_fc = -2400; /* SF2 default value */ + vp->v.key_to_fc = vp->v.vel_to_resonance = 0; + vp->v.envelope_velf_bpo = vp->v.modenv_velf_bpo = + vp->v.vel_to_fc_threshold = 64; + vp->v.key_to_fc_bpo = 60; + memset(vp->v.envelope_velf, 0, sizeof(vp->v.envelope_velf)); + memset(vp->v.modenv_velf, 0, sizeof(vp->v.modenv_velf)); + + vp->v.inst_type = INST_SF2; +} + +/*----------------------------------------------------------------*/ + +/* set global information */ + +void Instruments::set_init_info(SFInfo *sf, SampleList *vp, LayerTable *tbl) +{ + int val; + SFSampleInfo *sample; + sample = &sf->sample[tbl->val[SF_sampleId]]; + + /* key range */ + if(tbl->set[SF_keyRange]) + { + vp->low = LOWNUM(tbl->val[SF_keyRange]); + vp->high = HIGHNUM(tbl->val[SF_keyRange]); + } + else + { + vp->low = 0; + vp->high = 127; + } + vp->v.low_freq = freq_table[(int)vp->low]; + vp->v.high_freq = freq_table[(int)vp->high]; + + /* velocity range */ + if(tbl->set[SF_velRange]) { + vp->v.low_vel = LOWNUM(tbl->val[SF_velRange]); + vp->v.high_vel = HIGHNUM(tbl->val[SF_velRange]); + } else { + vp->v.low_vel = 0; + vp->v.high_vel = 127; + } + + /* fixed key & velocity */ + if(tbl->set[SF_keynum]) + vp->v.note_to_use = (int)tbl->val[SF_keynum]; + if(tbl->set[SF_velocity] && (int)tbl->val[SF_velocity] != 0) { + ctl_cmsg(CMSG_INFO,VERB_DEBUG,"error: fixed-velocity is not supported."); + } + + vp->v.sample_type = sample->sampletype; + vp->v.sf_sample_index = tbl->val[SF_sampleId]; + vp->v.sf_sample_link = sample->samplelink; + + /* Some sf2 files don't contain valid sample links, so see if the + previous sample was a matching Left / Right sample with the + link missing and add it */ + switch (sample->sampletype) { + case SF_SAMPLETYPE_LEFT: + if (vp->v.sf_sample_link == 0 && + last_sample_type == SF_SAMPLETYPE_RIGHT && + last_sample_instrument == tbl->val[SF_instrument] && + last_sample_keyrange == tbl->val[SF_keyRange]) { + /* The previous sample was a matching right sample + set the link */ + vp->v.sf_sample_link = last_sample_list->v.sf_sample_index; + } + break; + case SF_SAMPLETYPE_RIGHT: + if (last_sample_list && + last_sample_list->v.sf_sample_link == 0 && + last_sample_type == SF_SAMPLETYPE_LEFT && + last_sample_instrument == tbl->val[SF_instrument] && + last_sample_keyrange == tbl->val[SF_keyRange]) { + /* The previous sample was a matching left sample + set the link on the previous sample*/ + last_sample_list->v.sf_sample_link = tbl->val[SF_sampleId]; + } + break; + } + + /* Remember this sample in case the next one is a match */ + last_sample_type = sample->sampletype;; + last_sample_instrument = tbl->val[SF_instrument]; + last_sample_keyrange = tbl->val[SF_keyRange]; + last_sample_list = vp; + + /* panning position: 0 to 127 */ + val = (int)tbl->val[SF_panEffectsSend]; + if(sample->sampletype == SF_SAMPLETYPE_MONO || val != 0) { /* monoSample = 1 */ + if(val < -500) + vp->v.panning = 0; + else if(val > 500) + vp->v.panning = 127; + else + vp->v.panning = (int8_t)((val + 500) * 127 / 1000); + } else if(sample->sampletype == SF_SAMPLETYPE_RIGHT) { /* rightSample = 2 */ + vp->v.panning = 127; + } else if(sample->sampletype == SF_SAMPLETYPE_LEFT) { /* leftSample = 4 */ + vp->v.panning = 0; + } else if(sample->sampletype == SF_SAMPLETYPE_LINKED) { /* linkedSample = 8 */ + ctl_cmsg(CMSG_ERROR,VERB_NOISY,"error: linkedSample is not supported."); + } + + memset(vp->v.envelope_keyf, 0, sizeof(vp->v.envelope_keyf)); + memset(vp->v.modenv_keyf, 0, sizeof(vp->v.modenv_keyf)); + if(tbl->set[SF_autoHoldEnv2]) { + vp->v.envelope_keyf[1] = (int16_t)tbl->val[SF_autoHoldEnv2]; + } + if(tbl->set[SF_autoDecayEnv2]) { + vp->v.envelope_keyf[2] = (int16_t)tbl->val[SF_autoDecayEnv2]; + } + if(tbl->set[SF_autoHoldEnv1]) { + vp->v.modenv_keyf[1] = (int16_t)tbl->val[SF_autoHoldEnv1]; + } + if(tbl->set[SF_autoDecayEnv1]) { + vp->v.modenv_keyf[2] = (int16_t)tbl->val[SF_autoDecayEnv1]; + } + + current_sfrec->def_cutoff_allowed = 1; + current_sfrec->def_resonance_allowed = 1; + + /* initial cutoff & resonance */ + vp->cutoff_freq = 0; + if((int)tbl->val[SF_initialFilterFc] < 0) + tbl->set[SF_initialFilterFc] = tbl->val[SF_initialFilterFc] = 0; + if(current_sfrec->def_cutoff_allowed && tbl->set[SF_initialFilterFc] + && (int)tbl->val[SF_initialFilterFc] >= 1500 && (int)tbl->val[SF_initialFilterFc] <= 13500) + { + val = (int)tbl->val[SF_initialFilterFc]; + val = abscent_to_Hz(val); + + if(!opt_modulation_envelope) { + if(tbl->set[SF_env1ToFilterFc] && (int)tbl->val[SF_env1ToFilterFc] > 0) + { + val = int( val * pow(2.0,(double)tbl->val[SF_env1ToFilterFc] / 1200.0f)); + if(val > 20000) {val = 20000;} + } + } + + vp->cutoff_freq = val; + } + vp->v.cutoff_freq = vp->cutoff_freq; + + vp->resonance = 0; + if(current_sfrec->def_resonance_allowed && tbl->set[SF_initialFilterQ]) + { + val = (int)tbl->val[SF_initialFilterQ]; + vp->resonance = val; + } + vp->v.resonance = vp->resonance; +} + +void Instruments::reset_last_sample_info(void) +{ + last_sample_list = NULL; + last_sample_type = 0; + /* Set last instrument and keyrange to a value which cannot be represented + by LayerTable.val (which is a short) */ + last_sample_instrument = 0x80000000; + last_sample_keyrange = 0x80000000; +} + +int Instruments::abscent_to_Hz(int abscents) +{ + return (int)(8.176 * pow(2.0, (double)abscents / 1200.0)); +} + +/*----------------------------------------------------------------*/ + +#define SF_MODENV_CENT_MAX 1200 /* Live! allows only +-1200cents. */ + +/* calculate root key & fine tune */ +void Instruments::set_rootkey(SFInfo *sf, SampleList *vp, LayerTable *tbl) +{ + SFSampleInfo *sp = &sf->sample[tbl->val[SF_sampleId]]; + int temp; + + /* scale factor */ + vp->v.scale_factor = 1024 * (double) tbl->val[SF_scaleTuning] / 100 + 0.5; + /* set initial root key & fine tune */ + if (sf->version == 1 && tbl->set[SF_samplePitch]) { + /* set from sample pitch */ + vp->root = tbl->val[SF_samplePitch] / 100; + vp->tune = -tbl->val[SF_samplePitch] % 100; + if (vp->tune <= -50) + vp->root++, vp->tune += 100; + } else { + /* from sample info */ + vp->root = sp->originalPitch; + vp->tune = (int8_t) sp->pitchCorrection; + } + /* orverride root key */ + if (tbl->set[SF_rootKey]) + vp->root = tbl->val[SF_rootKey]; + else if (vp->bank == 128 && vp->v.scale_factor != 0) + vp->tune += int16_t((vp->keynote - sp->originalPitch) * 100 * (double) vp->v.scale_factor / 1024); + vp->tune += tbl->val[SF_coarseTune] * 100 + tbl->val[SF_fineTune]; + /* correct too high pitch */ + if (vp->root >= vp->high + 60) + vp->root -= 60; + vp->v.tremolo_to_pitch = + (tbl->set[SF_lfo1ToPitch]) ? tbl->val[SF_lfo1ToPitch] : 0; + vp->v.tremolo_to_fc = + (tbl->set[SF_lfo1ToFilterFc]) ? tbl->val[SF_lfo1ToFilterFc] : 0; + vp->v.modenv_to_pitch = + (tbl->set[SF_env1ToPitch]) ? tbl->val[SF_env1ToPitch] : 0; + /* correct tune with the sustain level of modulation envelope */ + temp = vp->v.modenv_to_pitch + * (double) (1000 - tbl->val[SF_sustainEnv1]) / 1000 + 0.5; + vp->tune += temp, vp->v.modenv_to_pitch -= temp; + vp->v.modenv_to_fc = + (tbl->set[SF_env1ToFilterFc]) ? tbl->val[SF_env1ToFilterFc] : 0; +} + +void Instruments::set_rootfreq(SampleList *vp) +{ + int root = vp->root; + int tune = 0.5 - 256 * (double) vp->tune / 100; + + /* 0 <= tune < 255 */ + while (tune < 0) + root--, tune += 256; + while (tune > 255) + root++, tune -= 256; + if (root < 0) { + vp->v.root_freq = freq_table[0] * (double) bend_fine[tune] + / bend_coarse[-root] + 0.5; + vp->v.scale_freq = 0; /* scale freq */ + } else if (root > 127) { + vp->v.root_freq = freq_table[127] * (double) bend_fine[tune] + * bend_coarse[root - 127] + 0.5; + vp->v.scale_freq = 127; /* scale freq */ + } else { + vp->v.root_freq = freq_table[root] * (double) bend_fine[tune] + 0.5; + vp->v.scale_freq = root; /* scale freq */ + } +} + +/*----------------------------------------------------------------*/ + + +/*Pseudo Reverb*/ +extern int32_t modify_release; + + +/* volume envelope parameters */ +void Instruments::convert_volume_envelope(SampleList *vp, LayerTable *tbl) +{ + vp->attack = to_rate(65535, tbl->val[SF_attackEnv2]); + vp->hold = to_rate(1, tbl->val[SF_holdEnv2]); + vp->sustain = calc_sustain(tbl->val[SF_sustainEnv2]); + vp->decay = to_rate(65533 - vp->sustain, tbl->val[SF_decayEnv2]); + if(modify_release) /* Pseudo Reverb */ + vp->release = calc_rate(65535, modify_release); + else + vp->release = to_rate(65535, tbl->val[SF_releaseEnv2]); + vp->v.envelope_delay = playback_rate * + to_msec(tbl->val[SF_delayEnv2]) * 0.001; + + /* convert modulation envelope */ + vp->modattack = to_rate(65535, tbl->val[SF_attackEnv1]); + vp->modhold = to_rate(1, tbl->val[SF_holdEnv1]); + vp->modsustain = calc_sustain(tbl->val[SF_sustainEnv1]); + vp->moddecay = to_rate(65533 - vp->modsustain, tbl->val[SF_decayEnv1]); + if(modify_release) /* Pseudo Reverb */ + vp->modrelease = calc_rate(65535, modify_release); + else + vp->modrelease = to_rate(65535, tbl->val[SF_releaseEnv1]); + vp->v.modenv_delay = playback_rate * + to_msec(tbl->val[SF_delayEnv1]) * 0.001; + + vp->v.modes |= MODES_ENVELOPE; +} + + +/*---------------------------------------------------------------- + * tremolo (LFO1) conversion + *----------------------------------------------------------------*/ + +void Instruments::convert_tremolo(SampleList *vp, LayerTable *tbl) +{ + int32_t freq; + double level; + + if (!tbl->set[SF_lfo1ToVolume]) + return; + + level = pow(10.0, (double)abs(tbl->val[SF_lfo1ToVolume]) / -200.0); + vp->v.tremolo_depth = 256 * (1.0 - level); + if ((int)tbl->val[SF_lfo1ToVolume] < 0) { vp->v.tremolo_depth = -vp->v.tremolo_depth; } + + /* frequency in mHz */ + if (!tbl->set[SF_freqLfo1]) + freq = 0; + else + { + freq = (int)tbl->val[SF_freqLfo1]; + freq = TO_MHZ(freq); + } + + /* convert mHz to sine table increment; 1024<v.tremolo_phase_increment = ((playback_rate / 1000 * freq) >> RATE_SHIFT) / control_ratio; + vp->v.tremolo_delay = playback_rate * + to_msec(tbl->val[SF_delayLfo1]) * 0.001; +} + +/*---------------------------------------------------------------- + * vibrato (LFO2) conversion + *----------------------------------------------------------------*/ + +void Instruments::convert_vibrato(SampleList *vp, LayerTable *tbl) +{ + int32_t shift, freq; + + if (!tbl->set[SF_lfo2ToPitch]) { + vp->v.vibrato_control_ratio = 0; + return; + } + + shift = (int)tbl->val[SF_lfo2ToPitch]; + + /* cents to linear; 400cents = 256 */ + shift = shift * 256 / 400; + if (shift > 255) { shift = 255; } + else if (shift < -255) { shift = -255; } + vp->v.vibrato_depth = (int16_t)shift; + + /* frequency in mHz */ + if (!tbl->set[SF_freqLfo2]) + freq = 0; + else + { + freq = (int)tbl->val[SF_freqLfo2]; + freq = TO_MHZ(freq); + if (freq == 0) { freq = 1; } + /* convert mHz to control ratio */ + vp->v.vibrato_control_ratio = (1000 * playback_rate) / + (freq * 2 * VIBRATO_SAMPLE_INCREMENTS); + } + + vp->v.vibrato_delay = playback_rate * + to_msec(tbl->val[SF_delayLfo2]) * 0.001; +} + +} \ No newline at end of file diff --git a/src/sound/timidity++/sysdep.h b/src/sound/timidity++/sysdep.h new file mode 100644 index 0000000000..d778b837c7 --- /dev/null +++ b/src/sound/timidity++/sysdep.h @@ -0,0 +1,102 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SYSDEP_H_INCLUDED +#define SYSDEP_H_INCLUDED 1 + +#include + +#define DEFAULT_AUDIO_BUFFER_BITS 12 +#define SAMPLE_LENGTH_BITS 32 + +#include // int types are defined here + +#include "m_swap.h" + +namespace TimidityPlus +{ + + + /* Instrument files are little-endian, MIDI files big-endian, so we + need to do some conversions. */ +#define XCHG_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) + +#define LE_SHORT(x) LittleShort(x) +#define LE_LONG(x) LittleLong(x) +#define BE_SHORT(x) BigShort(x) +#define BE_LONG(x) BigLong(x) + + /* max_channels is defined in "timidity.h" */ +#if MAX_CHANNELS <= 32 + typedef struct _ChannelBitMask + { + uint32_t b; /* 32-bit bitvector */ + } ChannelBitMask; +#define CLEAR_CHANNELMASK(bits) ((bits).b = 0) +#define FILL_CHANNELMASK(bits) ((bits).b = ~0) +#define IS_SET_CHANNELMASK(bits, c) ((bits).b & (1u << (c))) +#define SET_CHANNELMASK(bits, c) ((bits).b |= (1u << (c))) +#define UNSET_CHANNELMASK(bits, c) ((bits).b &= ~(1u << (c))) +#define TOGGLE_CHANNELMASK(bits, c) ((bits).b ^= (1u << (c))) +#define COPY_CHANNELMASK(dest, src) ((dest).b = (src).b) +#define REVERSE_CHANNELMASK(bits) ((bits).b = ~(bits).b) +#define COMPARE_CHANNELMASK(bitsA, bitsB) ((bitsA).b == (bitsB).b) +#endif + + typedef int16_t sample_t; + typedef int32_t final_volume_t; +# define FINAL_VOLUME(v) (v) +# define MAX_AMP_VALUE ((1<<(AMP_BITS+1))-1) +#define MIN_AMP_VALUE (MAX_AMP_VALUE >> 9) + + typedef uint32_t splen_t; +#define SPLEN_T_MAX (splen_t)((uint32_t)0xFFFFFFFF) + +# define TIM_FSCALE(a,b) ((a) * (double)(1<<(b))) +# define TIM_FSCALENEG(a,b) ((a) * (1.0 / (double)(1<<(b)))) + + +#ifdef _WIN32 +#undef PATCH_EXT_LIST +#define PATCH_EXT_LIST { ".pat", 0 } + +#endif + + +#define PATH_SEP '/' +#define PATH_STRING "/" +#define IS_PATH_SEP(c) ((c) == PATH_SEP) +#define NLS "\n" + + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif /* M_PI */ + + +#if defined(_MSC_VER) +#define strncasecmp(a,b,c) _strnicmp((a),(b),(c)) +#define strcasecmp(a,b) _stricmp((a),(b)) +#endif /* _MSC_VER */ + +#define SAFE_CONVERT_LENGTH(len) (6 * (len) + 1) + +} +#endif /* SYSDEP_H_INCUDED */ diff --git a/src/sound/timidity++/tables.cpp b/src/sound/timidity++/tables.cpp new file mode 100644 index 0000000000..66e95a6674 --- /dev/null +++ b/src/sound/timidity++/tables.cpp @@ -0,0 +1,1012 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + The 8-bit uLaw to 16-bit PCM and the 13-bit-PCM to 8-bit uLaw + tables were lifted from the rsynth-2.0 sources. The README says: + + This is a text to speech system produced by integrating various pieces + of code and tables of data, which are all (I believe) in the public domain. + + The bulk of the intergration was done by myself, that is Nick Ing-Simmons. + I can be reached via my employer at nik@tiuk.ti.com. +*/ + +#include +#include +#include "timidity.h" +#include "common.h" +#include "tables.h" +#include "instrum.h" + + +namespace TimidityPlus +{ + +int32_t freq_table[128]; +int32_t freq_table_tuning[128][128]; +int32_t freq_table_pytha[24][128]; +int32_t freq_table_meantone[48][128]; +int32_t freq_table_pureint[48][128]; + + +static void init_freq_table(void) +{ + int i; + + for (i = 0; i < 128; i++) { + freq_table[i] = 440 * pow(2.0, (i - 69) / 12.0) * 1000 + 0.5; + } +} + +static void init_freq_table_tuning(void) +{ + int p, i; + double f; + + for (i = 0; i < 128; i++) + freq_table_tuning[0][i] = freq_table[i]; + for (i = 0; i < 128; i++) { + f = 440 * pow(2.0, (i - 69) / 12.0); + for (p = 1; p < 128; p++) + freq_table_tuning[p][i] = f * 1000 + 0.5; + } +} + +static void init_freq_table_pytha(void) +{ + int i, j, k, l; + double f; + static const double major_ratio[] = { + 1.0 / 1, 256.0 / 243, 9.0 / 8, 32.0 / 27, + 81.0 / 64, 4.0 / 3, 729.0 / 512, 3.0 / 2, + 128.0 / 81, 27.0 / 16, 16.0 / 9, 243.0 / 128 + }; + static const double minor_ratio[] = { + 1.0 / 1, 2187.0 / 2048, 9.0 / 8, 19683.0 / 16384, + 81.0 / 64, 4.0 / 3, 729.0 / 512, 3.0 / 2, + 6561.0 / 4096, 27.0 / 16, 16.0 / 9, 243.0 / 128 + }; + + for (i = 0; i < 12; i++) + for (j = -1; j < 11; j++) { + f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5); + for (k = 0; k < 12; k++) { + l = i + j * 12 + k; + if (l < 0 || l >= 128) + continue; + freq_table_pytha[i][l] = f * major_ratio[k] * 1000 + 0.5; + freq_table_pytha[i + 12][l] = f * minor_ratio[k] * 1000 + 0.5; + } + } +} + +static void init_freq_table_meantone(void) +{ + int i, j, k, l; + double f; + static double major_ratio[12], minor_ratio[12]; + static const double sc = 81.0 / 80; + + major_ratio[0] = 1; + major_ratio[1] = 8 / pow(5.0, 5.0 / 4); + major_ratio[2] = pow(5.0, 1.0 / 2) / 2; + major_ratio[3] = 4 / pow(5.0, 3.0 / 4); + major_ratio[4] = 5.0 / 4; + major_ratio[5] = 2 / pow(5.0, 1.0 / 4); + major_ratio[6] = pow(5.0, 3.0 / 2) / 8; + major_ratio[7] = pow(5.0, 1.0 / 4); + major_ratio[8] = 8.0 / 5; + major_ratio[9] = pow(5.0, 3.0 / 4) / 2; + major_ratio[10] = 4 / pow(5.0, 1.0 / 2); + major_ratio[11] = pow(5.0, 5.0 / 4) / 4; + minor_ratio[0] = 1; + minor_ratio[1] = pow(10.0 / 3, 7.0 / 3) / 16; + minor_ratio[2] = pow(10.0 / 3, 2.0 / 3) / 2; + minor_ratio[3] = 125.0 / 108; + minor_ratio[4] = pow(10.0 / 3, 4.0 / 3) / 4; + minor_ratio[5] = 2 / pow(10.0 / 3, 1.0 / 3); + minor_ratio[6] = 25.0 / 18; + minor_ratio[7] = pow(10.0 / 3, 1.0 / 3); + minor_ratio[8] = pow(10.0 / 3, 8.0 / 3) / 16; + minor_ratio[9] = 5.0 / 3; + minor_ratio[10] = 4 / pow(10.0 / 3, 2.0 / 3); + minor_ratio[11] = pow(10.0 / 3, 5.0 / 3) / 4; + for (i = 0; i < 12; i++) + for (j = -1; j < 11; j++) { + f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5); + for (k = 0; k < 12; k++) { + l = i + j * 12 + k; + if (l < 0 || l >= 128) + continue; + freq_table_meantone[i][l] = + f * major_ratio[k] * 1000 + 0.5; + freq_table_meantone[i + 12][l] = + f * minor_ratio[k] * sc * 1000 + 0.5; + freq_table_meantone[i + 24][l] = + f * minor_ratio[k] * 1000 + 0.5; + freq_table_meantone[i + 36][l] = + f * major_ratio[k] * sc * 1000 + 0.5; + } + } +} + +static void init_freq_table_pureint(void) +{ + int i, j, k, l; + double f; + static const double major_ratio[] = { + 1.0 / 1, 16.0 / 15, 9.0 / 8, 6.0 / 5, 5.0 / 4, 4.0 / 3, + 45.0 / 32, 3.0 / 2, 8.0 / 5, 5.0 / 3, 9.0 / 5, 15.0 / 8 + }; + static const double minor_ratio[] = { + 1.0 / 1, 25.0 / 24, 10.0 / 9, 75.0 / 64, 5.0 / 4, 4.0 / 3, + 25.0 / 18, 3.0 / 2, 25.0 / 16, 5.0 / 3, 16.0 / 9, 15.0 / 8 + }; + static const double sc = 81.0 / 80; + + for (i = 0; i < 12; i++) + for (j = -1; j < 11; j++) { + f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5); + for (k = 0; k < 12; k++) { + l = i + j * 12 + k; + if (l < 0 || l >= 128) + continue; + freq_table_pureint[i][l] = + f * major_ratio[k] * 1000 + 0.5; + freq_table_pureint[i + 12][l] = + f * minor_ratio[k] * sc * 1000 + 0.5; + freq_table_pureint[i + 24][l] = + f * minor_ratio[k] * 1000 + 0.5; + freq_table_pureint[i + 36][l] = + f * major_ratio[k] * sc * 1000 + 0.5; + } + } +} + + +/* v=2.^((x/127-1) * 6) */ +double def_vol_table[1024]; + +static void init_def_vol_table(void) +{ + int i; + + for (i = 0; i < 1024; i++) + def_vol_table[i] = pow(2.0f,((double)i / 1023.0 - 1) * 6); +} + +/* v=2.^((x/127-1) * 8) */ +double gs_vol_table[1024]; + +static void init_gs_vol_table(void) +{ + int i; + + for (i = 0; i < 1024; i++) + gs_vol_table[i] = pow(2.0f,((double)i / 1023.0 - 1) * 8); +} + +double *xg_vol_table = gs_vol_table; + +double bend_fine[256]; +double bend_coarse[128]; + +static void init_bend_fine(void) +{ + int i; + + for (i = 0; i < 256; i++) + bend_fine[i] = pow(2.0, i / 12.0 / 256); +} + +static void init_bend_coarse(void) +{ + int i; + + for (i = 0; i < 128; i++) + bend_coarse[i] = pow(2.0, i / 12.0); +} + +/* + * midi_time_table(x + 16y) = midi_time_table(x) * (2^y) + * midi_time_table(64) = 1 + * then, + * midi_time_table(x) := (2^(x/16))/16 + */ +const double midi_time_table[128] = +{ + 0.06250, 0.06527, 0.06816, 0.07117, 0.07433, 0.07762, 0.08105, 0.08464, + 0.08839, 0.09230, 0.09639, 0.10066, 0.10511, 0.10977, 0.11463, 0.11970, + 0.12500, 0.13053, 0.13631, 0.14235, 0.14865, 0.15523, 0.16210, 0.16928, + 0.17678, 0.18460, 0.19278, 0.20131, 0.21022, 0.21953, 0.22925, 0.23940, + 0.25000, 0.26107, 0.27263, 0.28470, 0.29730, 0.31046, 0.32421, 0.33856, + 0.35355, 0.36921, 0.38555, 0.40262, 0.42045, 0.43906, 0.45850, 0.47880, + 0.50000, 0.52214, 0.54525, 0.56939, 0.59460, 0.62093, 0.64842, 0.67713, + 0.70711, 0.73841, 0.77111, 0.80525, 0.84090, 0.87813, 0.91700, 0.95760, + 1.00000, 1.04427, 1.09051, 1.13879, 1.18921, 1.24186, 1.29684, 1.35426, + 1.41421, 1.47683, 1.54221, 1.61049, 1.68179, 1.75625, 1.83401, 1.91521, + 2.00000, 2.08855, 2.18102, 2.27758, 2.37841, 2.48372, 2.59368, 2.70851, + 2.82843, 2.95365, 3.08442, 3.22098, 3.36359, 3.51250, 3.66802, 3.83041, + 4.00000, 4.17710, 4.36203, 4.55515, 4.75683, 4.96743, 5.18736, 5.41702, + 5.65685, 5.90730, 6.16884, 6.44196, 6.72717, 7.02501, 7.33603, 7.66083, + 8.00000, 8.35419, 8.72406, 9.11031, 9.51366, 9.93486,10.37472,10.83404, + 11.31371,11.81461,12.33769,12.88392,13.45434,14.05002,14.67206,15.32165 +}; +/* + * midi_time_table2(x) := 2^(x/16/128) (for lsb tunning) + */ +const double midi_time_table2[128] = +{ + 1.00000, 1.00034, 1.00068, 1.00102, 1.00135, 1.00169, 1.00203, 1.00237, + 1.00271, 1.00305, 1.00339, 1.00373, 1.00407, 1.00441, 1.00475, 1.00509, + 1.00543, 1.00577, 1.00611, 1.00645, 1.00679, 1.00713, 1.00747, 1.00781, + 1.00816, 1.00850, 1.00884, 1.00918, 1.00952, 1.00986, 1.01021, 1.01055, + 1.01089, 1.01123, 1.01157, 1.01192, 1.01226, 1.01260, 1.01294, 1.01329, + 1.01363, 1.01397, 1.01432, 1.01466, 1.01500, 1.01535, 1.01569, 1.01603, + 1.01638, 1.01672, 1.01707, 1.01741, 1.01776, 1.01810, 1.01844, 1.01879, + 1.01913, 1.01948, 1.01982, 1.02017, 1.02051, 1.02086, 1.02121, 1.02155, + 1.02190, 1.02224, 1.02259, 1.02294, 1.02328, 1.02363, 1.02397, 1.02432, + 1.02467, 1.02501, 1.02536, 1.02571, 1.02606, 1.02640, 1.02675, 1.02710, + 1.02745, 1.02779, 1.02814, 1.02849, 1.02884, 1.02919, 1.02953, 1.02988, + 1.03023, 1.03058, 1.03093, 1.03128, 1.03163, 1.03198, 1.03233, 1.03268, + 1.03302, 1.03337, 1.03372, 1.03407, 1.03442, 1.03477, 1.03512, 1.03548, + 1.03583, 1.03618, 1.03653, 1.03688, 1.03723, 1.03758, 1.03793, 1.03828, + 1.03863, 1.03899, 1.03934, 1.03969, 1.04004, 1.04039, 1.04075, 1.04110, + 1.04145, 1.04180, 1.04216, 1.04251, 1.04286, 1.04321, 1.04357, 1.04392 +}; + + +static double triangular_table[257]; + +void init_triangular_table(void) +{ + int i; + for (i = 0; i < 257; i++) { + triangular_table[i] = (double)(i/* - (genrand_int32() % 1)*/) / 256.0; + if(triangular_table[i] < 0) {triangular_table[i] = 0;} + else if(triangular_table[i] > 1.0) {triangular_table[i] = 1.0;} + } + triangular_table[0] = 0.0; + triangular_table[256] = 1.0; +} + +double lookup_triangular(int x) +{ + int xx = x & 0xFF; + switch ((x>>8) & 0x03) + { + default: + case 0: + return triangular_table[xx]; + case 1: + return triangular_table[0x100 - xx]; + case 2: + return -triangular_table[xx]; + case 3: + return -triangular_table[0x100 - xx]; + } +} + + +const uint8_t reverb_macro_presets[] = +{ /* CHARACTER,PRE-LPF,LEVEL,TIME,DELAY FEEDBACK,PREDELAY TIME */ + 0,3,64,80,0,0, /* 00: Room1 */ + 1,4,64,56,0,0, /* 01: Room2 */ + 2,0,64,64,0,0, /* 02: Room3 */ + 3,4,64,72,0,0, /* 03: Hall1 */ + 4,0,64,64,0,0, /* 04: Hall2 */ + 5,0,64,88,0,0, /* 05: Plate */ + 6,0,64,32,40,0, /* 06: Delay */ + 7,0,64,64,32,0, /* 07: Panning Delay */ +}; + +const uint8_t chorus_macro_presets[] = +{ /* PRE-LPF,LEVEL,FEEDBACK,DELAY,RATE,DEPTH,SEND TO REVERB,SEND TO DELAY */ + 0,64,0,112,3,5,0,0, /* 00: Chorus1 */ + 0,64,5,80,9,19,0,0, /* 01: Chorus2 */ + 0,64,8,80,3,19,0,0, /* 02: Chorus3 */ + 0,64,16,64,9,16,0,0, /* 03: Chorus4 */ + 0,64,64,127,2,24,0,0, /* 04: Feedback Chorus */ + 0,64,112,127,1,5,0,0, /* 05: Flanger */ + 0,64,0,127,0,127,0,0, /* 06: Short Delay */ + 0,64,80,127,0,127,0,0, /* 07: Short Delay(Feedback) */ +}; + +const uint8_t delay_macro_presets[] = +{ /* PRE-LPF,TIME(C),RATIO(L),RATIO(R),LEVEL(C),LEVEL(L),LEVEL(R),LEVEL,FEEDBACK,LEVEL TO REVERB */ + 0,97,1,1,127,0,0,64,79,0, /* 00: Delay1 */ + 0,106,1,1,127,0,0,64,79,0, /* 01: Delay2 */ + 0,115,1,1,127,0,0,64,63,0, /* 02: Delay3 */ + 0,83,1,1,127,0,0,64,71,0, /* 03: Delay4 */ + 0,90,12,24,0,125,60,64,73,0, /* 04: Pan Delay1 */ + 0,109,12,24,0,125,60,64,70,0, /* 05: Pan Delay2 */ + 0,115,12,24,0,120,64,64,72,0, /* 06: Pan Delay3 */ + 0,93,12,24,0,120,64,64,63,0, /* 07: Pan Delay4 */ + 0,109,12,24,0,114,60,64,60,36, /* 08: Delay to Reverb */ + 0,110,21,31,97,127,67,64,39,0, /* 09: Pan Repeat */ +}; + +const float delay_time_center_table[] = +{ /* 0x00~0x73, 0.1ms~1000ms */ + 0.1f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, + 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.2f, 4.4f, 4.6f, 4.8f, + 5.0f, 5.5f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.5f, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, + 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, + 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, + 200, 220, 240, 260, 280, 300, 320, 340, 360, 380, 400, 420, 440, 460, 480, + 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, +}; + +const float pre_delay_time_table[] = +{ + 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, + 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 2.5f, 2.6f, 2.7f, 2.8f, 2.9f, 3.0f, 3.1f, 3.2f, 3.3f, 3.4f, 3.5f, 3.6f, 3.7f, 3.8f, 3.9f, + 4.0f, 4.1f, 4.2f, 4.3f, 4.4f, 4.5f, 4.6f, 4.7f, 4.8f, 4.9f, + 5.0f, 5.5f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.5f, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, + 90, 92, 94, 96, 98, 100, 100, 100, +}; + +const float chorus_delay_time_table[] = +{ + 0.000000f, 0.078740f, 0.157480f, 0.236220f, 0.314961f, 0.393701f, 0.472441f, 0.551181f, + 0.629921f, 0.708661f, 0.787402f, 0.866142f, 0.944882f, 1.023622f, 1.102362f, 1.181102f, + 1.259843f, 1.338583f, 1.417323f, 1.496063f, 1.574803f, 1.653543f, 1.732283f, 1.811024f, + 1.889764f, 1.968504f, 2.047244f, 2.125984f, 2.204724f, 2.283465f, 2.362205f, 2.440945f, + 2.519685f, 2.598425f, 2.677165f, 2.755906f, 2.834646f, 2.913386f, 2.992126f, 3.070866f, + 3.149606f, 3.228346f, 3.307087f, 3.385827f, 3.464567f, 3.543307f, 3.622047f, 3.700787f, + 3.779528f, 3.858268f, 3.937008f, 4.015748f, 4.094488f, 4.173228f, 4.251969f, 4.330709f, + 4.409449f, 4.488189f, 4.566929f, 4.645669f, 4.724409f, 4.803150f, 4.881890f, 4.960630f, + 5.039370f, 5.118110f, 5.196850f, 5.275591f, 5.354331f, 5.433071f, 5.511811f, 5.590551f, + 5.669291f, 5.748031f, 5.826772f, 5.905512f, 5.984252f, 6.062992f, 6.141732f, 6.220472f, + 6.299213f, 6.377953f, 6.456693f, 6.535433f, 6.614173f, 6.692913f, 6.771654f, 6.850394f, + 6.929134f, 7.007874f, 7.086614f, 7.165354f, 7.244094f, 7.322835f, 7.401575f, 7.480315f, + 7.559055f, 7.637795f, 7.716535f, 7.795276f, 10.000000f, 10.555556f, 11.111111f, 11.666667f, + 12.222222f, 12.777778f, 13.333333f, 13.888889f, 14.444444f, 15.000000f, 15.555556f, 16.111111f, + 16.666667f, 17.222222f, 17.777778f, 18.333333f, 18.888889f, 19.444444f, 20.000000f, 20.555556f, + 21.111111f, 21.666667f, 22.222222f, 22.777778f, 23.333333f, 23.888889f, 24.444444f, 25.000000f, +}; + +const float rate1_table[] = +{ + 0.05f, 0.10f, 0.15f, 0.20f, 0.25f, 0.30f, 0.35f, 0.40f, 0.45f, 0.50f, 0.55f, 0.60f, 0.65f, 0.70f, 0.75f, 0.80f, + 0.85f, 0.90f, 0.95f, 1.00f, 1.05f, 1.10f, 1.15f, 1.20f, 1.25f, 1.30f, 1.35f, 1.40f, 1.45f, 1.50f, 1.55f, 1.60f, + 1.65f, 1.70f, 1.75f, 1.80f, 1.85f, 1.90f, 1.95f, 2.00f, 2.05f, 2.10f, 2.15f, 2.20f, 2.25f, 2.30f, 2.35f, 2.40f, + 2.45f, 2.50f, 2.55f, 2.60f, 2.65f, 2.70f, 2.75f, 2.80f, 2.85f, 2.90f, 2.95f, 3.00f, 3.05f, 3.10f, 3.15f, 3.20f, + 3.25f, 3.30f, 3.35f, 3.40f, 3.45f, 3.50f, 3.55f, 3.60f, 3.65f, 3.70f, 3.75f, 3.80f, 3.85f, 3.90f, 3.95f, 4.00f, + 4.05f, 4.10f, 4.15f, 4.20f, 4.25f, 4.30f, 4.35f, 4.40f, 4.45f, 4.50f, 4.55f, 4.60f, 4.65f, 4.70f, 4.75f, 4.80f, + 4.85f, 4.90f, 4.95f, 5.00f, 5.10f, 5.20f, 5.30f, 5.40f, 5.50f, 5.60f, 5.70f, 5.80f, 5.90f, 6.00f, 6.10f, 6.20f, + 6.30f, 6.40f, 6.50f, 6.60f, 6.70f, 6.80f, 6.90f, 7.00f, 7.50f, 8.00f, 8.50f, 9.00f, 9.50f, 10.00f, 10.00f, 10.00f, +}; + +/* Derivation of Perceived Volume Curve Equation: + * + * Given: delta dB = 20 * log10(amplitude_new / amplitude_old) + * delta dB of 10 == perceived volume change of 2x + * + * 10 = 20 * log10(?) + * 0.5 = log10(?) + * 10^0.5 = ? + * + * therefore: 2x perceived volume == ~3.16x amplitude + * 4x perceived volume == 10x amplitude + * + * Volume Amplitude + * ------------ --------------- + * 1 1 + * 0.25 0.1 + * 0.0625 0.01 + * 0.015625 0.001 + * 0.00390625 0.0001 + * 0.0009765625 0.00001 + * 0 0 + * + * Fit curve to table: + * + * amplification = pow(volume, 1.66096404744) + */ +double perceived_vol_table[128]; + +void init_perceived_vol_table(void) +{ + int i; + + for (i = 0; i < 128; i++) + perceived_vol_table[i] = + 127.0 * pow((double)i / 127.0, 1.66096404744); +} + +double gm2_vol_table[128]; + +void init_gm2_vol_table(void) +{ + int i; + + for(i = 0; i < 128; i++) + gm2_vol_table[i] = (i * i) / 127.0; +} + +/* measured value from SC-88STPro. + approximate expression: y = (-0.3768x6 + 0.9528x5 - 0.8253x4 + 0.2665x3 + 0.9892x2 - 0.0059x + 0.001) * 127 */ +double sc_vol_table[128] = +{ + 0.000000, 0.128905, 0.146482, 0.179815, 0.228982, 0.294049, 0.375078, 0.472120, + 0.585221, 0.714419, 0.859746, 1.021229, 1.198887, 1.392736, 1.602785, 1.829039, + 2.071501, 2.330166, 2.605028, 2.896078, 3.203301, 3.526682, 3.866202, 4.221841, + 4.593575, 4.981382, 5.385233, 5.805103, 6.240963, 6.692783, 7.160536, 7.644189, + 8.143714, 8.659080, 9.190256, 9.737215, 10.299925, 10.878360, 11.472491, 12.082292, + 12.707738, 13.348803, 14.005465, 14.677704, 15.365497, 16.068829, 16.787681, 17.522039, + 18.271890, 19.037223, 19.818029, 20.614301, 21.426034, 22.253225, 23.095873, 23.953980, + 24.827548, 25.716584, 26.621094, 27.541088, 28.476578, 29.427576, 30.394100, 31.376165, + 32.373791, 33.386998, 34.415810, 35.460249, 36.520342, 37.596115, 38.687597, 39.794815, + 40.917801, 42.056586, 43.211200, 44.381677, 45.568048, 46.770346, 47.988605, 49.222856, + 50.473131, 51.739464, 53.021883, 54.320420, 55.635102, 56.965957, 58.313010, 59.676284, + 61.055802, 62.451580, 63.863636, 65.291982, 66.736627, 68.197578, 69.674835, 71.168397, + 72.678255, 74.204399, 75.746811, 77.305466, 78.880337, 80.471388, 82.078576, 83.701851, + 85.341158, 86.996429, 88.667594, 90.354568, 92.057260, 93.775571, 95.509387, 97.258589, + 99.023042, 100.802603, 102.597116, 104.406412, 106.230309, 108.068613, 109.921115, 111.787592, + 113.667805, 115.561500, 117.468408, 119.388243, 121.320699, 123.265458, 125.222177, 127.000000, +}; + +/* measured value from SC-88STPro. + approximate expression: y = (-1.5374x6 + 4.4002x5 - 4.8309x4 + 2.572x3 + 0.1487x2 + 0.2412x + 0.0044) * 127 */ +double sc_vel_table[128] = +{ + 0.000000, 0.801328, 1.047122, 1.297056, 1.551953, 1.812583, 2.079668, 2.353885, + 2.635863, 2.926190, 3.225412, 3.534034, 3.852525, 4.181317, 4.520806, 4.871357, + 5.233303, 5.606946, 5.992560, 6.390392, 6.800666, 7.223577, 7.659301, 8.107993, + 8.569785, 9.044792, 9.533111, 10.034824, 10.549997, 11.078680, 11.620912, 12.176722, + 12.746124, 13.329126, 13.925725, 14.535911, 15.159666, 15.796968, 16.447787, 17.112090, + 17.789842, 18.481003, 19.185529, 19.903380, 20.634509, 21.378873, 22.136426, 22.907127, + 23.690932, 24.487801, 25.297696, 26.120582, 26.956425, 27.805197, 28.666872, 29.541428, + 30.428848, 31.329119, 32.242232, 33.168184, 34.106974, 35.058609, 36.023099, 37.000459, + 37.990710, 38.993877, 40.009989, 41.039080, 42.081190, 43.136361, 44.204639, 45.286075, + 46.380723, 47.488640, 48.609885, 49.744520, 50.892609, 52.054217, 53.229410, 54.418254, + 55.620816, 56.837160, 58.067351, 59.311450, 60.569517, 61.841607, 63.127770, 64.428053, + 65.742497, 67.071133, 68.413988, 69.771079, 71.142412, 72.527985, 73.927782, 75.341776, + 76.769925, 78.212172, 79.668444, 81.138652, 82.622687, 84.120420, 85.631702, 87.156361, + 88.694200, 90.244998, 91.808507, 93.384452, 94.972526, 96.572391, 98.183679, 99.805985, + 101.438869, 103.081852, 104.734417, 106.396007, 108.066018, 109.743805, 111.428675, 113.119886, + 114.816648, 116.518116, 118.223392, 119.931522, 121.641492, 123.352230, 125.062599, 127.000000, +}; + +double sc_pan_table[129] = +{ + 0.000000, 0.000000, 0.999479, 2.011744, 3.036530, 4.073569, 5.122593, 6.183332, + 7.255517, 8.338874, 9.433131, 10.538014, 11.653247, 12.778552, 13.913653, 15.058271, + 16.212123, 17.374930, 18.546409, 19.726275, 20.914243, 22.110027, 23.313339, 24.523890, + 25.741391, 26.965550, 28.196074, 29.432671, 30.675045, 31.922900, 33.175939, 34.433863, + 35.696373, 36.963168, 38.233946, 39.508403, 40.786235, 42.067137, 43.350800, 44.636918, + 45.925181, 47.215278, 48.506897, 49.799726, 51.093451, 52.387755, 53.682323, 54.976837, + 56.270977, 57.564424, 58.856855, 60.147950, 61.437382, 62.724829, 64.009963, 65.292456, + 66.571981, 67.848208, 69.120804, 70.389439, 71.653778, 72.913487, 74.168230, 75.417670, + 76.661468, 77.899286, 79.130781, 80.355614, 81.573439, 82.783913, 83.986691, 85.181425, + 86.367767, 87.545369, 88.713880, 89.872949, 91.022222, 92.161346, 93.289965, 94.407723, + 95.514263, 96.609225, 97.692249, 98.762975, 99.821039, 100.866079, 101.897729, 102.915623, + 103.919394, 104.908673, 105.883091, 106.842276, 107.785858, 108.713461, 109.624713, 110.519236, + 111.396655, 112.256590, 113.098663, 113.922493, 114.727699, 115.513896, 116.280702, 117.027730, + 117.754595, 118.460908, 119.146280, 119.810321, 120.452639, 121.072843, 121.670538, 122.245328, + 122.796819, 123.324612, 123.828308, 124.307509, 124.761812, 125.190815, 125.594115, 125.971307, + 126.321986, 126.645744, 126.942172, 127.210862, 127.451402, 127.663381, 127.846385, 128.000000, + 128.000000, +}; + +double gm2_pan_table[129]; +double *pan_table = sc_pan_table; + +void init_gm2_pan_table(void) +{ + int i; + + gm2_pan_table[0] = 0; + for(i = 0; i < 127; i++) + gm2_pan_table[i + 1] = sin(M_PI / 2 * i / 126) * 128; + /* lookup_sine(i * SINE_CYCLE_LENGTH / 4 / 126) */ + gm2_pan_table[128] = 128.0; +} + +double sc_drum_level_table[128] = +{ + 0.007874, 0.007874, 0.031496, 0.070866, 0.125984, 0.196850, 0.283465, 0.385827, + 0.503937, 0.637795, 0.787402, 0.952756, 1.133858, 1.330709, 1.543307, 1.771654, + 2.015748, 2.275591, 2.551181, 2.842520, 3.149606, 3.472441, 3.811024, 4.165354, + 4.535433, 4.921260, 5.322835, 5.740157, 6.173228, 6.622047, 7.086614, 7.566929, + 8.062992, 8.574803, 9.102362, 9.645669, 10.204724, 10.779528, 11.370079, 11.976378, + 12.598425, 13.236220, 13.889764, 14.559055, 15.244094, 15.944882, 16.661417, 17.393701, + 18.141732, 18.905512, 19.685039, 20.480315, 21.291339, 22.118110, 22.960630, 23.818898, + 24.692913, 25.582677, 26.488189, 27.409449, 28.346457, 29.299213, 30.267717, 31.251969, + 32.251969, 33.267717, 34.299213, 35.346457, 36.409449, 37.488189, 38.582677, 39.692913, + 40.818898, 41.960630, 43.118110, 44.291339, 45.480315, 46.685039, 47.905512, 49.141732, + 50.393701, 51.661417, 52.944882, 54.244094, 55.559055, 56.889764, 58.236220, 59.598425, + 60.976378, 62.370079, 63.779528, 65.204724, 66.645669, 68.102362, 69.574803, 71.062992, + 72.566929, 74.086614, 75.622047, 77.173228, 78.740157, 80.322835, 81.921260, 83.535433, + 85.165354, 86.811024, 88.472441, 90.149606, 91.842520, 93.551181, 95.275591, 97.015748, + 98.771654, 100.543307, 102.330709, 104.133858, 105.952756, 107.787402, 109.637795, 111.503937, + 113.385827, 115.283465, 117.196850, 119.125984, 121.070866, 123.031496, 125.007874, 127.000000, +}; + +double attack_vol_table[1024]; + +void init_attack_vol_table(void) +{ + int i; + + for (i = 0; i < 1024; i++) + attack_vol_table[i] = i / 1023.0; +} + +float sc_eg_decay_table[128] = +{ + 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, + 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, + 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 74.928977f, 68.580327f, + 62.750584f, 57.398521f, 52.486110f, 47.978275f, 43.842671f, 40.049475f, 36.571192f, 33.382477f, + 30.459975f, 27.782161f, 25.329207f, 23.082845f, 21.026252f, 19.143935f, 17.421629f, 15.846202f, + 14.405568f, 13.088604f, 11.885077f, 10.785570f, 9.781425f, 8.864676f, 8.028000f, 7.264663f, + 6.568475f, 5.933745f, 5.355241f, 4.828153f, 4.348058f, 3.910885f, 3.512890f, 3.150618f, + 2.820877f, 2.520709f, 2.247348f, 1.998183f, 1.770681f, 1.562261f, 1.369978f, 1.189386f, + 1.000000f, 0.838459f, 0.726301f, 0.635581f, 0.559656f, 0.494986f, 0.439286f, 0.390934f, + 0.348712f, 0.311669f, 0.279045f, 0.250221f, 0.224684f, 0.202006f, 0.181825f, 0.163831f, + 0.147761f, 0.133387f, 0.120513f, 0.108967f, 0.098600f, 0.089282f, 0.080897f, 0.073346f, + 0.066540f, 0.060399f, 0.054854f, 0.049845f, 0.045315f, 0.041217f, 0.037506f, 0.034144f, + 0.031097f, 0.028333f, 0.025826f, 0.023549f, 0.021480f, 0.019601f, 0.017892f, 0.016337f, + 0.014923f, 0.013635f, 0.012462f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, + 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, + 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, +}; + +float sc_eg_release_table[128] = +{ + 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, + 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, + 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 25.299110f, 23.425992f, + 21.691556f, 20.085537f, 18.598425f, 17.221418f, 15.946363f, 14.765711f, 13.672474f, 12.660179f, + 11.722833f, 10.854887f, 10.051203f, 9.307023f, 8.617941f, 7.979878f, 7.389056f, 6.841978f, + 6.335406f, 5.866339f, 5.432002f, 5.029822f, 4.657419f, 4.312589f, 3.993290f, 3.697631f, + 3.423862f, 3.170363f, 2.935633f, 2.718282f, 2.517023f, 2.330665f, 2.158106f, 1.998322f, + 1.850368f, 1.713369f, 1.586513f, 1.469049f, 1.360282f, 1.259569f, 1.166311f, 1.079959f, + 1.000000f, 0.925961f, 0.857404f, 0.793923f, 0.735141f, 0.680712f, 0.630313f, 0.583645f, + 0.540433f, 0.500420f, 0.463369f, 0.429062f, 0.397295f, 0.367879f, 0.340642f, 0.315421f, + 0.292068f, 0.270443f, 0.250420f, 0.231879f, 0.214711f, 0.198814f, 0.184094f, 0.170464f, + 0.157843f, 0.146157f, 0.135335f, 0.125315f, 0.116037f, 0.107446f, 0.099491f, 0.092124f, + 0.085304f, 0.078988f, 0.073140f, 0.067724f, 0.062710f, 0.058067f, 0.053768f, 0.049787f, + 0.046101f, 0.042688f, 0.039527f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, + 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, + 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, +}; + +float sc_eg_attack_table[128] = +{ + 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, + 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, + 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 75.473398f, 68.815182f, + 62.729632f, 57.168464f, 52.087395f, 47.445819f, 43.206507f, 39.335325f, 35.800987f, 32.574817f, + 29.630534f, 26.944060f, 24.493331f, 22.258137f, 20.219967f, 18.361866f, 16.668311f, 15.125088f, + 13.719184f, 12.438688f, 11.272700f, 10.211246f, 9.245197f, 8.366205f, 7.566631f, 6.839489f, + 6.178391f, 5.577493f, 5.031451f, 4.535378f, 4.084805f, 3.675641f, 3.304143f, 2.966879f, + 2.660703f, 2.382715f, 2.130237f, 1.900768f, 1.691929f, 1.501374f, 1.326560f, 1.163993f, + 1.000000f, 0.859112f, 0.753830f, 0.666057f, 0.591041f, 0.526103f, 0.469431f, 0.419689f, + 0.375841f, 0.337054f, 0.302650f, 0.272061f, 0.244810f, 0.220489f, 0.198750f, 0.179292f, + 0.161854f, 0.146210f, 0.132159f, 0.119529f, 0.108164f, 0.097931f, 0.088710f, 0.080394f, + 0.072891f, 0.066115f, 0.059994f, 0.054461f, 0.049456f, 0.044927f, 0.040827f, 0.037114f, + 0.033749f, 0.030699f, 0.027932f, 0.025422f, 0.023145f, 0.021077f, 0.019199f, 0.017492f, + 0.015941f, 0.014532f, 0.013250f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, + 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, + 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, +}; + +double sb_vol_table[1024]; + +void init_sb_vol_table(void) +{ + int i; + + for (i = 0; i < 1024; i++) + sb_vol_table[i] = pow(10.0, (double)(1023 - i) * 960.0 / (1023.0 * -200.0)); +} + +double modenv_vol_table[1024]; + +void init_modenv_vol_table(void) +{ + int i; + double x; + + modenv_vol_table[0] = (float)0; + for (i = 1; i < 1023; i++) { + x = (1.0 - (-20.0 / 96.0 * log(((double)i * (double)i) / (1023.0 * 1023.0)) / log(10.0))); + if (x < 0) {x = 0;} + modenv_vol_table[i] = log(x + 1) / log(2); + } + modenv_vol_table[1023] = (float)1.0; +} + +const float cb_to_amp_table[961] = +{ + 1.000000f, 0.995677f, 0.991373f, 0.987088f, 0.982821f, 0.978572f, 0.974342f, 0.970130f, + 0.965936f, 0.961761f, 0.957603f, 0.953464f, 0.949342f, 0.945238f, 0.941152f, 0.937084f, + 0.933033f, 0.929000f, 0.924984f, 0.920985f, 0.917004f, 0.913040f, 0.909093f, 0.905163f, + 0.901250f, 0.897355f, 0.893475f, 0.889613f, 0.885768f, 0.881939f, 0.878126f, 0.874330f, + 0.870551f, 0.866787f, 0.863040f, 0.859310f, 0.855595f, 0.851896f, 0.848214f, 0.844547f, + 0.840896f, 0.837261f, 0.833642f, 0.830038f, 0.826450f, 0.822878f, 0.819321f, 0.815779f, + 0.812252f, 0.808741f, 0.805245f, 0.801764f, 0.798298f, 0.794848f, 0.791412f, 0.787990f, + 0.784584f, 0.781192f, 0.777816f, 0.774453f, 0.771105f, 0.767772f, 0.764453f, 0.761149f, + 0.757858f, 0.754582f, 0.751320f, 0.748072f, 0.744839f, 0.741619f, 0.738413f, 0.735221f, + 0.732043f, 0.728878f, 0.725728f, 0.722590f, 0.719467f, 0.716357f, 0.713260f, 0.710177f, + 0.707107f, 0.704050f, 0.701007f, 0.697976f, 0.694959f, 0.691955f, 0.688964f, 0.685986f, + 0.683020f, 0.680068f, 0.677128f, 0.674201f, 0.671286f, 0.668384f, 0.665495f, 0.662618f, + 0.659754f, 0.656902f, 0.654062f, 0.651235f, 0.648420f, 0.645617f, 0.642826f, 0.640047f, + 0.637280f, 0.634525f, 0.631783f, 0.629051f, 0.626332f, 0.623625f, 0.620929f, 0.618245f, + 0.615572f, 0.612911f, 0.610262f, 0.607624f, 0.604997f, 0.602382f, 0.599778f, 0.597185f, + 0.594604f, 0.592033f, 0.589474f, 0.586926f, 0.584389f, 0.581862f, 0.579347f, 0.576843f, + 0.574349f, 0.571866f, 0.569394f, 0.566933f, 0.564482f, 0.562042f, 0.559612f, 0.557193f, + 0.554785f, 0.552387f, 0.549999f, 0.547621f, 0.545254f, 0.542897f, 0.540550f, 0.538213f, + 0.535887f, 0.533570f, 0.531264f, 0.528967f, 0.526681f, 0.524404f, 0.522137f, 0.519880f, + 0.517632f, 0.515395f, 0.513167f, 0.510949f, 0.508740f, 0.506541f, 0.504351f, 0.502171f, + 0.500000f, 0.497839f, 0.495687f, 0.493544f, 0.491410f, 0.489286f, 0.487171f, 0.485065f, + 0.482968f, 0.480880f, 0.478802f, 0.476732f, 0.474671f, 0.472619f, 0.470576f, 0.468542f, + 0.466516f, 0.464500f, 0.462492f, 0.460493f, 0.458502f, 0.456520f, 0.454547f, 0.452582f, + 0.450625f, 0.448677f, 0.446738f, 0.444807f, 0.442884f, 0.440969f, 0.439063f, 0.437165f, + 0.435275f, 0.433394f, 0.431520f, 0.429655f, 0.427798f, 0.425948f, 0.424107f, 0.422274f, + 0.420448f, 0.418631f, 0.416821f, 0.415019f, 0.413225f, 0.411439f, 0.409660f, 0.407889f, + 0.406126f, 0.404371f, 0.402623f, 0.400882f, 0.399149f, 0.397424f, 0.395706f, 0.393995f, + 0.392292f, 0.390596f, 0.388908f, 0.387227f, 0.385553f, 0.383886f, 0.382227f, 0.380574f, + 0.378929f, 0.377291f, 0.375660f, 0.374036f, 0.372419f, 0.370809f, 0.369207f, 0.367611f, + 0.366021f, 0.364439f, 0.362864f, 0.361295f, 0.359733f, 0.358178f, 0.356630f, 0.355088f, + 0.353553f, 0.352025f, 0.350503f, 0.348988f, 0.347480f, 0.345977f, 0.344482f, 0.342993f, + 0.341510f, 0.340034f, 0.338564f, 0.337100f, 0.335643f, 0.334192f, 0.332748f, 0.331309f, + 0.329877f, 0.328451f, 0.327031f, 0.325617f, 0.324210f, 0.322808f, 0.321413f, 0.320024f, + 0.318640f, 0.317263f, 0.315891f, 0.314526f, 0.313166f, 0.311812f, 0.310464f, 0.309122f, + 0.307786f, 0.306456f, 0.305131f, 0.303812f, 0.302499f, 0.301191f, 0.299889f, 0.298593f, + 0.297302f, 0.296017f, 0.294737f, 0.293463f, 0.292194f, 0.290931f, 0.289674f, 0.288421f, + 0.287175f, 0.285933f, 0.284697f, 0.283466f, 0.282241f, 0.281021f, 0.279806f, 0.278597f, + 0.277392f, 0.276193f, 0.274999f, 0.273811f, 0.272627f, 0.271448f, 0.270275f, 0.269107f, + 0.267943f, 0.266785f, 0.265632f, 0.264484f, 0.263340f, 0.262202f, 0.261068f, 0.259940f, + 0.258816f, 0.257697f, 0.256583f, 0.255474f, 0.254370f, 0.253270f, 0.252175f, 0.251085f, + 0.250000f, 0.248919f, 0.247843f, 0.246772f, 0.245705f, 0.244643f, 0.243585f, 0.242533f, + 0.241484f, 0.240440f, 0.239401f, 0.238366f, 0.237336f, 0.236310f, 0.235288f, 0.234271f, + 0.233258f, 0.232250f, 0.231246f, 0.230246f, 0.229251f, 0.228260f, 0.227273f, 0.226291f, + 0.225313f, 0.224339f, 0.223369f, 0.222403f, 0.221442f, 0.220485f, 0.219532f, 0.218583f, + 0.217638f, 0.216697f, 0.215760f, 0.214827f, 0.213899f, 0.212974f, 0.212053f, 0.211137f, + 0.210224f, 0.209315f, 0.208411f, 0.207510f, 0.206613f, 0.205719f, 0.204830f, 0.203945f, + 0.203063f, 0.202185f, 0.201311f, 0.200441f, 0.199575f, 0.198712f, 0.197853f, 0.196998f, + 0.196146f, 0.195298f, 0.194454f, 0.193613f, 0.192776f, 0.191943f, 0.191113f, 0.190287f, + 0.189465f, 0.188646f, 0.187830f, 0.187018f, 0.186210f, 0.185405f, 0.184603f, 0.183805f, + 0.183011f, 0.182220f, 0.181432f, 0.180648f, 0.179867f, 0.179089f, 0.178315f, 0.177544f, + 0.176777f, 0.176013f, 0.175252f, 0.174494f, 0.173740f, 0.172989f, 0.172241f, 0.171496f, + 0.170755f, 0.170017f, 0.169282f, 0.168550f, 0.167822f, 0.167096f, 0.166374f, 0.165655f, + 0.164938f, 0.164225f, 0.163516f, 0.162809f, 0.162105f, 0.161404f, 0.160706f, 0.160012f, + 0.159320f, 0.158631f, 0.157946f, 0.157263f, 0.156583f, 0.155906f, 0.155232f, 0.154561f, + 0.153893f, 0.153228f, 0.152565f, 0.151906f, 0.151249f, 0.150595f, 0.149944f, 0.149296f, + 0.148651f, 0.148008f, 0.147368f, 0.146731f, 0.146097f, 0.145466f, 0.144837f, 0.144211f, + 0.143587f, 0.142967f, 0.142349f, 0.141733f, 0.141121f, 0.140511f, 0.139903f, 0.139298f, + 0.138696f, 0.138097f, 0.137500f, 0.136905f, 0.136313f, 0.135724f, 0.135138f, 0.134553f, + 0.133972f, 0.133393f, 0.132816f, 0.132242f, 0.131670f, 0.131101f, 0.130534f, 0.129970f, + 0.129408f, 0.128849f, 0.128292f, 0.127737f, 0.127185f, 0.126635f, 0.126088f, 0.125543f, + 0.125000f, 0.124460f, 0.123922f, 0.123386f, 0.122853f, 0.122322f, 0.121793f, 0.121266f, + 0.120742f, 0.120220f, 0.119700f, 0.119183f, 0.118668f, 0.118155f, 0.117644f, 0.117135f, + 0.116629f, 0.116125f, 0.115623f, 0.115123f, 0.114626f, 0.114130f, 0.113637f, 0.113145f, + 0.112656f, 0.112169f, 0.111684f, 0.111202f, 0.110721f, 0.110242f, 0.109766f, 0.109291f, + 0.108819f, 0.108348f, 0.107880f, 0.107414f, 0.106949f, 0.106487f, 0.106027f, 0.105568f, + 0.105112f, 0.104658f, 0.104205f, 0.103755f, 0.103306f, 0.102860f, 0.102415f, 0.101972f, + 0.101532f, 0.101093f, 0.100656f, 0.100221f, 0.099787f, 0.099356f, 0.098926f, 0.098499f, + 0.098073f, 0.097649f, 0.097227f, 0.096807f, 0.096388f, 0.095972f, 0.095557f, 0.095144f, + 0.094732f, 0.094323f, 0.093915f, 0.093509f, 0.093105f, 0.092702f, 0.092302f, 0.091903f, + 0.091505f, 0.091110f, 0.090716f, 0.090324f, 0.089933f, 0.089545f, 0.089158f, 0.088772f, + 0.088388f, 0.088006f, 0.087626f, 0.087247f, 0.086870f, 0.086494f, 0.086120f, 0.085748f, + 0.085378f, 0.085008f, 0.084641f, 0.084275f, 0.083911f, 0.083548f, 0.083187f, 0.082827f, + 0.082469f, 0.082113f, 0.081758f, 0.081404f, 0.081052f, 0.080702f, 0.080353f, 0.080006f, + 0.079660f, 0.079316f, 0.078973f, 0.078631f, 0.078292f, 0.077953f, 0.077616f, 0.077281f, + 0.076947f, 0.076614f, 0.076283f, 0.075953f, 0.075625f, 0.075298f, 0.074972f, 0.074648f, + 0.074325f, 0.074004f, 0.073684f, 0.073366f, 0.073049f, 0.072733f, 0.072418f, 0.072105f, + 0.071794f, 0.071483f, 0.071174f, 0.070867f, 0.070560f, 0.070255f, 0.069952f, 0.069649f, + 0.069348f, 0.069048f, 0.068750f, 0.068453f, 0.068157f, 0.067862f, 0.067569f, 0.067277f, + 0.066986f, 0.066696f, 0.066408f, 0.066121f, 0.065835f, 0.065550f, 0.065267f, 0.064985f, + 0.064704f, 0.064424f, 0.064146f, 0.063869f, 0.063592f, 0.063318f, 0.063044f, 0.062771f, + 0.062500f, 0.062230f, 0.061961f, 0.061693f, 0.061426f, 0.061161f, 0.060896f, 0.060633f, + 0.060371f, 0.060110f, 0.059850f, 0.059591f, 0.059334f, 0.059077f, 0.058822f, 0.058568f, + 0.058315f, 0.058062f, 0.057811f, 0.057562f, 0.057313f, 0.057065f, 0.056818f, 0.056573f, + 0.056328f, 0.056085f, 0.055842f, 0.055601f, 0.055360f, 0.055121f, 0.054883f, 0.054646f, + 0.054409f, 0.054174f, 0.053940f, 0.053707f, 0.053475f, 0.053244f, 0.053013f, 0.052784f, + 0.052556f, 0.052329f, 0.052103f, 0.051877f, 0.051653f, 0.051430f, 0.051208f, 0.050986f, + 0.050766f, 0.050546f, 0.050328f, 0.050110f, 0.049894f, 0.049678f, 0.049463f, 0.049249f, + 0.049037f, 0.048825f, 0.048613f, 0.048403f, 0.048194f, 0.047986f, 0.047778f, 0.047572f, + 0.047366f, 0.047161f, 0.046958f, 0.046755f, 0.046552f, 0.046351f, 0.046151f, 0.045951f, + 0.045753f, 0.045555f, 0.045358f, 0.045162f, 0.044967f, 0.044772f, 0.044579f, 0.044386f, + 0.044194f, 0.044003f, 0.043813f, 0.043624f, 0.043435f, 0.043247f, 0.043060f, 0.042874f, + 0.042689f, 0.042504f, 0.042320f, 0.042138f, 0.041955f, 0.041774f, 0.041593f, 0.041414f, + 0.041235f, 0.041056f, 0.040879f, 0.040702f, 0.040526f, 0.040351f, 0.040177f, 0.040003f, + 0.039830f, 0.039658f, 0.039486f, 0.039316f, 0.039146f, 0.038977f, 0.038808f, 0.038640f, + 0.038473f, 0.038307f, 0.038141f, 0.037976f, 0.037812f, 0.037649f, 0.037486f, 0.037324f, + 0.037163f, 0.037002f, 0.036842f, 0.036683f, 0.036524f, 0.036366f, 0.036209f, 0.036053f, + 0.035897f, 0.035742f, 0.035587f, 0.035433f, 0.035280f, 0.035128f, 0.034976f, 0.034825f, + 0.034674f, 0.034524f, 0.034375f, 0.034226f, 0.034078f, 0.033931f, 0.033784f, 0.033638f, + 0.033493f, 0.033348f, 0.033204f, 0.033060f, 0.032918f, 0.032775f, 0.032634f, 0.032492f, + 0.032352f, 0.032212f, 0.032073f, 0.031934f, 0.031796f, 0.031659f, 0.031522f, 0.031386f, + 0.031250f, 0.031115f, 0.030980f, 0.030846f, 0.030713f, 0.030580f, 0.030448f, 0.030317f, + 0.030186f, 0.030055f, 0.029925f, 0.029796f, 0.029667f, 0.029539f, 0.029411f, 0.029284f, + 0.029157f, 0.029031f, 0.028906f, 0.028781f, 0.028656f, 0.028533f, 0.028409f, 0.028286f, + 0.028164f, 0.028042f, 0.027921f, 0.027800f, 0.027680f, 0.027561f, 0.027441f, 0.027323f, + 0.027205f, 0.027087f, 0.026970f, 0.026853f, 0.026737f, 0.026622f, 0.026507f, 0.026392f, + 0.026278f, 0.026164f, 0.026051f, 0.025939f, 0.025827f, 0.025715f, 0.025604f, 0.025493f, + 0.025383f, 0.025273f, 0.025164f, 0.025055f, 0.024947f, 0.024839f, 0.024732f, 0.024625f, + 0.024518f, 0.024412f, 0.024307f, 0.024202f, 0.024097f, 0.023993f, 0.023889f, 0.023786f, + 0.023683f, 0.023581f, 0.023479f, 0.023377f, 0.023276f, 0.023176f, 0.023075f, 0.022976f, + 0.022876f, 0.022777f, 0.022679f, 0.022581f, 0.022483f, 0.022386f, 0.022289f, 0.022193f, + 0.022097f, 0.022002f, 0.021906f, 0.021812f, 0.021717f, 0.021624f, 0.021530f, 0.021437f, + 0.021344f, 0.021252f, 0.021160f, 0.021069f, 0.020978f, 0.020887f, 0.020797f, 0.020707f, + 0.020617f, 0.020528f, 0.020439f, 0.020351f, 0.020263f, 0.020176f, 0.020088f, 0.020001f, + 0.019915f, 0.019829f, 0.019743f, 0.019658f, 0.019573f, 0.019488f, 0.019404f, 0.019320f, + 0.019237f, 0.019153f, 0.019071f, 0.018988f, 0.018906f, 0.018824f, 0.018743f, 0.018662f, + 0.018581f, 0.018501f, 0.018421f, 0.018341f, 0.018262f, 0.018183f, 0.018105f, 0.018026f, + 0.017948f, 0.017871f, 0.017794f, 0.017717f, 0.017640f, 0.017564f, 0.017488f, 0.017412f, + 0.017337f, 0.017262f, 0.017187f, 0.017113f, 0.017039f, 0.016966f, 0.016892f, 0.016819f, + 0.016746f, 0.016674f, 0.016602f, 0.016530f, 0.016459f, 0.016388f, 0.016317f, 0.016246f, + 0.016176f, 0.016106f, 0.016036f, 0.015967f, 0.015898f, 0.015829f, 0.015761f, 0.015693f, + 0.015625f, +}; + +/* Reverb Time in sec */ +const float reverb_time_table[128] = +{ + 0.410349f, 0.440872f, 0.468882f, 0.494640f, 0.518394f, 0.540373f, 0.560793f, 0.579854f, + 0.597743f, 0.614635f, 0.630688f, 0.646053f, 0.660866f, 0.675251f, 0.689325f, 0.703192f, + 0.716947f, 0.730676f, 0.744456f, 0.758358f, 0.772441f, 0.786761f, 0.801365f, 0.816293f, + 0.831583f, 0.847262f, 0.863356f, 0.879886f, 0.896866f, 0.914308f, 0.932223f, 0.950614f, + 0.969484f, 0.988835f, 1.008663f, 1.028967f, 1.049741f, 1.070980f, 1.092677f, 1.114826f, + 1.137419f, 1.160450f, 1.183914f, 1.207803f, 1.232115f, 1.256845f, 1.281992f, 1.307556f, + 1.333540f, 1.359947f, 1.386784f, 1.414061f, 1.441788f, 1.469982f, 1.498661f, 1.527845f, + 1.557561f, 1.587836f, 1.618703f, 1.650199f, 1.682363f, 1.715240f, 1.748879f, 1.783333f, + 1.818659f, 1.854921f, 1.892183f, 1.930517f, 1.970001f, 2.010713f, 2.052741f, 2.096173f, + 2.141107f, 2.187641f, 2.235880f, 2.285935f, 2.337920f, 2.391955f, 2.448163f, 2.506674f, + 2.567622f, 2.631144f, 2.697384f, 2.766490f, 2.838612f, 2.913907f, 2.992536f, 3.074662f, + 3.160454f, 3.250085f, 3.343730f, 3.441570f, 3.543786f, 3.650566f, 3.762098f, 3.878575f, + 4.000192f, 4.127146f, 4.259638f, 4.397868f, 4.542042f, 4.692364f, 4.849041f, 5.012281f, + 5.182294f, 5.359289f, 5.543476f, 5.735064f, 5.934264f, 6.141286f, 6.356336f, 6.356336f, + 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, + 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, +}; + +/* phase lag between left and right ear. (in ms) */ +const float pan_delay_table[128] = +{ + 0.000000f, 0.006136f, 0.012271f, 0.018404f, 0.024534f, 0.030660f, 0.036782f, 0.042899f, + 0.049009f, 0.055111f, 0.061205f, 0.067290f, 0.073365f, 0.079429f, 0.085481f, 0.091520f, + 0.097545f, 0.103556f, 0.109551f, 0.115529f, 0.121490f, 0.127433f, 0.133356f, 0.139260f, + 0.145142f, 0.151003f, 0.156841f, 0.162655f, 0.168445f, 0.174209f, 0.179948f, 0.185659f, + 0.191342f, 0.196996f, 0.202621f, 0.208215f, 0.213778f, 0.219308f, 0.224806f, 0.230269f, + 0.235698f, 0.241092f, 0.246449f, 0.251769f, 0.257051f, 0.262295f, 0.267499f, 0.272662f, + 0.277785f, 0.282866f, 0.287904f, 0.292899f, 0.297850f, 0.302756f, 0.307616f, 0.312430f, + 0.317197f, 0.321916f, 0.326586f, 0.331208f, 0.335779f, 0.340300f, 0.344770f, 0.349188f, + 0.353553f, 0.357865f, 0.362124f, 0.366327f, 0.370476f, 0.374568f, 0.378604f, 0.382584f, + 0.386505f, 0.390369f, 0.394173f, 0.397918f, 0.401604f, 0.405229f, 0.408792f, 0.412295f, + 0.415735f, 0.419112f, 0.422427f, 0.425678f, 0.428864f, 0.431986f, 0.435043f, 0.438035f, + 0.440961f, 0.443820f, 0.446612f, 0.449337f, 0.451995f, 0.454584f, 0.457105f, 0.459557f, + 0.461940f, 0.464253f, 0.466496f, 0.468670f, 0.470772f, 0.472804f, 0.474764f, 0.476653f, + 0.478470f, 0.480215f, 0.481888f, 0.483488f, 0.485016f, 0.486470f, 0.487851f, 0.489159f, + 0.490393f, 0.491553f, 0.492639f, 0.493651f, 0.494588f, 0.495451f, 0.496240f, 0.496953f, + 0.497592f, 0.498156f, 0.498645f, 0.499059f, 0.499398f, 0.499661f, 0.499849f, 0.500000f, +}; + +/* for 0dBf, 0.25dBf, 0.5dBf,...f, 24dB. */ +const float chamberlin_filter_db_to_q_table[97] = +{ + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.029207f, 1.113701f, 1.205132f, + 1.304068f, 1.411127f, 1.526975f, 1.652334f, 1.787984f, 1.934771f, 2.093608f, 2.265485f, + 2.451472f, 2.652729f, 2.870507f, 3.106165f, 3.361169f, 3.637108f, 3.935700f, 4.258806f, + 4.608437f, 4.986772f, 5.396167f, 5.839171f, 6.318544f, 6.837272f, 7.398585f, 8.005980f, + 8.663240f, 9.374459f, 10.144065f, 10.976853f, 11.878010f, 12.853149f, 13.908342f, 15.050163f, + 16.285723f, 17.622717f, 19.069474f, 20.635003f, 22.329057f, 24.162185f, 26.145807f, 28.292276f, + 30.614961f, 33.128330f, 35.848037f, 38.791022f, 41.975614f, 45.421648f, 49.150589f, 53.185661f, + 57.551996f, 62.276791f, 67.389473f, 72.921887f, 78.908490f, 85.386569f, 92.396474f, 99.981865f, + 108.189987f, 117.071964f, 126.683116f, 137.083307f, 148.337313f, 160.515229f, 173.692904f, 187.952416f, + 203.382577f, 220.079495f, 238.147165f, 257.698120f, 278.854132f, 301.746971f, 326.519223f, 353.325180f, + 382.331802f, +}; + +const uint8_t multi_eq_block_table_xg[] = +{ /* Gain1, Freq1, Q1, Shape1, Gain2, Freq2, Q2, Not Used, Gain3, Freq3, Q3, Not Used, + Gain4, Freq4, Q4, Not Used, Gain5, Freq5, Shape5 */ + 64, 12, 7, 0, 64, 28, 7, 0, 64, 34, 7, 0, 64, 46, 7, 0, 64, 52, 7, 0, /* Flat */ + 58, 8, 7, 0, 66, 16, 3, 0, 68, 33, 3, 0, 60, 44, 5, 0, 58, 50, 7, 0, /* Jazz */ + 68, 16, 7, 0, 60, 24, 20, 0, 67, 34, 7, 0, 60, 40, 20, 0, 70, 48, 7, 0, /* Pops */ + 71, 16, 7, 0, 68, 20, 7, 0, 60, 36, 5, 0, 68, 41, 10, 0, 66, 50, 7, 0, /* Rock */ + 67, 12, 7, 0, 68, 24, 7, 0, 64, 34, 5, 0, 66, 50, 7, 0, 61, 52, 7, 0, /* Concert */ +}; + +const float eq_freq_table_xg[] = +{ + 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, 63, 70, 80, 90, 100, 110, + 125, 140, 160, 180, 200, 225, 250, 280, 315, 355, 400, 450, 500, 560, 630, + 700, 800, 900, 1000, 1100, 1200, 1400, 1600, 1800, 2000, 2200, 2500, 2800, 3200, 3600, + 4000, 4500, 5000, 5600, 6300, 7000, 8000, 9000, 10000, 11000, 12000, 14000, 16000, 18000, 20000, +}; + +const float lfo_freq_table_xg[] = +{ + 0.00f, 0.04f, 0.08f, 0.13f, 0.17f, 0.21f, 0.25f, 0.29f, 0.34f, 0.38f, 0.42f, 0.46f, 0.51f, 0.55f, 0.59f, 0.63f, + 0.67f, 0.72f, 0.76f, 0.80f, 0.84f, 0.88f, 0.93f, 0.97f, 1.01f, 1.05f, 1.09f, 1.14f, 1.18f, 1.22f, 1.26f, 1.30f, + 1.35f, 1.39f, 1.43f, 1.47f, 1.51f, 1.56f, 1.60f, 1.64f, 1.68f, 1.72f, 1.77f, 1.81f, 1.85f, 1.89f, 1.94f, 1.98f, + 2.02f, 2.06f, 2.10f, 2.15f, 2.19f, 2.23f, 2.27f, 2.31f, 2.36f, 2.40f, 2.44f, 2.48f, 2.52f, 2.57f, 2.61f, 2.65f, + 2.69f, 2.78f, 2.86f, 2.94f, 3.03f, 3.11f, 3.20f, 2.28f, 3.37f, 3.45f, 3.53f, 3.62f, 3.70f, 3.87f, 4.04f, 4.21f, + 4.37f, 4.54f, 4.71f, 4.88f, 5.05f, 5.22f, 5.38f, 5.55f, 5.72f, 6.06f, 6.39f, 6.73f, 7.07f, 7.40f, 7.74f, 8.08f, + 8.41f, 8.75f, 9.08f, 9.42f, 9.76f, 10.1f, 10.8f, 11.4f, 12.1f, 12.8f, 13.5f, 14.1f, 14.8f, 15.5f, 16.2f, 16.8f, + 17.5f, 18.2f, 19.5f, 20.9f, 22.2f, 23.6f, 24.9f, 26.2f, 27.6f, 28.9f, 30.3f, 31.6f, 33.0f, 34.3f, 37.0f, 39.7f, +}; + +const float mod_delay_offset_table_xg[] = +{ + 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, + 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 2.5f, 2.6f, 2.7f, 2.8f, 2.9f, 3.0f, 3.1f, + 3.2f, 3.3f, 3.4f, 3.5f, 3.6f, 3.7f, 3.8f, 3.9f, 4.0f, 4.1f, 4.2f, 4.3f, 4.4f, 4.5f, 4.6f, 4.7f, + 4.8f, 4.9f, 5.0f, 5.1f, 5.2f, 5.3f, 5.4f, 5.5f, 5.6f, 5.7f, 5.8f, 5.9f, 6.0f, 6.1f, 6.2f, 6.3f, + 6.4f, 6.5f, 6.6f, 6.7f, 6.8f, 6.9f, 7.0f, 7.1f, 7.2f, 7.3f, 7.4f, 7.5f, 7.6f, 7.7f, 7.8f, 7.9f, + 8.0f, 8.1f, 8.2f, 8.3f, 8.4f, 8.5f, 8.6f, 8.7f, 8.8f, 8.9f, 9.0f, 9.1f, 9.2f, 9.3f, 9.4f, 9.5f, + 9.6f, 9.7f, 9.8f, 9.9f, 10.0f, 11.1f, 12.2f, 13.3f, 14.4f, 15.5f, 17.1f, 18.6f, 20.2f, 21.8f, 23.3f, 24.9f, + 26.5f, 28.0f, 29.6f, 31.2f, 32.8f, 34.3f, 35.9f, 37.5f, 39.0f, 40.6f, 42.2f, 43.7f, 45.3f, 46.9f, 48.4f, 50.0f, +}; + +const float reverb_time_table_xg[] = +{ + 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, + 1.9f, 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 2.5f, 2.6f, 2.7f, 2.8f, 2.9f, 3.0f, 3.1f, 3.2f, 3.3f, 3.4f, + 3.5f, 3.6f, 3.7f, 3.8f, 3.9f, 4.0f, 4.1f, 4.2f, 4.3f, 4.4f, 4.5f, 4.6f, 4.7f, 4.8f, 4.9f, 5.0f, + 5.5f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.5f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, + 17.0f, 18.0f, 19.0f, 20.0f, 25.0f, 30.0f, +}; + +const float delay_time_table_xg[] = +{ + 0.1f, 1.7f, 3.2f, 4.8f, 6.4f, 8.0f, 9.5f, 11.1f, 12.7f, 14.3f, 15.8f, 17.4f, 19.0f, 20.6f, 22.1f, 23.7f, + 25.3f, 26.9f, 28.4f, 30.0f, 31.6f, 33.2f, 34.7f, 36.3f, 37.9f, 39.5f, 41.0f, 42.6f, 44.2f, 45.7f, 47.3f, 48.9f, + 50.5f, 52.0f, 53.6f, 55.2f, 56.8f, 58.3f, 59.9f, 61.5f, 63.1f, 64.6f, 66.2f, 67.8f, 69.4f, 70.9f, 72.5f, 74.1f, + 75.7f, 77.2f, 78.8f, 80.4f, 81.9f, 83.5f, 85.1f, 86.7f, 88.2f, 89.8f, 91.4f, 93.0f, 94.5f, 96.1f, 97.7f, 99.3f, + 100.8f, 102.4f, 104.0f, 105.6f, 107.1f, 108.7f, 110.3f, 111.9f, 113.4f, 115.0f, 116.6f, 118.2f, 119.7f, 121.3f, 122.9f, 124.4f, + 126.0f, 127.6f, 129.2f, 130.7f, 132.3f, 133.9f, 135.5f, 137.0f, 138.6f, 140.2f, 141.8f, 143.3f, 144.9f, 146.5f, 148.1f, 149.6f, + 151.2f, 152.8f, 154.4f, 155.9f, 157.5f, 159.1f, 160.6f, 162.2f, 163.8f, 165.4f, 166.9f, 168.5f, 170.1f, 171.7f, 173.2f, 174.8f, + 176.4f, 178.0f, 179.5f, 181.1f, 182.7f, 184.3f, 185.8f, 187.4f, 189.0f, 190.6f, 192.1f, 193.7f, 195.3f, 196.9f, 198.4f, 200.0f, +}; + +const int16_t cutoff_freq_table_gs[] = +{ + 250, 250, 250, 250, 250, 250, 250, 250, + 315, 315, 315, 315, 315, 315, 315, 315, + 400, 400, 400, 400, 400, 400, 400, 400, + 500, 500, 500, 500, 500, 500, 500, 500, + 630, 630, 630, 630, 630, 630, 630, 630, + 800, 800, 800, 800, 800, 800, 800, 800, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250, + 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, + 3150, 3150, 3150, 3150, 3150, 3150, 3150, 3150, + 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, + 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, +}; + +const int16_t lpf_table_gs[] = +{ + 250, 250, 250, 250, 250, 250, 250, 250, + 315, 315, 315, 315, 315, 315, 315, 315, + 400, 400, 400, 400, 400, 400, 400, 400, + 500, 500, 500, 500, 500, 500, 500, 500, + 630, 630, 630, 630, 630, 630, 630, 630, + 800, 800, 800, 800, 800, 800, 800, 800, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250, + 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, + 3150, 3150, 3150, 3150, 3150, 3150, 3150, 3150, + 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, + 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +const int16_t eq_freq_table_gs[] = +{ + 200, 200, 200, 200, 200, 200, 200, 200, + 250, 250, 250, 250, 250, 250, 250, 250, + 315, 315, 315, 315, 315, 315, 315, 315, + 400, 400, 400, 400, 400, 400, 400, 400, + 500, 500, 500, 500, 500, 500, 500, 500, + 630, 630, 630, 630, 630, 630, 630, 630, + 800, 800, 800, 800, 800, 800, 800, 800, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250, + 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, + 3150, 3150, 3150, 3150, 3150, 3150, 3150, 3150, + 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, + 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, +}; + +const float lofi_sampling_freq_table_xg[] = +{ + 44100.0, 22100.0, 14700.0, 11000.0, 8800.0, 7400.0, 6300.0, 5500.0, + 4900.0, 4400.0, 4000.0, 3700.0, 3400.0, 3200.0, 2900.0, 2800.0, + 2600.0, 2500.0, 2300.0, 2200.0, 2100.0, 2000.0, 1920.0, 1840.0, + 1760.0, 1700.0, 1630.0, 1580.0, 1520.0, 1470.0, 1420.0, 1380.0, + 1340.0, 1300.0, 1260.0, 1230.0, 1190.0, 1160.0, 1130.0, 1110.0, + 1080.0, 1050.0, 1030.0, 1000.0, 980.0, 959.0, 938.0, 919.0, + 900.0, 882.0, 865.0, 848.0, 832.0, 817.0, 802.0, 788.0, + 774.0, 760.0, 747.0, 735.0, 723.0, 711.0, 700.0, 689.0, + 678.0, 668.0, 658.0, 649.0, 639.0, 630.0, 621.0, 613.0, + 604.0, 596.0, 588.0, 580.0, 573.0, 565.0, 558.0, 551.0, + 544.0, 538.0, 531.0, 525.0, 519.0, 513.0, 507.0, 501.0, + 496.0, 490.0, 485.0, 479.0, 474.0, 469.0, 464.0, 459.0, + 455.0, 450.0, 445.0, 441.0, 437.0, 432.0, 428.0, 424.0, + 420.0, 416.0, 412.0, 408.0, 405.0, 401.0, 397.0, 394.0, + 390.0, 387.0, 383.0, 380.0, 377.0, 374.0, 371.0, 368.0, + 364.0, 361.0, 359.0, 356.0, 353.0, 350.0, 347.0, 345.0, +}; + +void init_tables(void) +{ + // Only needs to be done once. + static bool done = false; + if (done) return; + done = true; + + init_freq_table(); + init_freq_table_tuning(); + init_freq_table_pytha(); + init_freq_table_meantone(); + init_freq_table_pureint(); + init_bend_fine(); + init_bend_coarse(); + init_triangular_table(); + init_gm2_pan_table(); + init_attack_vol_table(); + init_sb_vol_table(); + init_modenv_vol_table(); + init_def_vol_table(); + init_gs_vol_table(); + init_perceived_vol_table(); + init_gm2_vol_table(); +} + +int32_t get_note_freq(Sample *sp, int note) +{ + int32_t f; + int16_t sf, sn; + double ratio; + + f = freq_table[note]; + /* GUS/SF2 - Scale Tuning */ + if ((sf = sp->scale_factor) != 1024) { + sn = sp->scale_freq; + ratio = pow(2.0, (note - sn) * (sf - 1024) / 12288.0); + f = f * ratio + 0.5; + } + return f; +} +} \ No newline at end of file diff --git a/src/sound/timidity++/tables.h b/src/sound/timidity++/tables.h new file mode 100644 index 0000000000..9fbc5704ed --- /dev/null +++ b/src/sound/timidity++/tables.h @@ -0,0 +1,96 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + tables.h +*/ + +#ifndef ___TABLES_H_ +#define ___TABLES_H_ + +#include +#include "sysdep.h" + +namespace TimidityPlus +{ + + inline double lookup_sine(double x) + { + return (sin((2 * M_PI / 1024.0) * (x))); + } + + extern double lookup_triangular(int x); + extern double lookup_log(int x); + +#define SINE_CYCLE_LENGTH 1024 + extern int32_t freq_table[]; + extern int32_t freq_table_zapped[]; + extern int32_t freq_table_tuning[][128]; + extern int32_t freq_table_pytha[][128]; + extern int32_t freq_table_meantone[][128]; + extern int32_t freq_table_pureint[][128]; + extern double *vol_table; + extern double def_vol_table[]; + extern double gs_vol_table[]; + extern double *xg_vol_table; /* == gs_vol_table */ + extern double *pan_table; + extern double bend_fine[]; + extern double bend_coarse[]; + extern const double midi_time_table[], midi_time_table2[]; + extern const uint8_t reverb_macro_presets[]; + extern const uint8_t chorus_macro_presets[]; + extern const uint8_t delay_macro_presets[]; + extern const float delay_time_center_table[]; + extern const float pre_delay_time_table[]; + extern const float chorus_delay_time_table[]; + extern const float rate1_table[]; + extern double attack_vol_table[]; + extern double perceived_vol_table[]; + extern double gm2_vol_table[]; + extern float sc_eg_attack_table[]; + extern float sc_eg_decay_table[]; + extern float sc_eg_release_table[]; + extern double sc_vel_table[]; + extern double sc_vol_table[]; + extern double sc_pan_table[], gm2_pan_table[]; + extern double sc_drum_level_table[]; + extern double sb_vol_table[]; + extern double modenv_vol_table[]; + extern const float cb_to_amp_table[]; + extern const float reverb_time_table[]; + extern const float pan_delay_table[]; + extern const float chamberlin_filter_db_to_q_table[]; + extern const uint8_t multi_eq_block_table_xg[]; + extern const float eq_freq_table_xg[]; + extern const float lfo_freq_table_xg[]; + extern const float mod_delay_offset_table_xg[]; + extern const float reverb_time_table_xg[]; + extern const float delay_time_table_xg[]; + extern const int16_t cutoff_freq_table_gs[]; + extern const int16_t lpf_table_gs[]; + extern const int16_t eq_freq_table_gs[]; + extern const float lofi_sampling_freq_table_xg[]; + + extern void init_tables(void); + + struct Sample; + int32_t get_note_freq(Sample *sp, int note); + +} + +#endif /* ___TABLES_H_ */ diff --git a/src/sound/timidity++/timidity.cpp b/src/sound/timidity++/timidity.cpp new file mode 100644 index 0000000000..4212564b3e --- /dev/null +++ b/src/sound/timidity++/timidity.cpp @@ -0,0 +1,149 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2008 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#ifdef _WIN32 +#include +#include +#include +#endif +#include + +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "tables.h" +#include "reverb.h" +#include "resample.h" +#include "recache.h" +#include "aq.h" +#include "mix.h" +#include "quantity.h" +#include "c_cvars.h" + +namespace TimidityPlus +{ + Instruments *instruments; + +/* main interfaces (To be used another main) */ + +int timidity_pre_load_configuration(void); +void timidity_init_player(void); +int timidity_play_main(int nfiles, char **files); +int got_a_configuration; + + +CRITICAL_SECTION critSect; + +/* -------- functions for getopt_long ends here --------- */ + + + +int timidity_pre_load_configuration(void) +{ + /* Windows */ + char *strp; + char local[1024]; + + + /* First, try read configuration file which is in the + * TiMidity directory. + */ + if(GetModuleFileNameA(NULL, local, 1023)) + { + local[1023] = '\0'; + if (strp = strrchr(local, '\\')) + { + *(++strp) = '\0'; + strncat(local, "TIMIDITY.CFG", sizeof(local) - strlen(local) - 1); + if (true) + { + if (!instruments->load("timidity.cfg")) + { + got_a_configuration = 1; + return 0; + } + } + } + } + + return 0; +} + + +int dumb_pass_playing_list(int number_of_files, char *list_of_files[]); + +int timidity_play_main(int nfiles, char **files) +{ + int need_stdin = 0, need_stdout = 0; + int output_fail = 0; + int retval; + + +#ifdef _WIN32 + + + InitializeCriticalSection(&critSect); + +#endif + + /* Open output device */ + + if(play_mode->open_output() < 0) + { + output_fail = 1; + return 2; + } + + retval=dumb_pass_playing_list(nfiles, files); + + + play_mode->close_output(); +#ifdef _WIN32 + DeleteCriticalSection (&critSect); +#endif + + return retval; +} + +} + +using namespace TimidityPlus; + +void main(int argc, char **argv) +{ + int err; + char *files_nbuf = NULL; + int main_ret; + instruments = new Instruments; + + if ((err = timidity_pre_load_configuration()) != 0) + return; + + main_ret = timidity_play_main(1, &argv[1]); + //free_readmidi(); + free_global_mblock(); +} + diff --git a/src/sound/timidity++/timidity.h b/src/sound/timidity++/timidity.h new file mode 100644 index 0000000000..43ca6777c3 --- /dev/null +++ b/src/sound/timidity++/timidity.h @@ -0,0 +1,178 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Historical issues: This file once was a huge header file, but now is + * devided into some smaller ones. Please do not add things to this + * header, but consider put them on other files. + */ + + +#ifndef TIMIDITY_H_INCLUDED +#define TIMIDITY_H_INCLUDED 1 + +#include "c_cvars.h" +#include "output.h" +#include "controls.h" +#include "mblock.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244) // double->float truncation occurs so often in here that it's pointless to fix it all. +#endif + + +EXTERN_CVAR(Int, playback_rate) +EXTERN_CVAR(Int, key_adjust) +EXTERN_CVAR(Float, tempo_adjust) + +namespace TimidityPlus +{ + extern int32_t control_ratio; // derived from playback_rate +} + + +EXTERN_CVAR(Bool, opt_modulation_wheel) +EXTERN_CVAR(Bool, opt_portamento) +EXTERN_CVAR(Bool, opt_nrpn_vibrato) +EXTERN_CVAR(Int, opt_reverb_control) +EXTERN_CVAR(Int, opt_chorus_control) +EXTERN_CVAR(Bool, opt_surround_chorus) +EXTERN_CVAR(Bool, opt_channel_pressure) +EXTERN_CVAR(Int, opt_lpf_def) +EXTERN_CVAR(Bool, opt_temper_control) +EXTERN_CVAR(Bool, opt_modulation_envelope) + + +/* + Table of contents: + (1) Flags and definitions to customize timidity + (3) inportant definitions not to customize + (2) #includes -- include other headers + */ + +/*****************************************************************************\ + section 1: some customize issues +\*****************************************************************************/ + + +/* 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 + a sample is 1048576 samples (2 megabytes in memory). The GUS gets + by with just 9 bits and a little help from its friends... + "The GUS does not SUCK!!!" -- a happy user :) */ +#define FRACTION_BITS 12 + /* change FRACTION_BITS above, not this */ +#define FRACTION_MASK (~(0xFFFFFFFF << FRACTION_BITS)) + + +/* The number of samples to use for ramping out a dying note. Affects + click removal. */ +#define MAX_DIE_TIME 20 + + +/* Define the pre-resampling cache size. + * This value is default. You can change the cache saze with + * command line option. + */ +#define DEFAULT_CACHE_DATA_SIZE (2*1024*1024) + + +/*****************************************************************************\ + section 2: some important definitions +\*****************************************************************************/ +/* + Anything below this shouldn't need to be changed unless you're porting + to a new machine with other than 32-bit, big-endian words. + */ + + +/* Audio buffer size has to be a power of two to allow DMA buffer + fragments under the VoxWare (Linux & FreeBSD) audio driver */ +#define AUDIO_BUFFER_SIZE (1<<12) + +/* These affect general volume */ +#define GUARD_BITS 3 +#define AMP_BITS (15-GUARD_BITS) + +#define MAX_AMPLIFICATION 800 +#define MAX_CHANNELS 32 + +/* Vibrato and tremolo Choices of the Day */ +#define SWEEP_TUNING 38 +#define VIBRATO_AMPLITUDE_TUNING 1.0L +#define VIBRATO_RATE_TUNING 38 +#define TREMOLO_AMPLITUDE_TUNING 1.0L +#define TREMOLO_RATE_TUNING 38 + +#define SWEEP_SHIFT 16 +#define RATE_SHIFT 5 + +#define VIBRATO_SAMPLE_INCREMENTS 32 + +#define MODULATION_WHEEL_RATE (1.0/6.0) +/* #define MODULATION_WHEEL_RATE (midi_time_ratio/8.0) */ +/* #define MODULATION_WHEEL_RATE (current_play_tempo/500000.0/32.0) */ + +#define VIBRATO_DEPTH_TUNING (1.0/4.0) + +/* malloc's limit */ +#define MAX_SAFE_MALLOC_SIZE (1<<23) /* 8M */ + +#define DEFAULT_SOUNDFONT_ORDER 0 + + +/*****************************************************************************\ + section 3: include other headers +\*****************************************************************************/ + +namespace TimidityPlus +{ + +enum play_system_modes +{ + DEFAULT_SYSTEM_MODE, + GM_SYSTEM_MODE, + GM2_SYSTEM_MODE, + GS_SYSTEM_MODE, + XG_SYSTEM_MODE +}; + + +const int DEFAULT_VOICES = 256; + +// These were configurable in Timidity++ but it doesn't look like this is really needed. +// In case it becomes necessary, they can be turned into CVARs. +const int default_tonebank = 0; +const int special_tonebank = -1; +const int effect_lr_mode = -1; +const int effect_lr_delay_msec = 25; +const int adjust_panning_immediately = 1; +const int antialiasing_allowed = 0; +const int fast_decay = 0; +const int cutoff_allowed = 0; +const int opt_force_keysig = 8; +const int max_voices = DEFAULT_VOICES; +const int temper_type_mute = 0; +const int opt_preserve_silence = 0; +const int opt_init_keysig = 8; + +} +#endif /* TIMIDITY_H_INCLUDED */ diff --git a/src/sound/timidity++/w32_a.cpp b/src/sound/timidity++/w32_a.cpp new file mode 100644 index 0000000000..049b86cef1 --- /dev/null +++ b/src/sound/timidity++/w32_a.cpp @@ -0,0 +1,666 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + w32_a.c + + Functions to play sound on the Windows audio driver (Windows 95/98/NT). + + Modified by Masanao Izumo +*/ + +#include +#include +#include +#include +#include "timidity.h" + +/*****************************************************************************************************************************/ + + +/*****************************************************************************************************************************/ + +#include "instrum.h" +#include "playmidi.h" +#include "mblock.h" + +namespace TimidityPlus +{ + extern void *safe_malloc(size_t count); + + extern CRITICAL_SECTION critSect; + + static int opt_wmme_device_id = -2; + + UINT uDeviceID; + +#define NOT ! + +static int open_output (void); /* 0=success, 1=warning, -1=fatal error */ +static void close_output (void); +static int output_data (char * Data, int32_t Size); +static int acntl (int request, void * arg); + +static void print_device_list(void); + +#define DATA_BLOCK_SIZE (4 * AUDIO_BUFFER_SIZE) +#define DATA_BLOCK_NUM 8 +static int data_block_trunc_size; + +struct MMBuffer +{ + int Number; + int Prepared; // Non-zero if this buffer has been prepared. + + HGLOBAL hData; + void * Data; + + HGLOBAL hHead; + WAVEHDR * Head; + + struct MMBuffer * Next; +}; + +static struct MMBuffer * Buffers; + +static volatile struct MMBuffer * FreeBuffers; +static volatile int NumBuffersInUse; + +static HWAVEOUT hDevice; +static int BufferDelay; // in milliseconds + +static const int AllowSynchronousWaveforms = 1; + +/*****************************************************************************************************************************/ + +static void CALLBACK OnPlaybackEvent (HWAVE hWave, UINT Msg, DWORD_PTR UserData, DWORD_PTR Param1, DWORD_PTR Param2); +static void BufferPoolReset (void); +static struct MMBuffer * GetBuffer (); +static void PutBuffer (struct MMBuffer *); +static const char * MMErrorMessage (MMRESULT Result); +static void WaitForBuffer (int WaitForAllBuffers); + +/*****************************************************************************************************************************/ + +static int detect(void); + +#define dpm w32_play_mode + +PlayMode dpm = +{ + 44100, + PE_16BIT, + PF_PCM_STREAM|PF_BUFF_FRAGM_OPT, + -1, + {32}, + "Windows audio driver", 'd', + NULL, + open_output, + close_output, + output_data, + acntl, + detect +}; + +/*****************************************************************************************************************************/ + +static int open_output(void) +{ + int i; + int j; + int IsMono; + WAVEFORMATEX wf; + WAVEOUTCAPS woc; + MMRESULT Result; + UINT DeviceID; + int ret; + + if( dpm.name != NULL) + ret = sscanf(dpm.name, "%d", &opt_wmme_device_id); + if ( dpm.name == NULL || ret == 0 || ret == EOF) + opt_wmme_device_id = -2; + + if (opt_wmme_device_id == -1){ + print_device_list(); + return -1; + } + +/** Check if there is at least one audio device. **/ + + if (waveOutGetNumDevs() == 0) + { + //ctl_cmsg (CMSG_ERROR, VERB_NORMAL, "No audio devices present!"); + return -1; + } + +/** They can't mean these. **/ + + dpm.encoding = PE_16BIT; + + IsMono = false; + + memset(&wf, 0, sizeof(wf)); + + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = 2; + wf.nSamplesPerSec = dpm.rate; + + i = dpm.rate; + j = 4; + i *= j; + data_block_trunc_size = DATA_BLOCK_SIZE - (DATA_BLOCK_SIZE % j); + + wf.nAvgBytesPerSec = i; + wf.nBlockAlign = j; + wf.wBitsPerSample = 16; + wf.cbSize = sizeof(WAVEFORMAT); + +/** Open the device. **/ + + { CHAR b[256]; wsprintf(b, "Opening device...\n"); OutputDebugString(b); } + + hDevice = 0; + + if (opt_wmme_device_id == -2){ + uDeviceID = WAVE_MAPPER; + }else{ + uDeviceID= (UINT)opt_wmme_device_id; + } + + if (AllowSynchronousWaveforms) + Result = waveOutOpen(&hDevice, uDeviceID, (LPWAVEFORMATEX) &wf, (DWORD_PTR) OnPlaybackEvent, 0, CALLBACK_FUNCTION | WAVE_ALLOWSYNC); + else + Result = waveOutOpen(&hDevice, uDeviceID, (LPWAVEFORMATEX) &wf, (DWORD_PTR) OnPlaybackEvent, 0, CALLBACK_FUNCTION); + + if (Result) + { + return -1; + } + else + { CHAR b[256]; wsprintf(b, "Device opened.\n"); OutputDebugString(b); } + +/** Get the device ID. **/ + + DeviceID = 0; + waveOutGetID(hDevice, &DeviceID); + +/** Get the device capabilities. **/ + + memset(&woc, 0, sizeof(WAVEOUTCAPS)); + Result = waveOutGetDevCaps(DeviceID, &woc, sizeof(WAVEOUTCAPS)); + + //ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Device ID: %d", DeviceID); + //ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Manufacture ID: %d", woc.wMid); + //ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Product ID: %d", woc.wPid); + //ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Driver version: %d", woc.vDriverVersion); + //ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Product name: %s", woc.szPname); + //ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Formats supported: 0x%08X", woc.dwFormats); + //ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Max. channels: %d", woc.wChannels); + //ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Supported features: 0x%08X", woc.dwSupport); + +/** Calculate the buffer delay. **/ + + BufferDelay = AUDIO_BUFFER_SIZE * 4; + + BufferDelay = (BufferDelay * 1000) / dpm.rate; + +/** Create the buffer pool. **/ + + Buffers = (struct MMBuffer *) safe_malloc(DATA_BLOCK_NUM * sizeof(struct MMBuffer)); + + for (i = 0; i < DATA_BLOCK_NUM; i++) + { + struct MMBuffer * b; + + b = &Buffers[i]; + + b->hData = GlobalAlloc(GMEM_ZEROINIT, DATA_BLOCK_SIZE); + b->Data = (WAVEHDR*)GlobalLock (b->hData); + b->hHead = GlobalAlloc(GMEM_ZEROINIT, sizeof(WAVEHDR)); + b->Head = (WAVEHDR*)GlobalLock (b->hHead); + } + + BufferPoolReset(); + +/** Set the file descriptor. **/ + + dpm.fd = 0; + + return 0; +} + +/*****************************************************************************************************************************/ + +static void close_output(void) +{ + int i; + + if (dpm.fd != -1) + { + WaitForBuffer(1); + + { CHAR b[256]; wsprintf(b, "Closing device...\n"); OutputDebugString(b); } + + waveOutReset(hDevice); + waveOutClose(hDevice); + + { CHAR b[256]; wsprintf(b, "Device closed.\n"); OutputDebugString(b); } + + /** Free all buffers. **/ + + for (i = 0; i < DATA_BLOCK_NUM; i++) + { + struct MMBuffer * block; + + block = &Buffers[i]; + + GlobalUnlock(block->hHead); + GlobalFree (block->hHead); + + GlobalUnlock(block->hData); + GlobalFree (block->hData); + } + + free(Buffers); + + /** Reset the file descriptor. **/ + + dpm.fd = -1; + } +} + +static int detect(void) +{ + if (waveOutGetNumDevs() == 0) {return 0;} /* not found */ + return 1; /* found */ +} + + +/*****************************************************************************************************************************/ + +#ifdef OutputDebugString +#undef OutputDebugString +#endif + +void OutputDebugString(LPSTR sre) +{ +} + +static int output_data(char * Data, int32_t Size) +{ + char * d; + int32_t s; + + d = Data; + s = Size; + + while (s > 0) + { + int32_t n; + struct MMBuffer * b; + + MMRESULT Result; + LPWAVEHDR wh; + + if ((b = GetBuffer()) == NULL) + { + WaitForBuffer(0); + continue; + } + + if (s <= data_block_trunc_size) + n = s; + else + n = data_block_trunc_size; + + CopyMemory(b->Data, d, n); + + wh = b->Head; + + wh->dwBufferLength = n; + wh->lpData = (LPSTR)b->Data; + wh->dwUser = b->Number; + + /** Prepare the buffer. **/ + + { CHAR b[256]; wsprintf(b, "%2d: Preparing buffer %d...\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); } + + Result = waveOutPrepareHeader(hDevice, wh, sizeof(WAVEHDR)); + + if (Result) + { + { CHAR b[256]; wsprintf(b, "%2d: Buffer preparation failed.\n", NumBuffersInUse); OutputDebugString(b); } + + //ctl_cmsg (CMSG_ERROR, VERB_NORMAL, "waveOutPrepareHeader(): %s", MMErrorMessage(Result)); + return -1; + } + else + { CHAR b[256]; wsprintf(b, "%2d: Buffer %d prepared.\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); } + + b->Prepared = 1; + + /** Queue the buffer. **/ + + { CHAR b[256]; wsprintf(b, "%2d: Queueing buffer %d...\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); } + + Result = waveOutWrite(hDevice, wh, sizeof(WAVEHDR)); + + if (Result) + { + { CHAR b[256]; wsprintf(b, "%2d: Buffer queueing failed.\n", NumBuffersInUse); OutputDebugString(b); } + + //ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "waveOutWrite(): %s", MMErrorMessage(Result)); + return -1; + } + else + { CHAR b[256]; wsprintf(b, "%2d: Buffer %d queued.\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); } + + d += n; + s -= n; + } + + return 0; +} + +/*****************************************************************************************************************************/ + +static int acntl(int request, void *arg) +{ + static char dummy_sounds[4*AUDIO_BUFFER_SIZE]; + + switch(request) + { + case PM_REQ_DISCARD: + { + { CHAR b[256]; wsprintf(b, "Resetting audio device.\n"); OutputDebugString(b); } + + waveOutReset(hDevice); + close_output(); + open_output(); + + { CHAR b[256]; wsprintf(b, "Audio device reset.\n"); OutputDebugString(b); } + + return 0; + } + + case PM_REQ_FLUSH: + { + close_output(); + open_output(); + return 0; + } + } + + return -1; +} + +/*****************************************************************************************************************************/ + +static void CALLBACK OnPlaybackEvent(HWAVE hWave, UINT Msg, DWORD_PTR UserData, DWORD_PTR Param1, DWORD_PTR Param2) +{ + //ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Msg: 0x%08X, Num. buffers in use: %d", Msg, NumBuffersInUse); + + switch (Msg) + { + case WOM_OPEN: + { CHAR b[256]; wsprintf(b, "%2d: Device opened.\n", NumBuffersInUse); OutputDebugString(b); } + break; + + case WOM_CLOSE: + { CHAR b[256]; wsprintf(b, "%2d: Device closed.\n", NumBuffersInUse); OutputDebugString(b); } + break; + + case WOM_DONE: + { + WAVEHDR * wh; + + EnterCriticalSection(&critSect); + + wh = (WAVEHDR *) Param1; + +/* It's not safe to do this here. Read the remarks of waveOutProc() in the SDK on which functions are safe to call. + if (NOT Queueing) + { + { CHAR b[256]; wsprintf(b, "%2d: Dequeueing buffer %d...\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); } + + waveOutUnprepareHeader(hDevice, wh, sizeof(WAVEHDR)); + + { CHAR b[256]; wsprintf(b, "%2d: Buffer %d dequeued.\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); } + } + else + { CHAR b[256]; wsprintf(b, "%2d: *** Buffer %d not dequeued! ***\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); } + */ + PutBuffer(&Buffers[wh->dwUser]); + + LeaveCriticalSection(&critSect); + + break; + } + + default: + { + CHAR b[256]; + + wsprintf(b, "%2d: Unknown play back event 0x%08X.\n", NumBuffersInUse, Msg); + OutputDebugString(b); + } + } +} + +/*****************************************************************************************************************************/ + +#define DIM(a) sizeof(a) / sizeof(a[0]) + +static const char * mmsyserr_code_string[] = +{ + "no error", + "unspecified error", + "device ID out of range", + "driver failed enable", + "device already allocated", + "device handle is invalid", + "no device driver present", + "memory allocation error", + "function isn't supported", + "error value out of range", + "invalid flag passed", + "invalid parameter passed", + "handle being used", +}; + +static const char * waverr_code_sring[] = +{ + "unsupported wave format", + "still something playing", + "header not prepared", + "device is synchronous", +}; + +static const char * MMErrorMessage(MMRESULT ErrorCode) +{ + static char s[32]; + + if (ErrorCode >= WAVERR_BASE) + { + ErrorCode -= WAVERR_BASE; + + if (ErrorCode > DIM(waverr_code_sring)) + { + wsprintf(s, "Unknown wave error %d", ErrorCode); + return s; + } + else + return waverr_code_sring[ErrorCode]; + } + else + if (ErrorCode > DIM(mmsyserr_code_string)) + { + wsprintf(s, "Unknown multimedia error %d", ErrorCode); + return s; + } + else + return mmsyserr_code_string[ErrorCode]; +} + +#undef DIM + +/*****************************************************************************************************************************/ + +static void BufferPoolReset(void) +{ + int i; + + { CHAR b[256]; wsprintf(b, "Resetting buffer pool...\n"); OutputDebugString(b); } + + Buffers[0].Number = 0; + Buffers[0].Prepared = 0; + Buffers[0].Next = &Buffers[1]; + + for (i = 1; i < DATA_BLOCK_NUM - 1; i++) + { + Buffers[i].Number = i; + Buffers[i].Prepared = 0; + Buffers[i].Next = &Buffers[i + 1]; + } + + Buffers[i].Number = i; + Buffers[i].Prepared = 0; + Buffers[i].Next = NULL; + + FreeBuffers = &Buffers[0]; + NumBuffersInUse = 0; + + { CHAR b[256]; wsprintf(b, "Buffer pool reset.\n", NumBuffersInUse); OutputDebugString(b); } +} + +/*****************************************************************************************************************************/ + +static struct MMBuffer * GetBuffer() +{ + struct MMBuffer * b; + + { CHAR b[256]; wsprintf(b, "%2d: Getting buffer...\n", NumBuffersInUse); OutputDebugString(b); } + + EnterCriticalSection(&critSect); + + if (FreeBuffers) + { + b = (struct MMBuffer *)FreeBuffers; + FreeBuffers = FreeBuffers->Next; + NumBuffersInUse++; + + /** If this buffer is still prepared we can safely unprepare it because we got it from the free buffer list. **/ + + if (b->Prepared) + { + waveOutUnprepareHeader(hDevice, (LPWAVEHDR) b->Head, sizeof(WAVEHDR)); + + b->Prepared = 0; + } + + b->Next = NULL; + } + else + b = NULL; + + LeaveCriticalSection(&critSect); + + { CHAR b[256]; wsprintf(b, "%2d: Got buffer.\n", NumBuffersInUse); OutputDebugString(b); } + + return b; +} + +/*****************************************************************************************************************************/ + +static void PutBuffer(struct MMBuffer * b) +{ + { CHAR b[256]; wsprintf(b, "%2d: Putting buffer...\n", NumBuffersInUse); OutputDebugString(b); } + + b->Next = (struct MMBuffer *)FreeBuffers; + FreeBuffers = b; + NumBuffersInUse--; + + { CHAR b[256]; wsprintf(b, "%2d: Buffer put.\n", NumBuffersInUse); OutputDebugString(b); } +} + +/*****************************************************************************************************************************/ + +static void WaitForBuffer(int WaitForAllBuffers) +{ + int numbuf; + + if (WaitForAllBuffers) + { + { CHAR b[256]; wsprintf(b, "%2d: Waiting for all buffers to be dequeued...\n", NumBuffersInUse); OutputDebugString(b); } + + while (1) { + EnterCriticalSection(&critSect); + numbuf = NumBuffersInUse; + if (numbuf) { + LeaveCriticalSection(&critSect); + Sleep(BufferDelay); + continue; + } + break; + } + LeaveCriticalSection(&critSect); + + +// while (NumBuffersInUse) +// Sleep(BufferDelay); + + { CHAR b[256]; wsprintf(b, "%2d: All buffers dequeued.\n", NumBuffersInUse); OutputDebugString(b); } + + BufferPoolReset(); + } + else + { + { CHAR b[256]; wsprintf(b, "%2d: Waiting %dms...\n", NumBuffersInUse, BufferDelay); OutputDebugString(b); } + + Sleep(BufferDelay); + + { CHAR b[256]; wsprintf(b, "%2d: Wait finished.\n", NumBuffersInUse); OutputDebugString(b); } + } +} + +/*****************************************************************************************************************************/ + +#define DEVLIST_MAX 20 +static void print_device_list(void){ + UINT num; + int i, list_num; + WAVEOUTCAPS woc; + typedef struct tag_DEVICELIST{ + int deviceID; + char name[256]; + } DEVICELIST; + DEVICELIST device[DEVLIST_MAX]; + num = waveOutGetNumDevs(); + list_num=0; + for(i = 0 ; i < (int)num && i < DEVLIST_MAX ; i++){ + if (MMSYSERR_NOERROR == waveOutGetDevCaps((UINT)i, &woc, sizeof(woc)) ){ + device[list_num].deviceID=i; + strcpy(device[list_num].name, woc.szPname); + list_num++; + } + } + for(i=0;i