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