From f47c8c346d33405c3e57be26942d947a0272bd44 Mon Sep 17 00:00:00 2001 From: dashodanger <> Date: Wed, 18 Dec 2024 16:03:15 -0700 Subject: [PATCH 1/3] Music work --- docs/licenses/License Attribution.txt | 10 +- libraries/CMakeLists.txt | 2 +- libraries/ibxm/CMakeLists.txt | 8 + libraries/ibxm/LICENSE | 37 + libraries/ibxm/ibxm.h | 2017 +++++++++++++++++++ libraries/libRAD/CMakeLists.txt | 2 - libraries/libRAD/LICENSE.txt | 33 - libraries/libRAD/instruments.h | 747 ------- libraries/libRAD/patches.cpp | 336 ---- libraries/libRAD/patches.h | 43 - libraries/libRAD/radmidi.cpp | 675 ------- libraries/libRAD/radmidi.h | 160 -- libraries/libemidi/source/CSMFPlay.cpp | 34 +- libraries/libemidi/source/CSMFPlay.hpp | 9 +- libraries/m4p/CMakeLists.txt | 25 - libraries/m4p/LICENSE | 29 - libraries/m4p/cpu.h | 36 - libraries/m4p/ft_tables.c | 453 ----- libraries/m4p/ft_tables.h | 13 - libraries/m4p/it2drivers/sb16.c | 457 ----- libraries/m4p/it2drivers/sb16.h | 6 - libraries/m4p/it2drivers/sb16_m.c | 281 --- libraries/m4p/it2drivers/sb16_m.h | 8 - libraries/m4p/it2drivers/zerovol.c | 107 - libraries/m4p/it2drivers/zerovol.h | 8 - libraries/m4p/it_d_rm.c | 217 -- libraries/m4p/it_d_rm.h | 33 - libraries/m4p/it_m_eff.c | 1936 ------------------ libraries/m4p/it_m_eff.h | 59 - libraries/m4p/it_music.c | 2240 --------------------- libraries/m4p/it_music.h | 85 - libraries/m4p/it_structs.h | 252 --- libraries/m4p/it_tables.c | 212 -- libraries/m4p/it_tables.h | 13 - libraries/m4p/loaders/it.c | 744 ------- libraries/m4p/loaders/it.h | 7 - libraries/m4p/loaders/mmcmp/mmcmp.c | 307 --- libraries/m4p/loaders/mmcmp/mmcmp.h | 6 - libraries/m4p/loaders/s3m.c | 610 ------ libraries/m4p/loaders/s3m.h | 7 - libraries/m4p/m4p.c | 139 -- libraries/m4p/m4p.h | 35 - libraries/m4p/pmp_main.c | 1853 ----------------- libraries/m4p/pmp_main.h | 7 - libraries/m4p/pmp_mix.c | 468 ----- libraries/m4p/pmp_mix.h | 60 - libraries/m4p/pmplay.c | 1254 ------------ libraries/m4p/pmplay.h | 153 -- libraries/m4p/snd_masm.c | 676 ------- libraries/m4p/snd_masm.h | 227 --- source_files/ddf/ddf_playlist.cc | 2 +- source_files/ddf/ddf_playlist.h | 2 +- source_files/edge/CMakeLists.txt | 6 +- source_files/edge/i_sound.cc | 4 - source_files/edge/m_option.cc | 11 +- source_files/edge/s_emidi.cc | 2 +- source_files/edge/{s_m4p.cc => s_ibxm.cc} | 138 +- source_files/edge/{s_m4p.h => s_ibxm.h} | 8 +- source_files/edge/{s_opl.cc => s_imf.cc} | 218 +- source_files/edge/{s_opl.h => s_imf.h} | 10 +- source_files/edge/s_music.cc | 16 +- source_files/edge/snd_types.cc | 10 +- source_files/edge/snd_types.h | 2 +- 63 files changed, 2316 insertions(+), 15249 deletions(-) create mode 100644 libraries/ibxm/CMakeLists.txt create mode 100644 libraries/ibxm/LICENSE create mode 100644 libraries/ibxm/ibxm.h delete mode 100644 libraries/libRAD/instruments.h delete mode 100644 libraries/libRAD/patches.cpp delete mode 100644 libraries/libRAD/patches.h delete mode 100644 libraries/libRAD/radmidi.cpp delete mode 100644 libraries/libRAD/radmidi.h delete mode 100644 libraries/m4p/CMakeLists.txt delete mode 100644 libraries/m4p/LICENSE delete mode 100644 libraries/m4p/cpu.h delete mode 100644 libraries/m4p/ft_tables.c delete mode 100644 libraries/m4p/ft_tables.h delete mode 100644 libraries/m4p/it2drivers/sb16.c delete mode 100644 libraries/m4p/it2drivers/sb16.h delete mode 100644 libraries/m4p/it2drivers/sb16_m.c delete mode 100644 libraries/m4p/it2drivers/sb16_m.h delete mode 100644 libraries/m4p/it2drivers/zerovol.c delete mode 100644 libraries/m4p/it2drivers/zerovol.h delete mode 100644 libraries/m4p/it_d_rm.c delete mode 100644 libraries/m4p/it_d_rm.h delete mode 100644 libraries/m4p/it_m_eff.c delete mode 100644 libraries/m4p/it_m_eff.h delete mode 100644 libraries/m4p/it_music.c delete mode 100644 libraries/m4p/it_music.h delete mode 100644 libraries/m4p/it_structs.h delete mode 100644 libraries/m4p/it_tables.c delete mode 100644 libraries/m4p/it_tables.h delete mode 100644 libraries/m4p/loaders/it.c delete mode 100644 libraries/m4p/loaders/it.h delete mode 100644 libraries/m4p/loaders/mmcmp/mmcmp.c delete mode 100644 libraries/m4p/loaders/mmcmp/mmcmp.h delete mode 100644 libraries/m4p/loaders/s3m.c delete mode 100644 libraries/m4p/loaders/s3m.h delete mode 100644 libraries/m4p/m4p.c delete mode 100644 libraries/m4p/m4p.h delete mode 100644 libraries/m4p/pmp_main.c delete mode 100644 libraries/m4p/pmp_main.h delete mode 100644 libraries/m4p/pmp_mix.c delete mode 100644 libraries/m4p/pmp_mix.h delete mode 100644 libraries/m4p/pmplay.c delete mode 100644 libraries/m4p/pmplay.h delete mode 100644 libraries/m4p/snd_masm.c delete mode 100644 libraries/m4p/snd_masm.h rename source_files/edge/{s_m4p.cc => s_ibxm.cc} (53%) rename source_files/edge/{s_m4p.h => s_ibxm.h} (79%) rename source_files/edge/{s_opl.cc => s_imf.cc} (61%) rename source_files/edge/{s_opl.h => s_imf.h} (84%) diff --git a/docs/licenses/License Attribution.txt b/docs/licenses/License Attribution.txt index ddc235bbf..f23252c94 100644 --- a/docs/licenses/License Attribution.txt +++ b/docs/licenses/License Attribution.txt @@ -22,13 +22,7 @@ FMMIDI library - Copyright (c) 2003-2006 yuno Freedoom (various assets) - Copyright (c) 2001-2019 Contributors to the Freedoom project -libRAD (instruments.h) - Copyright (c) 2023 Contributors to the Freedoom project - Copyright (c) ConSiGno ("DMXOPL" instrument bank which formed the basis for Freedoom's GENMIDI) - -libRAD (patches.cpp/radmidi.cpp) - Copyright (c) 2021-2023 Devin Acker - -Mod4Play library - Copyright (c) 2024 dashodanger - Copyright (c) 2022 Olav Sørensen +IBXM - Copyright (c) 2019 Martin Cameron ZDoom FName implementation - Copyright (c) 2005-2007 Randy Heit @@ -127,7 +121,7 @@ dr_wav, dr_mp3, and dr_flac libraries - David Reid Fraction.hpp - Bisqwit -libRAD (opal.cpp/radplay.cpp) - Reality Productions +libRAD - Reality Productions prns.h - Marc B. Reynolds diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 4a04679ec..569894572 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -9,11 +9,11 @@ else() add_subdirectory(gl4es) endif() add_subdirectory(hmm) +add_subdirectory(ibxm) add_subdirectory(libemidi) add_subdirectory(libRAD) add_subdirectory(libvwad) add_subdirectory(lua) -add_subdirectory(m4p) add_subdirectory(miniz) add_subdirectory(pl_mpeg) add_subdirectory(prns) diff --git a/libraries/ibxm/CMakeLists.txt b/libraries/ibxm/CMakeLists.txt new file mode 100644 index 000000000..6b23dc7c3 --- /dev/null +++ b/libraries/ibxm/CMakeLists.txt @@ -0,0 +1,8 @@ +########################################## +# ibxm +########################################## + +add_library(ibxm INTERFACE) + +target_include_directories(ibxm INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/libraries/ibxm/LICENSE b/libraries/ibxm/LICENSE new file mode 100644 index 000000000..e838560b9 --- /dev/null +++ b/libraries/ibxm/LICENSE @@ -0,0 +1,37 @@ +--- +Copyright (c) 2019, Martin Cameron +All rights reserved. + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the +following conditions are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + + * Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + + * Neither the name of the organization nor the names of + its contributors may be used to endorse or promote + products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +--- diff --git a/libraries/ibxm/ibxm.h b/libraries/ibxm/ibxm.h new file mode 100644 index 000000000..6ac6da7a1 --- /dev/null +++ b/libraries/ibxm/ibxm.h @@ -0,0 +1,2017 @@ + +/* ibxm/ac mod/xm/s3m replay (c)mumart@gmail.com */ + +#include "stdlib.h" +#include "string.h" + +struct ibxm_data { + char *buffer; + int length; +}; + +struct ibxm_sample { + char name[ 32 ]; + int loop_start, loop_length; + short volume, panning, rel_note, fine_tune, *data; +}; + +struct ibxm_envelope { + char enabled, sustain, looped, num_points; + short sustain_tick, loop_start_tick, loop_end_tick; + short points_tick[ 16 ], points_ampl[ 16 ]; +}; + +struct ibxm_instrument { + int num_samples, vol_fadeout; + char name[ 32 ], key_to_sample[ 97 ]; + char vib_type, vib_sweep, vib_depth, vib_rate; + struct ibxm_envelope vol_env, pan_env; + struct ibxm_sample *samples; +}; + +struct ibxm_pattern { + int num_channels, num_rows; + char *data; +}; + +struct ibxm_module { + char name[ 32 ]; + int num_channels, num_instruments; + int num_patterns, sequence_len, restart_pos; + int default_gvol, default_speed, default_tempo, c2_rate, gain; + int linear_periods, fast_vol_slides; + unsigned char *default_panning, *sequence; + struct ibxm_pattern *patterns; + struct ibxm_instrument *instruments; +}; + +/* Allocate and initialize a module from the specified data, returns NULL on error. + Message must point to a 64-character buffer to receive error messages. */ +struct ibxm_module* ibxm_module_load( struct ibxm_data *data, char *message ); +/* Deallocate the specified module. */ +void ibxm_dispose_module( struct ibxm_module *module ); +/* Allocate and initialize a replay with the specified module and sampling rate. */ +struct ibxm_replay* ibxm_new_replay( struct ibxm_module *module, int sample_rate, int interpolation ); +/* Deallocate the specified replay. */ +void ibxm_dispose_replay( struct ibxm_replay *replay ); +/* Returns the song duration in samples at the current sampling rate. */ +int ibxm_replay_calculate_duration( struct ibxm_replay *replay ); +/* Seek to approximately the specified sample position. + The actual sample position reached is returned. */ +int ibxm_replay_seek( struct ibxm_replay *replay, int sample_pos ); +/* Set the pattern in the sequence to play. The tempo is reset to the default. */ +void ibxm_replay_set_sequence_pos( struct ibxm_replay *replay, int pos ); +/* Generates audio and returns the number of stereo samples written into mix_buf. + Individual channels may be excluded using the mute bitmask. */ +int ibxm_replay_get_audio( struct ibxm_replay *replay, int *mix_buf, int mute ); +/* Returns the currently playing pattern in the sequence.*/ +int ibxm_replay_get_sequence_pos( struct ibxm_replay *replay ); +/* Returns the currently playing row in the pattern. */ +int ibxm_replay_get_row( struct ibxm_replay *replay ); +/* Returns the length of the output buffer required by replay_get_audio(). */ +int ibxm_calculate_mix_buf_len( int sample_rate ); + +int ibxm_data_u16be( struct ibxm_data *data, int offset ); +int ibxm_data_u16le( struct ibxm_data *data, int offset ); +unsigned int ibxm_data_u32le( struct ibxm_data *data, int offset ); + + +static const int FP_SHIFT = 15, FP_ONE = 32768, FP_MASK = 32767; + +static const int exp2_table[] = { + 32768, 32946, 33125, 33305, 33486, 33667, 33850, 34034, + 34219, 34405, 34591, 34779, 34968, 35158, 35349, 35541, + 35734, 35928, 36123, 36319, 36516, 36715, 36914, 37114, + 37316, 37518, 37722, 37927, 38133, 38340, 38548, 38757, + 38968, 39180, 39392, 39606, 39821, 40037, 40255, 40473, + 40693, 40914, 41136, 41360, 41584, 41810, 42037, 42265, + 42495, 42726, 42958, 43191, 43425, 43661, 43898, 44137, + 44376, 44617, 44859, 45103, 45348, 45594, 45842, 46091, + 46341, 46593, 46846, 47100, 47356, 47613, 47871, 48131, + 48393, 48655, 48920, 49185, 49452, 49721, 49991, 50262, + 50535, 50810, 51085, 51363, 51642, 51922, 52204, 52488, + 52773, 53059, 53347, 53637, 53928, 54221, 54515, 54811, + 55109, 55408, 55709, 56012, 56316, 56622, 56929, 57238, + 57549, 57861, 58176, 58491, 58809, 59128, 59449, 59772, + 60097, 60423, 60751, 61081, 61413, 61746, 62081, 62419, + 62757, 63098, 63441, 63785, 64132, 64480, 64830, 65182, + 65536 +}; + +static const short sine_table[] = { + 0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, 235, 244, 250, 253, + 255, 253, 250, 244, 235, 224, 212, 197, 180, 161, 141, 120, 97, 74, 49, 24 +}; + +struct note { + unsigned char key, instrument, volume, effect, param; +}; + +struct channel { + struct ibxm_replay *replay; + struct ibxm_instrument *instrument; + struct ibxm_sample *sample; + struct note note; + int id, key_on, random_seed, pl_row; + int sample_off, sample_idx, sample_fra, freq, ampl, pann; + int volume, panning, fadeout_vol, vol_env_tick, pan_env_tick; + int period, porta_period, retrig_count, fx_count, av_count; + int porta_up_param, porta_down_param, tone_porta_param, offset_param; + int fine_porta_up_param, fine_porta_down_param, xfine_porta_param; + int arpeggio_param, vol_slide_param, gvol_slide_param, pan_slide_param; + int fine_vslide_up_param, fine_vslide_down_param; + int retrig_volume, retrig_ticks, tremor_on_ticks, tremor_off_ticks; + int vibrato_type, vibrato_phase, vibrato_speed, vibrato_depth; + int tremolo_type, tremolo_phase, tremolo_speed, tremolo_depth; + int tremolo_add, vibrato_add, arpeggio_add; +}; + +struct ibxm_replay { + int sample_rate, interpolation, global_vol; + int seq_pos, break_pos, row, next_row, tick; + int speed, tempo, pl_count, pl_chan; + int *ramp_buf; + char **play_count; + struct channel *channels; + struct ibxm_module *module; +}; + +static int exp_2( int x ) { + int c, m, y; + int x0 = ( x & FP_MASK ) >> ( FP_SHIFT - 7 ); + c = exp2_table[ x0 ]; + m = exp2_table[ x0 + 1 ] - c; + y = ( m * ( x & ( FP_MASK >> 7 ) ) >> 8 ) + c; + return ( y << FP_SHIFT ) >> ( FP_SHIFT - ( x >> FP_SHIFT ) ); +} + +static int log_2( int x ) { + int step; + int y = 16 << FP_SHIFT; + for( step = y; step > 0; step >>= 1 ) { + if( exp_2( y - step ) >= x ) { + y -= step; + } + } + return y; +} + +static char* data_ascii( struct ibxm_data *data, int offset, int length, char *dest ) { + int idx, chr; + memset( dest, 32, length ); + if( offset > data->length ) { + offset = data->length; + } + if( ( unsigned int ) offset + length > ( unsigned int ) data->length ) { + length = data->length - offset; + } + for( idx = 0; idx < length; idx++ ) { + chr = data->buffer[ offset + idx ] & 0xFF; + if( chr > 32 ) { + dest[ idx ] = chr; + } + } + return dest; +} + +static int data_s8( struct ibxm_data *data, int offset ) { + int value = 0; + if( offset < data->length ) { + value = data->buffer[ offset ]; + value = ( value & 0x7F ) - ( value & 0x80 ); + } + return value; +} + +static int data_u8( struct ibxm_data *data, int offset ) { + int value = 0; + if( offset < data->length ) { + value = data->buffer[ offset ] & 0xFF; + } + return value; +} + +int ibxm_data_u16be( struct ibxm_data *data, int offset ) { + int value = 0; + if( offset + 1 < data->length ) { + value = ( ( data->buffer[ offset ] & 0xFF ) << 8 ) + | ( data->buffer[ offset + 1 ] & 0xFF ); + } + return value; +} + +int ibxm_data_u16le( struct ibxm_data *data, int offset ) { + int value = 0; + if( offset + 1 < data->length ) { + value = ( data->buffer[ offset ] & 0xFF ) + | ( ( data->buffer[ offset + 1 ] & 0xFF ) << 8 ); + } + return value; +} + +unsigned int ibxm_data_u32le( struct ibxm_data *data, int offset ) { + unsigned int value = 0; + if( offset + 3 < data->length ) { + value = ( data->buffer[ offset ] & 0xFF ) + | ( ( data->buffer[ offset + 1 ] & 0xFF ) << 8 ) + | ( ( data->buffer[ offset + 2 ] & 0xFF ) << 16 ) + | ( ( data->buffer[ offset + 3 ] & 0xFF ) << 24 ); + } + return value; +} + +static void data_sam_s8( struct ibxm_data *data, int offset, int count, short *dest ) { + int idx, amp, length = data->length; + char *buffer = data->buffer; + if( offset > length ) { + offset = length; + } + if( offset + count > length ) { + count = length - offset; + } + for( idx = 0; idx < count; idx++ ) { + amp = ( buffer[ offset + idx ] & 0xFF ) << 8; + dest[ idx ] = ( amp & 0x7FFF ) - ( amp & 0x8000 ); + } +} + +static void data_sam_s16le( struct ibxm_data *data, int offset, int count, short *dest ) { + int idx, amp, length = data->length; + char *buffer = data->buffer; + if( offset > length ) { + offset = length; + } + if( offset + count * 2 > length ) { + count = ( length - offset ) / 2; + } + for( idx = 0; idx < count; idx++ ) { + amp = ( buffer[ offset + idx * 2 ] & 0xFF ) | ( buffer[ offset + idx * 2 + 1 ] << 8 ); + dest[ idx ] = ( amp & 0x7FFF ) - ( amp & 0x8000 ); + } +} + +static int envelope_next_tick( struct ibxm_envelope *envelope, int tick, int key_on ) { + tick++; + if( envelope->looped && tick >= envelope->loop_end_tick ) { + tick = envelope->loop_start_tick; + } + if( envelope->sustain && key_on && tick >= envelope->sustain_tick ) { + tick = envelope->sustain_tick; + } + return tick; +} + +static int envelope_calculate_ampl( struct ibxm_envelope *envelope, int tick ) { + int idx, point, dt, da; + int ampl = envelope->points_ampl[ envelope->num_points - 1 ]; + if( tick < envelope->points_tick[ envelope->num_points - 1 ] ) { + point = 0; + for( idx = 1; idx < envelope->num_points; idx++ ) { + if( envelope->points_tick[ idx ] <= tick ) { + point = idx; + } + } + dt = envelope->points_tick[ point + 1 ] - envelope->points_tick[ point ]; + da = envelope->points_ampl[ point + 1 ] - envelope->points_ampl[ point ]; + ampl = envelope->points_ampl[ point ]; + ampl += ( ( da << 24 ) / dt ) * ( tick - envelope->points_tick[ point ] ) >> 24; + } + return ampl; +} + +static void sample_ping_pong( struct ibxm_sample *sample ) { + int idx; + int loop_start = sample->loop_start; + int loop_length = sample->loop_length; + int loop_end = loop_start + loop_length; + short *sample_data = sample->data; + short *new_data = (short *)calloc( loop_end + loop_length + 1, sizeof( short ) ); + if( new_data ) { + memcpy( new_data, sample_data, loop_end * sizeof( short ) ); + for( idx = 0; idx < loop_length; idx++ ) { + new_data[ loop_end + idx ] = sample_data[ loop_end - idx - 1 ]; + } + free( sample->data ); + sample->data = new_data; + sample->loop_length *= 2; + sample->data[ loop_start + sample->loop_length ] = sample->data[ loop_start ]; + } +} + +/* Deallocate the specified module. */ +void ibxm_dispose_module( struct ibxm_module *module ) { + int idx, sam; + struct ibxm_instrument *instrument; + free( module->default_panning ); + free( module->sequence ); + if( module->patterns ) { + for( idx = 0; idx < module->num_patterns; idx++ ) { + free( module->patterns[ idx ].data ); + } + free( module->patterns ); + } + if( module->instruments ) { + for( idx = 0; idx <= module->num_instruments; idx++ ) { + instrument = &module->instruments[ idx ]; + if( instrument->samples ) { + for( sam = 0; sam < instrument->num_samples; sam++ ) { + free( instrument->samples[ sam ].data ); + } + free( instrument->samples ); + } + } + free( module->instruments ); + } + free( module ); +} + +static struct ibxm_module* module_load_xm( struct ibxm_data *data, char *message ) { + int delta_env, offset, next_offset, idx, entry; + int num_rows, num_notes, pat_data_len, pat_data_offset; + int sam, sam_head_offset, sam_data_bytes, sam_data_samples; + int num_samples, sam_loop_start, sam_loop_length, amp; + int note, flags, key, ins, vol, fxc, fxp; + int point, point_tick, point_offset; + int looped, ping_pong, sixteen_bit; + char ascii[ 16 ], *pattern_data; + struct ibxm_instrument *instrument; + struct ibxm_sample *sample; + struct ibxm_module *module = (ibxm_module *)calloc( 1, sizeof( struct ibxm_module ) ); + if( module ) { + if( ibxm_data_u16le( data, 58 ) != 0x0104 ) { + strcpy( message, "XM format version must be 0x0104!" ); + ibxm_dispose_module( module ); + return NULL; + } + data_ascii( data, 17, 20, module->name ); + delta_env = !memcmp( data_ascii( data, 38, 15, ascii ), "DigiBooster Pro", 15 ); + offset = 60 + ibxm_data_u32le( data, 60 ); + module->sequence_len = ibxm_data_u16le( data, 64 ); + module->restart_pos = ibxm_data_u16le( data, 66 ); + module->num_channels = ibxm_data_u16le( data, 68 ); + module->num_patterns = ibxm_data_u16le( data, 70 ); + module->num_instruments = ibxm_data_u16le( data, 72 ); + module->linear_periods = ibxm_data_u16le( data, 74 ) & 0x1; + module->default_gvol = 64; + module->default_speed = ibxm_data_u16le( data, 76 ); + module->default_tempo = ibxm_data_u16le( data, 78 ); + module->c2_rate = 8363; + module->gain = 64; + module->default_panning = (unsigned char *)calloc( module->num_channels, sizeof( unsigned char ) ); + if( !module->default_panning ) { + ibxm_dispose_module( module ); + return NULL; + } + for( idx = 0; idx < module->num_channels; idx++ ) { + module->default_panning[ idx ] = 128; + } + module->sequence = (unsigned char *)calloc( module->sequence_len, sizeof( unsigned char ) ); + if( !module->sequence ) { + ibxm_dispose_module( module ); + return NULL; + } + for( idx = 0; idx < module->sequence_len; idx++ ) { + entry = data_u8( data, 80 + idx ); + module->sequence[ idx ] = entry < module->num_patterns ? entry : 0; + } + module->patterns = (ibxm_pattern *)calloc( module->num_patterns, sizeof( struct ibxm_pattern ) ); + if( !module->patterns ) { + ibxm_dispose_module( module ); + return NULL; + } + for( idx = 0; idx < module->num_patterns; idx++ ) { + if( data_u8( data, offset + 4 ) ) { + strcpy( message, "Unknown pattern packing type!" ); + ibxm_dispose_module( module ); + return NULL; + } + num_rows = ibxm_data_u16le( data, offset + 5 ); + if( num_rows < 1 ) { + num_rows = 1; + } + pat_data_len = ibxm_data_u16le( data, offset + 7 ); + offset += ibxm_data_u32le( data, offset ); + next_offset = offset + pat_data_len; + num_notes = num_rows * module->num_channels; + pattern_data = (char *)calloc( num_notes, 5 ); + if( !pattern_data ) { + ibxm_dispose_module( module ); + return NULL; + } + module->patterns[ idx ].num_channels = module->num_channels; + module->patterns[ idx ].num_rows = num_rows; + module->patterns[ idx ].data = pattern_data; + if( pat_data_len > 0 ) { + pat_data_offset = 0; + for( note = 0; note < num_notes; note++ ) { + flags = data_u8( data, offset ); + if( ( flags & 0x80 ) == 0 ) { + flags = 0x1F; + } else { + offset++; + } + key = ( flags & 0x01 ) > 0 ? data_u8( data, offset++ ) : 0; + pattern_data[ pat_data_offset++ ] = key; + ins = ( flags & 0x02 ) > 0 ? data_u8( data, offset++ ) : 0; + pattern_data[ pat_data_offset++ ] = ins; + vol = ( flags & 0x04 ) > 0 ? data_u8( data, offset++ ) : 0; + pattern_data[ pat_data_offset++ ] = vol; + fxc = ( flags & 0x08 ) > 0 ? data_u8( data, offset++ ) : 0; + fxp = ( flags & 0x10 ) > 0 ? data_u8( data, offset++ ) : 0; + if( fxc >= 0x40 ) { + fxc = fxp = 0; + } + pattern_data[ pat_data_offset++ ] = fxc; + pattern_data[ pat_data_offset++ ] = fxp; + } + } + offset = next_offset; + } + module->instruments = (ibxm_instrument *)calloc( module->num_instruments + 1, sizeof( struct ibxm_instrument ) ); + if( !module->instruments ) { + ibxm_dispose_module( module ); + return NULL; + } + instrument = &module->instruments[ 0 ]; + instrument->samples = (ibxm_sample *)calloc( 1, sizeof( struct ibxm_sample ) ); + if( !instrument->samples ) { + ibxm_dispose_module( module ); + return NULL; + } + for( ins = 1; ins <= module->num_instruments; ins++ ) { + instrument = &module->instruments[ ins ]; + data_ascii( data, offset + 4, 22, instrument->name ); + num_samples = ibxm_data_u16le( data, offset + 27 ); + instrument->num_samples = ( num_samples > 0 ) ? num_samples : 1; + instrument->samples = (ibxm_sample *)calloc( instrument->num_samples, sizeof( struct ibxm_sample ) ); + if( !instrument->samples ) { + ibxm_dispose_module( module ); + return NULL; + } + if( num_samples > 0 ) { + for( key = 0; key < 96; key++ ) { + instrument->key_to_sample[ key + 1 ] = data_u8( data, offset + 33 + key ); + } + point_tick = 0; + for( point = 0; point < 12; point++ ) { + point_offset = offset + 129 + ( point * 4 ); + point_tick = ( delta_env ? point_tick : 0 ) + ibxm_data_u16le( data, point_offset ); + instrument->vol_env.points_tick[ point ] = point_tick; + instrument->vol_env.points_ampl[ point ] = ibxm_data_u16le( data, point_offset + 2 ); + } + point_tick = 0; + for( point = 0; point < 12; point++ ) { + point_offset = offset + 177 + ( point * 4 ); + point_tick = ( delta_env ? point_tick : 0 ) + ibxm_data_u16le( data, point_offset ); + instrument->pan_env.points_tick[ point ] = point_tick; + instrument->pan_env.points_ampl[ point ] = ibxm_data_u16le( data, point_offset + 2 ); + } + instrument->vol_env.num_points = data_u8( data, offset + 225 ); + if( instrument->vol_env.num_points > 12 ) { + instrument->vol_env.num_points = 0; + } + instrument->pan_env.num_points = data_u8( data, offset + 226 ); + if( instrument->pan_env.num_points > 12 ) { + instrument->pan_env.num_points = 0; + } + instrument->vol_env.sustain_tick = instrument->vol_env.points_tick[ data_u8( data, offset + 227 ) & 0xF ]; + instrument->vol_env.loop_start_tick = instrument->vol_env.points_tick[ data_u8( data, offset + 228 ) & 0xF ]; + instrument->vol_env.loop_end_tick = instrument->vol_env.points_tick[ data_u8( data, offset + 229 ) & 0xF ]; + instrument->pan_env.sustain_tick = instrument->pan_env.points_tick[ data_u8( data, offset + 230 ) & 0xF ]; + instrument->pan_env.loop_start_tick = instrument->pan_env.points_tick[ data_u8( data, offset + 231 ) & 0xF ]; + instrument->pan_env.loop_end_tick = instrument->pan_env.points_tick[ data_u8( data, offset + 232 ) & 0xF ]; + instrument->vol_env.enabled = instrument->vol_env.num_points > 0 && ( data_u8( data, offset + 233 ) & 0x1 ); + instrument->vol_env.sustain = ( data_u8( data, offset + 233 ) & 0x2 ) > 0; + instrument->vol_env.looped = ( data_u8( data, offset + 233 ) & 0x4 ) > 0; + instrument->pan_env.enabled = instrument->pan_env.num_points > 0 && ( data_u8( data, offset + 234 ) & 0x1 ); + instrument->pan_env.sustain = ( data_u8( data, offset + 234 ) & 0x2 ) > 0; + instrument->pan_env.looped = ( data_u8( data, offset + 234 ) & 0x4 ) > 0; + instrument->vib_type = data_u8( data, offset + 235 ); + instrument->vib_sweep = data_u8( data, offset + 236 ); + instrument->vib_depth = data_u8( data, offset + 237 ); + instrument->vib_rate = data_u8( data, offset + 238 ); + instrument->vol_fadeout = ibxm_data_u16le( data, offset + 239 ); + } + offset += ibxm_data_u32le( data, offset ); + sam_head_offset = offset; + offset += num_samples * 40; + for( sam = 0; sam < num_samples; sam++ ) { + sample = &instrument->samples[ sam ]; + sam_data_bytes = ibxm_data_u32le( data, sam_head_offset ); + sam_loop_start = ibxm_data_u32le( data, sam_head_offset + 4 ); + sam_loop_length = ibxm_data_u32le( data, sam_head_offset + 8 ); + sample->volume = data_u8( data, sam_head_offset + 12 ); + sample->fine_tune = data_s8( data, sam_head_offset + 13 ); + looped = ( data_u8( data, sam_head_offset + 14 ) & 0x3 ) > 0; + ping_pong = ( data_u8( data, sam_head_offset + 14 ) & 0x2 ) > 0; + sixteen_bit = ( data_u8( data, sam_head_offset + 14 ) & 0x10 ) > 0; + sample->panning = data_u8( data, sam_head_offset + 15 ) + 1; + sample->rel_note = data_s8( data, sam_head_offset + 16 ); + data_ascii( data, sam_head_offset + 18, 22, sample->name ); + sam_head_offset += 40; + sam_data_samples = sam_data_bytes; + if( sixteen_bit ) { + sam_data_samples = sam_data_samples >> 1; + sam_loop_start = sam_loop_start >> 1; + sam_loop_length = sam_loop_length >> 1; + } + if( !looped || ( sam_loop_start + sam_loop_length ) > sam_data_samples ) { + sam_loop_start = sam_data_samples; + sam_loop_length = 0; + } + sample->loop_start = sam_loop_start; + sample->loop_length = sam_loop_length; + sample->data = (short *)calloc( sam_data_samples + 1, sizeof( short ) ); + if( sample->data ) { + if( sixteen_bit ) { + data_sam_s16le( data, offset, sam_data_samples, sample->data ); + } else { + data_sam_s8( data, offset, sam_data_samples, sample->data ); + } + amp = 0; + for( idx = 0; idx < sam_data_samples; idx++ ) { + amp = amp + sample->data[ idx ]; + amp = ( amp & 0x7FFF ) - ( amp & 0x8000 ); + sample->data[ idx ] = amp; + } + sample->data[ sam_loop_start + sam_loop_length ] = sample->data[ sam_loop_start ]; + if( ping_pong ) { + sample_ping_pong( sample ); + } + } else { + ibxm_dispose_module( module ); + return NULL; + } + offset += sam_data_bytes; + } + } + } + return module; +} + +static struct ibxm_module* module_load_s3m( struct ibxm_data *data, char *message ) { + int idx, module_data_idx, inst_offset, flags; + int version, sixteen_bit, tune, signed_samples; + int stereo_mode, default_pan, channel_map[ 32 ]; + int sample_offset, sample_length, loop_start, loop_length; + int pat_offset, note_offset, row, chan, token; + int key, ins, volume, effect, param, panning; + char *pattern_data; + struct ibxm_instrument *instrument; + struct ibxm_sample *sample; + struct ibxm_module *module = (ibxm_module *)calloc( 1, sizeof( struct ibxm_module ) ); + if( module ) { + data_ascii( data, 0, 28, module->name ); + module->sequence_len = ibxm_data_u16le( data, 32 ); + module->num_instruments = ibxm_data_u16le( data, 34 ); + module->num_patterns = ibxm_data_u16le( data, 36 ); + flags = ibxm_data_u16le( data, 38 ); + version = ibxm_data_u16le( data, 40 ); + module->fast_vol_slides = ( ( flags & 0x40 ) == 0x40 ) || version == 0x1300; + signed_samples = ibxm_data_u16le( data, 42 ) == 1; + if( ibxm_data_u32le( data, 44 ) != 0x4d524353 ) { + strcpy( message, "Not an S3M file!" ); + ibxm_dispose_module( module ); + return NULL; + } + module->default_gvol = data_u8( data, 48 ); + module->default_speed = data_u8( data, 49 ); + module->default_tempo = data_u8( data, 50 ); + module->c2_rate = 8363; + module->gain = data_u8( data, 51 ) & 0x7F; + stereo_mode = ( data_u8( data, 51 ) & 0x80 ) == 0x80; + default_pan = data_u8( data, 53 ) == 0xFC; + for( idx = 0; idx < 32; idx++ ) { + channel_map[ idx ] = -1; + if( data_u8( data, 64 + idx ) < 16 ) { + channel_map[ idx ] = module->num_channels++; + } + } + module->sequence = (unsigned char *)calloc( module->sequence_len, sizeof( unsigned char ) ); + if( !module->sequence ){ + ibxm_dispose_module( module ); + return NULL; + } + for( idx = 0; idx < module->sequence_len; idx++ ) { + module->sequence[ idx ] = data_u8( data, 96 + idx ); + } + module_data_idx = 96 + module->sequence_len; + module->instruments = (ibxm_instrument *)calloc( module->num_instruments + 1, sizeof( struct ibxm_instrument ) ); + if( !module->instruments ) { + ibxm_dispose_module( module ); + return NULL; + } + instrument = &module->instruments[ 0 ]; + instrument->num_samples = 1; + instrument->samples = (ibxm_sample *)calloc( 1, sizeof( struct ibxm_sample ) ); + if( !instrument->samples ) { + ibxm_dispose_module( module ); + return NULL; + } + for( ins = 1; ins <= module->num_instruments; ins++ ) { + instrument = &module->instruments[ ins ]; + instrument->num_samples = 1; + instrument->samples = (ibxm_sample *)calloc( 1, sizeof( struct ibxm_sample ) ); + if( !instrument->samples ) { + ibxm_dispose_module( module ); + return NULL; + } + sample = &instrument->samples[ 0 ]; + inst_offset = ibxm_data_u16le( data, module_data_idx ) << 4; + module_data_idx += 2; + data_ascii( data, inst_offset + 48, 28, instrument->name ); + if( data_u8( data, inst_offset ) == 1 && ibxm_data_u16le( data, inst_offset + 76 ) == 0x4353 ) { + sample_offset = ( data_u8( data, inst_offset + 13 ) << 20 ) + + ( ibxm_data_u16le( data, inst_offset + 14 ) << 4 ); + sample_length = ibxm_data_u32le( data, inst_offset + 16 ); + loop_start = ibxm_data_u32le( data, inst_offset + 20 ); + loop_length = ibxm_data_u32le( data, inst_offset + 24 ) - loop_start; + sample->volume = data_u8( data, inst_offset + 28 ); + if( data_u8( data, inst_offset + 30 ) != 0 ) { + strcpy( message, "Packed samples not supported!" ); + ibxm_dispose_module( module ); + return NULL; + } + if( loop_start + loop_length > sample_length ) { + loop_length = sample_length - loop_start; + } + if( loop_length < 1 || !( data_u8( data, inst_offset + 31 ) & 0x1 ) ) { + loop_start = sample_length; + loop_length = 0; + } + sample->loop_start = loop_start; + sample->loop_length = loop_length; + /* stereo = data_u8( data, inst_offset + 31 ) & 0x2; */ + sixteen_bit = data_u8( data, inst_offset + 31 ) & 0x4; + tune = ( log_2( ibxm_data_u32le( data, inst_offset + 32 ) ) - log_2( module->c2_rate ) ) * 12; + sample->rel_note = tune >> FP_SHIFT; + sample->fine_tune = ( tune & FP_MASK ) >> ( FP_SHIFT - 7 ); + sample->data = (short *)calloc( sample_length + 1, sizeof( short ) ); + if( sample->data ) { + if( sixteen_bit ) { + data_sam_s16le( data, sample_offset, sample_length, sample->data ); + } else { + data_sam_s8( data, sample_offset, sample_length, sample->data ); + } + if( !signed_samples ) { + for( idx = 0; idx < sample_length; idx++ ) { + sample->data[ idx ] = ( sample->data[ idx ] & 0xFFFF ) - 32768; + } + } + sample->data[ loop_start + loop_length ] = sample->data[ loop_start ]; + } else { + ibxm_dispose_module( module ); + return NULL; + } + } + } + module->patterns = (ibxm_pattern *)calloc( module->num_patterns, sizeof( struct ibxm_pattern ) ); + if( !module->patterns ) { + ibxm_dispose_module( module ); + return NULL; + } + for( idx = 0; idx < module->num_patterns; idx++ ) { + module->patterns[ idx ].num_channels = module->num_channels; + module->patterns[ idx ].num_rows = 64; + pattern_data = (char *)calloc( module->num_channels * 64, 5 ); + if( !pattern_data ) { + ibxm_dispose_module( module ); + return NULL; + } + module->patterns[ idx ].data = pattern_data; + pat_offset = ( ibxm_data_u16le( data, module_data_idx ) << 4 ) + 2; + row = 0; + while( row < 64 ) { + token = data_u8( data, pat_offset++ ); + if( token ) { + key = ins = 0; + if( ( token & 0x20 ) == 0x20 ) { + /* Key + Instrument.*/ + key = data_u8( data, pat_offset++ ); + ins = data_u8( data, pat_offset++ ); + if( key < 0xFE ) { + key = ( key >> 4 ) * 12 + ( key & 0xF ) + 1; + } else if( key == 0xFF ) { + key = 0; + } + } + volume = 0; + if( ( token & 0x40 ) == 0x40 ) { + /* Volume Column.*/ + volume = ( data_u8( data, pat_offset++ ) & 0x7F ) + 0x10; + if( volume > 0x50 ) { + volume = 0; + } + } + effect = param = 0; + if( ( token & 0x80 ) == 0x80 ) { + /* Effect + Param.*/ + effect = data_u8( data, pat_offset++ ); + param = data_u8( data, pat_offset++ ); + if( effect < 1 || effect >= 0x40 ) { + effect = param = 0; + } else if( effect > 0 ) { + effect += 0x80; + } + } + chan = channel_map[ token & 0x1F ]; + if( chan >= 0 ) { + note_offset = ( row * module->num_channels + chan ) * 5; + pattern_data[ note_offset ] = key; + pattern_data[ note_offset + 1 ] = ins; + pattern_data[ note_offset + 2 ] = volume; + pattern_data[ note_offset + 3 ] = effect; + pattern_data[ note_offset + 4 ] = param; + } + } else { + row++; + } + } + module_data_idx += 2; + } + module->default_panning = (unsigned char *)calloc( module->num_channels, sizeof( unsigned char ) ); + if( module->default_panning ) { + for( chan = 0; chan < 32; chan++ ) { + if( channel_map[ chan ] >= 0 ) { + panning = 7; + if( stereo_mode ) { + panning = 12; + if( data_u8( data, 64 + chan ) < 8 ) { + panning = 3; + } + } + if( default_pan ) { + flags = data_u8( data, module_data_idx + chan ); + if( ( flags & 0x20 ) == 0x20 ) { + panning = flags & 0xF; + } + } + module->default_panning[ channel_map[ chan ] ] = panning * 17; + } + } + } else { + ibxm_dispose_module( module ); + return NULL; + } + } + return module; +} + +static struct ibxm_module* module_load_mod( struct ibxm_data *data, char *message ) { + int idx, pat, module_data_idx, pat_data_len, pat_data_idx; + int period, key, ins, effect, param, fine_tune; + int sample_length, loop_start, loop_length; + char *pattern_data; + struct ibxm_instrument *instrument; + struct ibxm_sample *sample; + struct ibxm_module *module = (ibxm_module *)calloc( 1, sizeof( struct ibxm_module ) ); + if( module ) { + data_ascii( data, 0, 20, module->name ); + module->sequence_len = data_u8( data, 950 ) & 0x7F; + module->restart_pos = data_u8( data, 951 ) & 0x7F; + if( module->restart_pos >= module->sequence_len ) { + module->restart_pos = 0; + } + module->sequence = (unsigned char *)calloc( 128, sizeof( unsigned char ) ); + if( !module->sequence ){ + ibxm_dispose_module( module ); + return NULL; + } + for( idx = 0; idx < 128; idx++ ) { + pat = data_u8( data, 952 + idx ) & 0x7F; + module->sequence[ idx ] = pat; + if( pat >= module->num_patterns ) { + module->num_patterns = pat + 1; + } + } + switch( ibxm_data_u16be( data, 1082 ) ) { + case 0x4b2e: /* M.K. */ + case 0x4b21: /* M!K! */ + case 0x5434: /* FLT4 */ + module->num_channels = 4; + module->c2_rate = 8287; + module->gain = 64; + break; + case 0x484e: /* xCHN */ + module->num_channels = data_u8( data, 1080 ) - 48; + module->c2_rate = 8363; + module->gain = 32; + break; + case 0x4348: /* xxCH */ + module->num_channels = ( data_u8( data, 1080 ) - 48 ) * 10; + module->num_channels += data_u8( data, 1081 ) - 48; + module->c2_rate = 8363; + module->gain = 32; + break; + default: + strcpy( message, "MOD Format not recognised!" ); + ibxm_dispose_module( module ); + return NULL; + } + module->default_gvol = 64; + module->default_speed = 6; + module->default_tempo = 125; + module->default_panning = (unsigned char *)calloc( module->num_channels, sizeof( unsigned char ) ); + if( !module->default_panning ) { + ibxm_dispose_module( module ); + return NULL; + } + for( idx = 0; idx < module->num_channels; idx++ ) { + module->default_panning[ idx ] = 51; + if( ( idx & 3 ) == 1 || ( idx & 3 ) == 2 ) { + module->default_panning[ idx ] = 204; + } + } + module_data_idx = 1084; + module->patterns = (ibxm_pattern *)calloc( module->num_patterns, sizeof( struct ibxm_pattern ) ); + if( !module->patterns ) { + ibxm_dispose_module( module ); + return NULL; + } + pat_data_len = module->num_channels * 64 * 5; + for( pat = 0; pat < module->num_patterns; pat++ ) { + module->patterns[ pat ].num_channels = module->num_channels; + module->patterns[ pat ].num_rows = 64; + pattern_data = (char *)calloc( 1, pat_data_len ); + if( !pattern_data ) { + ibxm_dispose_module( module ); + return NULL; + } + module->patterns[ pat ].data = pattern_data; + for( pat_data_idx = 0; pat_data_idx < pat_data_len; pat_data_idx += 5 ) { + period = ( data_u8( data, module_data_idx ) & 0xF ) << 8; + period = ( period | data_u8( data, module_data_idx + 1 ) ) * 4; + if( period >= 112 && period <= 6848 ) { + key = -12 * log_2( ( period << FP_SHIFT ) / 29021 ); + key = ( key + ( key & ( FP_ONE >> 1 ) ) ) >> FP_SHIFT; + pattern_data[ pat_data_idx ] = key; + } + ins = ( data_u8( data, module_data_idx + 2 ) & 0xF0 ) >> 4; + ins = ins | ( data_u8( data, module_data_idx ) & 0x10 ); + pattern_data[ pat_data_idx + 1 ] = ins; + effect = data_u8( data, module_data_idx + 2 ) & 0x0F; + param = data_u8( data, module_data_idx + 3 ); + if( param == 0 && ( effect < 3 || effect == 0xA ) ) { + effect = 0; + } + if( param == 0 && ( effect == 5 || effect == 6 ) ) { + effect -= 2; + } + if( effect == 8 ) { + if( module->num_channels == 4 ) { + effect = param = 0; + } else if( param > 128 ) { + param = 128; + } else { + param = ( param * 255 ) >> 7; + } + } + pattern_data[ pat_data_idx + 3 ] = effect; + pattern_data[ pat_data_idx + 4 ] = param; + module_data_idx += 4; + } + } + module->num_instruments = 31; + module->instruments = (ibxm_instrument *)calloc( module->num_instruments + 1, sizeof( struct ibxm_instrument ) ); + if( !module->instruments ) { + ibxm_dispose_module( module ); + return NULL; + } + instrument = &module->instruments[ 0 ]; + instrument->num_samples = 1; + instrument->samples = (ibxm_sample *)calloc( 1, sizeof( struct ibxm_sample ) ); + if( !instrument->samples ) { + ibxm_dispose_module( module ); + return NULL; + } + for( ins = 1; ins <= module->num_instruments; ins++ ) { + instrument = &module->instruments[ ins ]; + instrument->num_samples = 1; + instrument->samples = (ibxm_sample *)calloc( 1, sizeof( struct ibxm_sample ) ); + if( !instrument->samples ) { + ibxm_dispose_module( module ); + return NULL; + } + sample = &instrument->samples[ 0 ]; + data_ascii( data, ins * 30 - 10, 22, instrument->name ); + sample_length = ibxm_data_u16be( data, ins * 30 + 12 ) * 2; + fine_tune = ( data_u8( data, ins * 30 + 14 ) & 0xF ) << 4; + sample->fine_tune = ( fine_tune & 0x7F ) - ( fine_tune & 0x80 ); + sample->volume = data_u8( data, ins * 30 + 15 ) & 0x7F; + if( sample->volume > 64 ) { + sample->volume = 64; + } + loop_start = ibxm_data_u16be( data, ins * 30 + 16 ) * 2; + loop_length = ibxm_data_u16be( data, ins * 30 + 18 ) * 2; + if( loop_start + loop_length > sample_length ) { + if( loop_start / 2 + loop_length <= sample_length ) { + /* Some old modules have loop start in bytes. */ + loop_start = loop_start / 2; + } else { + loop_length = sample_length - loop_start; + } + } + if( loop_length < 4 ) { + loop_start = sample_length; + loop_length = 0; + } + sample->loop_start = loop_start; + sample->loop_length = loop_length; + sample->data = (short *)calloc( sample_length + 1, sizeof( short ) ); + if( sample->data ) { + data_sam_s8( data, module_data_idx, sample_length, sample->data ); + sample->data[ loop_start + loop_length ] = sample->data[ loop_start ]; + } else { + ibxm_dispose_module( module ); + return NULL; + } + module_data_idx += sample_length; + } + } + return module; +} + +/* Allocate and initialize a module from the specified data, returns NULL on error. + Message must point to a 64-character buffer to receive error messages. */ +struct ibxm_module* ibxm_module_load( struct ibxm_data *data, char *message ) { + char ascii[ 16 ]; + struct ibxm_module* module; + if( !memcmp( data_ascii( data, 0, 16, ascii ), "Extended Module:", 16 ) ) { + module = module_load_xm( data, message ); + } else if( !memcmp( data_ascii( data, 44, 4, ascii ), "SCRM", 4 ) ) { + module = module_load_s3m( data, message ); + } else { + module = module_load_mod( data, message ); + } + return module; +} + +static void pattern_get_note( struct ibxm_pattern *pattern, int row, int chan, struct note *dest ) { + int offset = ( row * pattern->num_channels + chan ) * 5; + if( offset >= 0 && row < pattern->num_rows && chan < pattern->num_channels ) { + dest->key = pattern->data[ offset ]; + dest->instrument = pattern->data[ offset + 1 ]; + dest->volume = pattern->data[ offset + 2 ]; + dest->effect = pattern->data[ offset + 3 ]; + dest->param = pattern->data[ offset + 4 ]; + } else { + memset( dest, 0, sizeof( struct note ) ); + } +} + +static void channel_init( struct channel *channel, struct ibxm_replay *replay, int idx ) { + memset( channel, 0, sizeof( struct channel ) ); + channel->replay = replay; + channel->id = idx; + channel->panning = replay->module->default_panning[ idx ]; + channel->instrument = &replay->module->instruments[ 0 ]; + channel->sample = &channel->instrument->samples[ 0 ]; + channel->random_seed = ( idx + 1 ) * 0xABCDEF; +} + +static void channel_volume_slide( struct channel *channel ) { + int up = channel->vol_slide_param >> 4; + int down = channel->vol_slide_param & 0xF; + if( down == 0xF && up > 0 ) { + /* Fine slide up.*/ + if( channel->fx_count == 0 ) { + channel->volume += up; + } + } else if( up == 0xF && down > 0 ) { + /* Fine slide down.*/ + if( channel->fx_count == 0 ) { + channel->volume -= down; + } + } else if( channel->fx_count > 0 || channel->replay->module->fast_vol_slides ) { + /* Normal.*/ + channel->volume += up - down; + } + if( channel->volume > 64 ) { + channel->volume = 64; + } + if( channel->volume < 0 ) { + channel->volume = 0; + } +} + +static void channel_porta_up( struct channel *channel, int param ) { + switch( param & 0xF0 ) { + case 0xE0: /* Extra-fine porta.*/ + if( channel->fx_count == 0 ) { + channel->period -= param & 0xF; + } + break; + case 0xF0: /* Fine porta.*/ + if( channel->fx_count == 0 ) { + channel->period -= ( param & 0xF ) << 2; + } + break; + default:/* Normal porta.*/ + if( channel->fx_count > 0 ) { + channel->period -= param << 2; + } + break; + } + if( channel->period < 0 ) { + channel->period = 0; + } +} + +static void channel_porta_down( struct channel *channel, int param ) { + if( channel->period > 0 ) { + switch( param & 0xF0 ) { + case 0xE0: /* Extra-fine porta.*/ + if( channel->fx_count == 0 ) { + channel->period += param & 0xF; + } + break; + case 0xF0: /* Fine porta.*/ + if( channel->fx_count == 0 ) { + channel->period += ( param & 0xF ) << 2; + } + break; + default:/* Normal porta.*/ + if( channel->fx_count > 0 ) { + channel->period += param << 2; + } + break; + } + if( channel->period > 65535 ) { + channel->period = 65535; + } + } +} + +static void channel_tone_porta( struct channel *channel ) { + if( channel->period > 0 ) { + if( channel->period < channel->porta_period ) { + channel->period += channel->tone_porta_param << 2; + if( channel->period > channel->porta_period ) { + channel->period = channel->porta_period; + } + } else { + channel->period -= channel->tone_porta_param << 2; + if( channel->period < channel->porta_period ) { + channel->period = channel->porta_period; + } + } + } +} + +static int channel_waveform( struct channel *channel, int phase, int type ) { + int amplitude = 0; + switch( type ) { + default: /* Sine. */ + amplitude = sine_table[ phase & 0x1F ]; + if( ( phase & 0x20 ) > 0 ) { + amplitude = -amplitude; + } + break; + case 6: /* Saw Up.*/ + amplitude = ( ( ( phase + 0x20 ) & 0x3F ) << 3 ) - 255; + break; + case 1: case 7: /* Saw Down. */ + amplitude = 255 - ( ( ( phase + 0x20 ) & 0x3F ) << 3 ); + break; + case 2: case 5: /* Square. */ + amplitude = ( phase & 0x20 ) > 0 ? 255 : -255; + break; + case 3: case 8: /* Random. */ + amplitude = ( channel->random_seed >> 20 ) - 255; + channel->random_seed = ( channel->random_seed * 65 + 17 ) & 0x1FFFFFFF; + break; + } + return amplitude; +} + +static void channel_vibrato( struct channel *channel, int fine ) { + int wave = channel_waveform( channel, channel->vibrato_phase, channel->vibrato_type & 0x3 ); + channel->vibrato_add = wave * channel->vibrato_depth >> ( fine ? 7 : 5 ); +} + +static void channel_tremolo( struct channel *channel ) { + int wave = channel_waveform( channel, channel->tremolo_phase, channel->tremolo_type & 0x3 ); + channel->tremolo_add = wave * channel->tremolo_depth >> 6; +} + +static void channel_tremor( struct channel *channel ) { + if( channel->retrig_count >= channel->tremor_on_ticks ) { + channel->tremolo_add = -64; + } + if( channel->retrig_count >= ( channel->tremor_on_ticks + channel->tremor_off_ticks ) ) { + channel->tremolo_add = channel->retrig_count = 0; + } +} + +static void channel_retrig_vol_slide( struct channel *channel ) { + if( channel->retrig_count >= channel->retrig_ticks ) { + channel->retrig_count = channel->sample_idx = channel->sample_fra = 0; + switch( channel->retrig_volume ) { + case 0x1: channel->volume = channel->volume - 1; break; + case 0x2: channel->volume = channel->volume - 2; break; + case 0x3: channel->volume = channel->volume - 4; break; + case 0x4: channel->volume = channel->volume - 8; break; + case 0x5: channel->volume = channel->volume - 16; break; + case 0x6: channel->volume = channel->volume * 2 / 3; break; + case 0x7: channel->volume = channel->volume >> 1; break; + case 0x8: /* ? */ break; + case 0x9: channel->volume = channel->volume + 1; break; + case 0xA: channel->volume = channel->volume + 2; break; + case 0xB: channel->volume = channel->volume + 4; break; + case 0xC: channel->volume = channel->volume + 8; break; + case 0xD: channel->volume = channel->volume + 16; break; + case 0xE: channel->volume = channel->volume * 3 / 2; break; + case 0xF: channel->volume = channel->volume << 1; break; + } + if( channel->volume < 0 ) { + channel->volume = 0; + } + if( channel->volume > 64 ) { + channel->volume = 64; + } + } +} + +static void channel_trigger( struct channel *channel ) { + int key, sam, porta, period, fine_tune, ins = channel->note.instrument; + struct ibxm_sample *sample; + if( ins > 0 && ins <= channel->replay->module->num_instruments ) { + channel->instrument = &channel->replay->module->instruments[ ins ]; + key = channel->note.key < 97 ? channel->note.key : 0; + sam = channel->instrument->key_to_sample[ key ]; + sample = &channel->instrument->samples[ sam ]; + channel->volume = sample->volume >= 64 ? 64 : sample->volume & 0x3F; + if( sample->panning > 0 ) { + channel->panning = ( sample->panning - 1 ) & 0xFF; + } + if( channel->period > 0 && sample->loop_length > 1 ) { + /* Amiga trigger.*/ + channel->sample = sample; + } + channel->sample_off = 0; + channel->vol_env_tick = channel->pan_env_tick = 0; + channel->fadeout_vol = 32768; + channel->key_on = 1; + } + if( channel->note.effect == 0x09 || channel->note.effect == 0x8F ) { + /* Set Sample Offset. */ + if( channel->note.param > 0 ) { + channel->offset_param = channel->note.param; + } + channel->sample_off = channel->offset_param << 8; + } + if( channel->note.volume >= 0x10 && channel->note.volume < 0x60 ) { + channel->volume = channel->note.volume < 0x50 ? channel->note.volume - 0x10 : 64; + } + switch( channel->note.volume & 0xF0 ) { + case 0x80: /* Fine Vol Down.*/ + channel->volume -= channel->note.volume & 0xF; + if( channel->volume < 0 ) { + channel->volume = 0; + } + break; + case 0x90: /* Fine Vol Up.*/ + channel->volume += channel->note.volume & 0xF; + if( channel->volume > 64 ) { + channel->volume = 64; + } + break; + case 0xA0: /* Set Vibrato Speed.*/ + if( ( channel->note.volume & 0xF ) > 0 ) { + channel->vibrato_speed = channel->note.volume & 0xF; + } + break; + case 0xB0: /* Vibrato.*/ + if( ( channel->note.volume & 0xF ) > 0 ) { + channel->vibrato_depth = channel->note.volume & 0xF; + } + channel_vibrato( channel, 0 ); + break; + case 0xC0: /* Set Panning.*/ + channel->panning = ( channel->note.volume & 0xF ) * 17; + break; + case 0xF0: /* Tone Porta.*/ + if( ( channel->note.volume & 0xF ) > 0 ) { + channel->tone_porta_param = channel->note.volume & 0xF; + } + break; + } + if( channel->note.key > 0 ) { + if( channel->note.key > 96 ) { + channel->key_on = 0; + } else { + porta = ( channel->note.volume & 0xF0 ) == 0xF0 || + channel->note.effect == 0x03 || channel->note.effect == 0x05 || + channel->note.effect == 0x87 || channel->note.effect == 0x8C; + if( !porta ) { + ins = channel->instrument->key_to_sample[ channel->note.key ]; + channel->sample = &channel->instrument->samples[ ins ]; + } + fine_tune = channel->sample->fine_tune; + if( channel->note.effect == 0x75 || channel->note.effect == 0xF2 ) { + /* Set Fine Tune. */ + fine_tune = ( ( channel->note.param & 0xF ) << 4 ) - 128; + } + key = channel->note.key + channel->sample->rel_note; + if( key < 1 ) { + key = 1; + } + if( key > 120 ) { + key = 120; + } + period = ( key << 6 ) + ( fine_tune >> 1 ); + if( channel->replay->module->linear_periods ) { + channel->porta_period = 7744 - period; + } else { + channel->porta_period = 29021 * exp_2( ( period << FP_SHIFT ) / -768 ) >> FP_SHIFT; + } + if( !porta ) { + channel->period = channel->porta_period; + channel->sample_idx = channel->sample_off; + channel->sample_fra = 0; + if( channel->vibrato_type < 4 ) { + channel->vibrato_phase = 0; + } + if( channel->tremolo_type < 4 ) { + channel->tremolo_phase = 0; + } + channel->retrig_count = channel->av_count = 0; + } + } + } +} + +static void channel_update_envelopes( struct channel *channel ) { + if( channel->instrument->vol_env.enabled ) { + if( !channel->key_on ) { + channel->fadeout_vol -= channel->instrument->vol_fadeout; + if( channel->fadeout_vol < 0 ) { + channel->fadeout_vol = 0; + } + } + channel->vol_env_tick = envelope_next_tick( &channel->instrument->vol_env, + channel->vol_env_tick, channel->key_on ); + } + if( channel->instrument->pan_env.enabled ) { + channel->pan_env_tick = envelope_next_tick( &channel->instrument->pan_env, + channel->pan_env_tick, channel->key_on ); + } +} + +static void channel_auto_vibrato( struct channel *channel ) { + int sweep, rate, type, wave; + int depth = channel->instrument->vib_depth & 0x7F; + if( depth > 0 ) { + sweep = channel->instrument->vib_sweep & 0x7F; + rate = channel->instrument->vib_rate & 0x7F; + type = channel->instrument->vib_type; + if( channel->av_count < sweep ) { + depth = depth * channel->av_count / sweep; + } + wave = channel_waveform( channel, channel->av_count * rate >> 2, type + 4 ); + channel->vibrato_add += wave * depth >> 8; + channel->av_count++; + } +} + +static void channel_calculate_freq( struct channel *channel ) { + int per = channel->period + channel->vibrato_add; + if( channel->replay->module->linear_periods ) { + per = per - ( channel->arpeggio_add << 6 ); + if( per < 28 || per > 7680 ) { + per = 7680; + } + channel->freq = ( ( channel->replay->module->c2_rate >> 4 ) + * exp_2( ( ( 4608 - per ) << FP_SHIFT ) / 768 ) ) >> ( FP_SHIFT - 4 ); + } else { + if( per > 29021 ) { + per = 29021; + } + per = ( per << FP_SHIFT ) / exp_2( ( channel->arpeggio_add << FP_SHIFT ) / 12 ); + if( per < 28 ) { + per = 29021; + } + channel->freq = channel->replay->module->c2_rate * 1712 / per; + } +} + +static void channel_calculate_ampl( struct channel *channel ) { + int vol, range, env_pan = 32, env_vol = channel->key_on ? 64 : 0; + if( channel->instrument->vol_env.enabled ) { + env_vol = envelope_calculate_ampl( &channel->instrument->vol_env, channel->vol_env_tick ); + } + vol = channel->volume + channel->tremolo_add; + if( vol > 64 ) { + vol = 64; + } + if( vol < 0 ) { + vol = 0; + } + vol = ( vol * channel->replay->module->gain * FP_ONE ) >> 13; + vol = ( vol * channel->fadeout_vol ) >> 15; + channel->ampl = ( vol * channel->replay->global_vol * env_vol ) >> 12; + if( channel->instrument->pan_env.enabled ) { + env_pan = envelope_calculate_ampl( &channel->instrument->pan_env, channel->pan_env_tick ); + } + range = ( channel->panning < 128 ) ? channel->panning : ( 255 - channel->panning ); + channel->pann = channel->panning + ( range * ( env_pan - 32 ) >> 5 ); +} + +static void channel_tick( struct channel *channel ) { + channel->vibrato_add = 0; + channel->fx_count++; + channel->retrig_count++; + if( !( channel->note.effect == 0x7D && channel->fx_count <= channel->note.param ) ) { + switch( channel->note.volume & 0xF0 ) { + case 0x60: /* Vol Slide Down.*/ + channel->volume -= channel->note.volume & 0xF; + if( channel->volume < 0 ) { + channel->volume = 0; + } + break; + case 0x70: /* Vol Slide Up.*/ + channel->volume += channel->note.volume & 0xF; + if( channel->volume > 64 ) { + channel->volume = 64; + } + break; + case 0xB0: /* Vibrato.*/ + channel->vibrato_phase += channel->vibrato_speed; + channel_vibrato( channel, 0 ); + break; + case 0xD0: /* Pan Slide Left.*/ + channel->panning -= channel->note.volume & 0xF; + if( channel->panning < 0 ) { + channel->panning = 0; + } + break; + case 0xE0: /* Pan Slide Right.*/ + channel->panning += channel->note.volume & 0xF; + if( channel->panning > 255 ) { + channel->panning = 255; + } + break; + case 0xF0: /* Tone Porta.*/ + channel_tone_porta( channel ); + break; + } + } + switch( channel->note.effect ) { + case 0x01: case 0x86: /* Porta Up. */ + channel_porta_up( channel, channel->porta_up_param ); + break; + case 0x02: case 0x85: /* Porta Down. */ + channel_porta_down( channel, channel->porta_down_param ); + break; + case 0x03: case 0x87: /* Tone Porta. */ + channel_tone_porta( channel ); + break; + case 0x04: case 0x88: /* Vibrato. */ + channel->vibrato_phase += channel->vibrato_speed; + channel_vibrato( channel, 0 ); + break; + case 0x05: case 0x8C: /* Tone Porta + Vol Slide. */ + channel_tone_porta( channel ); + channel_volume_slide( channel ); + break; + case 0x06: case 0x8B: /* Vibrato + Vol Slide. */ + channel->vibrato_phase += channel->vibrato_speed; + channel_vibrato( channel, 0 ); + channel_volume_slide( channel ); + break; + case 0x07: case 0x92: /* Tremolo. */ + channel->tremolo_phase += channel->tremolo_speed; + channel_tremolo( channel ); + break; + case 0x0A: case 0x84: /* Vol Slide. */ + channel_volume_slide( channel ); + break; + case 0x11: /* Global Volume Slide. */ + channel->replay->global_vol = channel->replay->global_vol + + ( channel->gvol_slide_param >> 4 ) + - ( channel->gvol_slide_param & 0xF ); + if( channel->replay->global_vol < 0 ) { + channel->replay->global_vol = 0; + } + if( channel->replay->global_vol > 64 ) { + channel->replay->global_vol = 64; + } + break; + case 0x19: /* Panning Slide. */ + channel->panning = channel->panning + + ( channel->pan_slide_param >> 4 ) + - ( channel->pan_slide_param & 0xF ); + if( channel->panning < 0 ) { + channel->panning = 0; + } + if( channel->panning > 255 ) { + channel->panning = 255; + } + break; + case 0x1B: case 0x91: /* Retrig + Vol Slide. */ + channel_retrig_vol_slide( channel ); + break; + case 0x1D: case 0x89: /* Tremor. */ + channel_tremor( channel ); + break; + case 0x79: /* Retrig. */ + if( channel->fx_count >= channel->note.param ) { + channel->fx_count = 0; + channel->sample_idx = channel->sample_fra = 0; + } + break; + case 0x7C: case 0xFC: /* Note Cut. */ + if( channel->note.param == channel->fx_count ) { + channel->volume = 0; + } + break; + case 0x7D: case 0xFD: /* Note Delay. */ + if( channel->note.param == channel->fx_count ) { + channel_trigger( channel ); + } + break; + case 0x8A: /* Arpeggio. */ + if( channel->fx_count == 1 ) { + channel->arpeggio_add = channel->arpeggio_param >> 4; + } else if( channel->fx_count == 2 ) { + channel->arpeggio_add = channel->arpeggio_param & 0xF; + } else { + channel->arpeggio_add = channel->fx_count = 0; + } + break; + case 0x95: /* Fine Vibrato. */ + channel->vibrato_phase += channel->vibrato_speed; + channel_vibrato( channel, 1 ); + break; + } + channel_auto_vibrato( channel ); + channel_calculate_freq( channel ); + channel_calculate_ampl( channel ); + channel_update_envelopes( channel ); +} + +static void channel_row( struct channel *channel, struct note *note ) { + channel->note = *note; + channel->retrig_count++; + channel->vibrato_add = channel->tremolo_add = channel->arpeggio_add = channel->fx_count = 0; + if( !( ( note->effect == 0x7D || note->effect == 0xFD ) && note->param > 0 ) ) { + /* Not note delay.*/ + channel_trigger( channel ); + } + switch( channel->note.effect ) { + case 0x01: case 0x86: /* Porta Up. */ + if( channel->note.param > 0 ) { + channel->porta_up_param = channel->note.param; + } + channel_porta_up( channel, channel->porta_up_param ); + break; + case 0x02: case 0x85: /* Porta Down. */ + if( channel->note.param > 0 ) { + channel->porta_down_param = channel->note.param; + } + channel_porta_down( channel, channel->porta_down_param ); + break; + case 0x03: case 0x87: /* Tone Porta. */ + if( channel->note.param > 0 ) { + channel->tone_porta_param = channel->note.param; + } + break; + case 0x04: case 0x88: /* Vibrato. */ + if( ( channel->note.param >> 4 ) > 0 ) { + channel->vibrato_speed = channel->note.param >> 4; + } + if( ( channel->note.param & 0xF ) > 0 ) { + channel->vibrato_depth = channel->note.param & 0xF; + } + channel_vibrato( channel, 0 ); + break; + case 0x05: case 0x8C: /* Tone Porta + Vol Slide. */ + if( channel->note.param > 0 ) { + channel->vol_slide_param = channel->note.param; + } + channel_volume_slide( channel ); + break; + case 0x06: case 0x8B: /* Vibrato + Vol Slide. */ + if( channel->note.param > 0 ) { + channel->vol_slide_param = channel->note.param; + } + channel_vibrato( channel, 0 ); + channel_volume_slide( channel ); + break; + case 0x07: case 0x92: /* Tremolo. */ + if( ( channel->note.param >> 4 ) > 0 ) { + channel->tremolo_speed = channel->note.param >> 4; + } + if( ( channel->note.param & 0xF ) > 0 ) { + channel->tremolo_depth = channel->note.param & 0xF; + } + channel_tremolo( channel ); + break; + case 0x08: /* Set Panning.*/ + channel->panning = channel->note.param & 0xFF; + break; + case 0x0A: case 0x84: /* Vol Slide. */ + if( channel->note.param > 0 ) { + channel->vol_slide_param = channel->note.param; + } + channel_volume_slide( channel ); + break; + case 0x0C: /* Set Volume. */ + channel->volume = channel->note.param >= 64 ? 64 : channel->note.param & 0x3F; + break; + case 0x10: case 0x96: /* Set Global Volume. */ + channel->replay->global_vol = channel->note.param >= 64 ? 64 : channel->note.param & 0x3F; + break; + case 0x11: /* Global Volume Slide. */ + if( channel->note.param > 0 ) { + channel->gvol_slide_param = channel->note.param; + } + break; + case 0x14: /* Key Off. */ + channel->key_on = 0; + break; + case 0x15: /* Set Envelope Tick. */ + channel->vol_env_tick = channel->pan_env_tick = channel->note.param & 0xFF; + break; + case 0x19: /* Panning Slide. */ + if( channel->note.param > 0 ) { + channel->pan_slide_param = channel->note.param; + } + break; + case 0x1B: case 0x91: /* Retrig + Vol Slide. */ + if( ( channel->note.param >> 4 ) > 0 ) { + channel->retrig_volume = channel->note.param >> 4; + } + if( ( channel->note.param & 0xF ) > 0 ) { + channel->retrig_ticks = channel->note.param & 0xF; + } + channel_retrig_vol_slide( channel ); + break; + case 0x1D: case 0x89: /* Tremor. */ + if( ( channel->note.param >> 4 ) > 0 ) { + channel->tremor_on_ticks = channel->note.param >> 4; + } + if( ( channel->note.param & 0xF ) > 0 ) { + channel->tremor_off_ticks = channel->note.param & 0xF; + } + channel_tremor( channel ); + break; + case 0x21: /* Extra Fine Porta. */ + if( channel->note.param > 0 ) { + channel->xfine_porta_param = channel->note.param; + } + switch( channel->xfine_porta_param & 0xF0 ) { + case 0x10: + channel_porta_up( channel, 0xE0 | ( channel->xfine_porta_param & 0xF ) ); + break; + case 0x20: + channel_porta_down( channel, 0xE0 | ( channel->xfine_porta_param & 0xF ) ); + break; + } + break; + case 0x71: /* Fine Porta Up. */ + if( channel->note.param > 0 ) { + channel->fine_porta_up_param = channel->note.param; + } + channel_porta_up( channel, 0xF0 | ( channel->fine_porta_up_param & 0xF ) ); + break; + case 0x72: /* Fine Porta Down. */ + if( channel->note.param > 0 ) { + channel->fine_porta_down_param = channel->note.param; + } + channel_porta_down( channel, 0xF0 | ( channel->fine_porta_down_param & 0xF ) ); + break; + case 0x74: case 0xF3: /* Set Vibrato Waveform. */ + if( channel->note.param < 8 ) { + channel->vibrato_type = channel->note.param; + } + break; + case 0x77: case 0xF4: /* Set Tremolo Waveform. */ + if( channel->note.param < 8 ) { + channel->tremolo_type = channel->note.param; + } + break; + case 0x7A: /* Fine Vol Slide Up. */ + if( channel->note.param > 0 ) { + channel->fine_vslide_up_param = channel->note.param; + } + channel->volume += channel->fine_vslide_up_param; + if( channel->volume > 64 ) { + channel->volume = 64; + } + break; + case 0x7B: /* Fine Vol Slide Down. */ + if( channel->note.param > 0 ) { + channel->fine_vslide_down_param = channel->note.param; + } + channel->volume -= channel->fine_vslide_down_param; + if( channel->volume < 0 ) { + channel->volume = 0; + } + break; + case 0x7C: case 0xFC: /* Note Cut. */ + if( channel->note.param <= 0 ) { + channel->volume = 0; + } + break; + case 0x8A: /* Arpeggio. */ + if( channel->note.param > 0 ) { + channel->arpeggio_param = channel->note.param; + } + break; + case 0x95: /* Fine Vibrato.*/ + if( ( channel->note.param >> 4 ) > 0 ) { + channel->vibrato_speed = channel->note.param >> 4; + } + if( ( channel->note.param & 0xF ) > 0 ) { + channel->vibrato_depth = channel->note.param & 0xF; + } + channel_vibrato( channel, 1 ); + break; + case 0xF8: /* Set Panning. */ + channel->panning = channel->note.param * 17; + break; + } + channel_auto_vibrato( channel ); + channel_calculate_freq( channel ); + channel_calculate_ampl( channel ); + channel_update_envelopes( channel ); +} + +static void channel_resample( struct channel *channel, int *mix_buf, + int offset, int count, int sample_rate, int interpolate ) { + struct ibxm_sample *sample = channel->sample; + int l_gain, r_gain, sam_idx, sam_fra, step; + int loop_len, loop_end, out_idx, out_end, y, m, c; + short *sample_data = channel->sample->data; + if( channel->ampl > 0 ) { + l_gain = channel->ampl * ( 255 - channel->pann ) >> 8; + r_gain = channel->ampl * channel->pann >> 8; + sam_idx = channel->sample_idx; + sam_fra = channel->sample_fra; + step = ( channel->freq << ( FP_SHIFT - 3 ) ) / ( sample_rate >> 3 ); + loop_len = sample->loop_length; + loop_end = sample->loop_start + loop_len; + out_idx = offset * 2; + out_end = ( offset + count ) * 2; + if( interpolate ) { + while( out_idx < out_end ) { + if( sam_idx >= loop_end ) { + if( loop_len > 1 ) { + while( sam_idx >= loop_end ) { + sam_idx -= loop_len; + } + } else { + break; + } + } + c = sample_data[ sam_idx ]; + m = sample_data[ sam_idx + 1 ] - c; + y = ( ( m * sam_fra ) >> FP_SHIFT ) + c; + mix_buf[ out_idx++ ] += ( y * l_gain ) >> FP_SHIFT; + mix_buf[ out_idx++ ] += ( y * r_gain ) >> FP_SHIFT; + sam_fra += step; + sam_idx += sam_fra >> FP_SHIFT; + sam_fra &= FP_MASK; + } + } else { + while( out_idx < out_end ) { + if( sam_idx >= loop_end ) { + if( loop_len > 1 ) { + while( sam_idx >= loop_end ) { + sam_idx -= loop_len; + } + } else { + break; + } + } + y = sample_data[ sam_idx ]; + mix_buf[ out_idx++ ] += ( y * l_gain ) >> FP_SHIFT; + mix_buf[ out_idx++ ] += ( y * r_gain ) >> FP_SHIFT; + sam_fra += step; + sam_idx += sam_fra >> FP_SHIFT; + sam_fra &= FP_MASK; + } + } + } +} + +static void channel_update_sample_idx( struct channel *channel, int count, int sample_rate ) { + struct ibxm_sample *sample = channel->sample; + int step = ( channel->freq << ( FP_SHIFT - 3 ) ) / ( sample_rate >> 3 ); + channel->sample_fra += step * count; + channel->sample_idx += channel->sample_fra >> FP_SHIFT; + if( channel->sample_idx > sample->loop_start ) { + if( sample->loop_length > 1 ) { + channel->sample_idx = sample->loop_start + + ( channel->sample_idx - sample->loop_start ) % sample->loop_length; + } else { + channel->sample_idx = sample->loop_start; + } + } + channel->sample_fra &= FP_MASK; +} + +static void replay_row( struct ibxm_replay *replay ) { + int idx, count; + struct note note; + struct ibxm_pattern *pattern; + struct channel *channel; + struct ibxm_module *module = replay->module; + if( replay->next_row < 0 ) { + replay->break_pos = replay->seq_pos + 1; + replay->next_row = 0; + } + if( replay->break_pos >= 0 ) { + if( replay->break_pos >= module->sequence_len ) { + replay->break_pos = replay->next_row = 0; + } + while( module->sequence[ replay->break_pos ] >= module->num_patterns ) { + replay->break_pos++; + if( replay->break_pos >= module->sequence_len ) { + replay->break_pos = replay->next_row = 0; + } + } + replay->seq_pos = replay->break_pos; + for( idx = 0; idx < module->num_channels; idx++ ) { + replay->channels[ idx ].pl_row = 0; + } + replay->break_pos = -1; + } + pattern = &module->patterns[ module->sequence[ replay->seq_pos ] ]; + replay->row = replay->next_row; + if( replay->row >= pattern->num_rows ) { + replay->row = 0; + } + if( replay->play_count && replay->play_count[ 0 ] ) { + count = replay->play_count[ replay->seq_pos ][ replay->row ]; + if( replay->pl_count < 0 && count < 127 ) { + replay->play_count[ replay->seq_pos ][ replay->row ] = count + 1; + } + } + replay->next_row = replay->row + 1; + if( replay->next_row >= pattern->num_rows ) { + replay->next_row = -1; + } + for( idx = 0; idx < module->num_channels; idx++ ) { + channel = &replay->channels[ idx ]; + pattern_get_note( pattern, replay->row, idx, ¬e ); + if( note.effect == 0xE ) { + note.effect = 0x70 | ( note.param >> 4 ); + note.param &= 0xF; + } + if( note.effect == 0x93 ) { + note.effect = 0xF0 | ( note.param >> 4 ); + note.param &= 0xF; + } + if( note.effect == 0 && note.param > 0 ) { + note.effect = 0x8A; + } + channel_row( channel, ¬e ); + switch( note.effect ) { + case 0x81: /* Set Speed. */ + if( note.param > 0 ) { + replay->tick = replay->speed = note.param; + } + break; + case 0xB: case 0x82: /* Pattern Jump.*/ + if( replay->pl_count < 0 ) { + replay->break_pos = note.param; + replay->next_row = 0; + } + break; + case 0xD: case 0x83: /* Pattern Break.*/ + if( replay->pl_count < 0 ) { + if( replay->break_pos < 0 ) { + replay->break_pos = replay->seq_pos + 1; + } + replay->next_row = ( note.param >> 4 ) * 10 + ( note.param & 0xF ); + } + break; + case 0xF: /* Set Speed/Tempo.*/ + if( note.param > 0 ) { + if( note.param < 32 ) { + replay->tick = replay->speed = note.param; + } else { + replay->tempo = note.param; + } + } + break; + case 0x94: /* Set Tempo.*/ + if( note.param > 32 ) { + replay->tempo = note.param; + } + break; + case 0x76: case 0xFB : /* Pattern Loop.*/ + if( note.param == 0 ) { + /* Set loop marker on this channel. */ + channel->pl_row = replay->row; + } + if( channel->pl_row < replay->row && replay->break_pos < 0 ) { + /* Marker valid. */ + if( replay->pl_count < 0 ) { + /* Not already looping, begin. */ + replay->pl_count = note.param; + replay->pl_chan = idx; + } + if( replay->pl_chan == idx ) { + /* Next Loop.*/ + if( replay->pl_count == 0 ) { + /* Loop finished. Invalidate current marker. */ + channel->pl_row = replay->row + 1; + } else { + /* Loop. */ + replay->next_row = channel->pl_row; + } + replay->pl_count--; + } + } + break; + case 0x7E: case 0xFE: /* Pattern Delay.*/ + replay->tick = replay->speed + replay->speed * note.param; + break; + } + } +} + +static int replay_tick( struct ibxm_replay *replay ) { + int idx, num_channels, count = 1; + if( --replay->tick <= 0 ) { + replay->tick = replay->speed; + replay_row( replay ); + } else { + num_channels = replay->module->num_channels; + for( idx = 0; idx < num_channels; idx++ ) { + channel_tick( &replay->channels[ idx ] ); + } + } + if( replay->play_count && replay->play_count[ 0 ] ) { + count = replay->play_count[ replay->seq_pos ][ replay->row ] - 1; + } + return count; +} + +static int module_init_play_count( struct ibxm_module *module, char **play_count ) { + int idx, pat, rows, len = 0; + for( idx = 0; idx < module->sequence_len; idx++ ) { + pat = module->sequence[ idx ]; + rows = ( pat < module->num_patterns ) ? module->patterns[ pat ].num_rows : 0; + if( play_count ) { + play_count[ idx ] = play_count[ 0 ] ? &play_count[ 0 ][ len ] : NULL; + } + len += rows; + } + return len; +} + +/* Set the pattern in the sequence to play. The tempo is reset to the default. */ +void ibxm_replay_set_sequence_pos( struct ibxm_replay *replay, int pos ) { + int idx; + struct ibxm_module *module = replay->module; + if( pos >= module->sequence_len ) { + pos = 0; + } + replay->break_pos = pos; + replay->next_row = 0; + replay->tick = 1; + replay->global_vol = module->default_gvol; + replay->speed = module->default_speed > 0 ? module->default_speed : 6; + replay->tempo = module->default_tempo > 0 ? module->default_tempo : 125; + replay->pl_count = replay->pl_chan = -1; + if( replay->play_count ) { + free( replay->play_count[ 0 ] ); + free( replay->play_count ); + } + replay->play_count = (char **)calloc( module->sequence_len, sizeof( char * ) ); + if( replay->play_count ) { + replay->play_count[ 0 ] = (char *)calloc( module_init_play_count( module, NULL ), sizeof( char ) ); + module_init_play_count( module, replay->play_count ); + } + for( idx = 0; idx < module->num_channels; idx++ ) { + channel_init( &replay->channels[ idx ], replay, idx ); + } + memset( replay->ramp_buf, 0, 128 * sizeof( int ) ); + replay_tick( replay ); +} + +/* Deallocate the specified replay. */ +void ibxm_dispose_replay( struct ibxm_replay *replay ) { + if( replay->play_count ) { + free( replay->play_count[ 0 ] ); + free( replay->play_count ); + } + free( replay->ramp_buf ); + free( replay->channels ); + free( replay ); +} + +/* Allocate and initialize a replay with the specified sampling rate and interpolation. */ +struct ibxm_replay* ibxm_new_replay( struct ibxm_module *module, int sample_rate, int interpolation ) { + struct ibxm_replay *replay = (ibxm_replay *)calloc( 1, sizeof( struct ibxm_replay ) ); + if( replay ) { + replay->module = module; + replay->sample_rate = sample_rate; + replay->interpolation = interpolation; + replay->ramp_buf = (int *)calloc( 128, sizeof( int ) ); + replay->channels = (channel *)calloc( module->num_channels, sizeof( struct channel ) ); + if( replay->ramp_buf && replay->channels ) { + ibxm_replay_set_sequence_pos( replay, 0 ); + } else { + ibxm_dispose_replay( replay ); + replay = NULL; + } + } + return replay; +} + +static int calculate_tick_len( int tempo, int sample_rate ) { + return ( sample_rate * 5 ) / ( tempo * 2 ); +} + +/* Returns the length of the output buffer required by replay_get_audio(). */ +int ibxm_calculate_mix_buf_len( int sample_rate ) { + return ( calculate_tick_len( 32, sample_rate ) + 65 ) * 4; +} + +/* Returns the song duration in samples at the current sampling rate. */ +int replay_calculate_duration( struct ibxm_replay *replay ) { + int count = 0, duration = 0; + ibxm_replay_set_sequence_pos( replay, 0 ); + while( count < 1 ) { + duration += calculate_tick_len( replay->tempo, replay->sample_rate ); + count = replay_tick( replay ); + } + ibxm_replay_set_sequence_pos( replay, 0 ); + return duration; +} + +/* Seek to approximately the specified sample position. + The actual sample position reached is returned. */ +int replay_seek( struct ibxm_replay *replay, int sample_pos ) { + int idx, tick_len, current_pos = 0; + ibxm_replay_set_sequence_pos( replay, 0 ); + tick_len = calculate_tick_len( replay->tempo, replay->sample_rate ); + while( ( sample_pos - current_pos ) >= tick_len ) { + for( idx = 0; idx < replay->module->num_channels; idx++ ) { + channel_update_sample_idx( &replay->channels[ idx ], + tick_len * 2, replay->sample_rate * 2 ); + } + current_pos += tick_len; + replay_tick( replay ); + tick_len = calculate_tick_len( replay->tempo, replay->sample_rate ); + } + return current_pos; +} + +static void replay_volume_ramp( struct ibxm_replay *replay, int *mix_buf, int tick_len ) { + int idx, a1, a2, ramp_rate = 256 * 2048 / replay->sample_rate; + for( idx = 0, a1 = 0; a1 < 256; idx += 2, a1 += ramp_rate ) { + a2 = 256 - a1; + mix_buf[ idx ] = ( mix_buf[ idx ] * a1 + replay->ramp_buf[ idx ] * a2 ) >> 8; + mix_buf[ idx + 1 ] = ( mix_buf[ idx + 1 ] * a1 + replay->ramp_buf[ idx + 1 ] * a2 ) >> 8; + } + memcpy( replay->ramp_buf, &mix_buf[ tick_len * 2 ], 128 * sizeof( int ) ); +} + +/* 2:1 downsampling with simple but effective anti-aliasing. Buf must contain count * 2 + 1 stereo samples. */ +static void downsample( int *buf, int count ) { + int idx, out_idx, out_len = count * 2; + for( idx = 0, out_idx = 0; out_idx < out_len; idx += 4, out_idx += 2 ) { + buf[ out_idx ] = ( buf[ idx ] >> 2 ) + ( buf[ idx + 2 ] >> 1 ) + ( buf[ idx + 4 ] >> 2 ); + buf[ out_idx + 1 ] = ( buf[ idx + 1 ] >> 2 ) + ( buf[ idx + 3 ] >> 1 ) + ( buf[ idx + 5 ] >> 2 ); + } +} + +/* Generates audio and returns the number of stereo samples written into mix_buf. + Individual channels may be excluded using the mute bitmask. */ +int ibxm_replay_get_audio( struct ibxm_replay *replay, int *mix_buf, int mute ) { + struct channel *channel; + int idx, num_channels, tick_len = calculate_tick_len( replay->tempo, replay->sample_rate ); + /* Clear output buffer. */ + memset( mix_buf, 0, ( tick_len + 65 ) * 4 * sizeof( int ) ); + /* Resample. */ + num_channels = replay->module->num_channels; + for( idx = 0; idx < num_channels; idx++ ) { + channel = &replay->channels[ idx ]; + if( !( mute & 1 ) ) { + channel_resample( channel, mix_buf, 0, ( tick_len + 65 ) * 2, + replay->sample_rate * 2, replay->interpolation ); + } + channel_update_sample_idx( channel, tick_len * 2, replay->sample_rate * 2 ); + mute >>= 1; + } + downsample( mix_buf, tick_len + 64 ); + replay_volume_ramp( replay, mix_buf, tick_len ); + replay_tick( replay ); + return tick_len; +} + +/* Returns the currently playing pattern in the sequence.*/ +int replay_get_sequence_pos( struct ibxm_replay *replay ) { + return replay->seq_pos; +} + +/* Returns the currently playing row in the pattern. */ +int replay_get_row( struct ibxm_replay *replay ) { + return replay->row; +} \ No newline at end of file diff --git a/libraries/libRAD/CMakeLists.txt b/libraries/libRAD/CMakeLists.txt index 1016e3b6e..dbbeaae86 100644 --- a/libraries/libRAD/CMakeLists.txt +++ b/libraries/libRAD/CMakeLists.txt @@ -6,8 +6,6 @@ add_library( libRAD opal.cpp radplay.cpp - patches.cpp - radmidi.cpp ) target_include_directories(libRAD PUBLIC ./) \ No newline at end of file diff --git a/libraries/libRAD/LICENSE.txt b/libraries/libRAD/LICENSE.txt index a686f0f6b..560ba326f 100644 --- a/libraries/libRAD/LICENSE.txt +++ b/libraries/libRAD/LICENSE.txt @@ -16,36 +16,3 @@ We'll clarify that in the documentation for the next release. Later... SHAYDE ================================================================ - - -radmidi.cpp/.h and patches.cpp/h are adapted from the YMFMIDI library with the following licensing: - -BSD 3-Clause License - -Copyright (c) 2021-2023, Devin Acker -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/libraries/libRAD/instruments.h b/libraries/libRAD/instruments.h deleted file mode 100644 index 361beb5fc..000000000 --- a/libraries/libRAD/instruments.h +++ /dev/null @@ -1,747 +0,0 @@ -static constexpr uint8_t default_instruments[11908] = { - 0x23, 0x4f, 0x50, 0x4c, 0x5f, 0x49, 0x49, 0x23, 0x04, 0x00, 0x82, 0x00, 0x33, 0xe1, 0x23, 0x02, - 0x80, 0x25, 0x0e, 0x31, 0xf1, 0xf4, 0x04, 0x00, 0x09, 0x00, 0xf4, 0xff, 0x32, 0xf1, 0x23, 0x02, - 0x00, 0x24, 0x0e, 0x31, 0xf1, 0xf4, 0x00, 0x00, 0x0a, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x82, 0x00, - 0x33, 0xf1, 0x33, 0x04, 0x80, 0x23, 0x0c, 0x31, 0xf1, 0xf4, 0x00, 0x00, 0x09, 0x00, 0xf4, 0xff, - 0x32, 0xf1, 0xf3, 0x02, 0x80, 0x20, 0x06, 0x31, 0xf1, 0xf6, 0x04, 0x00, 0x0f, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x82, 0x00, 0x31, 0xe1, 0xb3, 0x04, 0x00, 0x1a, 0x08, 0x30, 0xf1, 0xf4, 0x00, 0x80, - 0x08, 0x00, 0x00, 0x00, 0x30, 0xf1, 0xb3, 0x01, 0x00, 0x08, 0x0b, 0x11, 0xf2, 0x14, 0x02, 0x80, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x31, 0xfa, 0x73, 0x00, 0x80, 0x0e, 0x0a, 0x11, - 0xd1, 0xf4, 0x04, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x32, 0xfa, 0x73, 0x04, 0x80, 0x0c, 0x0a, 0x11, - 0xd1, 0xf4, 0x04, 0x00, 0x09, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x83, 0x00, 0x16, 0xf2, 0xe7, 0x02, - 0x40, 0x2f, 0x0c, 0x01, 0xf2, 0xb8, 0x00, 0x80, 0x00, 0x00, 0xf4, 0xff, 0x11, 0xb1, 0xe5, 0x00, - 0x80, 0x1c, 0x00, 0x11, 0xf2, 0xb8, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x84, 0x00, - 0x36, 0xf4, 0xc7, 0x02, 0x80, 0x1d, 0x0e, 0x31, 0xf2, 0xd7, 0x00, 0x00, 0x0a, 0x00, 0xf4, 0xff, - 0x21, 0xf0, 0xc7, 0x00, 0x00, 0x1e, 0x00, 0x11, 0xf1, 0xf8, 0x00, 0x00, 0x0f, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x80, 0x00, 0x34, 0xf1, 0x23, 0x05, 0x40, 0x11, 0x06, 0x11, 0xd1, 0xf5, 0x04, 0x00, - 0x08, 0x00, 0xf4, 0xff, 0x32, 0xfc, 0x03, 0x02, 0x40, 0x0b, 0x06, 0x11, 0xd1, 0xf7, 0x01, 0x00, - 0x09, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, 0x01, 0xf1, 0x59, 0x05, 0x40, 0x04, 0x06, 0x11, - 0xf1, 0xf7, 0x00, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x3f, 0x09, 0x11, - 0xf1, 0xf7, 0x04, 0x00, 0x0d, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x81, 0x00, 0x07, 0xf6, 0x87, 0x00, - 0x40, 0x14, 0x09, 0x31, 0xf2, 0xe4, 0x00, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x17, 0xf6, 0x87, 0x05, - 0x40, 0x14, 0x09, 0x31, 0xf2, 0xe4, 0x00, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, - 0x3f, 0xf3, 0xa5, 0x00, 0x80, 0x21, 0x0c, 0x12, 0xc3, 0xf2, 0x00, 0x00, 0x06, 0x00, 0xe8, 0xff, - 0x3f, 0xf4, 0xa5, 0x00, 0xc0, 0x15, 0x00, 0x92, 0xf3, 0xf2, 0x00, 0x00, 0x07, 0x00, 0xe8, 0xff, - 0x04, 0x00, 0x84, 0x00, 0x1a, 0x56, 0x04, 0x00, 0x80, 0x1e, 0x08, 0x01, 0xf6, 0x04, 0x00, 0x00, - 0x08, 0x00, 0xf4, 0xff, 0x17, 0xa4, 0x04, 0x00, 0x80, 0x1f, 0x08, 0x00, 0xf6, 0x04, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0xb2, 0xf2, 0xf6, 0x00, 0xc0, 0x25, 0x0f, 0x81, - 0xf2, 0xf6, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0xa4, 0xf2, 0xf6, 0x00, 0x40, 0x00, 0x01, 0x8f, - 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, 0x18, 0xf6, 0xf6, 0x00, - 0x40, 0x23, 0x06, 0x00, 0xe5, 0xf7, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xf4, 0xa6, 0x05, - 0x40, 0x1b, 0x00, 0x04, 0xd8, 0x07, 0x00, 0x80, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x80, 0x00, - 0x16, 0xf6, 0x96, 0x00, 0x00, 0x1f, 0x04, 0x11, 0xf1, 0xf5, 0x00, 0x00, 0x03, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x82, 0x00, 0x07, 0xf3, 0x82, 0x00, 0x80, 0x17, 0x06, 0x02, 0xf3, 0xe3, 0x00, 0x80, - 0x0a, 0x00, 0xe8, 0xff, 0x07, 0xd3, 0x82, 0x00, 0x80, 0x17, 0x06, 0x02, 0xf3, 0xe3, 0x00, 0x80, - 0x0a, 0x00, 0xe8, 0xff, 0x04, 0x00, 0x82, 0x00, 0x31, 0x91, 0x10, 0x04, 0x40, 0x12, 0x08, 0x22, - 0xf3, 0xb5, 0x03, 0x80, 0x08, 0x00, 0xf4, 0xff, 0x31, 0x91, 0x10, 0x04, 0x40, 0x12, 0x08, 0x22, - 0xf3, 0xb5, 0x03, 0x80, 0x08, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x8a, 0x00, 0xa4, 0xb1, 0x18, 0x01, - 0x80, 0x08, 0x07, 0x61, 0xd1, 0x1b, 0x01, 0x80, 0x00, 0x00, 0xf4, 0xff, 0xa3, 0xb1, 0x08, 0x01, - 0x80, 0x08, 0x05, 0x61, 0xd1, 0x1b, 0x01, 0x80, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, - 0x32, 0xf0, 0xf8, 0x00, 0x40, 0x25, 0x0e, 0x01, 0xf0, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xff, - 0x24, 0xf0, 0xf8, 0x00, 0x40, 0x03, 0x01, 0x09, 0xf0, 0x08, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xff, - 0x04, 0x00, 0x8a, 0x00, 0x70, 0xaa, 0x18, 0x00, 0x40, 0x04, 0x04, 0xb1, 0x8a, 0x08, 0x00, 0x00, - 0x0a, 0x00, 0xf4, 0xff, 0x71, 0xaa, 0x18, 0x01, 0x40, 0x0b, 0x04, 0xb2, 0x8a, 0x08, 0x00, 0x00, - 0x0a, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x82, 0x00, 0x31, 0x30, 0x04, 0x01, 0x80, 0x12, 0x0d, 0x32, - 0x54, 0x14, 0x01, 0x80, 0x00, 0x00, 0xf4, 0xff, 0x33, 0x50, 0x04, 0x01, 0x80, 0x12, 0x0b, 0x30, - 0x54, 0x14, 0x01, 0x80, 0x00, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, 0x01, 0x80, 0x17, 0x00, - 0x40, 0x14, 0x04, 0x82, 0x60, 0x17, 0x01, 0x80, 0x0b, 0x00, 0xf4, 0xff, 0x00, 0x80, 0x17, 0x00, - 0x40, 0x14, 0x06, 0x81, 0x60, 0x17, 0x01, 0x80, 0x0b, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x89, 0x00, - 0x21, 0xa7, 0x25, 0x00, 0x40, 0x0d, 0x06, 0x32, 0x41, 0x26, 0x01, 0x00, 0x08, 0x00, 0xf4, 0xff, - 0x21, 0x86, 0x25, 0x01, 0x40, 0x0f, 0x06, 0x33, 0x46, 0x26, 0x01, 0x00, 0x08, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x80, 0x00, 0x22, 0xf0, 0xea, 0x04, 0x80, 0x16, 0x0a, 0x21, 0x62, 0x1a, 0x00, 0x80, - 0x0d, 0x00, 0xf4, 0xff, 0x2c, 0xff, 0x55, 0x01, 0x80, 0x1d, 0x06, 0x22, 0x62, 0x1a, 0x00, 0x80, - 0x12, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x87, 0x00, 0x21, 0xf0, 0x07, 0x01, 0x80, 0x12, 0x08, 0x32, - 0x52, 0x05, 0x02, 0x00, 0x07, 0x00, 0xf4, 0xff, 0x21, 0xf0, 0x07, 0x01, 0x80, 0x12, 0x00, 0x32, - 0x52, 0x05, 0x02, 0x00, 0x07, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, 0x13, 0xa6, 0x74, 0x02, - 0xc0, 0x1d, 0x06, 0x01, 0xf4, 0xb8, 0x04, 0x00, 0x05, 0x00, 0xf4, 0xff, 0x34, 0x91, 0x24, 0x02, - 0x40, 0x1e, 0x0a, 0x01, 0xd3, 0xb8, 0x02, 0x00, 0x05, 0x00, 0xe8, 0xff, 0x04, 0x00, 0x81, 0x00, - 0x33, 0xf1, 0xe5, 0x05, 0x80, 0x19, 0x06, 0x11, 0xf2, 0xe4, 0x01, 0x00, 0x07, 0x00, 0xf4, 0xff, - 0x33, 0xf1, 0xe5, 0x01, 0x80, 0x1e, 0x00, 0x11, 0xf2, 0xe4, 0x05, 0x00, 0x0b, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x80, 0x00, 0x22, 0xf0, 0x1f, 0x02, 0x80, 0x25, 0x0a, 0x12, 0xf2, 0x88, 0x00, 0x40, - 0x08, 0x00, 0xf4, 0xff, 0x23, 0xf0, 0x1f, 0x01, 0x80, 0x1e, 0x0a, 0x11, 0xf2, 0x88, 0x00, 0x40, - 0x08, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x83, 0x00, 0x11, 0xed, 0x32, 0x01, 0x80, 0x07, 0x06, 0x11, - 0xd2, 0xe7, 0x03, 0x00, 0x0a, 0x00, 0xf4, 0xff, 0x12, 0xed, 0x32, 0x03, 0x80, 0x00, 0x02, 0x11, - 0xd2, 0xe7, 0x01, 0x40, 0x05, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, 0x31, 0xe4, 0xf4, 0x00, - 0x80, 0x17, 0x08, 0x31, 0xf3, 0xf5, 0x00, 0x80, 0x00, 0x00, 0xf4, 0xff, 0x01, 0xf7, 0xf0, 0x03, - 0x00, 0x00, 0x0e, 0x00, 0xf9, 0xf8, 0x01, 0x00, 0x11, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x06, 0x00, - 0x11, 0x81, 0xf7, 0x00, 0x40, 0x00, 0x08, 0x54, 0xf0, 0xf7, 0x03, 0x40, 0x05, 0x00, 0xf4, 0xff, - 0x40, 0x81, 0xf7, 0x00, 0x40, 0x00, 0x0c, 0x40, 0xf0, 0xf7, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, - 0x04, 0x00, 0x06, 0x00, 0x11, 0x81, 0xf7, 0x00, 0x40, 0x00, 0x0a, 0x54, 0xf0, 0xf7, 0x02, 0x40, - 0x05, 0x00, 0xf4, 0xff, 0x40, 0x81, 0xf7, 0x00, 0x40, 0x00, 0x0c, 0x40, 0xf0, 0xf7, 0x03, 0x00, - 0x05, 0x00, 0x02, 0x00, 0x04, 0x00, 0x8f, 0x00, 0xe8, 0x8a, 0xf7, 0x06, 0x00, 0x03, 0x0a, 0xf4, - 0xb1, 0x49, 0x06, 0x00, 0x0a, 0x00, 0xf4, 0xff, 0xe8, 0x8a, 0xf7, 0x06, 0x40, 0x03, 0x0a, 0xf4, - 0xb1, 0x49, 0x06, 0x40, 0x0b, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x82, 0x00, 0x21, 0x92, 0x20, 0x00, - 0x80, 0x17, 0x00, 0x31, 0xc1, 0xe6, 0x00, 0x00, 0x05, 0x00, 0xf4, 0xff, 0x21, 0x85, 0x60, 0x00, - 0xc0, 0x14, 0x04, 0x31, 0xa1, 0xe6, 0x00, 0x00, 0x05, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, - 0x30, 0xf0, 0xf9, 0x00, 0x80, 0x1d, 0x0a, 0x31, 0xf1, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x17, 0xf0, 0xf0, 0x07, 0x00, 0x00, 0x0a, 0x00, 0xb7, 0xf9, 0x07, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x80, 0x00, 0x21, 0xaa, 0x26, 0x00, 0xc0, 0x0f, 0x0a, 0x32, 0xf1, 0xd7, 0x00, 0x80, - 0x00, 0x00, 0xf4, 0xff, 0x0b, 0xf4, 0xf9, 0x05, 0x00, 0x00, 0x02, 0x11, 0xf8, 0x48, 0x04, 0xc0, - 0x0d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x83, 0x00, 0x31, 0xd3, 0x10, 0x00, 0x80, 0x1a, 0x06, 0x12, - 0x81, 0xb6, 0x00, 0x80, 0x00, 0x00, 0xf4, 0xff, 0x21, 0x41, 0x40, 0x00, 0x80, 0x1b, 0x06, 0x13, - 0x61, 0xb5, 0x00, 0xc0, 0x00, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, 0x19, 0x94, 0xe6, 0x02, - 0x40, 0x1e, 0x00, 0x11, 0xb3, 0xb6, 0x05, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x21, 0xfa, 0x77, 0x00, - 0x00, 0x07, 0x00, 0x21, 0x93, 0xf7, 0x00, 0x00, 0x0d, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, - 0x15, 0x95, 0xe6, 0x02, 0x00, 0x17, 0x00, 0x11, 0xb2, 0xb6, 0x01, 0x00, 0x08, 0x00, 0xf4, 0xff, - 0x21, 0xfa, 0x77, 0x00, 0x00, 0x07, 0x00, 0x32, 0xc3, 0x6a, 0x00, 0x00, 0x0d, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x80, 0x00, 0x30, 0xf4, 0xf5, 0x00, 0x00, 0x0e, 0x0a, 0x30, 0xf3, 0xf6, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x8a, 0x00, 0x31, 0x83, 0x46, 0x01, 0x00, 0x15, 0x0a, 0x32, - 0xd2, 0x17, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x31, 0x83, 0x46, 0x01, 0x00, 0x15, 0x0a, 0x32, - 0xd2, 0x17, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, 0x20, 0x70, 0x25, 0x02, - 0x80, 0x15, 0x0a, 0x61, 0x61, 0x26, 0x04, 0x00, 0x05, 0x00, 0xf4, 0xff, 0x21, 0x70, 0x25, 0x01, - 0x80, 0x19, 0x08, 0x41, 0x61, 0x26, 0x00, 0x00, 0x07, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x80, 0x00, - 0x70, 0x60, 0x42, 0x02, 0x40, 0x0f, 0x0a, 0x30, 0x41, 0x15, 0x02, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x81, 0x00, 0x70, 0xd0, 0x14, 0x02, 0x00, 0x0f, 0x02, 0x22, 0x52, 0x17, 0x01, 0x80, - 0x08, 0x00, 0xf4, 0xff, 0x71, 0xd0, 0x24, 0x05, 0x00, 0x13, 0x00, 0x22, 0x52, 0x07, 0x05, 0x80, - 0x08, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x80, 0x00, 0x71, 0xf5, 0x21, 0x05, 0x80, 0x10, 0x06, 0x22, - 0x60, 0x16, 0x04, 0x00, 0x09, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x85, 0x00, 0xf0, 0x51, 0x21, 0x05, - 0x80, 0x17, 0x04, 0x21, 0x60, 0x15, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xf2, 0xf8, 0x23, 0x02, - 0x80, 0x19, 0x0c, 0x21, 0x54, 0x05, 0x04, 0x80, 0x0a, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x8a, 0x00, - 0x11, 0x52, 0xa0, 0x00, 0x80, 0x1f, 0x0a, 0x11, 0xc4, 0xe4, 0x00, 0x00, 0x03, 0x00, 0xf4, 0xff, - 0x12, 0x96, 0xc7, 0x01, 0xc0, 0x19, 0x04, 0x11, 0x54, 0xe4, 0x00, 0x00, 0x03, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x82, 0x00, 0x33, 0xe1, 0x22, 0x00, 0x80, 0x22, 0x0e, 0x31, 0xf1, 0xf2, 0x00, 0x00, - 0x09, 0x00, 0xf4, 0xff, 0x32, 0xf1, 0x22, 0x00, 0x00, 0x24, 0x0e, 0x31, 0xf1, 0xf2, 0x00, 0x00, - 0x0a, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x82, 0x00, 0x13, 0xc4, 0xc3, 0x04, 0x80, 0x1b, 0x0a, 0x14, - 0xd2, 0xb4, 0x00, 0x40, 0x08, 0x00, 0xdc, 0xff, 0x00, 0xf9, 0xba, 0x05, 0x80, 0x07, 0x06, 0x11, - 0xf2, 0xb4, 0x00, 0x40, 0x08, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x85, 0x00, 0x70, 0x51, 0x21, 0x05, - 0x80, 0x17, 0x04, 0x21, 0x60, 0x15, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x32, 0xf8, 0x23, 0x02, - 0x80, 0x19, 0x0c, 0x21, 0x54, 0x05, 0x04, 0x80, 0x13, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x85, 0x00, - 0x70, 0x51, 0x21, 0x05, 0x80, 0x17, 0x04, 0x21, 0x30, 0x14, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x32, 0xf8, 0x23, 0x02, 0x80, 0x19, 0x0c, 0x21, 0x34, 0x04, 0x04, 0x80, 0x0a, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x84, 0x00, 0x32, 0x47, 0x25, 0x01, 0x80, 0x24, 0x08, 0xb1, 0x56, 0x02, 0x04, 0x00, - 0x0a, 0x00, 0xf4, 0xff, 0x34, 0x49, 0x05, 0x01, 0x80, 0x2a, 0x08, 0xb2, 0x56, 0x02, 0x05, 0x00, - 0x0d, 0x00, 0xe8, 0xff, 0x04, 0x00, 0x87, 0x00, 0x22, 0x11, 0x03, 0x00, 0x40, 0x18, 0x00, 0x21, - 0x61, 0x04, 0x01, 0x80, 0x04, 0x00, 0xf4, 0xff, 0x21, 0x11, 0x33, 0x02, 0x80, 0x0d, 0x08, 0x21, - 0x41, 0x36, 0x01, 0x80, 0x04, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x89, 0x00, 0x21, 0x50, 0x05, 0x00, - 0x00, 0x1c, 0x07, 0x22, 0x50, 0xf5, 0x00, 0x00, 0x0f, 0x00, 0xf4, 0xff, 0x66, 0x40, 0xf5, 0x00, - 0x40, 0x1a, 0x07, 0x24, 0x50, 0xf5, 0x00, 0x00, 0x0f, 0x00, 0xe8, 0xff, 0x04, 0x00, 0x89, 0x00, - 0x20, 0xb4, 0x39, 0x02, 0x40, 0x10, 0x06, 0x21, 0x61, 0x07, 0x00, 0x00, 0x05, 0x00, 0xf4, 0xff, - 0x62, 0xb4, 0x39, 0x01, 0x80, 0x11, 0x00, 0x61, 0x71, 0x0d, 0x00, 0x00, 0x14, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x8a, 0x00, 0xf1, 0x70, 0x54, 0x00, 0x40, 0x1a, 0x00, 0xb1, 0x60, 0x74, 0x00, 0x80, - 0x00, 0x00, 0xf4, 0xff, 0xf1, 0xa0, 0x54, 0x00, 0x40, 0x1e, 0x00, 0xb1, 0x60, 0x74, 0x00, 0x80, - 0x00, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x88, 0x00, 0x10, 0x61, 0x43, 0x02, 0x80, 0x10, 0x0c, 0x11, - 0xd2, 0x14, 0x01, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x92, 0x61, 0x43, 0x01, 0x80, 0x0f, 0x0c, 0x12, - 0xf3, 0x45, 0x01, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x80, 0x00, 0x21, 0x74, 0x14, 0x00, - 0x80, 0x14, 0x0c, 0x21, 0x75, 0x07, 0x00, 0x00, 0x04, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x82, 0x00, - 0x71, 0x52, 0x23, 0x00, 0x00, 0x1e, 0x0e, 0x61, 0x81, 0x19, 0x00, 0x00, 0x08, 0x00, 0xf4, 0xff, - 0x61, 0x53, 0x23, 0x00, 0x00, 0x1d, 0x0e, 0x61, 0x61, 0x19, 0x00, 0x00, 0x03, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x80, 0x00, 0x31, 0x53, 0x15, 0x00, 0x80, 0x14, 0x0a, 0x61, 0x82, 0x37, 0x00, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x7a, 0x53, 0x18, 0x01, 0x00, 0x21, 0x04, 0x32, - 0x74, 0x17, 0x05, 0x00, 0x00, 0x00, 0xe8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x82, 0x00, 0x21, 0x41, 0x36, 0x00, - 0x00, 0x21, 0x0c, 0x21, 0x72, 0x2b, 0x00, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x21, 0xf0, 0x26, 0x00, - 0x00, 0x26, 0x0c, 0x21, 0x61, 0x05, 0x00, 0x00, 0x03, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x84, 0x00, - 0x21, 0x83, 0x57, 0x00, 0x00, 0x19, 0x0c, 0x21, 0xc0, 0x17, 0x01, 0x00, 0x03, 0x00, 0xf4, 0xff, - 0x30, 0x75, 0xe7, 0x02, 0x00, 0x10, 0x08, 0x20, 0xf5, 0x07, 0x03, 0x00, 0x08, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x86, 0x00, 0x21, 0xf1, 0x36, 0x00, 0x80, 0x12, 0x0e, 0x21, 0xf1, 0x37, 0x03, 0x00, - 0x08, 0x00, 0xf4, 0xff, 0x21, 0x81, 0x36, 0x00, 0x80, 0x12, 0x0e, 0x21, 0xf1, 0x37, 0x00, 0x00, - 0x08, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x88, 0x00, 0x21, 0x61, 0xa6, 0x00, 0x80, 0x1b, 0x0e, 0x21, - 0x61, 0x97, 0x00, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x21, 0x61, 0xa6, 0x00, 0x80, 0x1b, 0x0e, 0x21, - 0x61, 0x97, 0x00, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x80, 0x00, 0x11, 0x70, 0xf3, 0x00, - 0x40, 0x11, 0x0a, 0x52, 0x50, 0xf6, 0x01, 0x00, 0x04, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, - 0x21, 0x70, 0x06, 0x01, 0x80, 0x0a, 0x06, 0x31, 0x52, 0x16, 0x01, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x00, 0x20, 0x98, 0x0b, 0x00, 0x80, 0x0e, 0x0a, 0x30, 0x53, 0x0b, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x20, 0x80, 0x0b, 0x01, 0x80, 0x07, 0x08, 0x30, - 0x63, 0x1b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x31, 0x50, 0x23, 0x00, - 0x40, 0x18, 0x0a, 0x64, 0x6c, 0x07, 0x00, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x31, 0xd5, 0x55, 0x00, - 0x00, 0x17, 0x06, 0x72, 0x6c, 0x07, 0x00, 0x00, 0x0d, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x80, 0x00, - 0x30, 0x78, 0x07, 0x02, 0x80, 0x14, 0x0a, 0x31, 0x63, 0x07, 0x02, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x00, 0x31, 0x90, 0x19, 0x00, 0x80, 0x15, 0x0a, 0x34, 0x61, 0x1b, 0x00, 0x80, - 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x32, 0x75, 0x17, 0x00, 0x80, 0x13, 0x0c, 0x31, - 0x45, 0x17, 0x00, 0x00, 0x03, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xf1, 0x6e, 0x8d, 0x00, - 0xc0, 0x00, 0x0e, 0x71, 0x35, 0x2a, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, - 0xa1, 0x60, 0x88, 0x00, 0x80, 0x26, 0x08, 0x61, 0x65, 0x2a, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x00, 0x22, 0x65, 0x17, 0x02, 0x40, 0x1a, 0x06, 0x21, 0x74, 0x27, 0x00, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x82, 0x00, 0x72, 0x54, 0x27, 0x01, 0x80, 0x0d, 0x04, 0xb1, - 0x45, 0x17, 0x00, 0x00, 0x05, 0x00, 0xf4, 0xff, 0x51, 0xf0, 0xf0, 0x02, 0x00, 0x03, 0x0a, 0x01, - 0x78, 0x9a, 0x00, 0x00, 0x12, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x83, 0x00, 0xf2, 0x57, 0x04, 0x00, - 0x80, 0x28, 0x0e, 0x61, 0x57, 0x37, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x73, 0x5c, 0x54, 0x02, - 0x00, 0x00, 0x0e, 0x61, 0x68, 0x77, 0x00, 0x00, 0x0d, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x82, 0x00, - 0x39, 0x37, 0x54, 0x06, 0x00, 0x28, 0x0e, 0xa1, 0x67, 0x5d, 0x02, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x32, 0x43, 0x24, 0x06, 0x00, 0x1e, 0x0c, 0x70, 0x63, 0x6d, 0x02, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x80, 0x00, 0xd1, 0x31, 0x0f, 0x00, 0xc0, 0x07, 0x0b, 0x71, 0x32, 0x05, 0x00, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xa2, 0x81, 0x05, 0x00, 0x00, 0x37, 0x06, 0x31, - 0x52, 0x29, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x88, 0x00, 0x22, 0xff, 0x0f, 0x02, - 0x40, 0x3f, 0x05, 0x21, 0xff, 0x2f, 0x06, 0x00, 0x14, 0x00, 0xf4, 0xff, 0x22, 0xff, 0x0f, 0x03, - 0x80, 0x3f, 0x05, 0x21, 0xff, 0x2f, 0x06, 0x00, 0x14, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x87, 0x00, - 0x20, 0xc1, 0x9b, 0x03, 0x80, 0x3f, 0x00, 0x30, 0xc0, 0x68, 0x03, 0x00, 0x06, 0x00, 0xf4, 0xff, - 0x20, 0x81, 0xab, 0x03, 0x80, 0x1e, 0x00, 0x30, 0xf1, 0x08, 0x03, 0x00, 0x06, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x87, 0x00, 0xf1, 0x57, 0x33, 0x01, 0x00, 0x21, 0x0a, 0xe1, 0x67, 0x07, 0x00, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0xf2, 0x57, 0x33, 0x04, 0x00, 0x28, 0x00, 0xe1, 0x67, 0x07, 0x00, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x7e, 0x00, 0x30, 0x48, 0x44, 0x02, 0x00, 0x22, 0x08, 0xa1, - 0x67, 0x3d, 0x04, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x21, 0xb8, 0x34, 0x05, 0x00, 0x1f, 0x0a, 0xa1, - 0x87, 0x3d, 0x01, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x80, 0x00, 0x20, 0x74, 0x2b, 0x03, - 0x40, 0x06, 0x08, 0x34, 0xf1, 0x2b, 0x01, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x8a, 0x00, - 0x72, 0x90, 0x02, 0x05, 0x80, 0x16, 0x08, 0x61, 0x90, 0x06, 0x00, 0x00, 0x09, 0x00, 0xf4, 0xff, - 0x31, 0x90, 0x01, 0x01, 0x00, 0x1a, 0x06, 0x61, 0x90, 0x06, 0x00, 0x00, 0x09, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x7d, 0x00, 0x20, 0xc1, 0x95, 0x01, 0x40, 0x03, 0x0a, 0x30, 0x70, 0x63, 0x01, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x91, 0x95, 0x01, 0x40, 0x09, 0x0a, 0x31, 0x61, 0x63, 0x01, 0x00, - 0x00, 0x00, 0xfb, 0xff, 0x04, 0x00, 0x82, 0x00, 0x10, 0xed, 0x32, 0x01, 0x80, 0x07, 0x06, 0x10, - 0xd0, 0xe7, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0xed, 0x32, 0x01, 0x80, 0x07, 0x06, 0x10, - 0xd0, 0xe7, 0x03, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x85, 0x00, 0x74, 0x63, 0x94, 0x02, - 0x80, 0x25, 0x02, 0xa1, 0xa0, 0x05, 0x00, 0x00, 0x05, 0x00, 0xf4, 0xff, 0x02, 0x5f, 0x05, 0x02, - 0x80, 0x28, 0x00, 0xe1, 0xff, 0x04, 0x00, 0x00, 0x05, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x87, 0x00, - 0x31, 0x11, 0x03, 0x00, 0xc0, 0x14, 0x04, 0x61, 0x43, 0x05, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0xb0, 0x11, 0x03, 0x02, 0xc0, 0x19, 0x04, 0xe1, 0x43, 0x05, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x85, 0x00, 0x21, 0xa1, 0x45, 0x02, 0x80, 0x18, 0x0c, 0x21, 0xa1, 0x26, 0x01, 0x00, - 0x05, 0x00, 0xf4, 0xff, 0x21, 0xa4, 0x55, 0x02, 0x80, 0x18, 0x0c, 0x21, 0xa1, 0x26, 0x01, 0x00, - 0x05, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x8a, 0x00, 0xe0, 0x70, 0x04, 0x02, 0x40, 0x19, 0x02, 0x61, - 0x81, 0x14, 0x01, 0x00, 0x03, 0x00, 0xf4, 0xff, 0xe1, 0x60, 0x04, 0x01, 0x40, 0x1f, 0x06, 0x61, - 0x81, 0x14, 0x02, 0x80, 0x03, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x87, 0x00, 0x86, 0x61, 0x33, 0x00, - 0x80, 0x27, 0x06, 0xe1, 0x52, 0x54, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0xa6, 0x51, 0x33, 0x01, - 0x80, 0x27, 0x00, 0xe1, 0x52, 0x54, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x84, 0x00, - 0x84, 0x90, 0x52, 0x02, 0x80, 0x1d, 0x0c, 0x41, 0x43, 0x53, 0x01, 0x80, 0x00, 0x00, 0xf4, 0xff, - 0x82, 0x90, 0x52, 0x02, 0x80, 0x1d, 0x0c, 0x41, 0x43, 0x53, 0x00, 0x80, 0x00, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x86, 0x00, 0x31, 0x52, 0x34, 0x02, 0x80, 0x18, 0x06, 0x20, 0x51, 0x13, 0x02, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x22, 0xf4, 0x10, 0x04, 0x00, 0x20, 0x0a, 0x31, 0x32, 0x23, 0x01, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x87, 0x00, 0x62, 0x21, 0x52, 0x01, 0x80, 0x19, 0x08, 0x21, - 0x20, 0x63, 0x01, 0x80, 0x00, 0x00, 0xf4, 0xff, 0x61, 0x21, 0x52, 0x01, 0x80, 0x19, 0x08, 0x21, - 0x30, 0x63, 0x01, 0x80, 0x00, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x86, 0x00, 0x61, 0xb2, 0x57, 0x01, - 0x00, 0x04, 0x07, 0x06, 0xf8, 0x19, 0x00, 0x40, 0x00, 0x00, 0xf4, 0xff, 0x61, 0xb2, 0x57, 0x01, - 0x00, 0x04, 0x07, 0x14, 0x51, 0x14, 0x00, 0x40, 0x00, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x88, 0x00, - 0x22, 0x21, 0x32, 0x02, 0x80, 0x0c, 0x06, 0x21, 0x32, 0x13, 0x00, 0x80, 0x12, 0x00, 0xf4, 0xff, - 0x21, 0x31, 0x03, 0x04, 0x80, 0x13, 0x04, 0x22, 0x21, 0x13, 0x00, 0x40, 0x08, 0x00, 0xfb, 0xff, - 0x04, 0x00, 0x87, 0x00, 0x24, 0xf6, 0x74, 0x00, 0xc0, 0x00, 0x00, 0x03, 0x93, 0x24, 0x00, 0x00, - 0x0d, 0x00, 0xf4, 0xff, 0x24, 0xf6, 0x74, 0x00, 0xc0, 0x00, 0x00, 0x03, 0x93, 0x24, 0x00, 0x00, - 0x0d, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x7d, 0x00, 0x92, 0xc0, 0xd2, 0x03, 0x80, 0x0e, 0x00, 0x31, - 0xd1, 0xd2, 0x01, 0x00, 0x03, 0x00, 0xf4, 0xff, 0x91, 0xd0, 0xd2, 0x00, 0x80, 0x0e, 0x00, 0x32, - 0x31, 0xd2, 0x01, 0x00, 0x03, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x88, 0x00, 0x54, 0x90, 0xf2, 0x05, - 0x80, 0x19, 0x0e, 0x41, 0xc2, 0xf2, 0x00, 0x00, 0x06, 0x00, 0xf4, 0xff, 0x11, 0x90, 0xf1, 0x01, - 0x00, 0x1a, 0x06, 0x41, 0xc2, 0xf2, 0x00, 0x00, 0x06, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x7b, 0x00, - 0xe1, 0x33, 0x52, 0x00, 0x00, 0x1a, 0x07, 0x71, 0x13, 0x13, 0x00, 0x80, 0x08, 0x00, 0xf4, 0xff, - 0x63, 0x23, 0x52, 0x05, 0x00, 0x1a, 0x07, 0x31, 0x13, 0x13, 0x00, 0x80, 0x0d, 0x00, 0xf4, 0xff, - 0x04, 0x00, 0x89, 0x00, 0x61, 0x70, 0xb6, 0x00, 0x80, 0x1c, 0x0a, 0x32, 0x80, 0x92, 0x00, 0x00, - 0x11, 0x00, 0xf4, 0xff, 0x21, 0x7f, 0x05, 0x00, 0x80, 0x1c, 0x0a, 0x62, 0x8f, 0x03, 0x00, 0x00, - 0x11, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x85, 0x00, 0x11, 0x55, 0x02, 0x00, 0x80, 0x14, 0x0e, 0x01, - 0x82, 0x74, 0x01, 0x00, 0x06, 0x00, 0xf4, 0xff, 0x01, 0x55, 0x04, 0x02, 0x00, 0x15, 0x0a, 0x01, - 0x55, 0x44, 0x02, 0x00, 0x0d, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x82, 0x00, 0x21, 0x71, 0xb3, 0x00, - 0x00, 0x16, 0x02, 0x21, 0x82, 0xf4, 0x05, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x02, 0x71, 0xb3, 0x02, - 0x80, 0x10, 0x06, 0x21, 0x82, 0xf4, 0x05, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, - 0x33, 0x75, 0x12, 0x01, 0x40, 0x18, 0x06, 0x11, 0xf2, 0xf4, 0x04, 0x00, 0x03, 0x00, 0xf4, 0xff, - 0x14, 0xf0, 0xf0, 0x03, 0x40, 0x00, 0x0a, 0x11, 0xf6, 0xf7, 0x06, 0x40, 0x00, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x80, 0x00, 0x04, 0xfa, 0xa6, 0x00, 0x00, 0x09, 0x14, 0x07, 0xf8, 0xfb, 0x00, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x20, 0xf3, 0x32, 0x03, 0x00, 0x0e, 0x08, 0x23, - 0x94, 0xe4, 0x06, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x16, 0x84, 0x32, 0x00, - 0x80, 0x21, 0x0f, 0x11, 0xb4, 0x73, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x84, 0x00, - 0x31, 0x79, 0x39, 0x01, 0x80, 0x00, 0x06, 0x22, 0x9b, 0x09, 0x02, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x31, 0x79, 0x13, 0x02, 0x40, 0x02, 0x0a, 0x22, 0x9b, 0x07, 0x01, 0xc0, 0x02, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x80, 0x00, 0x30, 0xa1, 0x02, 0x03, 0x00, 0x1e, 0x0e, 0x21, 0x62, 0x26, 0x00, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x31, 0x60, 0x13, 0x00, 0x00, 0x12, 0x08, 0x31, - 0x91, 0x16, 0x01, 0x80, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x15, 0xa1, 0x32, 0x00, - 0x00, 0x00, 0x01, 0x12, 0xb2, 0x72, 0x01, 0x80, 0x00, 0x00, 0xf9, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, - 0x9a, 0xe7, 0x01, 0x00, 0x80, 0x01, 0x04, 0x1c, 0x96, 0x67, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x82, 0x00, 0x06, 0x63, 0x04, 0x04, 0xc0, 0x0a, 0x06, 0x02, 0xa9, 0x05, 0x00, 0x00, - 0x08, 0x00, 0xf4, 0xff, 0x13, 0x54, 0x04, 0x00, 0x40, 0x11, 0x0a, 0x01, 0xa6, 0x05, 0x00, 0x00, - 0x08, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x80, 0x00, 0x1e, 0xf8, 0xd1, 0x06, 0x80, 0x27, 0x06, 0x12, - 0xf6, 0x98, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x12, 0xf3, 0x08, 0x00, - 0x00, 0x08, 0x04, 0x00, 0xf6, 0x04, 0x00, 0xc0, 0x08, 0x00, 0xe8, 0xff, 0x01, 0xfa, 0x7c, 0x02, - 0x00, 0x08, 0x00, 0x00, 0xf2, 0x04, 0x00, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x80, 0x00, - 0x00, 0xfb, 0xc8, 0x05, 0x00, 0x00, 0x00, 0x01, 0xe6, 0xb7, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x0f, 0xf0, 0xf0, 0x02, 0x00, 0x00, 0x0e, 0x00, 0xf8, 0xf8, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x80, 0x00, 0x90, 0xf8, 0x18, 0x05, 0x00, 0x00, 0x08, 0x10, 0xf3, 0xe7, 0x00, 0x00, - 0x00, 0x00, 0xe8, 0xff, 0x10, 0xf6, 0x50, 0x02, 0x00, 0x00, 0x06, 0x10, 0xf5, 0xe7, 0x00, 0xc0, - 0x08, 0x00, 0xf4, 0xff, 0x04, 0x00, 0x87, 0x00, 0x09, 0x41, 0x11, 0x02, 0x00, 0x05, 0x0a, 0x01, - 0x21, 0x1d, 0x05, 0x80, 0x00, 0x00, 0xf4, 0xff, 0x08, 0x41, 0x11, 0x02, 0x00, 0x05, 0x0a, 0x01, - 0x21, 0x1d, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x11, 0x33, 0x54, 0x04, - 0x00, 0x19, 0x0e, 0x1a, 0x45, 0x57, 0x03, 0x00, 0x03, 0x00, 0xe6, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, - 0xd2, 0x37, 0x04, 0x00, 0x80, 0x21, 0x0c, 0x71, 0x34, 0x34, 0x00, 0x00, 0x07, 0x00, 0xf4, 0xff, - 0x0c, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x67, 0xf6, 0x00, 0xc0, 0x0d, 0x00, 0xf4, 0xff, - 0x05, 0x00, 0x80, 0x41, 0x27, 0xc3, 0x00, 0x02, 0x80, 0x00, 0x0e, 0x00, 0x13, 0x02, 0x06, 0x00, - 0x08, 0x00, 0xdd, 0xff, 0x2b, 0xc3, 0x00, 0x02, 0x80, 0x00, 0x0e, 0x00, 0x13, 0x02, 0x06, 0x00, - 0x08, 0x00, 0xdc, 0xff, 0x00, 0x00, 0x80, 0x00, 0xd7, 0xeb, 0x03, 0x00, 0xc0, 0x14, 0x07, 0xd8, - 0x45, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x20, 0xda, 0x2f, 0x06, - 0x40, 0x02, 0x04, 0x09, 0xb0, 0x14, 0x06, 0x40, 0x08, 0x00, 0xdc, 0xff, 0x20, 0xda, 0x2f, 0x06, - 0x80, 0x02, 0x04, 0x09, 0xb0, 0x14, 0x06, 0x40, 0x08, 0x00, 0xe0, 0xff, 0x05, 0x00, 0x80, 0x11, - 0x30, 0xfe, 0x01, 0x01, 0x00, 0x00, 0x0c, 0x20, 0x21, 0x14, 0x06, 0x00, 0x00, 0x00, 0xd0, 0xff, - 0x60, 0x94, 0x01, 0x06, 0x00, 0x26, 0x0c, 0x20, 0x21, 0x14, 0x01, 0x00, 0x00, 0x00, 0xe6, 0xff, - 0x05, 0x00, 0x80, 0x41, 0x2f, 0x83, 0x00, 0x02, 0x80, 0x00, 0x0e, 0x20, 0x40, 0x04, 0x06, 0x00, - 0x00, 0x00, 0xd0, 0xff, 0x2f, 0x83, 0x00, 0x02, 0x80, 0x00, 0x0e, 0x20, 0x40, 0x04, 0x06, 0x00, - 0x00, 0x00, 0xd1, 0xff, 0x00, 0x00, 0x80, 0x00, 0x31, 0x54, 0x30, 0x02, 0x00, 0x00, 0x0e, 0x00, - 0xf6, 0xe7, 0x06, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x19, 0x00, 0xfb, 0x57, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xf8, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x15, - 0x00, 0xc9, 0x19, 0x00, 0x00, 0x01, 0x04, 0x00, 0xf7, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x80, 0x3d, 0x02, 0xf7, 0x87, 0x02, 0x80, 0x00, 0x0e, 0x02, 0xf8, 0x78, 0x06, 0x80, - 0x08, 0x00, 0x00, 0x00, 0x02, 0xf6, 0xf7, 0x02, 0x00, 0x00, 0x0e, 0x02, 0xf8, 0xf8, 0x00, 0x80, - 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x80, 0x26, 0x01, 0x88, 0xef, 0x06, 0x00, 0x0d, 0x00, 0x02, - 0xb5, 0x08, 0x06, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x0f, 0xf1, 0xf1, 0x00, 0x00, 0x00, 0x0c, 0x0f, - 0x84, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x80, 0x25, 0xc1, 0x76, 0x54, 0x00, - 0x80, 0x00, 0x00, 0xc5, 0x92, 0x08, 0x03, 0x00, 0x08, 0x00, 0xf4, 0xff, 0xc1, 0x77, 0x54, 0x00, - 0x00, 0x00, 0x0a, 0xc5, 0x92, 0x08, 0x03, 0x00, 0x08, 0x00, 0xf2, 0xff, 0x05, 0x00, 0x80, 0x26, - 0x00, 0x88, 0xef, 0x06, 0x00, 0x13, 0x00, 0x02, 0xf5, 0x08, 0x06, 0x00, 0x00, 0x00, 0xfc, 0xff, - 0x01, 0xd1, 0xf1, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x84, 0x07, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xff, - 0x01, 0x00, 0x80, 0x20, 0x01, 0xf6, 0x08, 0x05, 0x00, 0x08, 0x06, 0x00, 0xf6, 0x04, 0x01, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x30, 0x07, 0xf3, 0x54, 0x02, 0x00, 0x04, 0x06, 0x05, - 0xf9, 0x07, 0x03, 0x00, 0x0b, 0x00, 0xef, 0xff, 0x07, 0xf3, 0x54, 0x02, 0x00, 0x04, 0x08, 0x05, - 0xf9, 0x07, 0x02, 0x00, 0x0b, 0x00, 0xf4, 0xff, 0x01, 0x00, 0x80, 0x22, 0x01, 0xf6, 0x08, 0x05, - 0x00, 0x08, 0x06, 0x00, 0xf6, 0x04, 0x01, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x30, - 0x07, 0xd3, 0x54, 0x02, 0x00, 0x04, 0x0a, 0x05, 0x89, 0x28, 0x03, 0x00, 0x08, 0x00, 0xef, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x80, 0x25, 0x01, 0xf6, 0x08, 0x05, 0x00, 0x0c, 0x08, 0x00, 0xf6, 0x04, 0x01, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x30, 0x27, 0xe3, 0xf2, 0x02, 0x00, 0x04, 0x0a, 0x25, - 0xc5, 0xf5, 0x03, 0x00, 0x08, 0x00, 0xef, 0xff, 0x26, 0xf3, 0xf2, 0x02, 0x00, 0x04, 0x08, 0x25, - 0xc5, 0xf5, 0x02, 0x00, 0x08, 0x00, 0xf4, 0xff, 0x01, 0x00, 0x80, 0x28, 0x01, 0xf6, 0x08, 0x05, - 0x00, 0x0c, 0x08, 0x00, 0xf6, 0x04, 0x01, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x2b, - 0x01, 0xf6, 0x08, 0x05, 0x00, 0x0c, 0x08, 0x00, 0xf6, 0x04, 0x01, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x86, 0x3d, 0x08, 0xf1, 0x92, 0x02, 0x00, 0x00, 0x08, 0x01, 0xf2, 0x54, 0x03, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x08, 0xf1, 0x92, 0x02, 0x00, 0x00, 0x08, 0x01, 0xf2, 0x54, 0x03, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x2e, 0x01, 0xf6, 0x08, 0x05, 0x00, 0x0c, 0x08, 0x00, - 0xf6, 0x04, 0x01, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x05, 0x00, 0x84, 0x3c, 0x09, 0xf5, 0x10, 0x02, - 0x80, 0x0a, 0x0e, 0x03, 0xfe, 0x05, 0x03, 0x80, 0x08, 0x00, 0x00, 0x00, 0x08, 0xf5, 0x00, 0x02, - 0xc0, 0x07, 0x0c, 0x03, 0xfe, 0x05, 0x03, 0x80, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x85, 0x4f, - 0x08, 0xe1, 0x83, 0x02, 0x00, 0x00, 0x08, 0x00, 0xd7, 0x34, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x09, 0xe1, 0x83, 0x02, 0x00, 0x00, 0x08, 0x00, 0xd5, 0x34, 0x03, 0x00, 0x08, 0x00, 0xff, 0xff, - 0x01, 0x00, 0x80, 0x3e, 0x19, 0xf1, 0xe1, 0x02, 0x00, 0x04, 0x08, 0x1b, 0xf1, 0xf3, 0x03, 0x00, - 0x08, 0x00, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x05, 0x00, 0x88, 0x50, 0x03, 0x76, 0x77, 0x02, 0x80, 0x07, 0x06, 0x01, - 0x96, 0x67, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x43, 0xc6, 0x77, 0x02, 0x80, 0x07, 0x0e, 0x01, - 0x96, 0x67, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x85, 0x43, 0x06, 0xf2, 0x51, 0x02, - 0x00, 0x04, 0x0a, 0x04, 0xc5, 0x63, 0x02, 0x00, 0x09, 0x00, 0xf4, 0xff, 0x87, 0xf2, 0x41, 0x02, - 0x00, 0x05, 0x0a, 0x02, 0xb5, 0x53, 0x03, 0x00, 0x09, 0x00, 0xf5, 0xff, 0x01, 0x00, 0x80, 0x3a, - 0x05, 0xf9, 0x66, 0x03, 0x00, 0x00, 0x0c, 0x01, 0xf7, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x86, 0x3e, 0x08, 0xf1, 0x92, 0x02, 0x00, 0x00, 0x08, 0x01, 0xf2, 0x54, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x08, 0xf1, 0x92, 0x02, 0x00, 0x00, 0x08, 0x01, 0xf2, 0x54, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x18, 0x20, 0xf1, 0x22, 0x04, 0x80, 0x01, 0x0a, 0x0e, - 0xb4, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x3d, 0x04, 0xfd, 0x12, 0x02, - 0x00, 0x01, 0x08, 0x03, 0xfd, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x05, 0x00, 0x80, 0x29, - 0xe3, 0xf9, 0xa8, 0x02, 0x00, 0x1e, 0x02, 0x43, 0x96, 0x77, 0x00, 0x00, 0x08, 0x00, 0xfa, 0xff, - 0xe8, 0xf7, 0xa5, 0x00, 0xc0, 0x1e, 0x00, 0x49, 0x99, 0xd8, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x05, 0x00, 0x80, 0x23, 0xe3, 0xf9, 0xa8, 0x02, 0x00, 0x1e, 0x0e, 0x43, 0x96, 0x77, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xe9, 0xf7, 0xa5, 0x00, 0xc0, 0x1e, 0x0c, 0x4a, 0x99, 0xd8, 0x00, 0x00, - 0x08, 0x00, 0xf4, 0xff, 0x05, 0x00, 0x80, 0x1d, 0xe9, 0xf7, 0xa8, 0x00, 0x00, 0x08, 0x0c, 0x0a, - 0x99, 0xd8, 0x05, 0x00, 0x00, 0x00, 0xf4, 0xff, 0xe9, 0xf7, 0xa5, 0x00, 0x00, 0x29, 0x0c, 0x4a, - 0x99, 0xd8, 0x00, 0x00, 0x08, 0x00, 0xfe, 0xff, 0x05, 0x00, 0x80, 0x29, 0xe2, 0xf9, 0xa8, 0x02, - 0x00, 0x1e, 0x0e, 0x42, 0x96, 0x77, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xe9, 0xf7, 0xa5, 0x00, - 0x00, 0x08, 0x0c, 0x4a, 0x99, 0xd8, 0x05, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x05, 0x00, 0x80, 0x25, - 0xe2, 0xf9, 0xa8, 0x02, 0x00, 0x1e, 0x0e, 0x42, 0x96, 0x77, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, - 0xe9, 0xf7, 0xa5, 0x00, 0x00, 0x08, 0x0c, 0x4a, 0x99, 0xd8, 0x05, 0x00, 0x00, 0x00, 0xf4, 0xff, - 0x01, 0x00, 0x80, 0x37, 0x02, 0xfb, 0x56, 0x04, 0x80, 0x01, 0x00, 0x00, 0xf7, 0x17, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x30, 0x02, 0xfb, 0x56, 0x04, 0x80, 0x01, 0x00, 0x00, - 0xf7, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x05, 0x00, 0x80, 0x4e, 0x01, 0xfd, 0x67, 0x03, - 0x00, 0x00, 0x08, 0x01, 0xf6, 0x98, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0xfd, 0x67, 0x03, - 0x00, 0x00, 0x08, 0x01, 0xf9, 0x98, 0x00, 0x00, 0x0d, 0x00, 0xfa, 0xff, 0x05, 0x00, 0x80, 0x49, - 0x01, 0xfd, 0x67, 0x03, 0x00, 0x00, 0x08, 0x01, 0xf6, 0x98, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x10, 0xfd, 0x67, 0x03, 0x00, 0x00, 0x08, 0x01, 0xf9, 0x98, 0x00, 0x00, 0x0d, 0x00, 0xff, 0xff, - 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x28, 0x0f, 0x60, 0x98, 0x00, 0x00, 0x08, 0x0e, 0x90, - 0x85, 0xfc, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x2d, 0x20, 0xf0, 0x09, 0x00, - 0x00, 0x07, 0x0a, 0x88, 0xa5, 0x7d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x2a, - 0x20, 0xc0, 0x0f, 0x00, 0x00, 0x07, 0x0a, 0xa8, 0xa5, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x80, 0x30, 0x80, 0xf6, 0x06, 0x01, 0x00, 0x00, 0x0c, 0x10, 0xf6, 0x16, 0x00, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x00, 0x6f, 0x0f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x0f, 0x02, 0x00, - 0x00, 0x00, 0xf4, 0xff, 0x05, 0x00, 0x80, 0x30, 0x80, 0xf6, 0x06, 0x01, 0x00, 0x00, 0x06, 0x10, - 0xf6, 0x16, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x00, 0x4f, 0x0f, 0x02, 0x00, 0x00, 0x06, 0x00, - 0x4f, 0x0f, 0x02, 0x00, 0x00, 0x00, 0xf4, 0xff, 0x01, 0x00, 0x80, 0x49, 0x15, 0xf8, 0xd1, 0x00, - 0x40, 0x04, 0x08, 0x12, 0xf5, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x04, - 0x01, 0xe8, 0xec, 0x00, 0x00, 0x0d, 0x16, 0x01, 0xa5, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x80, 0x04, 0x2f, 0x73, 0xc2, 0x00, 0x00, 0x07, 0x3a, 0x31, 0xc4, 0xb4, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x10, 0x01, 0x5d, 0xdc, 0x01, 0x00, 0x0b, 0x0a, 0x9f, - 0xf7, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x10, 0x00, 0xc9, 0xc7, 0x01, - 0x00, 0x07, 0x0a, 0x9f, 0xf4, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x5a, - 0xc6, 0xf2, 0x60, 0x01, 0x40, 0x0f, 0x08, 0xd4, 0xf4, 0x7a, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x80, 0x5a, 0x86, 0xf2, 0x60, 0x01, 0x40, 0x0f, 0x08, 0x94, 0xf2, 0xb7, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x00, 0x41, 0x63, 0x6f, 0x75, 0x73, 0x74, 0x69, 0x63, 0x20, 0x47, 0x72, 0x61, - 0x6e, 0x64, 0x20, 0x50, 0x69, 0x61, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x41, 0x63, 0x6f, 0x75, 0x73, - 0x74, 0x69, 0x63, 0x20, 0x50, 0x69, 0x61, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x69, 0x63, 0x20, 0x47, 0x72, 0x61, - 0x6e, 0x64, 0x20, 0x50, 0x69, 0x61, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x6f, 0x6e, 0x6b, 0x79, 0x2d, 0x74, 0x6f, 0x6e, 0x6b, 0x20, 0x50, - 0x69, 0x61, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x69, 0x63, 0x20, 0x50, 0x69, 0x61, - 0x6e, 0x6f, 0x20, 0x28, 0x52, 0x68, 0x6f, 0x64, 0x65, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x69, 0x63, 0x20, 0x50, 0x69, 0x61, - 0x6e, 0x6f, 0x20, 0x28, 0x44, 0x58, 0x2d, 0x37, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x61, 0x72, 0x70, 0x73, 0x69, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x6c, 0x61, 0x76, 0x69, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x65, 0x6c, 0x65, 0x73, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x6e, 0x73, 0x70, 0x69, 0x65, 0x6c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4d, 0x75, 0x73, 0x69, 0x63, 0x20, 0x42, 0x6f, 0x78, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x62, 0x72, 0x61, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4d, 0x61, 0x72, 0x69, 0x6d, 0x62, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x58, 0x79, 0x6c, 0x6f, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x75, 0x62, 0x75, 0x6c, 0x61, 0x72, 0x20, 0x42, 0x65, 0x6c, 0x6c, - 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x44, 0x75, 0x6c, 0x63, 0x69, 0x6d, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x44, 0x72, 0x61, 0x77, 0x62, 0x61, 0x72, 0x20, 0x4f, 0x72, 0x67, 0x61, - 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x65, 0x72, 0x63, 0x75, 0x73, 0x73, 0x69, 0x76, 0x65, 0x20, 0x4f, - 0x72, 0x67, 0x61, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x52, 0x6f, 0x63, 0x6b, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x68, 0x75, 0x72, 0x63, 0x68, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x52, 0x65, 0x65, 0x64, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x63, 0x6f, 0x72, 0x64, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x61, 0x72, 0x6d, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x20, 0x28, 0x44, - 0x4d, 0x58, 0x20, 0x46, 0x49, 0x58, 0x45, 0x44, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x61, 0x6e, 0x67, 0x6f, 0x20, 0x41, 0x63, 0x63, 0x6f, 0x72, 0x64, - 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x6f, 0x75, 0x73, 0x74, 0x69, 0x63, 0x20, 0x47, 0x75, 0x69, - 0x74, 0x61, 0x72, 0x20, 0x28, 0x6e, 0x79, 0x6c, 0x6f, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x6f, 0x75, 0x73, 0x74, 0x69, 0x63, 0x20, 0x47, 0x75, 0x69, - 0x74, 0x61, 0x72, 0x20, 0x28, 0x73, 0x74, 0x65, 0x65, 0x6c, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x69, 0x63, 0x20, 0x47, 0x75, 0x69, - 0x74, 0x61, 0x72, 0x20, 0x28, 0x6a, 0x61, 0x7a, 0x7a, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x69, 0x63, 0x20, 0x47, 0x75, 0x69, - 0x74, 0x61, 0x72, 0x20, 0x28, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x69, 0x63, 0x20, 0x47, 0x75, 0x69, - 0x74, 0x61, 0x72, 0x20, 0x28, 0x6d, 0x75, 0x74, 0x65, 0x64, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x76, 0x65, 0x72, 0x64, 0x72, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x47, - 0x75, 0x69, 0x74, 0x61, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x44, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x47, - 0x75, 0x69, 0x74, 0x61, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x75, 0x69, 0x74, 0x61, 0x72, 0x20, 0x48, 0x61, 0x72, 0x6d, 0x6f, - 0x6e, 0x69, 0x63, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x6f, 0x75, 0x73, 0x74, 0x69, 0x63, 0x20, 0x42, 0x61, 0x73, - 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x69, 0x63, 0x20, 0x42, 0x61, 0x73, - 0x73, 0x20, 0x28, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x69, 0x63, 0x20, 0x42, 0x61, 0x73, - 0x73, 0x20, 0x28, 0x70, 0x69, 0x63, 0x6b, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x72, 0x65, 0x74, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x42, 0x61, 0x73, - 0x73, 0x20, 0x28, 0x44, 0x4d, 0x58, 0x20, 0x42, 0x55, 0x47, 0x20, 0x46, 0x49, 0x58, 0x29, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x6c, 0x61, 0x70, 0x20, 0x42, 0x61, 0x73, 0x73, 0x20, 0x31, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x6c, 0x61, 0x70, 0x20, 0x42, 0x61, 0x73, 0x73, 0x20, 0x32, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x20, 0x42, 0x61, 0x73, 0x73, 0x20, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x20, 0x42, 0x61, 0x73, 0x73, 0x20, 0x32, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x6f, 0x6c, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x62, 0x61, 0x73, 0x73, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x72, 0x65, 0x6d, 0x6f, 0x6c, 0x6f, 0x20, 0x53, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x73, 0x20, 0x28, 0x44, 0x4d, 0x58, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x69, 0x7a, 0x7a, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x20, 0x53, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x20, 0x48, - 0x61, 0x72, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x69, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x20, 0x28, 0x44, 0x4d, 0x58, - 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x45, 0x6e, 0x73, 0x65, 0x6d, - 0x62, 0x6c, 0x65, 0x20, 0x31, 0x20, 0x28, 0x44, 0x4d, 0x58, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x45, 0x6e, 0x73, 0x65, 0x6d, - 0x62, 0x6c, 0x65, 0x20, 0x32, 0x20, 0x28, 0x44, 0x4d, 0x58, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, - 0x20, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, - 0x20, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x68, 0x6f, 0x69, 0x72, 0x20, 0x41, 0x61, 0x68, 0x73, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x56, 0x6f, 0x69, 0x63, 0x65, 0x20, 0x4f, 0x6f, 0x68, 0x73, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x20, 0x56, 0x6f, 0x69, 0x63, 0x65, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x20, 0x48, 0x69, - 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x72, 0x75, 0x6d, 0x70, 0x65, 0x74, 0x20, 0x28, 0x44, 0x4d, 0x58, - 0x20, 0x46, 0x49, 0x58, 0x45, 0x44, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x72, 0x6f, 0x6d, 0x62, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x75, 0x62, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4d, 0x75, 0x74, 0x65, 0x64, 0x20, 0x54, 0x72, 0x75, 0x6d, 0x70, 0x65, - 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x72, 0x65, 0x6e, 0x63, 0x68, 0x20, 0x48, 0x6f, 0x72, 0x6e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x72, 0x61, 0x73, 0x73, 0x20, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x20, 0x42, 0x72, 0x61, 0x73, 0x73, 0x20, - 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x20, 0x42, 0x72, 0x61, 0x73, 0x73, 0x20, - 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x6f, 0x70, 0x72, 0x61, 0x6e, 0x6f, 0x20, 0x53, 0x61, 0x78, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x6c, 0x74, 0x6f, 0x20, 0x53, 0x61, 0x78, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x65, 0x6e, 0x6f, 0x72, 0x20, 0x53, 0x61, 0x78, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x61, 0x72, 0x69, 0x74, 0x6f, 0x6e, 0x65, 0x20, 0x53, 0x61, 0x78, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x62, 0x6f, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x6e, 0x67, 0x6c, 0x69, 0x73, 0x68, 0x20, 0x48, 0x6f, 0x72, 0x6e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x61, 0x73, 0x73, 0x6f, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x6c, 0x61, 0x72, 0x69, 0x6e, 0x65, 0x74, 0x20, 0x28, 0x44, 0x4d, - 0x58, 0x20, 0x46, 0x49, 0x58, 0x45, 0x44, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x69, 0x63, 0x63, 0x6f, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x6c, 0x75, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x61, 0x6e, 0x20, 0x46, 0x6c, 0x75, 0x74, 0x65, 0x20, 0x28, 0x44, - 0x4d, 0x58, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x6c, 0x6f, 0x77, 0x6e, 0x20, 0x42, 0x6f, 0x74, 0x74, 0x6c, 0x65, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x68, 0x61, 0x6b, 0x75, 0x68, 0x61, 0x63, 0x68, 0x69, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x63, 0x61, 0x72, 0x69, 0x6e, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x61, 0x64, 0x20, 0x31, 0x20, 0x28, 0x73, 0x71, 0x75, 0x61, - 0x72, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x61, 0x64, 0x20, 0x32, 0x20, 0x28, 0x73, 0x61, 0x77, 0x74, - 0x6f, 0x6f, 0x74, 0x68, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x61, 0x64, 0x20, 0x33, 0x20, 0x28, 0x63, 0x61, 0x6c, 0x6c, - 0x69, 0x6f, 0x70, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x61, 0x64, 0x20, 0x34, 0x20, 0x28, 0x63, 0x68, 0x69, 0x66, - 0x66, 0x65, 0x72, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x61, 0x64, 0x20, 0x35, 0x20, 0x28, 0x63, 0x68, 0x61, 0x72, - 0x61, 0x6e, 0x67, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x61, 0x64, 0x20, 0x36, 0x20, 0x28, 0x56, 0x6f, 0x69, 0x63, - 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x61, 0x64, 0x20, 0x37, 0x20, 0x28, 0x35, 0x74, 0x68, 0x20, - 0x73, 0x61, 0x77, 0x74, 0x6f, 0x6f, 0x74, 0x68, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x61, 0x64, 0x20, 0x38, 0x20, 0x28, 0x4c, 0x65, 0x61, 0x64, - 0x2b, 0x42, 0x61, 0x73, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x61, 0x64, 0x20, 0x31, 0x20, 0x28, 0x6e, 0x65, 0x77, 0x20, 0x61, - 0x67, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x61, 0x64, 0x20, 0x32, 0x20, 0x28, 0x77, 0x61, 0x72, 0x6d, 0x29, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x61, 0x64, 0x20, 0x33, 0x20, 0x28, 0x70, 0x6f, 0x6c, 0x79, 0x73, - 0x79, 0x6e, 0x74, 0x68, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x61, 0x64, 0x20, 0x34, 0x20, 0x28, 0x63, 0x68, 0x6f, 0x69, 0x72, - 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x61, 0x64, 0x20, 0x35, 0x20, 0x28, 0x62, 0x6f, 0x77, 0x65, 0x64, - 0x20, 0x67, 0x6c, 0x61, 0x73, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x61, 0x64, 0x20, 0x36, 0x20, 0x28, 0x6d, 0x65, 0x74, 0x61, 0x6c, - 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x61, 0x64, 0x20, 0x37, 0x20, 0x28, 0x68, 0x61, 0x6c, 0x6f, 0x29, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x61, 0x64, 0x20, 0x38, 0x20, 0x28, 0x73, 0x77, 0x65, 0x65, 0x70, - 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x58, 0x20, 0x31, 0x20, 0x28, 0x72, 0x61, 0x69, 0x6e, 0x29, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x58, 0x20, 0x32, 0x20, 0x28, 0x73, 0x6f, 0x75, 0x6e, 0x64, 0x74, - 0x72, 0x61, 0x63, 0x6b, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x58, 0x20, 0x33, 0x20, 0x28, 0x63, 0x72, 0x79, 0x73, 0x74, 0x61, - 0x6c, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x58, 0x20, 0x34, 0x20, 0x28, 0x61, 0x74, 0x6d, 0x6f, 0x73, 0x70, - 0x68, 0x65, 0x72, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x58, 0x20, 0x35, 0x20, 0x28, 0x62, 0x72, 0x69, 0x67, 0x68, 0x74, - 0x6e, 0x65, 0x73, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x58, 0x20, 0x36, 0x20, 0x28, 0x67, 0x6f, 0x62, 0x6c, 0x69, 0x6e, - 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x58, 0x20, 0x37, 0x20, 0x28, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x64, - 0x72, 0x6f, 0x70, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2a, 0x20, 0x46, 0x58, 0x20, 0x38, 0x20, 0x28, 0x73, 0x74, 0x61, 0x72, - 0x2d, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x69, 0x74, 0x61, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x61, 0x6e, 0x6a, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x68, 0x61, 0x6d, 0x69, 0x73, 0x65, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4b, 0x6f, 0x74, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4b, 0x61, 0x6c, 0x69, 0x6d, 0x62, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x61, 0x67, 0x20, 0x50, 0x69, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x68, 0x61, 0x6e, 0x61, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x69, 0x6e, 0x6b, 0x6c, 0x65, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x74, 0x65, 0x65, 0x6c, 0x20, 0x44, 0x72, 0x75, 0x6d, 0x73, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x57, 0x6f, 0x6f, 0x64, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x61, 0x69, 0x6b, 0x6f, 0x20, 0x44, 0x72, 0x75, 0x6d, 0x20, 0x28, - 0x6e, 0x65, 0x77, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4d, 0x65, 0x6c, 0x6f, 0x64, 0x69, 0x63, 0x20, 0x54, 0x6f, 0x6d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x20, 0x44, 0x72, 0x75, 0x6d, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x20, 0x43, 0x79, 0x6d, 0x62, - 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x75, 0x69, 0x74, 0x61, 0x72, 0x20, 0x46, 0x72, 0x65, 0x74, 0x20, - 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x20, 0x28, 0x44, 0x4d, 0x58, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x72, 0x65, 0x61, 0x74, 0x68, 0x20, 0x4e, 0x6f, 0x69, 0x73, 0x65, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x65, 0x61, 0x73, 0x68, 0x6f, 0x72, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x69, 0x72, 0x64, 0x20, 0x54, 0x77, 0x65, 0x65, 0x74, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x20, 0x52, 0x69, - 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x69, 0x63, 0x6f, 0x70, 0x74, 0x65, 0x72, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x70, 0x70, 0x6c, 0x61, 0x75, 0x73, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x75, 0x6e, 0x20, 0x53, 0x68, 0x6f, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x6f, 0x75, 0x73, 0x74, 0x69, 0x63, 0x20, 0x42, 0x61, 0x73, - 0x73, 0x20, 0x44, 0x72, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x61, 0x73, 0x73, 0x20, 0x44, 0x72, 0x75, 0x6d, 0x20, 0x31, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x6c, 0x69, 0x64, 0x65, 0x20, 0x53, 0x74, 0x69, 0x63, 0x6b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x6f, 0x75, 0x73, 0x74, 0x69, 0x63, 0x20, 0x53, 0x6e, 0x61, - 0x72, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x61, 0x6e, 0x64, 0x20, 0x43, 0x6c, 0x61, 0x70, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x69, 0x63, 0x20, 0x53, 0x6e, 0x61, - 0x72, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6f, 0x77, 0x20, 0x46, 0x6c, 0x6f, 0x6f, 0x72, 0x20, 0x54, 0x6f, - 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x20, 0x48, 0x69, 0x67, 0x68, 0x2d, - 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x69, 0x67, 0x68, 0x20, 0x46, 0x6c, 0x6f, 0x6f, 0x72, 0x20, 0x54, - 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x65, 0x64, 0x61, 0x6c, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x48, - 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6f, 0x77, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x48, 0x61, - 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6f, 0x77, 0x2d, 0x4d, 0x69, 0x64, 0x20, 0x54, 0x6f, 0x6d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x69, 0x2d, 0x4d, 0x69, 0x64, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x61, 0x73, 0x68, 0x20, 0x43, 0x79, 0x6d, 0x62, 0x61, 0x6c, - 0x20, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x69, 0x67, 0x68, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x52, 0x69, 0x64, 0x65, 0x20, 0x43, 0x79, 0x6d, 0x62, 0x61, 0x6c, 0x20, - 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x68, 0x69, 0x6e, 0x73, 0x65, 0x73, 0x20, 0x43, 0x79, 0x6d, 0x62, - 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x52, 0x69, 0x64, 0x65, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x61, 0x6d, 0x62, 0x6f, 0x75, 0x72, 0x69, 0x6e, 0x65, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x70, 0x6c, 0x61, 0x73, 0x68, 0x20, 0x43, 0x79, 0x6d, 0x62, 0x61, - 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x77, 0x62, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x61, 0x73, 0x68, 0x20, 0x43, 0x79, 0x6d, 0x62, 0x61, 0x6c, - 0x20, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x62, 0x72, 0x61, 0x73, 0x6c, 0x61, 0x70, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x52, 0x69, 0x64, 0x65, 0x20, 0x43, 0x79, 0x6d, 0x62, 0x61, 0x6c, 0x20, - 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x69, 0x67, 0x68, 0x20, 0x42, 0x6f, 0x6e, 0x67, 0x6f, 0x20, 0x28, - 0x4e, 0x65, 0x77, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6f, 0x77, 0x20, 0x42, 0x6f, 0x6e, 0x67, 0x6f, 0x20, 0x28, 0x4e, - 0x65, 0x77, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4d, 0x75, 0x74, 0x65, 0x20, 0x68, 0x69, 0x67, 0x68, 0x20, 0x63, 0x6f, - 0x6e, 0x67, 0x61, 0x20, 0x28, 0x4e, 0x65, 0x77, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x43, 0x6f, - 0x6e, 0x67, 0x61, 0x20, 0x28, 0x4e, 0x65, 0x77, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6f, 0x77, 0x20, 0x43, 0x6f, 0x6e, 0x67, 0x61, 0x20, 0x28, 0x4e, - 0x65, 0x77, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x69, 0x67, 0x68, 0x20, 0x54, 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6f, 0x77, 0x20, 0x54, 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x69, 0x67, 0x68, 0x20, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6f, 0x77, 0x20, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4e, 0x75, 0x6c, 0x6c, 0x20, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, - 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4d, 0x61, 0x72, 0x61, 0x63, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, - 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6f, 0x6e, 0x67, 0x20, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, 0x65, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6f, 0x6e, 0x67, 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x6c, 0x61, 0x76, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x69, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x20, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x6f, 0x77, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x20, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4d, 0x75, 0x74, 0x65, 0x20, 0x43, 0x75, 0x69, 0x63, 0x61, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x43, 0x75, 0x69, 0x63, 0x61, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4d, 0x75, 0x74, 0x65, 0x20, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, - 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, - 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; \ No newline at end of file diff --git a/libraries/libRAD/patches.cpp b/libraries/libRAD/patches.cpp deleted file mode 100644 index af7bee5df..000000000 --- a/libraries/libRAD/patches.cpp +++ /dev/null @@ -1,336 +0,0 @@ -#include "patches.h" - -#include -#include - -#include "instruments.h" -#include "radmidi.h" - -const char *OPLPatch::names[256] = { - "Acoustic Grand Piano", - "Bright Acoustic Piano", - "Electric Grand Piano", - "Honky-tonk Piano", - "Electric Piano 1", - "Electric Piano 2", - "Harpsichord", - "Clavi", - "Celesta", - "Glockenspiel", - "Music Box", - "Vibraphone", - "Marimba", - "Xylophone", - "Tubular Bells", - "Dulcimer", - "Drawbar Organ", - "Percussive Organ", - "Rock Organ", - "Church Organ", - "Reed Organ", - "Accordion", - "Harmonica", - "Tango Accordion", - "Acoustic Guitar (nylon)", - "Acoustic Guitar (steel)", - "Electric Guitar (jazz)", - "Electric Guitar (clean)", - "Electric Guitar (muted)", - "Overdriven Guitar", - "Distortion Guitar", - "Guitar Harmonics", - "Acoustic Bass", - "Electric Bass (finger)", - "Electric Bass (pick)", - "Fretless Bass", - "Slap Bass 1", - "Slap Bass 2", - "Synth Bass 1", - "Synth Bass 2", - "Violin", - "Viola", - "Cello", - "Contrabass", - "Tremolo Strings", - "Pizzicato Strings", - "Orchestral Harp", - "Timpani", - "String Ensemble 1", - "String Ensemble 2", - "SynthStrings 1", - "SynthStrings 2", - "Choir Aahs", - "Voice Oohs", - "Synth Voice", - "Orchestra Hit", - "Trumpet", - "Trombone", - "Tuba", - "Muted Trumpet", - "French Horn", - "Brass Section", - "SynthBrass 1", - "SynthBrass 2", - "Soprano Sax", - "Alto Sax", - "Tenor Sax", - "Baritone Sax", - "Oboe", - "English Horn", - "Bassoon", - "Clarinet", - "Piccolo", - "Flute", - "Recorder", - "Pan Flute", - "Blown Bottle", - "Shakuhachi", - "Whistle", - "Ocarina", - "Lead 1 (square)", - "Lead 2 (sawtooth)", - "Lead 3 (calliope)", - "Lead 4 (chiff)", - "Lead 5 (charang)", - "Lead 6 (voice)", - "Lead 7 (fifths)", - "Lead 8 (bass + lead)", - "Pad 1 (new age)", - "Pad 2 (warm)", - "Pad 3 (polysynth)", - "Pad 4 (choir)", - "Pad 5 (bowed)", - "Pad 6 (metallic)", - "Pad 7 (halo)", - "Pad 8 (sweep)", - "FX 1 (rain)", - "FX 2 (soundtrack)", - "FX 3 (crystal)", - "FX 4 (atmosphere)", - "FX 5 (brightness)", - "FX 6 (goblins)", - "FX 7 (echoes)", - "FX 8 (sci-fi)", - "Sitar", - "Banjo", - "Shamisen", - "Koto", - "Kalimba", - "Bagpipe", - "Fiddle", - "Shanai", - "Tinkle Bell", - "Agogo", - "Steel Drums", - "Woodblock", - "Taiko Drum", - "Melodic Tom", - "Synth Drum", - "Reverse Cymbal", - "Guitar Fret Noise", - "Breath Noise", - "Seashore", - "Bird Tweet", - "Telephone Ring", - "Helicopter", - "Applause", - "Gunshot", - - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "Acoustic Bass Drum", - "Bass Drum 1", - "Side Stick", - "Acoustic Snare", - "Hand Clap", - "Electric Snare", - "Low Floor Tom", - "Closed Hi Hat", - "High Floor Tom", - "Pedal Hi-Hat", - "Low Tom", - "Open Hi-Hat", - "Low-Mid Tom", - "Hi-Mid Tom", - "Crash Cymbal 1", - "High Tom", - "Ride Cymbal 1", - "Chinese Cymbal", - "Ride Bell", - "Tambourine", - "Splash Cymbal", - "Cowbell", - "Crash Cymbal 2", - "Vibraslap", - "Ride Cymbal 2", - "Hi Bongo", - "Low Bongo", - "Mute Hi Conga", - "Open Hi Conga", - "Low Conga", - "High Timbale", - "Low Timbale", - "High Agogo", - "Low Agogo", - "Cabasa", - "Maracas", - "Short Whistle", - "Long Whistle", - "Short Guiro", - "Long Guiro", - "Claves", - "Hi Wood Block", - "Low Wood Block", - "Mute Cuica", - "Open Cuica", - "Mute Triangle", - "Open Triangle", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", -}; - -// ---------------------------------------------------------------------------- -bool OPLPatch::load(OPLPatchSet &patches) -{ - // read data for all patches (128 melodic + 47 percussion) - for (int i = 0; i < 128+47; i++) - { - // patches 0-127 are melodic; the rest are for percussion notes 35 thru 81 - unsigned key = (i < 128) ? i : (i + 35); - - OPLPatch &patch = patches[key]; - // clear patch data - patch = OPLPatch(); - - // seek to patch data - const uint8_t *bytes = default_instruments + (36*i) + 8; - - // read the common data for both 2op voices - // flag bit 0 is "fixed pitch" (for drums), but it's seemingly only used for drum patches anyway, so ignore it? - patch.dualTwoOp = (bytes[0] & 4); - // second voice detune - patch.voice[1].finetune = OPLPlayer::midiCalcBend((int8_t)(bytes[2] - 128) / 64.0); - - patch.fixedNote = bytes[3]; - - // read data for both 2op voices - unsigned pos = 4; - for (int j = 0; j < 2; j++) - { - PatchVoice &voice = patch.voice[j]; - - for (int op = 0; op < 2; op++) - { - // operator mode - voice.op_mode[op] = bytes[pos++]; - // operator envelope - voice.op_ad[op] = bytes[pos++]; - voice.op_sr[op] = bytes[pos++]; - // operator waveform - voice.op_wave[op] = bytes[pos++]; - // KSR & output level - voice.op_ksr[op] = bytes[pos++] & 0xc0; - voice.op_level[op] = bytes[pos++] & 0x3f; - - // feedback/connection (first op only) - if (op == 0) - voice.conn = bytes[pos]; - pos++; - } - - // midi note offset (int16, but only really need the LSB) - voice.tune = (int8_t)bytes[pos]; - pos += 2; - } - - // fix for some bugged DMX patches (e.g. Doom II electric snare) - if (!(patch.voice[1].op_ad[0] | patch.voice[1].op_ad[1])) - patch.dualTwoOp = false; - - // seek to patch name - bytes = default_instruments + (32*i) + (36*175) + 8; - if (bytes[0]) - patch.name = std::string((const char*)bytes, 31); - else - patch.name = names[key]; - } - - return true; -} \ No newline at end of file diff --git a/libraries/libRAD/patches.h b/libraries/libRAD/patches.h deleted file mode 100644 index ba09c4071..000000000 --- a/libraries/libRAD/patches.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef __PATCHES_H -#define __PATCHES_H - -#include - -#include -#include -#include - -// one carrier/modulator pair in a patch, out of a possible two -struct PatchVoice -{ - uint8_t op_mode[2] = {0}; // regs 0x20+ - uint8_t op_ksr[2] = {0}; // regs 0x40+ (upper bits) - uint8_t op_level[2] = {0}; // regs 0x40+ (lower bits) - uint8_t op_ad[2] = {0}; // regs 0x60+ - uint8_t op_sr[2] = {0}; // regs 0x80+ - uint8_t conn = 0; // regs 0xC0+ - uint8_t op_wave[2] = {0}; // regs 0xE0+ - - int8_t tune = 0; // MIDI note offset - double finetune = 1.0; // frequency multiplier -}; - -typedef std::unordered_map OPLPatchSet; - -struct OPLPatch -{ - std::string name; - bool fourOp = false; // true 4op - bool dualTwoOp = false; // only valid if fourOp = false - uint8_t fixedNote = 0; - int8_t velocity = 0; // MIDI velocity offset - - PatchVoice voice[2]; - - // default names - static const char *names[256]; - - static bool load(OPLPatchSet &patches); -}; - -#endif // __PATCHES_H diff --git a/libraries/libRAD/radmidi.cpp b/libraries/libRAD/radmidi.cpp deleted file mode 100644 index 40e949798..000000000 --- a/libraries/libRAD/radmidi.cpp +++ /dev/null @@ -1,675 +0,0 @@ -#include "radmidi.h" - -#include -#include - -static const unsigned voice_num[18] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, - 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108}; - -static const unsigned oper_num[18] = {0x0, 0x1, 0x2, 0x8, 0x9, 0xA, 0x10, 0x11, 0x12, - 0x100, 0x101, 0x102, 0x108, 0x109, 0x10A, 0x110, 0x111, 0x112}; - -// ---------------------------------------------------------------------------- -OPLPlayer::OPLPlayer(int frequency) -{ - m_voices.resize(18); - - rate = frequency; - m_output.first = 0; - m_output.second = 0; - - reset(); -} - -// ---------------------------------------------------------------------------- -OPLPlayer::~OPLPlayer() -{ - delete m_opl3; -} - -// ---------------------------------------------------------------------------- -bool OPLPlayer::loadPatches() -{ - return OPLPatch::load(m_patches); -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::generate(int16_t *data, unsigned numSamples) -{ - unsigned int samp = 0; - - while (samp < numSamples * 2) - { - updateMIDI(); - - data[samp] = m_output.first; - data[samp + 1] = m_output.second; - - samp += 2; - } -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::updateMIDI() -{ - for (auto &voice : m_voices) - { - if (voice.duration < UINT_MAX) - voice.duration++; - voice.justChanged = false; - } - - if (m_sampleFIFO.empty()) - { - m_opl3->Sample(&m_output.first, &m_output.second); - } - else - { - m_output = m_sampleFIFO.front(); - m_sampleFIFO.pop(); - } -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::reset() -{ - delete m_opl3; - m_opl3 = new Opal(rate); - // enable OPL3 stuff - write(REG_NEW, 1); - - // reset MIDI channel and OPL voice status - m_midiType = GeneralMIDI; - for (int i = 0; i < 16; i++) - { - m_channels[i] = MIDIChannel(); - m_channels[i].num = i; - } - m_channels[9].percussion = true; - - for (int i = 0; i < m_voices.size(); i++) - { - m_voices[i] = OPLVoice(); - m_voices[i].chip = i / 18; - m_voices[i].num = voice_num[i % 18]; - m_voices[i].op = oper_num[i % 18]; - - // configure 4op voices - - switch (i % 9) - { - case 0: - case 1: - case 2: - m_voices[i].fourOpPrimary = true; - m_voices[i].fourOpOther = &m_voices[i + 3]; - break; - case 3: - case 4: - case 5: - m_voices[i].fourOpPrimary = false; - m_voices[i].fourOpOther = &m_voices[i - 3]; - break; - default: - m_voices[i].fourOpPrimary = false; - m_voices[i].fourOpOther = nullptr; - break; - } - } -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::runSamples(unsigned count) -{ - // clock one sample after changing the 4op state before writing other registers - // so that opal can reassign operators to channels, etc - // add some delay between register writes where needed - // (i.e. when forcing a voice off, changing 4op flags, etc.) - while (count--) - { - std::pair output; - m_opl3->Sample(&output.first, &output.second); - m_sampleFIFO.push(output); - } -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::write(uint16_t addr, uint8_t data) -{ - m_opl3->Port(addr, data); -} - -// ---------------------------------------------------------------------------- -OPLVoice *OPLPlayer::findVoice(uint8_t channel, const OPLPatch *patch, uint8_t note) -{ - OPLVoice *found = nullptr; - uint32_t duration = 0; - - // try to find the "oldest" voice, prioritizing released notes - // (or voices that haven't ever been used yet) - for (auto &voice : m_voices) - { - if (useFourOp(patch) && !voice.fourOpPrimary) - continue; - - if (!voice.channel) - return &voice; - - if (!voice.on && !voice.justChanged) - { - if (voice.channel->num == channel && voice.note == note && voice.duration < UINT_MAX) - { - // found an old voice that was using the same note and patch - // don't immediately use it, but make it a high priority candidate for later - // (to help avoid pop/click artifacts when retriggering a recently off note) - silenceVoice(voice); - if (useFourOp(voice.patch) && voice.fourOpOther) - silenceVoice(*voice.fourOpOther); - } - else if (voice.duration > duration) - { - found = &voice; - duration = voice.duration; - } - } - } - - if (found) - return found; - // if we didn't find one yet, just try to find an old one - // using the same patch, even if it should still be playing. - - for (auto &voice : m_voices) - { - if (useFourOp(patch) && !voice.fourOpPrimary) - continue; - - if (voice.patch == patch && voice.duration > duration) - { - found = &voice; - duration = voice.duration; - } - } - - if (found) - return found; - // last resort - just find any old voice at all - - for (auto &voice : m_voices) - { - if (useFourOp(patch) && !voice.fourOpPrimary) - continue; - // don't let a 2op instrument steal an active voice from a 4op one - if (!useFourOp(patch) && voice.on && useFourOp(voice.patch)) - continue; - - if (voice.duration > duration) - { - found = &voice; - duration = voice.duration; - } - } - - return found; -} - -// ---------------------------------------------------------------------------- -OPLVoice *OPLPlayer::findVoice(uint8_t channel, uint8_t note, bool justChanged) -{ - channel &= 15; - for (auto &voice : m_voices) - { - if (voice.on && voice.justChanged == justChanged && voice.channel == &m_channels[channel] && voice.note == note) - { - return &voice; - } - } - - return nullptr; -} - -// ---------------------------------------------------------------------------- -const OPLPatch *OPLPlayer::findPatch(uint8_t channel, uint8_t note) const -{ - uint16_t key; - const MIDIChannel &ch = m_channels[channel & 15]; - - if (ch.percussion) - key = 0x80 | note | (ch.patchNum << 8); - else - key = ch.patchNum | (ch.bank << 8); - - // if this patch+bank combo doesn't exist, default to bank 0 - if (!m_patches.count(key)) - key &= 0x00ff; - // if patch still doesn't exist in bank 0, use patch 0 (or drum note 0) - if (!m_patches.count(key)) - key &= 0x0080; - // if that somehow still doesn't exist, forget it - if (!m_patches.count(key)) - return nullptr; - - return &m_patches.at(key); -} - -// ---------------------------------------------------------------------------- -bool OPLPlayer::useFourOp(const OPLPatch *patch) const -{ - return patch->fourOp; -} - -// ---------------------------------------------------------------------------- -std::pair OPLPlayer::activeCarriers(const OPLVoice &voice) const -{ - bool scale[2] = {0}; - const auto patchVoice = voice.patchVoice; - - if (!patchVoice) - { - scale[0] = scale[1] = false; - } - else if (!useFourOp(voice.patch)) - { - // 2op FM (0): scale op 2 only - // 2op AM (1): scale op 1 and 2 - scale[0] = (patchVoice->conn & 1); - scale[1] = true; - } - else if (voice.fourOpPrimary) - { - // 4op FM+FM (0, 0): don't scale op 1 or 2 - // 4op AM+FM (1, 0): scale op 1 only - // 4op FM+AM (0, 1): scale op 2 only - // 4op AM+AM (1, 1): scale op 1 only - scale[0] = (voice.patch->voice[0].conn & 1); - scale[1] = (voice.patch->voice[1].conn & 1) && !scale[0]; - } - else - { - // 4op FM+FM (0, 0): scale op 4 only - // 4op AM+FM (1, 0): scale op 4 only - // 4op FM+AM (0, 1): scale op 4 only - // 4op AM+AM (1, 1): scale op 3 and 4 - scale[0] = (voice.patch->voice[0].conn & 1) && (voice.patch->voice[1].conn & 1); - scale[1] = true; - } - - return std::make_pair(scale[0], scale[1]); -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::updateChannelVoices(uint8_t channel, void (OPLPlayer::*func)(OPLVoice &)) -{ - for (auto &voice : m_voices) - { - if (voice.channel == &m_channels[channel & 15]) - (this->*func)(voice); - } -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::updatePatch(OPLVoice &voice, const OPLPatch *newPatch, uint8_t numVoice) -{ - // assign the MIDI channel's current patch (or the current drum patch) to this voice - - const PatchVoice &patchVoice = newPatch->voice[numVoice]; - - if (voice.patchVoice != &patchVoice) - { - bool oldFourOp = voice.patch ? useFourOp(voice.patch) : false; - - voice.patch = newPatch; - voice.patchVoice = &patchVoice; - - // update enable status for 4op channels on this chip - if (useFourOp(newPatch) != oldFourOp) - { - // if going from part of a 4op patch to a 2op one, kill the other one - OPLVoice *other = voice.fourOpOther; - if (other && other->patch && useFourOp(other->patch) && !useFourOp(newPatch)) - { - silenceVoice(*other); - } - - uint8_t enable = 0x00; - uint8_t bit = 0x01; - for (unsigned i = voice.chip * 18; i < voice.chip * 18 + 18; i++) - { - if (m_voices[i].fourOpPrimary) - { - if (m_voices[i].patch && useFourOp(m_voices[i].patch)) - enable |= bit; - bit <<= 1; - } - } - - write(REG_4OP, enable); - } - - // kill an existing voice, then send the chip far enough forward in time to let the envelope die off - // (ROTT: fixes nasty reverse cymbal noises in spray.mid - // without disrupting note timing too much for the staccato drums in fanfare2.mid) - silenceVoice(voice); - runSamples(48); - - // 0x20: vibrato, sustain, multiplier - write(REG_OP_MODE + voice.op, patchVoice.op_mode[0]); - write(REG_OP_MODE + voice.op + 3, patchVoice.op_mode[1]); - // 0x60: attack/decay - write(REG_OP_AD + voice.op, patchVoice.op_ad[0]); - write(REG_OP_AD + voice.op + 3, patchVoice.op_ad[1]); - // 0xe0: waveform - write(REG_OP_WAVEFORM + voice.op, patchVoice.op_wave[0]); - write(REG_OP_WAVEFORM + voice.op + 3, patchVoice.op_wave[1]); - } - - // 0x80: sustain/release - // update even for the same patch in case silenceVoice was called from somewhere else on this voice - write(REG_OP_SR + voice.op, patchVoice.op_sr[0]); - write(REG_OP_SR + voice.op + 3, patchVoice.op_sr[1]); -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::updateVolume(OPLVoice &voice) -{ - // lookup table shamelessly stolen from Nuke.YKT - static const uint8_t opl_volume_map[32] = {80, 63, 40, 36, 32, 28, 23, 21, 19, 17, 15, 14, 13, 12, 11, 10, - 9, 8, 7, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0}; - - if (!voice.patch || !voice.channel) - return; - - uint8_t atten = opl_volume_map[(voice.velocity * voice.channel->volume) >> 10]; - uint8_t level; - - const auto patchVoice = voice.patchVoice; - const auto scale = activeCarriers(voice); - - // 0x40: key scale / volume - if (scale.first) - level = std::min(0x3f, patchVoice->op_level[0] + atten); - else - level = patchVoice->op_level[0]; - write(REG_OP_LEVEL + voice.op, level | patchVoice->op_ksr[0]); - - if (scale.second) - level = std::min(0x3f, patchVoice->op_level[1] + atten); - else - level = patchVoice->op_level[1]; - write(REG_OP_LEVEL + voice.op + 3, level | patchVoice->op_ksr[1]); -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::updatePanning(OPLVoice &voice) -{ - if (!voice.patch || !voice.channel) - return; - - // 0xc0: output/feedback/mode - uint8_t pan = 0x30; - if (voice.channel->pan < 32) - pan = 0x10; - else if (voice.channel->pan >= 96) - pan = 0x20; - - write(REG_VOICE_CNT + voice.num, voice.patchVoice->conn | pan); -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::updateFrequency(OPLVoice &voice) -{ - static const uint16_t noteFreq[12] = {// calculated from A440 - 345, 365, 387, 410, 435, 460, 488, 517, 547, 580, 615, 651}; - - if (!voice.patch || !voice.channel) - return; - if (useFourOp(voice.patch) && !voice.fourOpPrimary) - return; - - int note = (!voice.channel->percussion ? voice.note : voice.patch->fixedNote) + voice.patchVoice->tune; - - int octave = note / 12; - note %= 12; - - // calculate base frequency (and apply pitch bend / patch detune) - unsigned freq = (note >= 0) ? noteFreq[note] : (noteFreq[note + 12] >> 1); - if (octave < 0) - freq >>= -octave; - else if (octave > 0) - freq <<= octave; - - freq *= voice.channel->pitch * voice.patchVoice->finetune; - - // convert the calculated frequency back to a block and F-number - octave = 0; - while (freq > 0x3ff) - { - freq >>= 1; - octave++; - } - octave = std::min(7, octave); - voice.freq = freq | (octave << 10); - - write(REG_VOICE_FREQL + voice.num, voice.freq & 0xff); - write(REG_VOICE_FREQH + voice.num, (voice.freq >> 8) | (voice.on ? (1 << 5) : 0)); -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::silenceVoice(OPLVoice &voice) -{ - voice.on = false; - voice.justChanged = true; - voice.duration = UINT_MAX; - - write(REG_OP_SR + voice.op, 0xff); - write(REG_OP_SR + voice.op + 3, 0xff); - write(REG_VOICE_FREQH + voice.num, voice.freq >> 8); -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::midiNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) -{ - note &= 0x7f; - velocity &= 0x7f; - - // if we just now turned this same note on, don't do it again - if (findVoice(channel, note, true)) - return; - - if (!velocity) - return midiNoteOff(channel, note); - - const OPLPatch *newPatch = findPatch(channel, note); - if (!newPatch) - return; - - const int numVoices = ((useFourOp(newPatch) || newPatch->dualTwoOp) ? 2 : 1); - - OPLVoice *voice = nullptr; - for (int i = 0; i < numVoices; i++) - { - if (voice && useFourOp(newPatch) && voice->fourOpOther) - voice = voice->fourOpOther; - else - voice = findVoice(channel, newPatch, note); - if (!voice) - continue; // ?? - - updatePatch(*voice, newPatch, i); - - // update the note parameters for this voice - voice->channel = &m_channels[channel & 15]; - voice->on = voice->justChanged = true; - voice->note = note; - voice->velocity = (((int)velocity + newPatch->velocity) & 127); - voice->duration = 0; - - updateVolume(*voice); - updatePanning(*voice); - - // for 4op instruments, don't key on until we've written both voices... - if (!useFourOp(newPatch)) - { - updateFrequency(*voice); - } - else if (i > 0) - { - updateFrequency(*voice->fourOpOther); - } - } -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::midiNoteOff(uint8_t channel, uint8_t note) -{ - note &= 0x7f; - - // printf("midiNoteOff: chn %u, note %u\n", channel, note); - OPLVoice *voice; - while ((voice = findVoice(channel, note)) != nullptr) - { - voice->justChanged = voice->on; - voice->on = false; - - write(REG_VOICE_FREQH + voice->num, voice->freq >> 8); - } -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::midiPitchControl(uint8_t channel, double pitch) -{ - // printf("midiPitchControl: chn %u, val %.02f\n", channel, pitch); - MIDIChannel &ch = m_channels[channel & 15]; - - ch.basePitch = pitch; - ch.pitch = midiCalcBend(pitch * ch.bendRange); - updateChannelVoices(channel, &OPLPlayer::updateFrequency); -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::midiProgramChange(uint8_t channel, uint8_t patchNum) -{ - m_channels[channel & 15].patchNum = patchNum & 0x7f; - // patch change will take effect on the next note for this channel -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::midiControlChange(uint8_t channel, uint8_t control, uint8_t value) -{ - channel &= 15; - control &= 0x7f; - value &= 0x7f; - - MIDIChannel &ch = m_channels[channel]; - - switch (control) - { - case 0: - if (m_midiType == RolandGS) - ch.bank = value; - else if (m_midiType == YamahaXG) - ch.percussion = (value == 0x7f); - break; - - case 6: - if (ch.rpn == 0) - { - ch.bendRange = value; - midiPitchControl(channel, ch.basePitch); - } - break; - - case 7: - ch.volume = value; - updateChannelVoices(channel, &OPLPlayer::updateVolume); - break; - - case 10: - ch.pan = value; - updateChannelVoices(channel, &OPLPlayer::updatePanning); - break; - - case 32: - if (m_midiType == YamahaXG || m_midiType == GeneralMIDI2) - ch.bank = value; - break; - - case 98: - case 99: - ch.rpn = 0x3fff; - break; - - case 100: - ch.rpn &= 0x3f80; - ch.rpn |= value; - break; - - case 101: - ch.rpn &= 0x7f; - ch.rpn |= (value << 7); - break; - } -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::midiSysEx(const uint8_t *data, uint32_t length) -{ - if (length > 0 && data[0] == 0xF0) - { - data++; - length--; - } - - if (length == 0) - return; - - if (data[0] == 0x7e) // universal non-realtime - { - if (length == 5 && data[1] == 0x7f && data[2] == 0x09) - { - if (data[3] == 0x01) - m_midiType = GeneralMIDI; - else if (data[3] == 0x03) - m_midiType = GeneralMIDI2; - } - } - else if (data[0] == 0x41 && length >= 10 // Roland - && data[2] == 0x42 && data[3] == 0x12) - { - // if we received one of these, assume GS mode - // (some MIDIs seem to e.g. send drum map messages without a GS reset) - m_midiType = RolandGS; - - uint32_t address = (data[4] << 16) | (data[5] << 8) | data[6]; - // for single part parameters, map "part number" to channel number - // (using the default mapping) - uint8_t channel = (address & 0xf00) >> 8; - if (channel == 0) - channel = 9; - else if (channel <= 9) - channel--; - - // Roland GS part parameters - if ((address & 0xfff0ff) == 0x401015) // set drum map - m_channels[channel].percussion = (data[7] != 0x00); - } - else if (length >= 8 && !memcmp(data, "\x43\x10\x4c\x00\x00\x7e\x00\xf7", 8)) // Yamaha - { - m_midiType = YamahaXG; - } -} - -// ---------------------------------------------------------------------------- -void OPLPlayer::midiRawOPL(uint16_t addr, uint8_t data) -{ - if ((addr & 0xF0) == 0xC0) - data |= 0x30; - write(addr, data); - runSamples(1); -} - -// ---------------------------------------------------------------------------- -double OPLPlayer::midiCalcBend(double semitones) -{ - return pow(2, semitones / 12.0); -} diff --git a/libraries/libRAD/radmidi.h b/libraries/libRAD/radmidi.h deleted file mode 100644 index d9ca0250b..000000000 --- a/libraries/libRAD/radmidi.h +++ /dev/null @@ -1,160 +0,0 @@ -#ifndef __RADMIDI_H -#define __RADMIDI_H - -#include -#include -#include - -#include "opal.h" -#include "patches.h" - -struct MIDIChannel -{ - uint8_t num = 0; - - bool percussion = false; - uint8_t bank = 0; - uint8_t patchNum = 0; - uint8_t volume = 127; - uint8_t pan = 64; - double basePitch = 0.0; // pitch wheel position - double pitch = 1.0; // frequency multiplier - - uint16_t rpn = 0x3fff; - - uint8_t bendRange = 2; -}; - -struct OPLVoice -{ - int chip = 0; - const MIDIChannel *channel = nullptr; - const OPLPatch *patch = nullptr; - const PatchVoice *patchVoice = nullptr; - - uint16_t num = 0; - uint16_t op = 0; // base operator number, set based on voice num. - bool fourOpPrimary = false; - OPLVoice *fourOpOther = nullptr; - - bool on = false; - bool justChanged = false; // true after note on/off, false after generating at least 1 sample - uint8_t note = 0; - uint8_t velocity = 0; - - // block and F number, calculated from note and channel pitch - uint16_t freq = 0; - - // how long has this note been playing (incremented each midi update) - uint32_t duration = UINT_MAX; -}; - -class OPLPlayer -{ - public: - enum MIDIType - { - GeneralMIDI, - RolandGS, - YamahaXG, - GeneralMIDI2 - }; - - OPLPlayer(int frequency); - virtual ~OPLPlayer(); - - // load built-in instrument patches - bool loadPatches(); - - void generate(int16_t *data, unsigned numSamples); - - // reset OPL and midi file - void reset(); - - // helpers for midiEvent - void midiNoteOn(uint8_t channel, uint8_t note, uint8_t velocity); - void midiNoteOff(uint8_t channel, uint8_t note); - void midiPitchControl(uint8_t channel, double pitch); // range is -1.0 to 1.0 - void midiProgramChange(uint8_t channel, uint8_t patchNum); - void midiControlChange(uint8_t channel, uint8_t control, uint8_t value); - // sysex data (data and length *don't* include the opening 0xF0) - void midiSysEx(const uint8_t *data, uint32_t length); - void midiRawOPL(uint16_t addr, uint8_t data); - - // helper for pitch bend and finetune - static double midiCalcBend(double semitones); - - private: - static const unsigned masterClock = 14318181; - - enum - { - REG_TEST = 0x01, - - REG_OP_MODE = 0x20, - REG_OP_LEVEL = 0x40, - REG_OP_AD = 0x60, - REG_OP_SR = 0x80, - REG_VOICE_FREQL = 0xA0, - REG_VOICE_FREQH = 0xB0, - REG_VOICE_CNT = 0xC0, - REG_OP_WAVEFORM = 0xE0, - - REG_4OP = 0x104, - REG_NEW = 0x105, - }; - - void updateMIDI(); - - void runSamples(unsigned count); - - void write(uint16_t addr, uint8_t data); - - // find a voice with the oldest note, or the same patch & note - // if no "off" voices are found, steal one using the same patch or MIDI channel - OPLVoice *findVoice(uint8_t channel, const OPLPatch *patch, uint8_t note); - // find a voice that's playing a specific note on a specific channel - OPLVoice *findVoice(uint8_t channel, uint8_t note, bool justChanged = false); - - // find the patch to use for a specific MIDI channel and note - const OPLPatch *findPatch(uint8_t channel, uint8_t note) const; - - // determine whether this patch should be configured as 4op - bool useFourOp(const OPLPatch *patch) const; - - // determine which operator(s) to scale based on the current operator settings - std::pair activeCarriers(const OPLVoice &voice) const; - - // update a property of all currently playing voices on a MIDI channel - void updateChannelVoices(uint8_t channel, void (OPLPlayer::*func)(OPLVoice &)); - - // update the patch parameters for a voice - void updatePatch(OPLVoice &voice, const OPLPatch *newPatch, uint8_t numVoice = 0); - - // update the volume level for a voice - void updateVolume(OPLVoice &voice); - - // update the pan position for a voice - void updatePanning(OPLVoice &voice); - - // update the block and F-number for a voice (also key on/off) - void updateFrequency(OPLVoice &voice); - - // silence a voice immediately - void silenceVoice(OPLVoice &voice); - - Opal *m_opl3 = nullptr; - int rate; - - std::pair m_output; // output sample data - // if we need to clock one of the OPLs between register writes, save the resulting sample - std::queue> m_sampleFIFO; - - MIDIChannel m_channels[16]; - std::vector m_voices; - MIDIType m_midiType; - - OPLPatchSet m_patches; -}; - -#endif // __RADMIDI_H diff --git a/libraries/libemidi/source/CSMFPlay.cpp b/libraries/libemidi/source/CSMFPlay.cpp index aae3679b8..c77e3e065 100644 --- a/libraries/libemidi/source/CSMFPlay.cpp +++ b/libraries/libemidi/source/CSMFPlay.cpp @@ -7,20 +7,15 @@ using namespace dsa; -CSMFPlay::CSMFPlay(uint32_t rate, CSMFPlay::PlayerMode mode, int mods) { - m_mods = mods; - if (mode == SCC_PSG_MODE) - for(int i=0;i - -#ifdef _WIN32 - -#ifdef _WIN64 -#define CPU_32BIT 0 -#define CPU_64BIT 1 -#else -#define CPU_32BIT 1 -#define CPU_64BIT 0 -#endif - -#else -#include - -#if __WORDSIZE == 64 -#define CPU_32BIT 0 -#define CPU_64BIT 1 -#else -#define CPU_32BIT 1 -#define CPU_64BIT 0 -#endif - -#endif - -#if CPU_64BIT -#define CPU_BITS 64 -#define uintCPUWord_t uint64_t -#define intCPUWord_t int64_t -#else -#define CPU_BITS 32 -#define uintCPUWord_t uint32_t -#define intCPUWord_t int32_t -#endif diff --git a/libraries/m4p/ft_tables.c b/libraries/m4p/ft_tables.c deleted file mode 100644 index 6c6c78d92..000000000 --- a/libraries/m4p/ft_tables.c +++ /dev/null @@ -1,453 +0,0 @@ -#include - -// 8bb: bit-accurate FT2 tables (FT2.08/FT2.09) - -/* -** for (int32_t i = 0; i < 257; i++) -** panningTab[i] = (int32_t)round(65536.0 * sqrt(i / 256.0)); -*/ -const uint32_t panningTab[257] = -{ - 0, 4096, 5793, 7094, 8192, 9159,10033,10837,11585,12288,12953,13585,14189,14768,15326,15864, - 16384,16888,17378,17854,18318,18770,19212,19644,20066,20480,20886,21283,21674,22058,22435,22806, - 23170,23530,23884,24232,24576,24915,25249,25580,25905,26227,26545,26859,27170,27477,27780,28081, - 28378,28672,28963,29251,29537,29819,30099,30377,30652,30924,31194,31462,31727,31991,32252,32511, - 32768,33023,33276,33527,33776,34024,34270,34514,34756,34996,35235,35472,35708,35942,36175,36406, - 36636,36864,37091,37316,37540,37763,37985,38205,38424,38642,38858,39073,39287,39500,39712,39923, - 40132,40341,40548,40755,40960,41164,41368,41570,41771,41972,42171,42369,42567,42763,42959,43154, - 43348,43541,43733,43925,44115,44305,44494,44682,44869,45056,45242,45427,45611,45795,45977,46160, - 46341,46522,46702,46881,47059,47237,47415,47591,47767,47942,48117,48291,48465,48637,48809,48981, - 49152,49322,49492,49661,49830,49998,50166,50332,50499,50665,50830,50995,51159,51323,51486,51649, - 51811,51972,52134,52294,52454,52614,52773,52932,53090,53248,53405,53562,53719,53874,54030,54185, - 54340,54494,54647,54801,54954,55106,55258,55410,55561,55712,55862,56012,56162,56311,56459,56608, - 56756,56903,57051,57198,57344,57490,57636,57781,57926,58071,58215,58359,58503,58646,58789,58931, - 59073,59215,59357,59498,59639,59779,59919,60059,60199,60338,60477,60615,60753,60891,61029,61166, - 61303,61440,61576,61712,61848,61984,62119,62254,62388,62523,62657,62790,62924,63057,63190,63323, - 63455,63587,63719,63850,63982,64113,64243,64374,64504,64634,64763,64893,65022,65151,65279,65408, - 65536 -}; - -// 8bb: the last 17 values are off (but identical to FT2.08/FT2.09) because of a bug in how it calculates this table -const uint16_t amigaPeriods[1936] = -{ - 29024,28912,28800,28704,28608,28496,28384,28288,28192,28096,28000,27888,27776,27680,27584,27488, - 27392,27296,27200,27104,27008,26912,26816,26720,26624,26528,26432,26336,26240,26144,26048,25952, - 25856,25760,25664,25568,25472,25392,25312,25216,25120,25024,24928,24848,24768,24672,24576,24480, - 24384,24304,24224,24144,24064,23968,23872,23792,23712,23632,23552,23456,23360,23280,23200,23120, - 23040,22960,22880,22784,22688,22608,22528,22448,22368,22288,22208,22128,22048,21968,21888,21792, - 21696,21648,21600,21520,21440,21360,21280,21200,21120,21040,20960,20896,20832,20752,20672,20576, - 20480,20416,20352,20288,20224,20160,20096,20016,19936,19872,19808,19728,19648,19584,19520,19424, - 19328,19280,19232,19168,19104,19024,18944,18880,18816,18752,18688,18624,18560,18480,18400,18320, - 18240,18192,18144,18080,18016,17952,17888,17824,17760,17696,17632,17568,17504,17440,17376,17296, - 17216,17168,17120,17072,17024,16960,16896,16832,16768,16704,16640,16576,16512,16464,16416,16336, - 16256,16208,16160,16112,16064,16000,15936,15872,15808,15760,15712,15648,15584,15536,15488,15424, - 15360,15312,15264,15216,15168,15104,15040,14992,14944,14880,14816,14768,14720,14672,14624,14568, - 14512,14456,14400,14352,14304,14248,14192,14144,14096,14048,14000,13944,13888,13840,13792,13744, - 13696,13648,13600,13552,13504,13456,13408,13360,13312,13264,13216,13168,13120,13072,13024,12976, - 12928,12880,12832,12784,12736,12696,12656,12608,12560,12512,12464,12424,12384,12336,12288,12240, - 12192,12152,12112,12072,12032,11984,11936,11896,11856,11816,11776,11728,11680,11640,11600,11560, - 11520,11480,11440,11392,11344,11304,11264,11224,11184,11144,11104,11064,11024,10984,10944,10896, - 10848,10824,10800,10760,10720,10680,10640,10600,10560,10520,10480,10448,10416,10376,10336,10288, - 10240,10208,10176,10144,10112,10080,10048,10008, 9968, 9936, 9904, 9864, 9824, 9792, 9760, 9712, - 9664, 9640, 9616, 9584, 9552, 9512, 9472, 9440, 9408, 9376, 9344, 9312, 9280, 9240, 9200, 9160, - 9120, 9096, 9072, 9040, 9008, 8976, 8944, 8912, 8880, 8848, 8816, 8784, 8752, 8720, 8688, 8648, - 8608, 8584, 8560, 8536, 8512, 8480, 8448, 8416, 8384, 8352, 8320, 8288, 8256, 8232, 8208, 8168, - 8128, 8104, 8080, 8056, 8032, 8000, 7968, 7936, 7904, 7880, 7856, 7824, 7792, 7768, 7744, 7712, - 7680, 7656, 7632, 7608, 7584, 7552, 7520, 7496, 7472, 7440, 7408, 7384, 7360, 7336, 7312, 7284, - 7256, 7228, 7200, 7176, 7152, 7124, 7096, 7072, 7048, 7024, 7000, 6972, 6944, 6920, 6896, 6872, - 6848, 6824, 6800, 6776, 6752, 6728, 6704, 6680, 6656, 6632, 6608, 6584, 6560, 6536, 6512, 6488, - 6464, 6440, 6416, 6392, 6368, 6348, 6328, 6304, 6280, 6256, 6232, 6212, 6192, 6168, 6144, 6120, - 6096, 6076, 6056, 6036, 6016, 5992, 5968, 5948, 5928, 5908, 5888, 5864, 5840, 5820, 5800, 5780, - 5760, 5740, 5720, 5696, 5672, 5652, 5632, 5612, 5592, 5572, 5552, 5532, 5512, 5492, 5472, 5448, - 5424, 5412, 5400, 5380, 5360, 5340, 5320, 5300, 5280, 5260, 5240, 5224, 5208, 5188, 5168, 5144, - 5120, 5104, 5088, 5072, 5056, 5040, 5024, 5004, 4984, 4968, 4952, 4932, 4912, 4896, 4880, 4856, - 4832, 4820, 4808, 4792, 4776, 4756, 4736, 4720, 4704, 4688, 4672, 4656, 4640, 4620, 4600, 4580, - 4560, 4548, 4536, 4520, 4504, 4488, 4472, 4456, 4440, 4424, 4408, 4392, 4376, 4360, 4344, 4324, - 4304, 4292, 4280, 4268, 4256, 4240, 4224, 4208, 4192, 4176, 4160, 4144, 4128, 4116, 4104, 4084, - 4064, 4052, 4040, 4028, 4016, 4000, 3984, 3968, 3952, 3940, 3928, 3912, 3896, 3884, 3872, 3856, - 3840, 3828, 3816, 3804, 3792, 3776, 3760, 3748, 3736, 3720, 3704, 3692, 3680, 3668, 3656, 3642, - 3628, 3614, 3600, 3588, 3576, 3562, 3548, 3536, 3524, 3512, 3500, 3486, 3472, 3460, 3448, 3436, - 3424, 3412, 3400, 3388, 3376, 3364, 3352, 3340, 3328, 3316, 3304, 3292, 3280, 3268, 3256, 3244, - 3232, 3220, 3208, 3196, 3184, 3174, 3164, 3152, 3140, 3128, 3116, 3106, 3096, 3084, 3072, 3060, - 3048, 3038, 3028, 3018, 3008, 2996, 2984, 2974, 2964, 2954, 2944, 2932, 2920, 2910, 2900, 2890, - 2880, 2870, 2860, 2848, 2836, 2826, 2816, 2806, 2796, 2786, 2776, 2766, 2756, 2746, 2736, 2724, - 2712, 2706, 2700, 2690, 2680, 2670, 2660, 2650, 2640, 2630, 2620, 2612, 2604, 2594, 2584, 2572, - 2560, 2552, 2544, 2536, 2528, 2520, 2512, 2502, 2492, 2484, 2476, 2466, 2456, 2448, 2440, 2428, - 2416, 2410, 2404, 2396, 2388, 2378, 2368, 2360, 2352, 2344, 2336, 2328, 2320, 2310, 2300, 2290, - 2280, 2274, 2268, 2260, 2252, 2244, 2236, 2228, 2220, 2212, 2204, 2196, 2188, 2180, 2172, 2162, - 2152, 2146, 2140, 2134, 2128, 2120, 2112, 2104, 2096, 2088, 2080, 2072, 2064, 2058, 2052, 2042, - 2032, 2026, 2020, 2014, 2008, 2000, 1992, 1984, 1976, 1970, 1964, 1956, 1948, 1942, 1936, 1928, - 1920, 1914, 1908, 1902, 1896, 1888, 1880, 1874, 1868, 1860, 1852, 1846, 1840, 1834, 1828, 1821, - 1814, 1807, 1800, 1794, 1788, 1781, 1774, 1768, 1762, 1756, 1750, 1743, 1736, 1730, 1724, 1718, - 1712, 1706, 1700, 1694, 1688, 1682, 1676, 1670, 1664, 1658, 1652, 1646, 1640, 1634, 1628, 1622, - 1616, 1610, 1604, 1598, 1592, 1587, 1582, 1576, 1570, 1564, 1558, 1553, 1548, 1542, 1536, 1530, - 1524, 1519, 1514, 1509, 1504, 1498, 1492, 1487, 1482, 1477, 1472, 1466, 1460, 1455, 1450, 1445, - 1440, 1435, 1430, 1424, 1418, 1413, 1408, 1403, 1398, 1393, 1388, 1383, 1378, 1373, 1368, 1362, - 1356, 1353, 1350, 1345, 1340, 1335, 1330, 1325, 1320, 1315, 1310, 1306, 1302, 1297, 1292, 1286, - 1280, 1276, 1272, 1268, 1264, 1260, 1256, 1251, 1246, 1242, 1238, 1233, 1228, 1224, 1220, 1214, - 1208, 1205, 1202, 1198, 1194, 1189, 1184, 1180, 1176, 1172, 1168, 1164, 1160, 1155, 1150, 1145, - 1140, 1137, 1134, 1130, 1126, 1122, 1118, 1114, 1110, 1106, 1102, 1098, 1094, 1090, 1086, 1081, - 1076, 1073, 1070, 1067, 1064, 1060, 1056, 1052, 1048, 1044, 1040, 1036, 1032, 1029, 1026, 1021, - 1016, 1013, 1010, 1007, 1004, 1000, 996, 992, 988, 985, 982, 978, 974, 971, 968, 964, - 960, 957, 954, 951, 948, 944, 940, 937, 934, 930, 926, 923, 920, 917, 914, 910, - 907, 903, 900, 897, 894, 890, 887, 884, 881, 878, 875, 871, 868, 865, 862, 859, - 856, 853, 850, 847, 844, 841, 838, 835, 832, 829, 826, 823, 820, 817, 814, 811, - 808, 805, 802, 799, 796, 793, 791, 788, 785, 782, 779, 776, 774, 771, 768, 765, - 762, 759, 757, 754, 752, 749, 746, 743, 741, 738, 736, 733, 730, 727, 725, 722, - 720, 717, 715, 712, 709, 706, 704, 701, 699, 696, 694, 691, 689, 686, 684, 681, - 678, 676, 675, 672, 670, 667, 665, 662, 660, 657, 655, 653, 651, 648, 646, 643, - 640, 638, 636, 634, 632, 630, 628, 625, 623, 621, 619, 616, 614, 612, 610, 607, - 604, 602, 601, 599, 597, 594, 592, 590, 588, 586, 584, 582, 580, 577, 575, 572, - 570, 568, 567, 565, 563, 561, 559, 557, 555, 553, 551, 549, 547, 545, 543, 540, - 538, 536, 535, 533, 532, 530, 528, 526, 524, 522, 520, 518, 516, 514, 513, 510, - 508, 506, 505, 503, 502, 500, 498, 496, 494, 492, 491, 489, 487, 485, 484, 482, - 480, 478, 477, 475, 474, 472, 470, 468, 467, 465, 463, 461, 460, 458, 457, 455, - 453, 451, 450, 448, 447, 445, 443, 441, 440, 438, 437, 435, 434, 432, 431, 429, - 428, 426, 425, 423, 422, 420, 419, 417, 416, 414, 413, 411, 410, 408, 407, 405, - 404, 402, 401, 399, 398, 396, 395, 393, 392, 390, 389, 388, 387, 385, 384, 382, - 381, 379, 378, 377, 376, 374, 373, 371, 370, 369, 368, 366, 365, 363, 362, 361, - 360, 358, 357, 355, 354, 353, 352, 350, 349, 348, 347, 345, 344, 343, 342, 340, - 339, 338, 337, 336, 335, 333, 332, 331, 330, 328, 327, 326, 325, 324, 323, 321, - 320, 319, 318, 317, 316, 315, 314, 312, 311, 310, 309, 308, 307, 306, 305, 303, - 302, 301, 300, 299, 298, 297, 296, 295, 294, 293, 292, 291, 290, 288, 287, 286, - 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, - 269, 268, 267, 266, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256, 255, - 254, 253, 252, 251, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 242, 241, - 240, 239, 238, 237, 237, 236, 235, 234, 233, 232, 231, 230, 230, 229, 228, 227, - 227, 226, 225, 224, 223, 222, 222, 221, 220, 219, 219, 218, 217, 216, 215, 214, - 214, 213, 212, 211, 211, 210, 209, 208, 208, 207, 206, 205, 205, 204, 203, 202, - 202, 201, 200, 199, 199, 198, 198, 197, 196, 195, 195, 194, 193, 192, 192, 191, - 190, 189, 189, 188, 188, 187, 186, 185, 185, 184, 184, 183, 182, 181, 181, 180, - 180, 179, 179, 178, 177, 176, 176, 175, 175, 174, 173, 172, 172, 171, 171, 170, - 169, 169, 169, 168, 167, 166, 166, 165, 165, 164, 164, 163, 163, 162, 161, 160, - 160, 159, 159, 158, 158, 157, 157, 156, 156, 155, 155, 154, 153, 152, 152, 151, - 151, 150, 150, 149, 149, 148, 148, 147, 147, 146, 146, 145, 145, 144, 144, 143, - 142, 142, 142, 141, 141, 140, 140, 139, 139, 138, 138, 137, 137, 136, 136, 135, - 134, 134, 134, 133, 133, 132, 132, 131, 131, 130, 130, 129, 129, 128, 128, 127, - 127, 126, 126, 125, 125, 124, 124, 123, 123, 123, 123, 122, 122, 121, 121, 120, - 120, 119, 119, 118, 118, 117, 117, 117, 117, 116, 116, 115, 115, 114, 114, 113, - 113, 112, 112, 112, 112, 111, 111, 110, 110, 109, 109, 108, 108, 108, 108, 107, - 107, 106, 106, 105, 105, 105, 105, 104, 104, 103, 103, 102, 102, 102, 102, 101, - 101, 100, 100, 99, 99, 99, 99, 98, 98, 97, 97, 97, 97, 96, 96, 95, - 95, 95, 95, 94, 94, 93, 93, 93, 93, 92, 92, 91, 91, 91, 91, 90, - 90, 89, 89, 89, 89, 88, 88, 87, 87, 87, 87, 86, 86, 85, 85, 85, - 85, 84, 84, 84, 84, 83, 83, 82, 82, 82, 82, 81, 81, 81, 81, 80, - 80, 79, 79, 79, 79, 78, 78, 78, 78, 77, 77, 77, 77, 76, 76, 75, - 75, 75, 75, 75, 75, 74, 74, 73, 73, 73, 73, 72, 72, 72, 72, 71, - 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, - 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 64, 64, 64, 64, 63, - 63, 63, 63, 63, 63, 62, 62, 62, 62, 61, 61, 61, 61, 60, 60, 60, - 60, 60, 60, 59, 59, 59, 59, 58, 58, 58, 58, 57, 57, 57, 57, 57, - 57, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 53, - 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 50, - 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, - 48, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 45, 45, 45, - 45, 45, 45, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 42, - 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 40, 40, 40, - 40, 40, 40, 39, 39, 39, 39, 39, 39, 39, 39, 38, 38, 38, 38, 38, - 38, 38, 38, 37, 37, 37, 37, 37, 37, 36, 36, 36, 36, 36, 36, 36, - 36, 35, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 34, 34, - 34, 33, 33, 33, 33, 33, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 22, - 16, 8, 0, 16, 32, 24, 16, 8, 0, 16, 32, 24, 16, 8, 0, 0 -}; - -const uint16_t linearPeriods[1936] = -{ - 7744,7740,7736,7732,7728,7724,7720,7716,7712,7708,7704,7700,7696,7692,7688,7684, - 7680,7676,7672,7668,7664,7660,7656,7652,7648,7644,7640,7636,7632,7628,7624,7620, - 7616,7612,7608,7604,7600,7596,7592,7588,7584,7580,7576,7572,7568,7564,7560,7556, - 7552,7548,7544,7540,7536,7532,7528,7524,7520,7516,7512,7508,7504,7500,7496,7492, - 7488,7484,7480,7476,7472,7468,7464,7460,7456,7452,7448,7444,7440,7436,7432,7428, - 7424,7420,7416,7412,7408,7404,7400,7396,7392,7388,7384,7380,7376,7372,7368,7364, - 7360,7356,7352,7348,7344,7340,7336,7332,7328,7324,7320,7316,7312,7308,7304,7300, - 7296,7292,7288,7284,7280,7276,7272,7268,7264,7260,7256,7252,7248,7244,7240,7236, - 7232,7228,7224,7220,7216,7212,7208,7204,7200,7196,7192,7188,7184,7180,7176,7172, - 7168,7164,7160,7156,7152,7148,7144,7140,7136,7132,7128,7124,7120,7116,7112,7108, - 7104,7100,7096,7092,7088,7084,7080,7076,7072,7068,7064,7060,7056,7052,7048,7044, - 7040,7036,7032,7028,7024,7020,7016,7012,7008,7004,7000,6996,6992,6988,6984,6980, - 6976,6972,6968,6964,6960,6956,6952,6948,6944,6940,6936,6932,6928,6924,6920,6916, - 6912,6908,6904,6900,6896,6892,6888,6884,6880,6876,6872,6868,6864,6860,6856,6852, - 6848,6844,6840,6836,6832,6828,6824,6820,6816,6812,6808,6804,6800,6796,6792,6788, - 6784,6780,6776,6772,6768,6764,6760,6756,6752,6748,6744,6740,6736,6732,6728,6724, - 6720,6716,6712,6708,6704,6700,6696,6692,6688,6684,6680,6676,6672,6668,6664,6660, - 6656,6652,6648,6644,6640,6636,6632,6628,6624,6620,6616,6612,6608,6604,6600,6596, - 6592,6588,6584,6580,6576,6572,6568,6564,6560,6556,6552,6548,6544,6540,6536,6532, - 6528,6524,6520,6516,6512,6508,6504,6500,6496,6492,6488,6484,6480,6476,6472,6468, - 6464,6460,6456,6452,6448,6444,6440,6436,6432,6428,6424,6420,6416,6412,6408,6404, - 6400,6396,6392,6388,6384,6380,6376,6372,6368,6364,6360,6356,6352,6348,6344,6340, - 6336,6332,6328,6324,6320,6316,6312,6308,6304,6300,6296,6292,6288,6284,6280,6276, - 6272,6268,6264,6260,6256,6252,6248,6244,6240,6236,6232,6228,6224,6220,6216,6212, - 6208,6204,6200,6196,6192,6188,6184,6180,6176,6172,6168,6164,6160,6156,6152,6148, - 6144,6140,6136,6132,6128,6124,6120,6116,6112,6108,6104,6100,6096,6092,6088,6084, - 6080,6076,6072,6068,6064,6060,6056,6052,6048,6044,6040,6036,6032,6028,6024,6020, - 6016,6012,6008,6004,6000,5996,5992,5988,5984,5980,5976,5972,5968,5964,5960,5956, - 5952,5948,5944,5940,5936,5932,5928,5924,5920,5916,5912,5908,5904,5900,5896,5892, - 5888,5884,5880,5876,5872,5868,5864,5860,5856,5852,5848,5844,5840,5836,5832,5828, - 5824,5820,5816,5812,5808,5804,5800,5796,5792,5788,5784,5780,5776,5772,5768,5764, - 5760,5756,5752,5748,5744,5740,5736,5732,5728,5724,5720,5716,5712,5708,5704,5700, - 5696,5692,5688,5684,5680,5676,5672,5668,5664,5660,5656,5652,5648,5644,5640,5636, - 5632,5628,5624,5620,5616,5612,5608,5604,5600,5596,5592,5588,5584,5580,5576,5572, - 5568,5564,5560,5556,5552,5548,5544,5540,5536,5532,5528,5524,5520,5516,5512,5508, - 5504,5500,5496,5492,5488,5484,5480,5476,5472,5468,5464,5460,5456,5452,5448,5444, - 5440,5436,5432,5428,5424,5420,5416,5412,5408,5404,5400,5396,5392,5388,5384,5380, - 5376,5372,5368,5364,5360,5356,5352,5348,5344,5340,5336,5332,5328,5324,5320,5316, - 5312,5308,5304,5300,5296,5292,5288,5284,5280,5276,5272,5268,5264,5260,5256,5252, - 5248,5244,5240,5236,5232,5228,5224,5220,5216,5212,5208,5204,5200,5196,5192,5188, - 5184,5180,5176,5172,5168,5164,5160,5156,5152,5148,5144,5140,5136,5132,5128,5124, - 5120,5116,5112,5108,5104,5100,5096,5092,5088,5084,5080,5076,5072,5068,5064,5060, - 5056,5052,5048,5044,5040,5036,5032,5028,5024,5020,5016,5012,5008,5004,5000,4996, - 4992,4988,4984,4980,4976,4972,4968,4964,4960,4956,4952,4948,4944,4940,4936,4932, - 4928,4924,4920,4916,4912,4908,4904,4900,4896,4892,4888,4884,4880,4876,4872,4868, - 4864,4860,4856,4852,4848,4844,4840,4836,4832,4828,4824,4820,4816,4812,4808,4804, - 4800,4796,4792,4788,4784,4780,4776,4772,4768,4764,4760,4756,4752,4748,4744,4740, - 4736,4732,4728,4724,4720,4716,4712,4708,4704,4700,4696,4692,4688,4684,4680,4676, - 4672,4668,4664,4660,4656,4652,4648,4644,4640,4636,4632,4628,4624,4620,4616,4612, - 4608,4604,4600,4596,4592,4588,4584,4580,4576,4572,4568,4564,4560,4556,4552,4548, - 4544,4540,4536,4532,4528,4524,4520,4516,4512,4508,4504,4500,4496,4492,4488,4484, - 4480,4476,4472,4468,4464,4460,4456,4452,4448,4444,4440,4436,4432,4428,4424,4420, - 4416,4412,4408,4404,4400,4396,4392,4388,4384,4380,4376,4372,4368,4364,4360,4356, - 4352,4348,4344,4340,4336,4332,4328,4324,4320,4316,4312,4308,4304,4300,4296,4292, - 4288,4284,4280,4276,4272,4268,4264,4260,4256,4252,4248,4244,4240,4236,4232,4228, - 4224,4220,4216,4212,4208,4204,4200,4196,4192,4188,4184,4180,4176,4172,4168,4164, - 4160,4156,4152,4148,4144,4140,4136,4132,4128,4124,4120,4116,4112,4108,4104,4100, - 4096,4092,4088,4084,4080,4076,4072,4068,4064,4060,4056,4052,4048,4044,4040,4036, - 4032,4028,4024,4020,4016,4012,4008,4004,4000,3996,3992,3988,3984,3980,3976,3972, - 3968,3964,3960,3956,3952,3948,3944,3940,3936,3932,3928,3924,3920,3916,3912,3908, - 3904,3900,3896,3892,3888,3884,3880,3876,3872,3868,3864,3860,3856,3852,3848,3844, - 3840,3836,3832,3828,3824,3820,3816,3812,3808,3804,3800,3796,3792,3788,3784,3780, - 3776,3772,3768,3764,3760,3756,3752,3748,3744,3740,3736,3732,3728,3724,3720,3716, - 3712,3708,3704,3700,3696,3692,3688,3684,3680,3676,3672,3668,3664,3660,3656,3652, - 3648,3644,3640,3636,3632,3628,3624,3620,3616,3612,3608,3604,3600,3596,3592,3588, - 3584,3580,3576,3572,3568,3564,3560,3556,3552,3548,3544,3540,3536,3532,3528,3524, - 3520,3516,3512,3508,3504,3500,3496,3492,3488,3484,3480,3476,3472,3468,3464,3460, - 3456,3452,3448,3444,3440,3436,3432,3428,3424,3420,3416,3412,3408,3404,3400,3396, - 3392,3388,3384,3380,3376,3372,3368,3364,3360,3356,3352,3348,3344,3340,3336,3332, - 3328,3324,3320,3316,3312,3308,3304,3300,3296,3292,3288,3284,3280,3276,3272,3268, - 3264,3260,3256,3252,3248,3244,3240,3236,3232,3228,3224,3220,3216,3212,3208,3204, - 3200,3196,3192,3188,3184,3180,3176,3172,3168,3164,3160,3156,3152,3148,3144,3140, - 3136,3132,3128,3124,3120,3116,3112,3108,3104,3100,3096,3092,3088,3084,3080,3076, - 3072,3068,3064,3060,3056,3052,3048,3044,3040,3036,3032,3028,3024,3020,3016,3012, - 3008,3004,3000,2996,2992,2988,2984,2980,2976,2972,2968,2964,2960,2956,2952,2948, - 2944,2940,2936,2932,2928,2924,2920,2916,2912,2908,2904,2900,2896,2892,2888,2884, - 2880,2876,2872,2868,2864,2860,2856,2852,2848,2844,2840,2836,2832,2828,2824,2820, - 2816,2812,2808,2804,2800,2796,2792,2788,2784,2780,2776,2772,2768,2764,2760,2756, - 2752,2748,2744,2740,2736,2732,2728,2724,2720,2716,2712,2708,2704,2700,2696,2692, - 2688,2684,2680,2676,2672,2668,2664,2660,2656,2652,2648,2644,2640,2636,2632,2628, - 2624,2620,2616,2612,2608,2604,2600,2596,2592,2588,2584,2580,2576,2572,2568,2564, - 2560,2556,2552,2548,2544,2540,2536,2532,2528,2524,2520,2516,2512,2508,2504,2500, - 2496,2492,2488,2484,2480,2476,2472,2468,2464,2460,2456,2452,2448,2444,2440,2436, - 2432,2428,2424,2420,2416,2412,2408,2404,2400,2396,2392,2388,2384,2380,2376,2372, - 2368,2364,2360,2356,2352,2348,2344,2340,2336,2332,2328,2324,2320,2316,2312,2308, - 2304,2300,2296,2292,2288,2284,2280,2276,2272,2268,2264,2260,2256,2252,2248,2244, - 2240,2236,2232,2228,2224,2220,2216,2212,2208,2204,2200,2196,2192,2188,2184,2180, - 2176,2172,2168,2164,2160,2156,2152,2148,2144,2140,2136,2132,2128,2124,2120,2116, - 2112,2108,2104,2100,2096,2092,2088,2084,2080,2076,2072,2068,2064,2060,2056,2052, - 2048,2044,2040,2036,2032,2028,2024,2020,2016,2012,2008,2004,2000,1996,1992,1988, - 1984,1980,1976,1972,1968,1964,1960,1956,1952,1948,1944,1940,1936,1932,1928,1924, - 1920,1916,1912,1908,1904,1900,1896,1892,1888,1884,1880,1876,1872,1868,1864,1860, - 1856,1852,1848,1844,1840,1836,1832,1828,1824,1820,1816,1812,1808,1804,1800,1796, - 1792,1788,1784,1780,1776,1772,1768,1764,1760,1756,1752,1748,1744,1740,1736,1732, - 1728,1724,1720,1716,1712,1708,1704,1700,1696,1692,1688,1684,1680,1676,1672,1668, - 1664,1660,1656,1652,1648,1644,1640,1636,1632,1628,1624,1620,1616,1612,1608,1604, - 1600,1596,1592,1588,1584,1580,1576,1572,1568,1564,1560,1556,1552,1548,1544,1540, - 1536,1532,1528,1524,1520,1516,1512,1508,1504,1500,1496,1492,1488,1484,1480,1476, - 1472,1468,1464,1460,1456,1452,1448,1444,1440,1436,1432,1428,1424,1420,1416,1412, - 1408,1404,1400,1396,1392,1388,1384,1380,1376,1372,1368,1364,1360,1356,1352,1348, - 1344,1340,1336,1332,1328,1324,1320,1316,1312,1308,1304,1300,1296,1292,1288,1284, - 1280,1276,1272,1268,1264,1260,1256,1252,1248,1244,1240,1236,1232,1228,1224,1220, - 1216,1212,1208,1204,1200,1196,1192,1188,1184,1180,1176,1172,1168,1164,1160,1156, - 1152,1148,1144,1140,1136,1132,1128,1124,1120,1116,1112,1108,1104,1100,1096,1092, - 1088,1084,1080,1076,1072,1068,1064,1060,1056,1052,1048,1044,1040,1036,1032,1028, - 1024,1020,1016,1012,1008,1004,1000, 996, 992, 988, 984, 980, 976, 972, 968, 964, - 960, 956, 952, 948, 944, 940, 936, 932, 928, 924, 920, 916, 912, 908, 904, 900, - 896, 892, 888, 884, 880, 876, 872, 868, 864, 860, 856, 852, 848, 844, 840, 836, - 832, 828, 824, 820, 816, 812, 808, 804, 800, 796, 792, 788, 784, 780, 776, 772, - 768, 764, 760, 756, 752, 748, 744, 740, 736, 732, 728, 724, 720, 716, 712, 708, - 704, 700, 696, 692, 688, 684, 680, 676, 672, 668, 664, 660, 656, 652, 648, 644, - 640, 636, 632, 628, 624, 620, 616, 612, 608, 604, 600, 596, 592, 588, 584, 580, - 576, 572, 568, 564, 560, 556, 552, 548, 544, 540, 536, 532, 528, 524, 520, 516, - 512, 508, 504, 500, 496, 492, 488, 484, 480, 476, 472, 468, 464, 460, 456, 452, - 448, 444, 440, 436, 432, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, - 384, 380, 376, 372, 368, 364, 360, 356, 352, 348, 344, 340, 336, 332, 328, 324, - 320, 316, 312, 308, 304, 300, 296, 292, 288, 284, 280, 276, 272, 268, 264, 260, - 256, 252, 248, 244, 240, 236, 232, 228, 224, 220, 216, 212, 208, 204, 200, 196, - 192, 188, 184, 180, 176, 172, 168, 164, 160, 156, 152, 148, 144, 140, 136, 132, - 128, 124, 120, 116, 112, 108, 104, 100, 96, 92, 88, 84, 80, 76, 72, 68, - 64, 60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4 -}; - -/* -** for (int32_t i = 0; i < 768; i++) -** logTab[i] = (int32_t)round(16777216.0 * exp2(i / 768.0)); -*/ -const int32_t logTab[768] = -{ - 16777216,16792365,16807527,16822704,16837894,16853097,16868315,16883546, - 16898791,16914049,16929322,16944608,16959908,16975222,16990549,17005891, - 17021246,17036615,17051999,17067396,17082806,17098231,17113670,17129123, - 17144589,17160070,17175564,17191073,17206595,17222132,17237683,17253247, - 17268826,17284419,17300026,17315646,17331282,17346931,17362594,17378271, - 17393963,17409669,17425389,17441123,17456871,17472634,17488410,17504202, - 17520007,17535826,17551660,17567508,17583371,17599248,17615139,17631044, - 17646964,17662898,17678847,17694810,17710787,17726779,17742785,17758806, - 17774841,17790891,17806955,17823034,17839127,17855235,17871357,17887494, - 17903645,17919811,17935992,17952187,17968397,17984621,18000860,18017114, - 18033382,18049665,18065963,18082276,18098603,18114945,18131302,18147673, - 18164060,18180461,18196877,18213307,18229753,18246213,18262689,18279179, - 18295684,18312204,18328739,18345288,18361853,18378433,18395028,18411637, - 18428262,18444902,18461556,18478226,18494911,18511611,18528325,18545056, - 18561801,18578561,18595336,18612127,18628932,18645753,18662589,18679441, - 18696307,18713189,18730086,18746998,18763925,18780868,18797826,18814800, - 18831788,18848792,18865812,18882846,18899897,18916962,18934043,18951139, - 18968251,18985378,19002521,19019679,19036853,19054042,19071247,19088467, - 19105703,19122954,19140221,19157504,19174802,19192116,19209445,19226790, - 19244151,19261527,19278919,19296327,19313750,19331190,19348645,19366115, - 19383602,19401104,19418622,19436156,19453706,19471271,19488853,19506450, - 19524063,19541692,19559337,19576998,19594675,19612368,19630077,19647802, - 19665543,19683300,19701072,19718861,19736666,19754488,19772325,19790178, - 19808047,19825933,19843835,19861752,19879686,19897637,19915603,19933586, - 19951585,19969600,19987631,20005679,20023743,20041823,20059920,20078033, - 20096162,20114308,20132470,20150648,20168843,20187054,20205282,20223526, - 20241787,20260064,20278358,20296668,20314995,20333338,20351698,20370074, - 20388467,20406877,20425303,20443746,20462206,20480682,20499175,20517684, - 20536211,20554754,20573313,20591890,20610483,20629093,20647720,20666364, - 20685025,20703702,20722396,20741107,20759835,20778580,20797342,20816121, - 20834917,20853729,20872559,20891406,20910270,20929150,20948048,20966963, - 20985895,21004844,21023810,21042794,21061794,21080812,21099846,21118898, - 21137968,21157054,21176158,21195278,21214417,21233572,21252745,21271935, - 21291142,21310367,21329609,21348868,21368145,21387439,21406751,21426080, - 21445426,21464790,21484172,21503571,21522987,21542421,21561873,21581342, - 21600829,21620333,21639855,21659395,21678952,21698527,21718119,21737729, - 21757357,21777003,21796666,21816348,21836046,21855763,21875498,21895250, - 21915020,21934808,21954614,21974438,21994279,22014139,22034016,22053912, - 22073825,22093757,22113706,22133674,22153659,22173663,22193684,22213724, - 22233781,22253857,22273951,22294063,22314194,22334342,22354509,22374693, - 22394897,22415118,22435357,22455615,22475891,22496186,22516499,22536830, - 22557179,22577547,22597933,22618338,22638761,22659202,22679662,22700141, - 22720638,22741153,22761687,22782240,22802811,22823400,22844009,22864635, - 22885281,22905945,22926628,22947329,22968049,22988788,23009546,23030322, - 23051117,23071931,23092764,23113615,23134485,23155374,23176282,23197209, - 23218155,23239120,23260103,23281106,23302127,23323168,23344227,23365306, - 23386403,23407520,23428656,23449810,23470984,23492177,23513389,23534620, - 23555871,23577140,23598429,23619737,23641065,23662411,23683777,23705162, - 23726566,23747990,23769433,23790896,23812377,23833879,23855399,23876939, - 23898499,23920078,23941676,23963294,23984932,24006589,24028265,24049962, - 24071677,24093413,24115168,24136942,24158736,24180550,24202384,24224237, - 24246111,24268003,24289916,24311848,24333801,24355773,24377765,24399776, - 24421808,24443859,24465931,24488022,24510133,24532265,24554416,24576587, - 24598778,24620990,24643221,24665472,24687744,24710036,24732347,24754679, - 24777031,24799403,24821796,24844209,24866641,24889095,24911568,24934062, - 24956576,24979110,25001665,25024240,25046835,25069451,25092088,25114744, - 25137421,25160119,25182837,25205576,25228335,25251115,25273915,25296736, - 25319578,25342440,25365322,25388226,25411150,25434095,25457060,25480047, - 25503054,25526081,25549130,25572199,25595290,25618401,25641533,25664686, - 25687859,25711054,25734270,25757506,25780764,25804042,25827342,25850662, - 25874004,25897367,25920751,25944156,25967582,25991029,26014497,26037987, - 26061498,26085030,26108583,26132158,26155754,26179371,26203009,26226669, - 26250350,26274053,26297777,26321522,26345289,26369077,26392887,26416718, - 26440571,26464445,26488341,26512259,26536198,26560158,26584141,26608145, - 26632170,26656218,26680287,26704377,26728490,26752624,26776780,26800958, - 26825158,26849380,26873623,26897888,26922176,26946485,26970816,26995169, - 27019544,27043941,27068360,27092802,27117265,27141750,27166258,27190787, - 27215339,27239913,27264509,27289127,27313768,27338430,27363116,27387823, - 27412552,27437304,27462079,27486875,27511695,27536536,27561400,27586286, - 27611195,27636126,27661080,27686057,27711056,27736077,27761121,27786188, - 27811277,27836389,27861524,27886681,27911861,27937064,27962290,27987538, - 28012809,28038103,28063420,28088760,28114122,28139508,28164916,28190347, - 28215802,28241279,28266779,28292302,28317849,28343418,28369011,28394626, - 28420265,28445927,28471612,28497320,28523052,28548806,28574584,28600385, - 28626210,28652058,28677929,28703823,28729741,28755683,28781647,28807636, - 28833647,28859682,28885741,28911823,28937929,28964058,28990211,29016388, - 29042588,29068811,29095059,29121330,29147625,29173944,29200286,29226652, - 29253042,29279456,29305894,29332355,29358841,29385350,29411883,29438441, - 29465022,29491627,29518256,29544910,29571587,29598288,29625014,29651764, - 29678538,29705336,29732158,29759004,29785875,29812770,29839689,29866633, - 29893600,29920593,29947609,29974650,30001716,30028805,30055920,30083059, - 30110222,30137410,30164622,30191859,30219120,30246407,30273717,30301053, - 30328413,30355798,30383207,30410642,30438101,30465584,30493093,30520627, - 30548185,30575768,30603377,30631010,30658668,30686351,30714059,30741792, - 30769550,30797333,30825141,30852975,30880833,30908717,30936625,30964559, - 30992519,31020503,31048513,31076548,31104608,31132694,31160805,31188941, - 31217103,31245290,31273503,31301741,31330005,31358294,31386609,31414949, - 31443315,31471707,31500124,31528567,31557035,31585529,31614049,31642595, - 31671166,31699764,31728387,31757036,31785710,31814411,31843138,31871890, - 31900669,31929473,31958304,31987160,32016043,32044951,32073886,32102847, - 32131834,32160847,32189887,32218952,32248044,32277162,32306307,32335478, - 32364675,32393898,32423148,32452424,32481727,32511056,32540412,32569794, - 32599202,32628638,32658099,32687588,32717103,32746645,32776213,32805808, - 32835430,32865078,32894754,32924456,32954184,32983940,33013723,33043532, - 33073369,33103232,33133122,33163040,33192984,33222955,33252954,33282979, - 33313032,33343112,33373219,33403353,33433514,33463703,33493919,33524162 -}; - -const char *MODSig[16] = -{ - "2CHN","M.K.","6CHN","8CHN","10CH","12CH","14CH","16CH", - "18CH","20CH","22CH","24CH","26CH","28CH","30CH","32CH" -}; - -const uint16_t amigaPeriod[96] = -{ - 6848,6464,6096,5760,5424,5120,4832,4560,4304,4064,3840,3624, - 3424,3232,3048,2880,2712,2560,2416,2280,2152,2032,1920,1812, - 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016, 960, 906, - 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, - 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, - 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, - 107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56, - 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28, -}; - -const uint8_t vibTab[32] = -{ - 0, 24, 49, 74, 97,120,141,161, - 180,197,212,224,235,244,250,253, - 255,253,250,244,235,224,212,197, - 180,161,141,120, 97, 74, 49, 24 -}; - -const int8_t vibSineTab[256] = -{ - 0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23, - -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, - -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, - -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, - -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, - -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, - -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, - -24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2, - 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, - 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, - 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, - 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, - 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, - 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2 -}; - -const uint8_t arpTab[256] = -{ - 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0, - - /* 8bb: the following are overflown bytes from FT2's binary, read by the buggy - ** arpeggio routine. (values confirmed to be the same on FT2.08 and FT2.09) - */ - 0x00,0x18,0x31,0x4A,0x61,0x78,0x8D,0xA1,0xB4,0xC5,0xD4,0xE0,0xEB,0xF4,0xFA,0xFD, - 0xFF,0xFD,0xFA,0xF4,0xEB,0xE0,0xD4,0xC5,0xB4,0xA1,0x8D,0x78,0x61,0x4A,0x31,0x18, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x00,0x02,0x00,0x04,0x00,0x00, - 0x00,0x05,0x06,0x00,0x00,0x07,0x00,0x01,0x00,0x02,0x00,0x03,0x04,0x05,0x00,0x00, - 0x0B,0x00,0x0A,0x02,0x01,0x03,0x04,0x07,0x00,0x05,0x06,0x00,0x00,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x79,0x02,0x00,0x00,0x8F,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x4F,0x52,0x4D,0x49,0x4C,0x42,0x4D,0x42,0x4D -}; diff --git a/libraries/m4p/ft_tables.h b/libraries/m4p/ft_tables.h deleted file mode 100644 index bca12777c..000000000 --- a/libraries/m4p/ft_tables.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -extern const uint32_t panningTab[257]; -extern const uint16_t amigaPeriods[1936]; -extern const uint16_t linearPeriods[1936]; -extern const int32_t logTab[768]; -extern const char *MODSig[16]; -extern const uint16_t amigaPeriod[96]; -extern const uint8_t vibTab[32]; -extern const int8_t vibSineTab[256]; -extern const uint8_t arpTab[256]; diff --git a/libraries/m4p/it2drivers/sb16.c b/libraries/m4p/it2drivers/sb16.c deleted file mode 100644 index f795371e9..000000000 --- a/libraries/m4p/it2drivers/sb16.c +++ /dev/null @@ -1,457 +0,0 @@ -/* -** ---- SB16 IT2 driver ---- -*/ - -#include -#include -#include -#include -#include -#include "../cpu.h" -#include "../it_structs.h" -#include "../it_music.h" // Update() -#include "sb16_m.h" -#include "zerovol.h" - -static uint16_t MixVolume; -static int32_t BytesToMix, *MixBuffer, MixTransferRemaining, MixTransferOffset; - -static void SB16_MixSamples(void) -{ - MixTransferOffset = 0; - - memset(MixBuffer, 0, BytesToMix * 2 * sizeof (int32_t)); - - slaveChn_t *sc = sChn; - for (uint32_t i = 0; i < Driver.NumChannels; i++, sc++) - { - if (!(sc->Flags & SF_CHAN_ON) || sc->Smp == 100) - continue; - - if (sc->Flags & SF_NOTE_STOP) - { - sc->Flags &= ~SF_CHAN_ON; - continue; - } - - if (sc->Flags & SF_FREQ_CHANGE) - { - if ((uint32_t)sc->Frequency>>MIX_FRAC_BITS >= Driver.MixSpeed) - { - sc->Flags = SF_NOTE_STOP; - if (!(sc->HostChnNum & CHN_DISOWNED)) - ((hostChn_t *)sc->HostChnPtr)->Flags &= ~HF_CHAN_ON; // Turn off channel - - continue; - } - - // 8bb: calculate mixer delta - uint32_t Quotient = (uint32_t)sc->Frequency / Driver.MixSpeed; - uint32_t Remainder = (uint32_t)sc->Frequency % Driver.MixSpeed; - sc->Delta32 = (Quotient << MIX_FRAC_BITS) | (uint16_t)((Remainder << MIX_FRAC_BITS) / Driver.MixSpeed); - } - - if (sc->Flags & (SF_RECALC_FINALVOL | SF_LOOP_CHANGED | SF_PAN_CHANGED)) - { - if (!(sc->Flags & SF_CHN_MUTED)) - { - if (!(Song.Header.Flags & ITF_STEREO)) // 8bb: mono? - { - sc->LeftVolume = sc->RightVolume = (sc->FinalVol15Bit * MixVolume) >> 8; // 8bb: 0..16384 - } - else if (sc->FinalPan == PAN_SURROUND) - { - sc->LeftVolume = sc->RightVolume = (sc->FinalVol15Bit * MixVolume) >> 9; // 8bb: 0..8192 - } - else // 8bb: normal (panned) - { - sc->LeftVolume = ((64-sc->FinalPan) * MixVolume * sc->FinalVol15Bit) >> 14; // 8bb: 0..16384 - sc->RightVolume = ( sc->FinalPan * MixVolume * sc->FinalVol15Bit) >> 14; - } - } - } - - if (sc->Delta32 == 0) // 8bb: added this protection just in case (shouldn't happen) - continue; - - uint32_t MixBlockSize = BytesToMix; - const uint32_t LoopLength = sc->LoopEnd - sc->LoopBegin; // 8bb: also length for non-loopers - - if ((sc->Flags & SF_CHN_MUTED) || (sc->LeftVolume == 0 && sc->RightVolume == 0)) - { - if ((int32_t)LoopLength > 0) - { - if (sc->LoopMode == LOOP_PINGPONG) - UpdatePingPongLoop(sc, MixBlockSize); - else if (sc->LoopMode == LOOP_FORWARDS) - UpdateForwardsLoop(sc, MixBlockSize); - else - UpdateNoLoop(sc, MixBlockSize); - } - - sc->Flags &= ~(SF_RECALC_PAN | SF_RECALC_VOL | SF_FREQ_CHANGE | - SF_RECALC_FINALVOL | SF_NEW_NOTE | SF_NOTE_STOP | - SF_LOOP_CHANGED | SF_PAN_CHANGED); - - continue; - } - - const bool Surround = (sc->FinalPan == PAN_SURROUND); - const bool Sample16it = !!(sc->SmpBitDepth & SMPF_16BIT); - const mixFunc Mix = SB16_MixFunctionTables[(Driver.MixMode << 2) + (Surround << 1) + Sample16it]; - int32_t *MixBufferPtr = MixBuffer; - - if ((int32_t)LoopLength > 0) - { - if (sc->LoopMode == LOOP_PINGPONG) - { - while (MixBlockSize > 0) - { - uint32_t NewLoopPos; - if (sc->LoopDirection == DIR_BACKWARDS) - { - if (sc->SamplingPosition <= sc->LoopBegin) - { - NewLoopPos = (uint32_t)(sc->LoopBegin - sc->SamplingPosition) % (LoopLength << 1); - if (NewLoopPos >= LoopLength) - { - sc->SamplingPosition = (sc->LoopEnd - 1) - (NewLoopPos - LoopLength); - } - else - { - sc->LoopDirection = DIR_FORWARDS; - sc->SamplingPosition = sc->LoopBegin + NewLoopPos; - sc->Frac32 = (uint16_t)(0 - sc->Frac32); - } - } - } - else // 8bb: forwards - { - if ((uint32_t)sc->SamplingPosition >= (uint32_t)sc->LoopEnd) - { - NewLoopPos = (uint32_t)(sc->SamplingPosition - sc->LoopEnd) % (LoopLength << 1); - if (NewLoopPos >= LoopLength) - { - sc->SamplingPosition = sc->LoopBegin + (NewLoopPos - LoopLength); - } - else - { - sc->LoopDirection = DIR_BACKWARDS; - sc->SamplingPosition = (sc->LoopEnd - 1) - NewLoopPos; - sc->Frac32 = (uint16_t)(0 - sc->Frac32); - } - } - } - - uint32_t SamplesToMix; - if (sc->LoopDirection == DIR_BACKWARDS) - { - SamplesToMix = sc->SamplingPosition - (sc->LoopBegin + 1); -#if CPU_32BIT - if (SamplesToMix > UINT16_MAX) // 8bb: limit it so we can do a hardware 32-bit div (instead of slow software 64-bit div) - SamplesToMix = UINT16_MAX; -#endif - SamplesToMix = ((((uintCPUWord_t)SamplesToMix << MIX_FRAC_BITS) | (uint16_t)sc->Frac32) / sc->Delta32) + 1; - Driver.Delta32 = 0 - sc->Delta32; - } - else // 8bb: forwards - { - SamplesToMix = (sc->LoopEnd - 1) - sc->SamplingPosition; -#if CPU_32BIT - if (SamplesToMix > UINT16_MAX) - SamplesToMix = UINT16_MAX; -#endif - SamplesToMix = ((((uintCPUWord_t)SamplesToMix << MIX_FRAC_BITS) | ((uint16_t)sc->Frac32 ^ MIX_FRAC_MASK)) / sc->Delta32) + 1; - Driver.Delta32 = sc->Delta32; - } - - if (SamplesToMix > MixBlockSize) - SamplesToMix = MixBlockSize; - - Mix(sc, MixBufferPtr, SamplesToMix); - MixBufferPtr += SamplesToMix << 1; - - MixBlockSize -= SamplesToMix; - } - } - else if (sc->LoopMode == LOOP_FORWARDS) - { - while (MixBlockSize > 0) - { - if ((uint32_t)sc->SamplingPosition >= (uint32_t)sc->LoopEnd) - sc->SamplingPosition = sc->LoopBegin + ((uint32_t)(sc->SamplingPosition - sc->LoopEnd) % LoopLength); - - uint32_t SamplesToMix = (sc->LoopEnd - 1) - sc->SamplingPosition; -#if CPU_32BIT - if (SamplesToMix > UINT16_MAX) - SamplesToMix = UINT16_MAX; -#endif - SamplesToMix = ((((uintCPUWord_t)SamplesToMix << MIX_FRAC_BITS) | ((uint16_t)sc->Frac32 ^ MIX_FRAC_MASK)) / sc->Delta32) + 1; - if (SamplesToMix > MixBlockSize) - SamplesToMix = MixBlockSize; - - Driver.Delta32 = sc->Delta32; - Mix(sc, MixBufferPtr, SamplesToMix); - MixBufferPtr += SamplesToMix << 1; - - MixBlockSize -= SamplesToMix; - } - } - else // 8bb: no loop - { - while (MixBlockSize > 0) - { - if ((uint32_t)sc->SamplingPosition >= (uint32_t)sc->LoopEnd) // 8bb: LoopEnd = sample end, even for non-loopers - { - sc->Flags = SF_NOTE_STOP; - if (!(sc->HostChnNum & CHN_DISOWNED)) - ((hostChn_t *)sc->HostChnPtr)->Flags &= ~HF_CHAN_ON; // Signify channel off - - break; - } - - uint32_t SamplesToMix = (sc->LoopEnd - 1) - sc->SamplingPosition; -#if CPU_32BIT - if (SamplesToMix > UINT16_MAX) - SamplesToMix = UINT16_MAX; -#endif - SamplesToMix = ((((uintCPUWord_t)SamplesToMix << MIX_FRAC_BITS) | ((uint16_t)sc->Frac32 ^ MIX_FRAC_MASK)) / sc->Delta32) + 1; - if (SamplesToMix > MixBlockSize) - SamplesToMix = MixBlockSize; - - Driver.Delta32 = sc->Delta32; - Mix(sc, MixBufferPtr, SamplesToMix); - MixBufferPtr += SamplesToMix << 1; - - MixBlockSize -= SamplesToMix; - } - } - } - - sc->Flags &= ~(SF_RECALC_PAN | SF_RECALC_VOL | SF_FREQ_CHANGE | - SF_RECALC_FINALVOL | SF_NEW_NOTE | SF_NOTE_STOP | - SF_LOOP_CHANGED | SF_PAN_CHANGED); - } -} - -static void SB16_SetTempo(uint8_t Tempo) -{ - assert(Tempo >= LOWEST_BPM_POSSIBLE); - BytesToMix = ((Driver.MixSpeed << 1) + (Driver.MixSpeed >> 1)) / Tempo; -} - -static void SB16_SetMixVolume(uint8_t vol) -{ - MixVolume = vol; - RecalculateAllVolumes(); -} - -static void SB16_ResetMixer(void) // 8bb: added this -{ - MixTransferRemaining = 0; - MixTransferOffset = 0; -} - -static int32_t SB16_PostMix(int16_t *AudioOut16, int32_t SamplesToOutput) // 8bb: added this -{ - const uint8_t SampleShiftValue = (Song.Header.Flags & ITF_STEREO) ? 13 : 14; - - int32_t SamplesTodo = (SamplesToOutput == 0) ? BytesToMix : SamplesToOutput; - for (int32_t i = 0; i < SamplesTodo * 2; i++) - { - int32_t Sample = MixBuffer[MixTransferOffset++] >> SampleShiftValue; - - if (Sample < INT16_MIN) - Sample = INT16_MIN; - else if (Sample > INT16_MAX) - Sample = INT16_MAX; - - *AudioOut16++ = (int16_t)Sample; - } - - return SamplesTodo; -} - -static int32_t SB16_PostMix_Float(float *AudioOut32, int32_t SamplesToOutput) -{ - const uint8_t SampleShiftValue = (Song.Header.Flags & ITF_STEREO) ? 13 : 14; - - int32_t SamplesTodo = (SamplesToOutput == 0) ? BytesToMix : SamplesToOutput; - for (int32_t i = 0; i < SamplesTodo * 2; i++) - { - int32_t Sample = MixBuffer[MixTransferOffset++] >> SampleShiftValue; - - if (Sample < INT16_MIN) - Sample = INT16_MIN; - else if (Sample > INT16_MAX) - Sample = INT16_MAX; -#if defined _MSC_VER || (defined __SIZEOF_FLOAT__ && __SIZEOF_FLOAT__ == 4) - *(uint32_t *)AudioOut32=0x43818000^((uint16_t)Sample); - *AudioOut32++ -= 259.0f; -#else - *AudioOut32++ = (float)Sample * 0.000030517578125f; -#endif - } - - return SamplesTodo; -} - -static void SB16_Mix(int32_t numSamples, int16_t *audioOut) // 8bb: added this (original SB16 driver uses IRQ callback) -{ - int32_t SamplesLeft = numSamples; - while (SamplesLeft > 0) - { - if (MixTransferRemaining == 0) - { - Update(); - SB16_MixSamples(); - MixTransferRemaining = BytesToMix; - } - - int32_t SamplesToTransfer = SamplesLeft; - if (SamplesToTransfer > MixTransferRemaining) - SamplesToTransfer = MixTransferRemaining; - - SB16_PostMix(audioOut, SamplesToTransfer); - audioOut += SamplesToTransfer * 2; - - MixTransferRemaining -= SamplesToTransfer; - SamplesLeft -= SamplesToTransfer; - } -} - -static void SB16_Mix_Float(int32_t numSamples, float *audioOut) -{ - int32_t SamplesLeft = numSamples; - while (SamplesLeft > 0) - { - if (MixTransferRemaining == 0) - { - Update(); - SB16_MixSamples(); - MixTransferRemaining = BytesToMix; - } - - int32_t SamplesToTransfer = SamplesLeft; - if (SamplesToTransfer > MixTransferRemaining) - SamplesToTransfer = MixTransferRemaining; - - SB16_PostMix_Float(audioOut, SamplesToTransfer); - audioOut += SamplesToTransfer * 2; - - MixTransferRemaining -= SamplesToTransfer; - SamplesLeft -= SamplesToTransfer; - } -} - -/* 8bb: -** Fixes sample end bytes for interpolation (yes, we have room after the data). -** Sustain loops are always handled as non-looping during fix in IT2. -*/ -static void SB16_FixSamples(void) -{ - sample_t *s = Song.Smp; - for (int32_t i = 0; i < Song.Header.SmpNum; i++, s++) - { - if (s->Data == NULL || s->Length == 0) - continue; - - int8_t *data8 = (int8_t *)s->Data; - const bool Sample16Bit = !!(s->Flags & SMPF_16BIT); - const bool HasLoop = !!(s->Flags & SMPF_USE_LOOP); - - int8_t *smp8Ptr = &data8[s->Length << Sample16Bit]; - - // 8bb: added this protection for looped samples - if (HasLoop && s->LoopEnd-s->LoopBegin < 2) - { - *smp8Ptr++ = 0; - *smp8Ptr++ = 0; - return; - } - - int8_t byte1 = 0; - int8_t byte2 = 0; - - if (HasLoop) - { - int32_t src; - if (s->Flags & SMPF_LOOP_PINGPONG) - { - src = s->LoopEnd - 2; - if (src < 0) - src = 0; - } - else // 8bb: forward loop - { - src = s->LoopBegin; - } - - if (Sample16Bit) - src <<= 1; - - byte1 = data8[src+0]; - byte2 = data8[src+1]; - } - - *smp8Ptr++ = byte1; - *smp8Ptr++ = byte2; - } -} - -static void SB16_CloseDriver(void) -{ - if (MixBuffer != NULL) - { - free(MixBuffer); - MixBuffer = NULL; - } - - DriverClose = NULL; - DriverMix = NULL; - DriverMixFloat = NULL; - DriverSetTempo = NULL; - DriverSetMixVolume = NULL; - DriverFixSamples = NULL; - DriverResetMixer = NULL; - DriverPostMix = NULL; - DriverPostMixFloat = NULL; - DriverMixSamples = NULL; -} - -bool SB16_InitDriver(int32_t mixingFrequency) -{ - if (mixingFrequency < 8000) - mixingFrequency = 8000; - else if (mixingFrequency > 64000) - mixingFrequency = 64000; - - const int32_t MaxSamplesToMix = (((mixingFrequency << 1) + (mixingFrequency >> 1)) / LOWEST_BPM_POSSIBLE) + 1; - - MixBuffer = (int32_t *)malloc(MaxSamplesToMix * 2 * sizeof (int32_t)); - if (MixBuffer == NULL) - return false; - - Driver.Flags = DF_SUPPORTS_MIDI; - Driver.NumChannels = 64; - Driver.MixSpeed = mixingFrequency; - - // 8bb: setup driver functions - DriverClose = SB16_CloseDriver; - DriverMix = SB16_Mix; // 8bb: added this (original driver uses IRQ callback) - DriverMixFloat = SB16_Mix_Float; - DriverSetTempo = SB16_SetTempo; - DriverSetMixVolume = SB16_SetMixVolume; - DriverFixSamples = SB16_FixSamples; - DriverResetMixer = SB16_ResetMixer; // 8bb: added this - DriverPostMix = SB16_PostMix; // 8bb: added this - DriverPostMixFloat = SB16_PostMix_Float; - DriverMixSamples = SB16_MixSamples; // 8bb: added this - - /* - ** MixMode 0 = "32 Bit Non-interpolated" - ** MixMode 1 = "32 Bit Interpolated" - */ - Driver.MixMode = 1; // 8bb: "32 Bit Interpolated" - return true; -} diff --git a/libraries/m4p/it2drivers/sb16.h b/libraries/m4p/it2drivers/sb16.h deleted file mode 100644 index b24953feb..000000000 --- a/libraries/m4p/it2drivers/sb16.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include - -bool SB16_InitDriver(int32_t mixingFrequency); diff --git a/libraries/m4p/it2drivers/sb16_m.c b/libraries/m4p/it2drivers/sb16_m.c deleted file mode 100644 index 4f5441abe..000000000 --- a/libraries/m4p/it2drivers/sb16_m.c +++ /dev/null @@ -1,281 +0,0 @@ -#include -#include -#include "../it_structs.h" -#include "../it_music.h" -#include "sb16_m.h" - -static void M32Mix8(slaveChn_t *sc, int32_t *mixBufPtr, int32_t numSamples); -static void M32Mix16(slaveChn_t *sc, int32_t *mixBufPtr, int32_t numSamples); -static void M32Mix8S(slaveChn_t *sc, int32_t *mixBufPtr, int32_t numSamples); -static void M32Mix16S(slaveChn_t *sc, int32_t *mixBufPtr, int32_t numSamples); -static void M32Mix8I(slaveChn_t *sc, int32_t *mixBufPtr, int32_t numSamples); -static void M32Mix16I(slaveChn_t *sc, int32_t *mixBufPtr, int32_t numSamples); -static void M32Mix8IS(slaveChn_t *sc, int32_t *mixBufPtr, int32_t numSamples); -static void M32Mix16IS(slaveChn_t *sc, int32_t *mixBufPtr, int32_t numSamples); - -#define Get32Bit8Waveform \ - sample = smp[0]; \ - sample2 = smp[1]; \ - sample2 -= sample; \ - sample2 *= (int32_t)sc->Frac32; \ - sample2 >>= MIX_FRAC_BITS-8; \ - sample <<= 8; \ - sample += sample2; - -#define Get32Bit16Waveform \ - sample = smp[0]; \ - sample2 = smp[1]; \ - sample2 -= sample; \ - sample2 >>= 1; \ - sample2 *= (int32_t)sc->Frac32; \ - sample2 >>= MIX_FRAC_BITS-1; \ - sample += sample2; - -#define UpdatePos \ - sc->Frac32 += Driver.Delta32; \ - smp += (int32_t)sc->Frac32 >> MIX_FRAC_BITS; \ - sc->Frac32 &= MIX_FRAC_MASK; - -#define M32Mix8_M \ - sample = *smp << 8; \ - (*MixBufPtr++) -= sample * sc->LeftVolume; \ - (*MixBufPtr++) -= sample * sc->RightVolume; \ - UpdatePos - -#define M32Mix16_M \ - sample = *smp; \ - (*MixBufPtr++) -= sample * sc->LeftVolume; \ - (*MixBufPtr++) -= sample * sc->RightVolume; \ - UpdatePos - -#define M32Mix8S_M \ - sample = *smp << 8; \ - (*MixBufPtr++) -= sample * sc->LeftVolume; \ - (*MixBufPtr++) += sample * sc->RightVolume; \ - UpdatePos - -#define M32Mix16S_M \ - sample = *smp; \ - (*MixBufPtr++) -= sample * sc->LeftVolume; \ - (*MixBufPtr++) += sample * sc->RightVolume; \ - UpdatePos - -#define M32Mix8I_M \ - Get32Bit8Waveform \ - (*MixBufPtr++) -= sample * sc->LeftVolume; \ - (*MixBufPtr++) -= sample * sc->RightVolume; \ - UpdatePos - -#define M32Mix16I_M \ - Get32Bit16Waveform \ - (*MixBufPtr++) -= sample * sc->LeftVolume; \ - (*MixBufPtr++) -= sample * sc->RightVolume; \ - UpdatePos - -#define M32Mix8IS_M \ - Get32Bit8Waveform \ - (*MixBufPtr++) -= sample * sc->LeftVolume; \ - (*MixBufPtr++) += sample * sc->RightVolume; \ - UpdatePos - -#define M32Mix16IS_M \ - Get32Bit16Waveform \ - (*MixBufPtr++) -= sample * sc->LeftVolume; \ - (*MixBufPtr++) += sample * sc->RightVolume; \ - UpdatePos - -const mixFunc SB16_MixFunctionTables[8] = -{ - (mixFunc)M32Mix8, - (mixFunc)M32Mix16, - (mixFunc)M32Mix8S, - (mixFunc)M32Mix16S, - (mixFunc)M32Mix8I, - (mixFunc)M32Mix16I, - (mixFunc)M32Mix8IS, - (mixFunc)M32Mix16IS -}; - -void M32Mix8(slaveChn_t *sc, int32_t *MixBufPtr, int32_t NumSamples) -{ - const int8_t *base = (int8_t *)sc->SmpPtr->Data; - const int8_t *smp = base + sc->SamplingPosition; - int32_t sample; - - for (int32_t i = 0; i < (NumSamples & 3); i++) - { - M32Mix8_M - } - NumSamples >>= 2; - - for (int32_t i = 0; i < NumSamples; i++) - { - M32Mix8_M - M32Mix8_M - M32Mix8_M - M32Mix8_M - } - - sc->SamplingPosition = (int32_t)(smp - base); -} - -void M32Mix16(slaveChn_t *sc, int32_t *MixBufPtr, int32_t NumSamples) -{ - const int16_t *base = (int16_t *)sc->SmpPtr->Data; - const int16_t *smp = base + sc->SamplingPosition; - int32_t sample; - - for (int32_t i = 0; i < (NumSamples & 3); i++) - { - M32Mix16_M - } - NumSamples >>= 2; - - for (int32_t i = 0; i < NumSamples; i++) - { - M32Mix16_M - M32Mix16_M - M32Mix16_M - M32Mix16_M - } - - sc->SamplingPosition = (int32_t)(smp - base); -} - -void M32Mix8S(slaveChn_t *sc, int32_t *MixBufPtr, int32_t NumSamples) -{ - const int8_t *base = (int8_t *)sc->SmpPtr->Data; - const int8_t *smp = base + sc->SamplingPosition; - int32_t sample; - - for (int32_t i = 0; i < (NumSamples & 3); i++) - { - M32Mix8S_M - } - NumSamples >>= 2; - - for (int32_t i = 0; i < NumSamples; i++) - { - M32Mix8S_M - M32Mix8S_M - M32Mix8S_M - M32Mix8S_M - } - - sc->SamplingPosition = (int32_t)(smp - base); -} - -void M32Mix16S(slaveChn_t *sc, int32_t *MixBufPtr, int32_t NumSamples) -{ - const int16_t *base = (int16_t *)sc->SmpPtr->Data; - const int16_t *smp = base + sc->SamplingPosition; - int32_t sample; - - for (int32_t i = 0; i < (NumSamples & 3); i++) - { - M32Mix16S_M - } - NumSamples >>= 2; - - for (int32_t i = 0; i < NumSamples; i++) - { - M32Mix16S_M - M32Mix16S_M - M32Mix16S_M - M32Mix16S_M - } - - sc->SamplingPosition = (int32_t)(smp - base); -} - -void M32Mix8I(slaveChn_t *sc, int32_t *MixBufPtr, int32_t NumSamples) -{ - const int8_t *base = (int8_t *)sc->SmpPtr->Data; - const int8_t *smp = base + sc->SamplingPosition; - int32_t sample, sample2; - - for (int32_t i = 0; i < (NumSamples & 3); i++) - { - M32Mix8I_M - } - NumSamples >>= 2; - - for (int32_t i = 0; i < NumSamples; i++) - { - M32Mix8I_M - M32Mix8I_M - M32Mix8I_M - M32Mix8I_M - } - - sc->SamplingPosition = (int32_t)(smp - base); -} - -void M32Mix16I(slaveChn_t *sc, int32_t *MixBufPtr, int32_t NumSamples) -{ - const int16_t *base = (int16_t *)sc->SmpPtr->Data; - const int16_t *smp = base + sc->SamplingPosition; - int32_t sample, sample2; - - for (int32_t i = 0; i < (NumSamples & 3); i++) - { - M32Mix16I_M - } - NumSamples >>= 2; - - for (int32_t i = 0; i < NumSamples; i++) - { - M32Mix16I_M - M32Mix16I_M - M32Mix16I_M - M32Mix16I_M - } - - sc->SamplingPosition = (int32_t)(smp - base); -} - -void M32Mix8IS(slaveChn_t *sc, int32_t *MixBufPtr, int32_t NumSamples) -{ - const int8_t *base = (int8_t *)sc->SmpPtr->Data; - const int8_t *smp = base + sc->SamplingPosition; - int32_t sample, sample2; - - for (int32_t i = 0; i < (NumSamples & 3); i++) - { - M32Mix8IS_M - } - NumSamples >>= 2; - - for (int32_t i = 0; i < NumSamples; i++) - { - M32Mix8IS_M - M32Mix8IS_M - M32Mix8IS_M - M32Mix8IS_M - } - - sc->SamplingPosition = (int32_t)(smp - base); -} - -void M32Mix16IS(slaveChn_t *sc, int32_t *MixBufPtr, int32_t NumSamples) -{ - const int16_t *base = (int16_t *)sc->SmpPtr->Data; - const int16_t *smp = base + sc->SamplingPosition; - int32_t sample, sample2; - - for (int32_t i = 0; i < (NumSamples & 3); i++) - { - M32Mix16IS_M - } - NumSamples >>= 2; - - for (int32_t i = 0; i < NumSamples; i++) - { - M32Mix16IS_M - M32Mix16IS_M - M32Mix16IS_M - M32Mix16IS_M - } - - sc->SamplingPosition = (int32_t)(smp - base); -} diff --git a/libraries/m4p/it2drivers/sb16_m.h b/libraries/m4p/it2drivers/sb16_m.h deleted file mode 100644 index ad6814523..000000000 --- a/libraries/m4p/it2drivers/sb16_m.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include -#include "../it_structs.h" - -typedef void (*mixFunc)(slaveChn_t *sc, int32_t *mixBufPtr, int32_t numSamples); - -extern const mixFunc SB16_MixFunctionTables[8]; diff --git a/libraries/m4p/it2drivers/zerovol.c b/libraries/m4p/it2drivers/zerovol.c deleted file mode 100644 index c64825ca9..000000000 --- a/libraries/m4p/it2drivers/zerovol.c +++ /dev/null @@ -1,107 +0,0 @@ -/* -** 8bb: -** ---- Code for "zero-vol" update routines ---- -** -** These are used when the final volume is zero, and they'll only update -** the sampling position instead of doing actual mixing. They are the same -** for SB16/"SB16 MMX"/"WAV writer". -*/ - -#include -#include "../it_structs.h" -#include "../it_music.h" - -void UpdateNoLoop(slaveChn_t *sc, uint32_t numSamples) -{ - const uint64_t SamplesToMix = (uint64_t)sc->Delta32 * (uint32_t)numSamples; - - uint32_t SampleOffset = sc->SamplingPosition + (uint32_t)(SamplesToMix >> MIX_FRAC_BITS); - sc->Frac32 += SamplesToMix & MIX_FRAC_MASK; - SampleOffset += (uint32_t)sc->Frac32 >> MIX_FRAC_BITS; - sc->Frac32 &= MIX_FRAC_MASK; - - if (SampleOffset >= (uint32_t)sc->LoopEnd) - { - sc->Flags = SF_NOTE_STOP; - if (!(sc->HostChnNum & CHN_DISOWNED)) - { - ((hostChn_t *)sc->HostChnPtr)->Flags &= ~HF_CHAN_ON; // Signify channel off - return; - } - } - - sc->SamplingPosition = SampleOffset; -} - -void UpdateForwardsLoop(slaveChn_t *sc, uint32_t numSamples) -{ - const uint64_t SamplesToMix = (uint64_t)sc->Delta32 * (uint32_t)numSamples; - - sc->Frac32 += SamplesToMix & MIX_FRAC_MASK; - sc->SamplingPosition += sc->Frac32 >> MIX_FRAC_BITS; - sc->SamplingPosition += (uint32_t)(SamplesToMix >> MIX_FRAC_BITS); - sc->Frac32 &= MIX_FRAC_MASK; - - if ((uint32_t)sc->SamplingPosition >= (uint32_t)sc->LoopEnd) // Reset position... - { - const uint32_t LoopLength = sc->LoopEnd - sc->LoopBegin; - if (LoopLength == 0) - sc->SamplingPosition = 0; - else - sc->SamplingPosition = sc->LoopBegin + ((sc->SamplingPosition - sc->LoopEnd) % LoopLength); - } -} - -void UpdatePingPongLoop(slaveChn_t *sc, uint32_t numSamples) -{ - const uint32_t LoopLength = sc->LoopEnd - sc->LoopBegin; - - const uint64_t SamplesToMix = (uint64_t)sc->Delta32 * (uint32_t)numSamples; - uint32_t IntSamples = (uint32_t)(SamplesToMix >> MIX_FRAC_BITS); - uint16_t FracSamples = (uint16_t)(SamplesToMix & MIX_FRAC_MASK); - - if (sc->LoopDirection == DIR_BACKWARDS) - { - sc->Frac32 -= FracSamples; - sc->SamplingPosition += ((int32_t)sc->Frac32 >> MIX_FRAC_BITS); - sc->SamplingPosition -= IntSamples; - sc->Frac32 &= MIX_FRAC_MASK; - - if (sc->SamplingPosition <= sc->LoopBegin) - { - uint32_t NewLoopPos = (uint32_t)(sc->LoopBegin - sc->SamplingPosition) % (LoopLength << 1); - if (NewLoopPos >= LoopLength) - { - sc->SamplingPosition = (sc->LoopEnd - 1) + (LoopLength - NewLoopPos); - } - else - { - sc->LoopDirection = DIR_FORWARDS; - sc->SamplingPosition = sc->LoopBegin + NewLoopPos; - sc->Frac32 = (uint16_t)(0 - sc->Frac32); - } - } - } - else // 8bb: forwards - { - sc->Frac32 += FracSamples; - sc->SamplingPosition += sc->Frac32 >> MIX_FRAC_BITS; - sc->SamplingPosition += IntSamples; - sc->Frac32 &= MIX_FRAC_MASK; - - if ((uint32_t)sc->SamplingPosition >= (uint32_t)sc->LoopEnd) - { - uint32_t NewLoopPos = (uint32_t)(sc->SamplingPosition - sc->LoopEnd) % (LoopLength << 1); - if (NewLoopPos >= LoopLength) - { - sc->SamplingPosition = sc->LoopBegin + (NewLoopPos - LoopLength); - } - else - { - sc->LoopDirection = DIR_BACKWARDS; - sc->SamplingPosition = (sc->LoopEnd - 1) - NewLoopPos; - sc->Frac32 = (uint16_t)(0 - sc->Frac32); - } - } - } -} diff --git a/libraries/m4p/it2drivers/zerovol.h b/libraries/m4p/it2drivers/zerovol.h deleted file mode 100644 index 24b7018a6..000000000 --- a/libraries/m4p/it2drivers/zerovol.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include -#include "../it_structs.h" - -void UpdateNoLoop(slaveChn_t *sc, uint32_t numSamples); -void UpdateForwardsLoop(slaveChn_t *sc, uint32_t numSamples); -void UpdatePingPongLoop(slaveChn_t *sc, uint32_t numSamples); diff --git a/libraries/m4p/it_d_rm.c b/libraries/m4p/it_d_rm.c deleted file mode 100644 index b3fa4fa54..000000000 --- a/libraries/m4p/it_d_rm.c +++ /dev/null @@ -1,217 +0,0 @@ -/* -** 8bb: IT2 module loading routines -** -** NOTE: This file is not directly ported from the IT2 code, -** so routines have non-original names. All comments in -** this file are by me (8bitbubsy). -*/ - -#include -#include -#include -#include -#include -#include "loaders/mmcmp/mmcmp.h" -#include "it_structs.h" -#include "it_music.h" -#include "it_d_rm.h" -#include "loaders/it.h" -#include "loaders/s3m.h" - -static bool FirstTimeLoading = true; - -static int8_t GetModuleType(MEMFILE *m) // 8bb: added this -{ - static uint8_t Header[44+4]; - - size_t OldOffset = mtell(m); - - mseek(m, 0, SEEK_END); - size_t DataLen = mtell(m); - mseek(m, 0, SEEK_SET); - - mread(Header, 1, sizeof (Header), m); - - int8_t Format = FORMAT_UNKNOWN; - if (DataLen >= 4 && !memcmp(&Header[0], "IMPM", 4)) - Format = FORMAT_IT; - else if (DataLen >= 44+4 && !memcmp(&Header[44], "SCRM", 4)) - Format = FORMAT_S3M; - - mseek(m, OldOffset, SEEK_SET); - return Format; -} - -bool Music_LoadFromData(uint8_t *Data, uint32_t DataLen) -{ - bool WasCompressed = false; - if (DataLen >= 4+4) // find out if module is MMCMP compressed - { - uint32_t Sig1 = *(uint32_t *)&Data[0]; - uint32_t Sig2 = *(uint32_t *)&Data[4]; - if (Sig1 == 0x4352697A && Sig2 == 0x61694E4F) // Sig1 = "ziRCONia" - { - if (unpackMMCMP(&Data, &DataLen)) - WasCompressed = true; - else - return false; - } - } - - MEMFILE *m = mopen(Data, DataLen); - if (m == NULL) - return false; - - if (FirstTimeLoading) - { - memset(&Song, 0, sizeof (Song)); - FirstTimeLoading = false; - } - else - { - Music_FreeSong(); - } - - bool WasLoaded = false; - - uint8_t Format = GetModuleType(m); - if (Format != FORMAT_UNKNOWN) - { - Music_SetDefaultMIDIDataArea(); - switch (Format) - { - default: break; - case FORMAT_IT: WasLoaded = LoadIT(m); break; - case FORMAT_S3M: WasLoaded = LoadS3M(m); break; - } - } - - mclose(&m); - if (WasCompressed) - free(Data); - - if (WasLoaded) - { - DriverSetMixVolume(Song.Header.MixVolume); - DriverFixSamples(); - - Song.Loaded = true; - return true; - } - else - { - Music_FreeSong(); - - Song.Loaded = false; - return false; - } -} - -// routines for handling data in RAM as a "FILE" type - -MEMFILE *mopen(const uint8_t *src, uint32_t length) -{ - MEMFILE *b; - - if (src == NULL || length == 0) - return NULL; - - b = (MEMFILE *)malloc(sizeof (MEMFILE)); - if (b == NULL) - return NULL; - - b->_base = (uint8_t *)src; - b->_ptr = (uint8_t *)src; - b->_cnt = length; - b->_bufsiz = length; - b->_eof = false; - - return b; -} - -void mclose(MEMFILE **buf) -{ - if (*buf != NULL) - { - free(*buf); - *buf = NULL; - } -} - -size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf) -{ - int32_t pcnt; - size_t wrcnt; - - if (buf == NULL || buf->_ptr == NULL) - return 0; - - wrcnt = size * count; - if (size == 0 || buf->_eof) - return 0; - - pcnt = (buf->_cnt > (uint32_t)wrcnt) ? (uint32_t)wrcnt : buf->_cnt; - memcpy(buffer, buf->_ptr, pcnt); - - buf->_cnt -= pcnt; - buf->_ptr += pcnt; - - if (buf->_cnt <= 0) - { - buf->_ptr = buf->_base + buf->_bufsiz; - buf->_cnt = 0; - buf->_eof = true; - } - - return pcnt / size; -} - -size_t mtell(MEMFILE *buf) -{ - return (buf->_ptr - buf->_base); -} - -int32_t meof(MEMFILE *buf) -{ - if (buf == NULL) - return true; - - return buf->_eof; -} - -void mseek(MEMFILE *buf, size_t offset, int32_t whence) -{ - if (buf == NULL) - return; - - if (buf->_base) - { - switch (whence) - { - case SEEK_SET: buf->_ptr = buf->_base + offset; break; - case SEEK_CUR: buf->_ptr += offset; break; - case SEEK_END: buf->_ptr = buf->_base + buf->_bufsiz + offset; break; - default: break; - } - - buf->_eof = false; - if (buf->_ptr >= buf->_base+buf->_bufsiz) - { - buf->_ptr = buf->_base + buf->_bufsiz; - buf->_eof = true; - } - - buf->_cnt = (uint32_t)((buf->_base + buf->_bufsiz) - buf->_ptr); - } -} - -bool ReadBytes(MEMFILE *m, void *dst, uint32_t num) -{ - if ((m == NULL) || meof(m)) - return false; - - if (mread(dst, 1, num, m) != num) - return false; - - return true; -} diff --git a/libraries/m4p/it_d_rm.h b/libraries/m4p/it_d_rm.h deleted file mode 100644 index 47be52fe9..000000000 --- a/libraries/m4p/it_d_rm.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include -#include -#include "it_structs.h" - -enum -{ - FORMAT_UNKNOWN = 0, - FORMAT_IT = 1, - FORMAT_S3M = 2 -}; - -// routines for handling data in RAM as a "FILE" type (IT2 doesn't have these) -typedef struct mem_t -{ - bool _eof; - uint8_t *_ptr, *_base; - uint32_t _cnt, _bufsiz; -} MEMFILE; - -MEMFILE *mopen(const uint8_t *src, uint32_t length); -void mclose(MEMFILE **buf); -size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf); -size_t mtell(MEMFILE *buf); -int32_t meof(MEMFILE *buf); -void mseek(MEMFILE *buf, size_t offset, int32_t whence); -bool ReadBytes(MEMFILE *m, void *dst, uint32_t num); -// ------------------------------------------------------- - -bool Music_LoadFromData(uint8_t *Data, uint32_t DataLen); -void Music_FreeSong(void); \ No newline at end of file diff --git a/libraries/m4p/it_m_eff.c b/libraries/m4p/it_m_eff.c deleted file mode 100644 index fce949804..000000000 --- a/libraries/m4p/it_m_eff.c +++ /dev/null @@ -1,1936 +0,0 @@ -// 8bb: IT2 replayer command routines - -#include -#include -#include -#include "it_structs.h" -#include "it_tables.h" -#include "it_music.h" -#include "it_m_eff.h" - -static const uint8_t SlideTable[9] = { 1, 4, 8, 16, 32, 64, 96, 128, 255 }; - -static void InitCommandG11(hostChn_t *hc); -static void InitCommandM2(hostChn_t *hc, uint8_t vol); -static void InitCommandX2(hostChn_t *hc, uint8_t pan); // 8bb: pan = 0..63 -static void CommandH5(hostChn_t *hc, slaveChn_t *sc, int8_t VibratoData); -static void CommandR2(hostChn_t *hc, slaveChn_t *sc, int8_t TremoloData); - -void NoCommand(hostChn_t *hc) -{ - (void)hc; - return; -} - -static void CommandEChain(hostChn_t *hc, uint16_t SlideValue) -{ - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - PitchSlideDown(hc, sc, SlideValue); - sc->FrequencySet = sc->Frequency; -} - -static void CommandFChain(hostChn_t *hc, uint16_t SlideValue) -{ - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - PitchSlideUp(hc, sc, SlideValue); - sc->FrequencySet = sc->Frequency; -} - -static void CommandD2(hostChn_t *hc, slaveChn_t *sc, uint8_t vol) -{ - sc->Vol = sc->VolSet = hc->VolSet = vol; - sc->Flags |= SF_RECALC_VOL; -} - -static void InitVibrato(hostChn_t *hc) -{ - if (Song.Header.Flags & ITF_OLD_EFFECTS) - { - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - - sc->Flags |= SF_FREQ_CHANGE; - CommandH5(hc, sc, hc->LastVibratoData); - } - else - { - CommandH(hc); - } -} - -static void InitCommandD7(hostChn_t *hc, slaveChn_t *sc) // Jmp point for Lxx (8bb: and Dxx/Kxx) -{ - sc->Flags |= SF_RECALC_VOL; - - uint8_t hi = hc->DKL & 0xF0; - uint8_t lo = hc->DKL & 0x0F; - - if (lo == 0) - { - // Slide up. - hc->VolSlideDelta = hi >> 4; - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; - - if (hc->VolSlideDelta == 0x0F) - CommandD(hc); - } - else if (hi == 0) - { - // Slide down - - hc->VolSlideDelta = -lo; - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; - - if (hc->VolSlideDelta == -15) - CommandD(hc); - } - else if (lo == 0x0F) - { - // Slide up (fine) - hc->VolSlideDelta = 0; - - uint8_t vol = sc->VolSet + (hi >> 4); - if (vol > 64) - vol = 64; - - sc->Vol = sc->VolSet = hc->VolSet = vol; - } - else if (hi == 0xF0) - { - // Slide down (fine) - hc->VolSlideDelta = 0; - - uint8_t vol = sc->VolSet - lo; - if ((int8_t)vol < 0) - vol = 0; - - sc->Vol = sc->VolSet = hc->VolSet = vol; - } -} - -static void InitVolumeEffect(hostChn_t *hc) -{ - if (!(hc->NotePackMask & 0x44)) - return; - - int8_t volCmd = (hc->Vol & 0x7F) - 65; - if (volCmd < 0) - return; - - if (hc->Vol & 0x80) - volCmd += 60; - - uint8_t cmd = (uint8_t)volCmd / 10; - uint8_t val = (uint8_t)volCmd % 10; - - hc->VolCmd = cmd; // Store effect number - - /* Memory for effects A->D, (EFG)/H don't share. - ** - ** Effects Ax and Bx (fine volume slide up and down) require immediate - ** handling. No flags required. (effect 0 and 1) - ** - ** Effects Cx, Dx, Ex, Fx (volume/pitch slides) require flag to be - ** set (effects 2->5) - ** - ** Effects Gx and Hx need init (handling) code + flags. - ** (effects 6 and 7). - */ - - if (val > 0) - { - if (cmd < 4) - { - hc->VolCmdVal = val; - } - else if (cmd < 6) - { - hc->EFG = val << 2; - } - else if (cmd == 6) - { - if (Song.Header.Flags & ITF_COMPAT_GXX) - hc->GOE = SlideTable[val-1]; - else - hc->EFG = SlideTable[val-1]; - } - } - - if (hc->Flags & HF_CHAN_ON) - { - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - - if (cmd > 1) - { - hc->Flags |= HF_UPDATE_VOLEFX_IF_CHAN_ON; - - if (cmd > 6) - { - if (val != 0) - hc->VibratoDepth = val << 2; - - if (hc->Flags & HF_CHAN_ON) - InitVibrato(hc); - } - else if (cmd == 6) - { - InitCommandG11(hc); - } - } - else if (cmd == 1) - { - // Fine volume slide down - - int8_t vol = sc->VolSet - hc->VolCmdVal; - if (vol < 0) - vol = 0; - - CommandD2(hc, sc, vol); - } - else - { - // Fine volume slide up - - int8_t vol = sc->VolSet + hc->VolCmdVal; - if (vol > 64) - vol = 64; - - CommandD2(hc, sc, vol); - } - } - else - { - // Channel not on! - - if (cmd == 7) // Vibrato? - { - if (val != 0) - hc->VibratoDepth = val << 2; - - if (hc->Flags & HF_CHAN_ON) - InitVibrato(hc); - } - } -} - -void VolumeCommandC(hostChn_t *hc) -{ - slaveChn_t *sc = hc->SlaveChnPtr; - - int8_t vol = sc->VolSet + hc->VolCmdVal; - if (vol > 64) - { - hc->Flags &= ~HF_UPDATE_VOLEFX_IF_CHAN_ON; // Turn off effect calling - vol = 64; - } - - CommandD2(hc, sc, vol); -} - -void VolumeCommandD(hostChn_t *hc) -{ - slaveChn_t *sc = hc->SlaveChnPtr; - - int8_t vol = sc->VolSet - hc->VolCmdVal; - if (vol < 0) - { - hc->Flags &= ~HF_UPDATE_VOLEFX_IF_CHAN_ON; // Turn off effect calling - vol = 0; - } - - CommandD2(hc, sc, vol); -} - -void VolumeCommandE(hostChn_t *hc) -{ - CommandEChain(hc, hc->EFG << 2); -} - -void VolumeCommandF(hostChn_t *hc) -{ - CommandFChain(hc, hc->EFG << 2); -} - -void VolumeCommandG(hostChn_t *hc) -{ - if (!(hc->Flags & HF_PITCH_SLIDE_ONGOING)) - return; - - int16_t SlideValue = hc->EFG << 2; - if (Song.Header.Flags & ITF_COMPAT_GXX) - SlideValue = hc->GOE << 2; - - if (SlideValue == 0) - return; - - slaveChn_t *sc = hc->SlaveChnPtr; - - if (hc->MiscEfxData[2] == 1) // 8bb: slide up? - { - PitchSlideUp(hc, sc, SlideValue); - sc->FrequencySet = sc->Frequency; - - if ((sc->Flags & SF_NOTE_STOP) || sc->Frequency >= hc->PortaFreq) - { - sc->Flags &= ~SF_NOTE_STOP; - hc->Flags |= HF_CHAN_ON; // Turn on - - sc->FrequencySet = sc->Frequency = hc->PortaFreq; - hc->Flags &= ~(HF_PITCH_SLIDE_ONGOING | HF_UPDATE_VOLEFX_IF_CHAN_ON); // Turn off calling - } - } - else // 8bb: slide down - { - PitchSlideDown(hc, sc, SlideValue); - - if (sc->Frequency <= hc->PortaFreq) - { - sc->Frequency = hc->PortaFreq; - hc->Flags &= ~(HF_PITCH_SLIDE_ONGOING | HF_UPDATE_VOLEFX_IF_CHAN_ON); // Turn off calling - } - - sc->FrequencySet = sc->Frequency; - } -} - -static void InitNoCommand3(hostChn_t *hc, uint8_t hcFlags) -{ - // Randomise volume if required. - - bool ApplyRandomVolume = !!(hc->Flags & HF_APPLY_RANDOM_VOL); - - hc->Flags = (hc->Flags & 0xFF00) | hcFlags; - - if (ApplyRandomVolume) - ApplyRandomValues(hc); - - InitVolumeEffect(hc); -} - -static void NoOldEffect(hostChn_t *hc, uint8_t hcFlags) -{ - uint8_t vol = hc->Vol; - if (!((hc->NotePackMask & 0x44) && vol <= 64)) // 8bb: improve this yucky logic... - { - if ((hc->NotePackMask & 0x44) && (vol & 0x7F) < 65) - { - // Panning set! - hc->Flags = (hc->Flags & 0xFF00) | hcFlags; - InitCommandX2(hc, vol - 128); - } - - if (!(hc->NotePackMask & 0x22) || hc->Smp == 0) // Instrument present? - { - InitNoCommand3(hc, hcFlags); - return; - } - - vol = Song.Smp[hc->Smp-1].Vol; // Default volume - } - - hc->VolSet = vol; - - if (hcFlags & HF_CHAN_ON) - { - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - sc->Vol = sc->VolSet = vol; - sc->Flags |= SF_RECALC_VOL; - } - - InitNoCommand3(hc, hcFlags); -} - -static void InitNoCommand11(hostChn_t *hc, slaveChn_t *sc, uint8_t hcFlags) -{ - GetLoopInformation(sc); - - if (!(hc->NotePackMask & (0x22+0x44))) - { - InitNoCommand3(hc, hcFlags); - return; - } - - if ((Song.Header.Flags & (ITF_INSTR_MODE | ITF_OLD_EFFECTS)) == ITF_INSTR_MODE+ITF_OLD_EFFECTS) - { - if ((hc->NotePackMask & 0x22) && hc->Ins != 255) - { - sc->FadeOut = 1024; - InitPlayInstrument(hc, sc, &Song.Ins[hc->Ins-1]); - } - } - - NoOldEffect(hc, hcFlags); -} - -void InitNoCommand(hostChn_t *hc) -{ - uint8_t hcFlags = hc->Flags & 0xFF; - - if (!(hc->NotePackMask & 0x33)) - { - NoOldEffect(hc, hcFlags); - return; - } - - // Note here! Check for noteoff. - if (hc->TranslatedNote >= 120) - { - if (hcFlags & HF_CHAN_ON) - { - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - - if (hc->TranslatedNote == 255) // 8bb: note off - { - sc->Flags |= SF_NOTE_OFF; - InitNoCommand11(hc, sc, hcFlags); - return; - } - else if (hc->TranslatedNote == 254) // 8bb: note cut - { - hcFlags &= ~HF_CHAN_ON; - - if (sc->Smp == 100 || (Driver.Flags & DF_USES_VOLRAMP)) - sc->Flags |= SF_NOTE_STOP; - else - sc->Flags = SF_NOTE_STOP; - } - else // 8bb: note fade (?) - { - sc->Flags |= SF_FADEOUT; - } - } - - NoOldEffect(hc, hcFlags); - return; - } - - if (hcFlags & HF_CHAN_ON) - { - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - if (!(hc->NotePackMask & 0x11) && sc->Note == hc->RawNote && sc->Ins == hc->Ins) - { - NoOldEffect(hc, hcFlags); - return; - } - } - - if ((hc->NotePackMask & 0x44) && hc->Vol >= 193 && hc->Vol <= 202 && (hc->Flags & HF_CHAN_ON)) - { - InitVolumeEffect(hc); - return; - } - - slaveChn_t *sc = AllocateChannel(hc, &hcFlags); - if (sc == NULL) - { - NoOldEffect(hc, hcFlags); - return; - } - - // Channel allocated. - - sample_t *s = sc->SmpPtr; - - sc->Vol = sc->VolSet = hc->VolSet; - - if (!(Song.Header.Flags & ITF_INSTR_MODE)) - { - if (s->DefPan & 0x80) - hc->ChnPan = sc->Pan = s->DefPan & 127; - } - - sc->SamplingPosition = 0; - sc->Frac32 = 0; // 8bb: clear fractional sampling position - sc->Frac64 = 0; // 8bb: also clear frac for my high-quality driver/mixer - sc->Frequency = sc->FrequencySet = ((uint64_t)s->C5Speed * (uint32_t)PitchTable[hc->TranslatedNote]) >> 16; - - hcFlags |= HF_CHAN_ON; - hcFlags &= ~HF_PITCH_SLIDE_ONGOING; - - InitNoCommand11(hc, sc, hcFlags); -} - -void InitCommandA(hostChn_t *hc) -{ - if (hc->CmdVal != 0) - { - Song.CurrentTick = (Song.CurrentTick - Song.CurrentSpeed) + hc->CmdVal; - Song.CurrentSpeed = hc->CmdVal; - } - - InitNoCommand(hc); -} - -void InitCommandB(hostChn_t *hc) -{ - /* - if (hc->CmdVal <= Song.CurrentOrder) - Song.StopSong = true; // 8bb: for WAV writer - */ - - Song.ProcessOrder = hc->CmdVal - 1; - Song.ProcessRow = 0xFFFE; - - InitNoCommand(hc); -} - -void InitCommandC(hostChn_t *hc) -{ - if (!Song.PatternLooping) - { - Song.BreakRow = hc->CmdVal; - Song.ProcessRow = 0xFFFE; - } - - InitNoCommand(hc); -} - -void InitCommandD(hostChn_t *hc) -{ - InitNoCommand(hc); - - uint8_t CmdVal = hc->CmdVal; - if (CmdVal == 0) - CmdVal = hc->DKL; - - hc->DKL = CmdVal; - - if (!(hc->Flags & HF_CHAN_ON)) - return; - - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - InitCommandD7(hc, sc); -} - -void InitCommandE(hostChn_t *hc) -{ - InitNoCommand(hc); - - uint8_t CmdVal = hc->CmdVal; - if (CmdVal == 0) - CmdVal = hc->EFG; - - hc->EFG = CmdVal; - - if (!(hc->Flags & HF_CHAN_ON) || hc->EFG == 0) - return; - - if ((hc->EFG & 0xF0) < 0xE0) - { - *(uint16_t *)&hc->MiscEfxData[0] = hc->EFG << 2; - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; // call update only if necess. - return; - } - - if ((hc->EFG & 0x0F) == 0) - return; - - uint16_t SlideVal = hc->EFG & 0x0F; - if ((hc->EFG & 0xF0) != 0xE0) - SlideVal <<= 2; - - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - PitchSlideDown(hc, sc, SlideVal); - sc->FrequencySet = sc->Frequency; -} - -void InitCommandF(hostChn_t *hc) -{ - InitNoCommand(hc); - - uint8_t CmdVal = hc->CmdVal; - if (CmdVal == 0) - CmdVal = hc->EFG; - - hc->EFG = CmdVal; - - if (!(hc->Flags & HF_CHAN_ON) || hc->EFG == 0) - return; - - if ((hc->EFG & 0xF0) < 0xE0) - { - *(uint16_t *)&hc->MiscEfxData[0] = hc->EFG << 2; - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; // call update only if necess. - return; - } - - if ((hc->EFG & 0x0F) == 0) - return; - - uint16_t SlideVal = hc->EFG & 0x0F; - if ((hc->EFG & 0xF0) != 0xE0) - SlideVal <<= 2; - - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - PitchSlideUp(hc, sc, SlideVal); - sc->FrequencySet = sc->Frequency; -} - -static bool Gxx_ChangeSample(hostChn_t *hc, slaveChn_t *sc, uint8_t sample) -{ - sc->Flags &= ~(SF_NOTE_STOP | SF_LOOP_CHANGED | SF_CHN_MUTED | SF_VOLENV_ON | - SF_PANENV_ON | SF_PITCHENV_ON | SF_PAN_CHANGED); - - sc->Flags |= SF_NEW_NOTE; - - // Now to update sample info. - - sample_t *s = sc->SmpPtr = &Song.Smp[sample]; - sc->Smp = sample; - sc->AutoVibratoDepth = 0; - sc->LoopDirection = 0; - sc->Frac32 = 0; // 8bb: reset sampling position fraction - sc->Frac64 = 0; // 8bb: also clear frac for my high-quality driver/mixer - sc->SamplingPosition = 0; - sc->SmpVol = s->GlobVol * 2; - - if (!(s->Flags & SMPF_ASSOCIATED_WITH_HEADER)) - { - // 8bb: turn off channel - sc->Flags = SF_NOTE_STOP; - hc->Flags &= ~HF_CHAN_ON; - return false; - } - - sc->SmpBitDepth = s->Flags & SMPF_16BIT; - GetLoopInformation(sc); - - return true; -} - -static void InitCommandG11(hostChn_t *hc) // Jumped to from Lxx (8bb: and normal tone portamento) -{ - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - - if ((hc->NotePackMask & 0x22) && hc->Smp > 0) - { - // Checking for change of sample or instrument. - - bool ChangeInstrument = false; - - if (Song.Header.Flags & ITF_COMPAT_GXX) - { - hc->Smp = sc->Smp+1; - sc->SmpVol = Song.Smp[sc->Smp].GlobVol * 2; - - ChangeInstrument = true; - } - else if (hc->Smp != 101) // Don't overwrite note if MIDI! - { - const uint8_t hcSmp = hc->Smp-1; - const uint8_t oldSlaveIns = sc->Ins; - - sc->Note = hc->RawNote; - sc->Ins = hc->Ins; - - if (sc->Ins != oldSlaveIns) // Ins the same? - { - if (sc->Smp != hcSmp) // Sample the same? - { - if (!Gxx_ChangeSample(hc, sc, hcSmp)) - return; // 8bb: sample was not assciated with sample header - } - - ChangeInstrument = true; - } - else if (sc->Smp != hcSmp) - { - if (!Gxx_ChangeSample(hc, sc, hcSmp)) - return; // 8bb: sample was not assciated with sample header - - ChangeInstrument = true; - } - } - - if ((Song.Header.Flags & ITF_INSTR_MODE) && ChangeInstrument) - { - // Now for instruments - - instrument_t *ins = &Song.Ins[hc->Ins-1]; - - sc->FadeOut = 1024; - - uint16_t oldSCFlags = sc->Flags; - InitPlayInstrument(hc, sc, ins); - - if (oldSCFlags & SF_CHAN_ON) - sc->Flags &= ~SF_NEW_NOTE; - - sc->SmpVol = (ins->GlobVol * sc->SmpVol) >> 7; - } - } - - if ((Song.Header.Flags & ITF_INSTR_MODE) || (hc->NotePackMask & 0x11)) - { - // OK. Time to calc freq. - - if (hc->TranslatedNote <= 119) - { - // Don't overwrite note if MIDI! - if (hc->Smp != 101) - sc->Note = hc->TranslatedNote; - - sample_t *s = sc->SmpPtr; - - hc->PortaFreq = ((uint64_t)s->C5Speed * (uint32_t)PitchTable[hc->TranslatedNote]) >> 16; - hc->Flags |= HF_PITCH_SLIDE_ONGOING; - } - else if (hc->Flags & HF_CHAN_ON) - { - if (hc->TranslatedNote == 255) - { - sc->Flags |= SF_NOTE_OFF; - GetLoopInformation(sc); - } - else if (hc->TranslatedNote == 254) - { - hc->Flags &= ~HF_CHAN_ON; - sc->Flags = SF_NOTE_STOP; - } - else - { - sc->Flags |= SF_FADEOUT; - } - } - } - - bool volFromVolColumn = false; - uint8_t vol = 0; // 8bb: set to 0, just to make the compiler happy.. - - if (hc->NotePackMask & 0x44) - { - if (hc->Vol <= 64) - { - vol = hc->Vol; - volFromVolColumn = true; - } - else - { - if ((hc->Vol & 0x7F) < 65) - InitCommandX2(hc, hc->Vol - 128); - } - } - - if (volFromVolColumn || (hc->NotePackMask & 0x22)) - { - if (!volFromVolColumn) - vol = sc->SmpPtr->Vol; - - sc->Flags |= SF_RECALC_VOL; - sc->Vol = sc->VolSet = hc->VolSet = vol; - } - - if (hc->Flags & HF_PITCH_SLIDE_ONGOING) // Slide on??? - { - // Work out magnitude + dirn - - uint16_t SlideSpeed; - if (Song.Header.Flags & ITF_COMPAT_GXX) // Command G memory - SlideSpeed = hc->GOE << 2; - else - SlideSpeed = hc->EFG << 2; - - if (SlideSpeed > 0) - { - *(uint16_t *)&hc->MiscEfxData[0] = SlideSpeed; - - if (sc->FrequencySet != hc->PortaFreq) - { - if (sc->FrequencySet > hc->PortaFreq) - hc->MiscEfxData[2] = 0; // slide down - else - hc->MiscEfxData[2] = 1; // slide up - - if (!(hc->Flags & HF_UPDATE_VOLEFX_IF_CHAN_ON)) - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; // Update effect if necess. - } - } - } - - // Don't call volume effects if it has a Gxx! - if (!(hc->Flags & HF_UPDATE_VOLEFX_IF_CHAN_ON)) - InitVolumeEffect(hc); -} - -void InitCommandG(hostChn_t *hc) -{ - if (hc->CmdVal != 0) - { - if (Song.Header.Flags & ITF_COMPAT_GXX) // Compatibility Gxx? - hc->GOE = hc->CmdVal; - else - hc->EFG = hc->CmdVal; - } - - if (!(hc->Flags & HF_CHAN_ON)) - { - InitNoCommand(hc); - return; - } - - InitCommandG11(hc); -} - -void InitCommandH(hostChn_t *hc) -{ - if ((hc->NotePackMask & 0x11) && hc->RawNote <= 119) - hc->VibratoPos = hc->LastVibratoData = 0; - - uint8_t speed = (hc->CmdVal >> 4) << 2; - uint8_t depth = (hc->CmdVal & 0x0F) << 2; - - if (speed > 0) - hc->VibratoSpeed = speed; - - if (depth > 0) - { - if (Song.Header.Flags & ITF_OLD_EFFECTS) - depth <<= 1; - - hc->VibratoDepth = depth; - } - - InitNoCommand(hc); - - if (hc->Flags & HF_CHAN_ON) - { - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; // Update mode. - InitVibrato(hc); - } -} - -void InitCommandI(hostChn_t *hc) -{ - InitNoCommand(hc); - - uint8_t CmdVal = hc->CmdVal; - if (CmdVal > 0) - hc->I00 = CmdVal; - - if (hc->Flags & HF_CHAN_ON) - { - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; - - uint8_t OffTime = hc->I00 & 0x0F; - uint8_t OnTime = hc->I00 >> 4; - - if (Song.Header.Flags & ITF_OLD_EFFECTS) - { - OffTime++; - OnTime++; - } - - hc->MiscEfxData[0] = OffTime; - hc->MiscEfxData[1] = OnTime; - - CommandI(hc); - } -} - -void InitCommandJ(hostChn_t *hc) -{ - InitNoCommand(hc); - - *(uint16_t *)&hc->MiscEfxData[0] = 0; // 8bb: clear arp tick counter - - uint8_t CmdVal = hc->CmdVal; - if (CmdVal == 0) - CmdVal = hc->J00; - - hc->J00 = CmdVal; - - if (hc->Flags & HF_CHAN_ON) - { - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; // Update when channel on - - /* 8bb: Original code stores 16-bit PitchTable memory addresses here, - ** but we store notes instead because we work with bigger pointer sizes. - ** The outcome is the same. - */ - *(uint16_t *)&hc->MiscEfxData[2] = 60 + (hc->J00 >> 4); // 8bb: Tick 1 note - *(uint16_t *)&hc->MiscEfxData[4] = 60 + (hc->J00 & 0x0F); // 8bb: Tick 2 note - } -} - -void InitCommandK(hostChn_t *hc) -{ - if (hc->CmdVal > 0) - hc->DKL = hc->CmdVal; - - InitNoCommand(hc); - - if (hc->Flags & HF_CHAN_ON) - { - InitVibrato(hc); - InitCommandD7(hc, (slaveChn_t *)hc->SlaveChnPtr); - - hc->Flags |= HF_ALWAYS_UPDATE_EFX; // Always update. - } -} - -void InitCommandL(hostChn_t *hc) -{ - uint8_t CmdVal = hc->CmdVal; - if (CmdVal > 0) - hc->DKL = CmdVal; - - if (hc->Flags & HF_CHAN_ON) - { - InitCommandG11(hc); - InitCommandD7(hc, (slaveChn_t *)hc->SlaveChnPtr); - } -} - -static void InitCommandM2(hostChn_t *hc, uint8_t vol) -{ - if (hc->Flags & HF_CHAN_ON) - { - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - sc->ChnVol = vol; - sc->Flags |= SF_RECALC_VOL; - } - - hc->ChnVol = vol; -} - -void InitCommandM(hostChn_t *hc) -{ - InitNoCommand(hc); - - if (hc->CmdVal <= 0x40) - InitCommandM2(hc, hc->CmdVal); -} - -void InitCommandN(hostChn_t *hc) -{ - uint8_t CmdVal = hc->CmdVal; - if (CmdVal > 0) - hc->N00 = CmdVal; - - InitNoCommand(hc); - - uint8_t hi = hc->N00 & 0xF0; - uint8_t lo = hc->N00 & 0x0F; - - if (lo == 0) - { - hc->MiscEfxData[0] = hi >> 4; - hc->Flags |= HF_ALWAYS_UPDATE_EFX; - } - else if (hi == 0) - { - hc->MiscEfxData[0] = -lo; - hc->Flags |= HF_ALWAYS_UPDATE_EFX; - } - else if (lo == 0x0F) - { - uint8_t vol = hc->ChnVol + (hi >> 4); - if (vol > 64) - vol = 64; - - InitCommandM2(hc, vol); - } - else if (hi == 0xF0) - { - uint8_t vol = hc->ChnVol - lo; - if ((int8_t)vol < 0) - vol = 0; - - InitCommandM2(hc, vol); - } -} - -void InitCommandO(hostChn_t *hc) -{ - uint8_t CmdVal = hc->CmdVal; - if (CmdVal == 0) - CmdVal = hc->O00; - - hc->O00 = CmdVal; - - InitNoCommand(hc); - - if ((hc->NotePackMask & 0x33) && hc->TranslatedNote < 120 && (hc->Flags & HF_CHAN_ON)) - { - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - - int32_t offset = ((hc->HighSmpOffs << 8) | hc->O00) << 8; - if (offset >= sc->LoopEnd) - { - if (!(Song.Header.Flags & ITF_OLD_EFFECTS)) - return; - - offset = sc->LoopEnd - 1; - } - - sc->SamplingPosition = offset; - sc->Frac32 = 0; // 8bb: clear fractional sampling position - sc->Frac64 = 0; // 8bb: also clear frac for my high-quality driver/mixer - } -} - -void InitCommandP(hostChn_t *hc) -{ - uint8_t CmdVal = hc->CmdVal; - if (CmdVal > 0) - hc->P00 = CmdVal; - - InitNoCommand(hc); - - uint8_t pan = hc->ChnPan; - if (hc->Flags & HF_CHAN_ON) - pan = ((slaveChn_t *)hc->SlaveChnPtr)->PanSet; - - if (pan == PAN_SURROUND) // Surround?? - return; - - uint8_t hi = hc->P00 & 0xF0; - uint8_t lo = hc->P00 & 0x0F; - - if (lo == 0) - { - hc->MiscEfxData[0] = -(hi >> 4); - hc->Flags |= HF_ALWAYS_UPDATE_EFX; - } - else if (hi == 0) - { - hc->MiscEfxData[0] = lo; - hc->Flags |= HF_ALWAYS_UPDATE_EFX; - } - else if (lo == 0x0F) - { - pan -= hi >> 4; - if ((int8_t)pan < 0) - pan = 0; - - InitCommandX2(hc, pan); - } - else if (hi == 0xF0) - { - pan += lo; - if (pan > 64) - pan = 64; - - InitCommandX2(hc, pan); - } -} - -void InitCommandQ(hostChn_t *hc) -{ - InitNoCommand(hc); - - if (hc->CmdVal > 0) - hc->Q00 = hc->CmdVal; - - if (!(hc->Flags & HF_CHAN_ON)) - return; - - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; - - if (hc->NotePackMask & 0x11) - hc->RetrigCount = hc->Q00 & 0x0F; - else - CommandQ(hc); -} - -static void InitTremelo(hostChn_t *hc) -{ - if (Song.Header.Flags & ITF_OLD_EFFECTS) - { - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - - sc->Flags |= SF_RECALC_FINALVOL; // Volume change... - CommandR2(hc, sc, hc->LastTremoloData); - } - else - { - CommandR(hc); - } -} - -void InitCommandR(hostChn_t *hc) -{ - uint8_t speed = hc->CmdVal >> 4; - uint8_t depth = hc->CmdVal & 0x0F; - - if (speed > 0) - hc->TremoloSpeed = speed << 2; - - if (depth > 0) - hc->TremoloDepth = depth << 1; - - InitNoCommand(hc); - - if (hc->Flags & HF_CHAN_ON) - { - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; - InitTremelo(hc); - } -} - -void InitCommandS(hostChn_t *hc) -{ - uint8_t CmdVal = hc->CmdVal; - if (CmdVal == 0) - CmdVal = hc->S00; - - hc->S00 = CmdVal; - - uint8_t cmd = CmdVal & 0xF0; - uint8_t val = CmdVal & 0x0F; - - hc->MiscEfxData[0] = cmd; - hc->MiscEfxData[1] = val; - - switch (cmd) - { - default: - case 0x00: - case 0x10: - case 0x20: - InitNoCommand(hc); - break; - - case 0x30: // set vibrato waveform - { - if (val <= 3) - hc->VibratoWaveform = val; - - InitNoCommand(hc); - } - break; - - case 0x40: // set tremelo waveform - { - if (val <= 3) - hc->TremoloWaveform = val; - - InitNoCommand(hc); - } - break; - - case 0x50: // set panbrello waveform - { - if (val <= 3) - { - hc->PanbrelloWaveform = val; - hc->PanbrelloPos = 0; - } - - InitNoCommand(hc); - } - break; - - case 0x60: // extra delay of x frames - { - Song.CurrentTick += val; - Song.ProcessTick += val; - InitNoCommand(hc); - } - break; - - case 0x70: // instrument functions - { - switch (val) - { - default: - case 0xD: - case 0xE: - case 0xF: - InitNoCommand(hc); - break; - - case 0x0: // Past note cut - { - InitNoCommand(hc); - - const uint8_t targetHostChnNum = hc->HostChnNum | CHN_DISOWNED; - - slaveChn_t *sc = sChn; - for (int32_t i = 0; i < MAX_SLAVE_CHANNELS; i++, sc++) - { - if (sc->HostChnNum == targetHostChnNum) - { - if (Driver.Flags & DF_USES_VOLRAMP) - sc->Flags |= SF_NOTE_STOP; - else - sc->Flags = SF_NOTE_STOP; - } - } - } - break; - - case 0x1: // Past note off - { - InitNoCommand(hc); - - const uint8_t targetHostChnNum = hc->HostChnNum | CHN_DISOWNED; - - slaveChn_t *sc = sChn; - for (int32_t i = 0; i < MAX_SLAVE_CHANNELS; i++, sc++) - { - if (sc->HostChnNum == targetHostChnNum) - sc->Flags |= SF_NOTE_OFF; - } - } - break; - - case 0x2: // Past note fade - { - InitNoCommand(hc); - - const uint8_t targetHostChnNum = hc->HostChnNum | CHN_DISOWNED; - - slaveChn_t *sc = sChn; - for (int32_t i = 0; i < MAX_SLAVE_CHANNELS; i++, sc++) - { - if (sc->HostChnNum == targetHostChnNum) - sc->Flags |= SF_FADEOUT; - } - } - break; - - case 0x3: // Set NNA to cut - { - InitNoCommand(hc); - if (hc->Flags & HF_CHAN_ON) - ((slaveChn_t *)hc->SlaveChnPtr)->NNA = 0; - } - break; - - case 0x4: // Set NNA to continue - { - InitNoCommand(hc); - if (hc->Flags & HF_CHAN_ON) - ((slaveChn_t *)hc->SlaveChnPtr)->NNA = 1; - } - break; - - case 0x5: // Set NNA to off - { - InitNoCommand(hc); - if (hc->Flags & HF_CHAN_ON) - ((slaveChn_t *)hc->SlaveChnPtr)->NNA = 2; - } - break; - - case 0x6: // Set NNA to fade - { - InitNoCommand(hc); - if (hc->Flags & HF_CHAN_ON) - ((slaveChn_t *)hc->SlaveChnPtr)->NNA = 3; - } - break; - - case 0x7: // Set volume envelope off - { - InitNoCommand(hc); - if (hc->Flags & HF_CHAN_ON) - ((slaveChn_t *)hc->SlaveChnPtr)->Flags &= ~SF_VOLENV_ON; - } - break; - - case 0x8: // Set volume envelope on - { - InitNoCommand(hc); - if (hc->Flags & HF_CHAN_ON) - ((slaveChn_t *)hc->SlaveChnPtr)->Flags |= SF_VOLENV_ON; - } - break; - - case 0x9: // Set panning envelope off - { - InitNoCommand(hc); - if (hc->Flags & HF_CHAN_ON) - ((slaveChn_t *)hc->SlaveChnPtr)->Flags &= ~SF_PANENV_ON; - } - break; - - case 0xA: // Set panning envelope on - { - InitNoCommand(hc); - if (hc->Flags & HF_CHAN_ON) - ((slaveChn_t *)hc->SlaveChnPtr)->Flags |= SF_PANENV_ON; - } - break; - - case 0xB: // Set pitch envelope off - { - InitNoCommand(hc); - if (hc->Flags & HF_CHAN_ON) - ((slaveChn_t *)hc->SlaveChnPtr)->Flags &= ~SF_PITCHENV_ON; - } - break; - - case 0xC: // Set pitch envelope on - { - InitNoCommand(hc); - if (hc->Flags & HF_CHAN_ON) - ((slaveChn_t *)hc->SlaveChnPtr)->Flags |= SF_PITCHENV_ON; - } - break; - } - } - break; - - case 0x80: // set pan - { - uint8_t pan = (((val << 4) | val) + 2) >> 2; - InitNoCommand(hc); - InitCommandX2(hc, pan); - } - break; - - case 0x90: // set surround - { - InitNoCommand(hc); - if (val == 1) - InitCommandX2(hc, PAN_SURROUND); - } - break; - - case 0xA0: // Set high order offset - { - hc->HighSmpOffs = val; - InitNoCommand(hc); - } - break; - - case 0xB0: // loop control (8bb: pattern loop) - { - InitNoCommand(hc); - - if (val == 0) - { - hc->PattLoopStartRow = (uint8_t)Song.CurrentRow; - } - else if (hc->PattLoopCount == 0) - { - hc->PattLoopCount = val; - Song.ProcessRow = hc->PattLoopStartRow - 1; - Song.PatternLooping = true; - } - else if (--hc->PattLoopCount != 0) - { - Song.ProcessRow = hc->PattLoopStartRow - 1; - Song.PatternLooping = true; - } - else - { - hc->PattLoopStartRow = (uint8_t)Song.CurrentRow + 1; - } - } - break; - - case 0xC0: // note cut - { - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; - InitNoCommand(hc); - } - break; - - case 0xD0: // note delay - { - hc->Flags |= HF_ALWAYS_UPDATE_EFX; - } - break; - - case 0xE0: // pattern delay - { - if (!Song.RowDelayOn) - { - Song.RowDelay = val + 1; - Song.RowDelayOn = true; - } - - InitNoCommand(hc); - } - break; - - case 0xF0: // MIDI Macro select - { - hc->SFx = val; - InitNoCommand(hc); - } - break; - } -} - -void InitCommandT(hostChn_t *hc) -{ - uint8_t CmdVal = hc->CmdVal; - if (CmdVal == 0) - CmdVal = hc->T00; - - hc->T00 = CmdVal; - - if (CmdVal >= 0x20) - { - Song.Tempo = CmdVal; - Music_InitTempo(); - InitNoCommand(hc); - } - else - { - InitNoCommand(hc); - hc->Flags |= HF_ALWAYS_UPDATE_EFX; // Update mode - } -} - -void InitCommandU(hostChn_t *hc) -{ - if (hc->NotePackMask & 0x11) - hc->VibratoPos = hc->LastVibratoData = 0; - - uint8_t speed = (hc->CmdVal >> 4) << 2; - uint8_t depth = hc->CmdVal & 0x0F; - - if (speed > 0) - hc->VibratoSpeed = speed; - - if (depth > 0) - { - if (Song.Header.Flags & ITF_OLD_EFFECTS) - depth <<= 1; - - hc->VibratoDepth = depth; - } - - InitNoCommand(hc); - - if (hc->Flags & HF_CHAN_ON) - { - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; // Update mode. - InitVibrato(hc); - } -} - -void InitCommandV(hostChn_t *hc) -{ - if (hc->CmdVal <= 0x80) - { - Song.GlobalVolume = hc->CmdVal; - RecalculateAllVolumes(); - } - - InitNoCommand(hc); -} - -void InitCommandW(hostChn_t *hc) -{ - InitNoCommand(hc); - - if (hc->CmdVal > 0) - hc->W00 = hc->CmdVal; - - if (hc->W00 == 0) - return; - - uint8_t hi = hc->W00 & 0xF0; - uint8_t lo = hc->W00 & 0x0F; - - if (lo == 0) - { - hc->MiscEfxData[0] = hi >> 4; - hc->Flags |= HF_ALWAYS_UPDATE_EFX; - } - else if (hi == 0) - { - hc->MiscEfxData[0] = -lo; - hc->Flags |= HF_ALWAYS_UPDATE_EFX; - } - else if (lo == 0x0F) - { - uint16_t vol = Song.GlobalVolume + (hi >> 4); - if (vol > 128) - vol = 128; - - Song.GlobalVolume = vol; - RecalculateAllVolumes(); - } - else if (hi == 0xF0) - { - uint16_t vol = Song.GlobalVolume - lo; - if ((int16_t)vol < 0) - vol = 0; - - Song.GlobalVolume = vol; - RecalculateAllVolumes(); - } -} - -static void InitCommandX2(hostChn_t *hc, uint8_t pan) // 8bb: pan = 0..63 -{ - if (hc->Flags & HF_CHAN_ON) - { - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - sc->Pan = sc->PanSet = pan; - sc->Flags |= (SF_RECALC_PAN | SF_RECALC_FINALVOL); - } - - hc->ChnPan = pan; -} - -void InitCommandX(hostChn_t *hc) -{ - InitNoCommand(hc); - - uint8_t pan = (hc->CmdVal + 2) >> 2; // 8bb: 0..255 -> 0..63 (rounded) - InitCommandX2(hc, pan); -} - -void InitCommandY(hostChn_t *hc) -{ - uint8_t speed = hc->CmdVal >> 4; - uint8_t depth = hc->CmdVal & 0x0F; - - if (speed > 0) - hc->PanbrelloSpeed = speed; - - if (depth > 0) - hc->PanbrelloDepth = depth << 1; - - InitNoCommand(hc); - - if (hc->Flags & HF_CHAN_ON) - { - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; // Update mode. - CommandY(hc); - } -} - -void InitCommandZ(hostChn_t *hc) // Macros start at 120h, 320h -{ - InitNoCommand(hc); - - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - - if (hc->CmdVal >= 0x80) // Macros! - MIDITranslate(hc, sc, 0x320 + ((hc->CmdVal & 0x7F) << 5)); - else - MIDITranslate(hc, sc, 0x120 + ((hc->SFx & 0xF) << 5)); -} - -void CommandD(hostChn_t *hc) -{ - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - - uint8_t vol = sc->VolSet + hc->VolSlideDelta; - if ((int8_t)vol < 0) - { - hc->Flags &= ~HF_UPDATE_EFX_IF_CHAN_ON; - vol = 0; - } - else if (vol > 64) - { - hc->Flags &= ~HF_UPDATE_EFX_IF_CHAN_ON; - vol = 64; - } - - CommandD2(hc, sc, vol); -} - -void CommandE(hostChn_t *hc) -{ - CommandEChain(hc, *(uint16_t *)&hc->MiscEfxData[0]); -} - -void CommandF(hostChn_t *hc) -{ - CommandFChain(hc, *(uint16_t *)&hc->MiscEfxData[0]); -} - -void CommandG(hostChn_t *hc) -{ - if (!(hc->Flags & HF_PITCH_SLIDE_ONGOING)) - return; - - uint16_t SlideValue = *(uint16_t *)&hc->MiscEfxData[0]; - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - - if (hc->MiscEfxData[2] == 1) // 8bb: slide direction - { - // Slide up! - - PitchSlideUp(hc, sc, SlideValue); - - /* Check that: - ** 1) Channel is on - ** 2) Frequency (set) is below porta to frequency - */ - - if (!(sc->Flags & SF_NOTE_STOP) && sc->Frequency < hc->PortaFreq) - { - sc->FrequencySet = sc->Frequency; - } - else - { - sc->Flags &= ~SF_NOTE_STOP; - hc->Flags |= HF_CHAN_ON; // Turn on. - - sc->Frequency = sc->FrequencySet = hc->PortaFreq; - hc->Flags &= ~(HF_UPDATE_EFX_IF_CHAN_ON | HF_ALWAYS_UPDATE_EFX | HF_PITCH_SLIDE_ONGOING); // Turn off calling - } - } - else - { - // Slide down - - PitchSlideDown(hc, sc, SlideValue); - - // Check that frequency is above porta to frequency. - if (sc->Frequency > hc->PortaFreq) - { - sc->FrequencySet = sc->Frequency; - } - else - { - sc->Frequency = sc->FrequencySet = hc->PortaFreq; - hc->Flags &= ~(HF_UPDATE_EFX_IF_CHAN_ON | HF_ALWAYS_UPDATE_EFX | HF_PITCH_SLIDE_ONGOING); // Turn off calling - } - } -} - -static void CommandH5(hostChn_t *hc, slaveChn_t *sc, int8_t VibratoData) -{ - VibratoData = (((VibratoData * (int8_t)hc->VibratoDepth) << 2) + 128) >> 8; - if (Song.Header.Flags & ITF_OLD_EFFECTS) - VibratoData = -VibratoData; - - if (VibratoData < 0) - PitchSlideDown(hc, sc, -VibratoData); - else - PitchSlideUp(hc, sc, VibratoData); -} - -void CommandH(hostChn_t *hc) -{ - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - sc->Flags |= SF_FREQ_CHANGE; - - hc->VibratoPos += hc->VibratoSpeed; - - int8_t VibratoData; - if (hc->VibratoWaveform == 3) - VibratoData = (Random() & 127) - 64; - else - VibratoData = FineSineData[(hc->VibratoWaveform << 8) + hc->VibratoPos]; - - hc->LastVibratoData = VibratoData; // Save last vibrato. - CommandH5(hc, sc, VibratoData); -} - -void CommandI(hostChn_t *hc) -{ - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - sc->Flags |= SF_RECALC_VOL; - - hc->TremorCount--; - if ((int8_t)hc->TremorCount <= 0) - { - hc->TremorOnOff ^= 1; - hc->TremorCount = hc->MiscEfxData[hc->TremorOnOff]; - } - - if (hc->TremorOnOff != 1) - sc->Vol = 0; -} - -void CommandJ(hostChn_t *hc) -{ - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - uint16_t tick = *(uint16_t *)&hc->MiscEfxData[0]; - - sc->Flags |= SF_FREQ_CHANGE; - - // 8bb: used as an index to a 16-bit LUT (hence increments of 2) - tick += 2; - if (tick >= 6) - { - *(uint16_t *)&hc->MiscEfxData[0] = 0; - return; - } - - *(uint16_t *)&hc->MiscEfxData[0] = tick; - - const uint16_t arpNote = *(uint16_t *)&hc->MiscEfxData[tick]; - - uint64_t freq = (uint64_t)sc->Frequency * (uint32_t)PitchTable[arpNote]; - if (freq & 0xFFFF000000000000) // 8bb: arp freq overflow - sc->Frequency = 0; - else - sc->Frequency = (uint32_t)(freq >> 16); -} - -void CommandK(hostChn_t *hc) -{ - CommandH(hc); - CommandD(hc); -} - -void CommandL(hostChn_t *hc) -{ - if (hc->Flags & HF_PITCH_SLIDE_ONGOING) - { - CommandG(hc); - hc->Flags |= HF_UPDATE_EFX_IF_CHAN_ON; - } - - CommandD(hc); -} - -void CommandN(hostChn_t *hc) -{ - uint8_t vol = hc->ChnVol + (int8_t)hc->MiscEfxData[0]; - - if ((int8_t)vol < 0) - vol = 0; - else if (vol > 64) - vol = 64; - - InitCommandM2(hc, vol); -} - -void CommandP(hostChn_t *hc) -{ - uint8_t pan = hc->ChnPan; - if (hc->Flags & HF_CHAN_ON) - pan = ((slaveChn_t *)hc->SlaveChnPtr)->PanSet; - - pan += hc->MiscEfxData[0]; - - if ((int8_t)pan < 0) - pan = 0; - else if (pan > 64) - pan = 64; - - InitCommandX2(hc, pan); -} - -void CommandQ(hostChn_t *hc) -{ - hc->RetrigCount--; - if ((int8_t)hc->RetrigCount > 0) - return; - - // OK... reset counter. - hc->RetrigCount = hc->Q00 & 0x0F; - - // retrig count done. - - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - if (Driver.Flags & DF_USES_VOLRAMP) - { - if (Song.Header.Flags & ITF_INSTR_MODE) - { - slaveChn_t *scTmp = sChn; - for (int32_t i = 0; i < MAX_SLAVE_CHANNELS; i++, scTmp++) - { - if (!(scTmp->Flags & SF_CHAN_ON)) - { - memcpy(scTmp, sc, sizeof (slaveChn_t)); - sc->Flags |= SF_NOTE_STOP; // Cut - sc->HostChnNum |= CHN_DISOWNED; - - sc = scTmp; - hc->SlaveChnPtr = scTmp; - break; - } - } - } - else // 8bb: samples-only mode - { - slaveChn_t *scTmp = sc + MAX_HOST_CHANNELS; - memcpy(scTmp, sc, sizeof (slaveChn_t)); - scTmp->Flags |= SF_NOTE_STOP; // Cut - scTmp->HostChnNum |= CHN_DISOWNED; - } - } - - sc->Frac32 = 0; // 8bb: clear sampling position fraction - sc->Frac64 = 0; // 8bb: also clear frac for my high-quality driver/mixer - sc->SamplingPosition = 0; - - sc->Flags |= (SF_RECALC_FINALVOL | SF_NEW_NOTE | SF_LOOP_CHANGED); - - uint8_t vol = sc->VolSet; - switch (hc->Q00 >> 4) - { - default: - case 0x0: return; - case 0x1: vol -= 1; break; - case 0x2: vol -= 2; break; - case 0x3: vol -= 4; break; - case 0x4: vol -= 8; break; - case 0x5: vol -= 16; break; - case 0x6: vol = (vol << 1) / 3; break; - case 0x7: vol >>= 1; break; - case 0x8: return; - case 0x9: vol += 1; break; - case 0xA: vol += 2; break; - case 0xB: vol += 4; break; - case 0xC: vol += 8; break; - case 0xD: vol += 16; break; - case 0xE: vol = (vol * 3) >> 1; break; - case 0xF: vol <<= 1; break; - } - - if ((int8_t)vol < 0) - vol = 0; - else if (vol > 64) - vol = 64; - - sc->VolSet = sc->Vol = hc->VolSet = vol; - sc->Flags |= SF_RECALC_VOL; - - if (hc->Smp == 101) // MIDI sample - MIDITranslate(hc, sc, MIDICOMMAND_STOPNOTE); -} - -static void CommandR2(hostChn_t *hc, slaveChn_t *sc, int8_t TremoloData) -{ - TremoloData = (((TremoloData * (int8_t)hc->TremoloDepth) << 2) + 128) >> 8; - - int16_t vol = sc->Vol + TremoloData; - if (vol < 0) - vol = 0; - else if (vol > 64) - vol = 64; - - sc->Vol = (uint8_t)vol; -} - -void CommandR(hostChn_t *hc) -{ - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - sc->Flags |= SF_RECALC_VOL; - - hc->TremoloPos += hc->TremoloSpeed; - - int8_t TremoloData; - if (hc->TremoloWaveform == 3) - TremoloData = (Random() & 127) - 64; - else - TremoloData = FineSineData[(hc->TremoloWaveform << 8) + hc->TremoloPos]; - - hc->LastTremoloData = TremoloData; // Save last tremelo - CommandR2(hc, sc, TremoloData); -} - -void CommandS(hostChn_t *hc) -{ - // Have to handle SDx, SCx - - const uint8_t SCmd = hc->MiscEfxData[0]; - if (SCmd == 0xD0) // 8bb: Note delay - { - hc->MiscEfxData[1]--; - if ((int8_t)hc->MiscEfxData[1] > 0) - return; - - hc->Flags &= ~(HF_UPDATE_EFX_IF_CHAN_ON | HF_ALWAYS_UPDATE_EFX); - InitNoCommand(hc); - hc->Flags |= HF_ROW_UPDATED; - - bool ChannelMuted = !!(Song.Header.ChnlPan[hc->HostChnNum] & 128); - if (ChannelMuted && !(hc->Flags & HF_FREEPLAY_NOTE) && (hc->Flags & HF_CHAN_ON)) - ((slaveChn_t *)hc->SlaveChnPtr)->Flags |= SF_CHN_MUTED; - } - else if (SCmd == 0xC0) // Note cut. - { - if (!(hc->Flags & HF_CHAN_ON)) - return; - - hc->MiscEfxData[1]--; - if ((int8_t)hc->MiscEfxData[1] > 0) - return; - - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - - hc->Flags &= ~HF_CHAN_ON; - - if (sc->Smp == 100 || (Driver.Flags & DF_USES_VOLRAMP)) - sc->Flags |= SF_NOTE_STOP; - else - sc->Flags = SF_NOTE_STOP; - } -} - -void CommandT(hostChn_t *hc) -{ - int16_t Tempo = Song.Tempo; - - if (hc->T00 & 0xF0) - { - // Slide Up - Tempo += hc->T00 - 16; - if (Tempo > 255) - Tempo = 255; - } - else - { - // Slide Down - Tempo -= hc->T00; - if (Tempo < 32) - Tempo = 32; - } - - Song.Tempo = Tempo; - DriverSetTempo((uint8_t)Tempo); -} - -void CommandW(hostChn_t *hc) -{ - uint16_t vol = Song.GlobalVolume + (int8_t)hc->MiscEfxData[0]; - - if ((int16_t)vol < 0) - vol = 0; - else if (vol > 128) - vol = 128; - - Song.GlobalVolume = vol; - RecalculateAllVolumes(); -} - -void CommandY(hostChn_t *hc) -{ - if (!(hc->Flags & HF_CHAN_ON)) - return; - - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - - int8_t panData; - if (hc->PanbrelloWaveform >= 3) // 8bb: panbrello waveform - { - // Random panning make speed the delay time. - - hc->PanbrelloPos--; - if ((int8_t)hc->PanbrelloPos <= 0) - { - hc->PanbrelloPos = hc->PanbrelloSpeed; // reset countdown. - hc->LastPanbrelloData = panData = (Random() & 127) - 64; - } - else - { - panData = hc->LastPanbrelloData; - } - } - else - { - hc->PanbrelloPos += hc->PanbrelloSpeed; - panData = FineSineData[(hc->PanbrelloWaveform << 8) + hc->PanbrelloPos]; - } - - if (sc->PanSet != PAN_SURROUND) - { - panData = (((panData * (int8_t)hc->PanbrelloDepth) << 2) + 128) >> 8; - panData += sc->PanSet; - - if (panData < 0) - panData = 0; - else if (panData > 64) - panData = 64; - - sc->Flags |= SF_RECALC_PAN; - sc->Pan = panData; - } -} diff --git a/libraries/m4p/it_m_eff.h b/libraries/m4p/it_m_eff.h deleted file mode 100644 index 24b34eb50..000000000 --- a/libraries/m4p/it_m_eff.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include -#include - -void NoCommand(hostChn_t *hc); - -void VolumeCommandC(hostChn_t *hc); -void VolumeCommandD(hostChn_t *hc); -void VolumeCommandE(hostChn_t *hc); -void VolumeCommandF(hostChn_t *hc); -void VolumeCommandG(hostChn_t *hc); - -void InitNoCommand(hostChn_t *hc); -void InitCommandA(hostChn_t *hc); -void InitCommandB(hostChn_t *hc); -void InitCommandC(hostChn_t *hc); -void InitCommandD(hostChn_t *hc); -void InitCommandE(hostChn_t *hc); -void InitCommandF(hostChn_t *hc); -void InitCommandG(hostChn_t *hc); -void InitCommandH(hostChn_t *hc); -void InitCommandI(hostChn_t *hc); -void InitCommandI(hostChn_t *hc); -void InitCommandJ(hostChn_t *hc); -void InitCommandK(hostChn_t *hc); -void InitCommandL(hostChn_t *hc); -void InitCommandM(hostChn_t *hc); -void InitCommandN(hostChn_t *hc); -void InitCommandO(hostChn_t *hc); -void InitCommandP(hostChn_t *hc); -void InitCommandQ(hostChn_t *hc); -void InitCommandR(hostChn_t *hc); -void InitCommandS(hostChn_t *hc); -void InitCommandT(hostChn_t *hc); -void InitCommandU(hostChn_t *hc); -void InitCommandV(hostChn_t *hc); -void InitCommandW(hostChn_t *hc); -void InitCommandX(hostChn_t *hc); -void InitCommandY(hostChn_t *hc); -void InitCommandZ(hostChn_t *hc); - -void CommandD(hostChn_t *hc); -void CommandE(hostChn_t *hc); -void CommandF(hostChn_t *hc); -void CommandG(hostChn_t *hc); -void CommandH(hostChn_t *hc); -void CommandI(hostChn_t *hc); -void CommandJ(hostChn_t *hc); -void CommandK(hostChn_t *hc); -void CommandL(hostChn_t *hc); -void CommandN(hostChn_t *hc); -void CommandP(hostChn_t *hc); -void CommandQ(hostChn_t *hc); -void CommandR(hostChn_t *hc); -void CommandS(hostChn_t *hc); -void CommandT(hostChn_t *hc); -void CommandW(hostChn_t *hc); -void CommandY(hostChn_t *hc); diff --git a/libraries/m4p/it_music.c b/libraries/m4p/it_music.c deleted file mode 100644 index 4cc7af013..000000000 --- a/libraries/m4p/it_music.c +++ /dev/null @@ -1,2240 +0,0 @@ -/* -** 8bb: IT2 replayer system -** -** NOTE: MIDI logic is incomplete, and it was never meant to be ported anyway -*/ - -#include -#include -#include -#include -#include -#include -#include -#include "it_structs.h" -#include "it_tables.h" -#include "it_m_eff.h" -#include "it_music.h" -#include "it2drivers/sb16.h" - -hostChn_t hChn[MAX_HOST_CHANNELS]; -slaveChn_t sChn[MAX_SLAVE_CHANNELS]; -driver_t Driver; -song_t Song; - -enum -{ - NNA_NOTE_CUT = 0, - NNA_CONTINUE = 1, - NNA_NOTE_OFF = 2, - NNA_NOTE_FADE = 3, - - DCT_DISABLED = 0, - DCT_NOTE = 1, - DCT_SAMPLE = 2, - DCT_INSTRUMENT = 3, - - DCA_NOTE_CUT = 0 -}; - -// 8bb: globalized -void (*DriverClose)(void) = NULL; -void (*DriverMix)(int32_t, int16_t *) = NULL; -void (*DriverMixFloat)(int32_t, float *) = NULL; -void (*DriverResetMixer)(void) = NULL; -int32_t (*DriverPostMix)(int16_t *, int32_t) = NULL; -int32_t (*DriverPostMixFloat)(float *, int32_t) = NULL; -void (*DriverMixSamples)(void) = NULL; -void (*DriverSetTempo)(uint8_t) = NULL; -void (*DriverSetMixVolume)(uint8_t) = NULL; -void (*DriverFixSamples)(void) = NULL; -// ------------------------ - -static bool FirstTimeInit = true; -static uint8_t MIDIInterpretState, MIDIInterpretType; // 8bb: for MIDISendFilter() -static uint16_t RandSeed1 = 0x1234, RandSeed2 = 0x5678; - -static char MIDIDataArea[(9+16+128)*32]; - -/* 8bb: These have been changed to be easier to understand, -** and to be 32-bit/64-bit pointer compliant. -*/ -static uint8_t ChannelCountTable[100], ChannelVolumeTable[100]; -static slaveChn_t *ChannelLocationTable[100]; -// -------------------------------------------- - -static uint32_t AllocateNumChannels; -static slaveChn_t *AllocateSlaveOffset, *LastSlaveChannel; - -static uint8_t EmptyPattern[72] = -{ - 64,0,64,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -}; - -static void (*InitCommandTable[])(hostChn_t *hc) = -{ - InitNoCommand, InitCommandA, - InitCommandB, InitCommandC, - InitCommandD, InitCommandE, - InitCommandF, InitCommandG, - InitCommandH, InitCommandI, - InitCommandJ, InitCommandK, - InitCommandL, InitCommandM, - InitCommandN, InitCommandO, - InitCommandP, InitCommandQ, - InitCommandR, InitCommandS, - InitCommandT, InitCommandU, - InitCommandV, InitCommandW, - InitCommandX, InitCommandY, - InitCommandZ, InitNoCommand, - InitNoCommand, InitNoCommand, - InitNoCommand, InitNoCommand -}; - -static void (*CommandTable[])(hostChn_t *hc) = -{ - NoCommand, NoCommand, - NoCommand, NoCommand, - CommandD, CommandE, - CommandF, CommandG, - CommandH, CommandI, - CommandJ, CommandK, - CommandL, NoCommand, - CommandN, NoCommand, - CommandP, CommandQ, - CommandR, CommandS, - CommandT, CommandH, - NoCommand, CommandW, - NoCommand, CommandY, - NoCommand, NoCommand, - NoCommand, NoCommand -}; - -static void (*VolumeEffectTable[])(hostChn_t *hc) = -{ - NoCommand, NoCommand, - VolumeCommandC, VolumeCommandD, - VolumeCommandE, VolumeCommandF, - VolumeCommandG, CommandH -}; - -void RecalculateAllVolumes(void) -{ - slaveChn_t *sc = sChn; - for (uint32_t i = 0; i < Driver.NumChannels; i++, sc++) - sc->Flags |= (SF_RECALC_PAN | SF_RECALC_VOL); -} - -void Music_SetDefaultMIDIDataArea(void) // 8bb: added this -{ - // fill default MIDI configuration values (important for filters) - - memset(MIDIDataArea, 0, (9+16+128)*32); // data is padded with zeroes, not spaces! - - // MIDI commands - memcpy(&MIDIDataArea[0*32], "FF", 2); - memcpy(&MIDIDataArea[1*32], "FC", 2); - memcpy(&MIDIDataArea[3*32], "9c n v", 6); - memcpy(&MIDIDataArea[4*32], "9c n 0", 6); - memcpy(&MIDIDataArea[8*32], "Cc p", 4); - - // macro setup (SF0) - memcpy(&MIDIDataArea[9*32], "F0F000z", 7); - - // macro setup (Z80..Z8F) - memcpy(&MIDIDataArea[25*32], "F0F00100", 8); - memcpy(&MIDIDataArea[26*32], "F0F00108", 8); - memcpy(&MIDIDataArea[27*32], "F0F00110", 8); - memcpy(&MIDIDataArea[28*32], "F0F00118", 8); - memcpy(&MIDIDataArea[29*32], "F0F00120", 8); - memcpy(&MIDIDataArea[30*32], "F0F00128", 8); - memcpy(&MIDIDataArea[31*32], "F0F00130", 8); - memcpy(&MIDIDataArea[32*32], "F0F00138", 8); - memcpy(&MIDIDataArea[33*32], "F0F00140", 8); - memcpy(&MIDIDataArea[34*32], "F0F00148", 8); - memcpy(&MIDIDataArea[35*32], "F0F00150", 8); - memcpy(&MIDIDataArea[36*32], "F0F00158", 8); - memcpy(&MIDIDataArea[37*32], "F0F00160", 8); - memcpy(&MIDIDataArea[38*32], "F0F00168", 8); - memcpy(&MIDIDataArea[39*32], "F0F00170", 8); - memcpy(&MIDIDataArea[40*32], "F0F00178", 8); -} - -char *Music_GetMIDIDataArea(void) -{ - return (char *)MIDIDataArea; -} - -static void MIDISendFilter(hostChn_t *hc, slaveChn_t *sc, uint8_t Data) -{ - if (!(Driver.Flags & DF_SUPPORTS_MIDI)) - return; - - if (Data >= 0x80 && Data < 0xF0) - { - if (Data == Song.LastMIDIByte) - return; - - Song.LastMIDIByte = Data; - } - - /* 8bb: We implement the SendUARTOut() code found in the - ** SB16 MMX driver and WAV writer driver and use it here - ** instead of doing real MIDI data handling. - ** - ** It will only interpret filter commands (set and clear). - */ - if (MIDIInterpretState < 2) - { - if (Data == 0xF0) - { - MIDIInterpretState++; - } - else - { - if (Data == 0xFA || Data == 0xFC || Data == 0xFF) - { - // 8bb: reset filters - for (int32_t i = 0; i < MAX_HOST_CHANNELS; i++) - { - Driver.FilterParameters[ i] = 127; // 8bb: Cutoff - Driver.FilterParameters[64+i] = 0; // 8bb: Q - } - } - - MIDIInterpretState = 0; - } - } - else if (MIDIInterpretState == 2) - { - if (Data < 2) // 8bb: must be 0..1 (Cutoff or Q) - { - MIDIInterpretType = Data; - MIDIInterpretState++; - } - else - { - MIDIInterpretState = 0; - } - } - else if (MIDIInterpretState == 3) - { - // Have InterpretType, now get parameter, then return to normal. - - if (Data <= 0x7F) - { - bool IsFilterQ = (MIDIInterpretType == 1); - if (IsFilterQ) - Driver.FilterParameters[(64 + hc->HostChnNum) & 127] = Data; - else - Driver.FilterParameters[hc->HostChnNum & 127] = Data; - - if (sc != NULL) - sc->Flags |= SF_RECALC_FINALVOL; - } - - MIDIInterpretState = 0; - } -} - -static void SetFilterCutoff(hostChn_t *hc, slaveChn_t *sc, uint8_t value) // Assumes that channel is non-disowned -{ - MIDISendFilter(hc, sc, 0xF0); - MIDISendFilter(hc, sc, 0xF0); - MIDISendFilter(hc, sc, 0x00); - MIDISendFilter(hc, sc, value); -} - -static void SetFilterResonance(hostChn_t *hc, slaveChn_t *sc, uint8_t value) // Assumes that channel is non-disowned -{ - MIDISendFilter(hc, sc, 0xF0); - MIDISendFilter(hc, sc, 0xF0); - MIDISendFilter(hc, sc, 0x01); - MIDISendFilter(hc, sc, value); -} - -void MIDITranslate(hostChn_t *hc, slaveChn_t *sc, uint16_t Input) -{ - if (!(Driver.Flags & DF_SUPPORTS_MIDI)) - return; - - if (Input >= 0xF000) - return; // 8bb: we don't support (nor need) MIDI commands - - if (Input/32 >= 9+16+128) // 8bb: added protection, just in case - return; - - uint8_t MIDIData = 0; - uint8_t CharsParsed = 0; - - while (true) - { - int16_t Byte = MIDIDataArea[Input++]; - - if (Byte == 0) - { - if (CharsParsed > 0) - MIDISendFilter(hc, sc, MIDIData); - - break; // 8bb: and we're done! - } - - if (Byte == ' ') - { - if (CharsParsed > 0) - MIDISendFilter(hc, sc, MIDIData); - - continue; - } - - // Interpretation time. - - Byte -= '0'; - if (Byte < 0) - continue; - - if (Byte <= 9) - { - MIDIData = (MIDIData << 4) | (uint8_t)Byte; - CharsParsed++; - - if (CharsParsed >= 2) - { - MIDISendFilter(hc, sc, MIDIData); - CharsParsed = 0; - MIDIData = 0; - } - - continue; - } - - Byte -= 'A'-'0'; - if (Byte < 0) - continue; - - if (Byte <= 'F'-'A') - { - MIDIData = (MIDIData << 4) | (uint8_t)(Byte+10); - CharsParsed++; - - if (CharsParsed >= 2) - { - MIDISendFilter(hc, sc, MIDIData); - CharsParsed = 0; - MIDIData = 0; - } - - continue; - } - - Byte -= 'a'-'A'; - if (Byte < 0) - continue; - - if (Byte > 'z'-'a') - continue; - - if (Byte == 'c'-'a') - { - if (sc == NULL) - continue; - - MIDIData = (MIDIData << 4) | (sc->MIDIChn-1); - CharsParsed++; - - if (CharsParsed >= 2) - { - MIDISendFilter(hc, sc, MIDIData); - CharsParsed = 0; - MIDIData = 0; - } - - continue; - } - - if (CharsParsed > 0) - { - MIDISendFilter(hc, sc, MIDIData); - MIDIData = 0; - } - - if (Byte == 'z'-'a') // Zxx? - { - MIDISendFilter(hc, sc, hc->CmdVal); - } - else if (Byte == 'o'-'a') // 8bb: sample offset? - { - MIDISendFilter(hc, sc, hc->O00); - } - else if (sc != NULL) - { - if (Byte == 'n'-'a') // Note? - { - MIDISendFilter(hc, sc, sc->Note); - } - else if (Byte == 'm'-'a') // 8bb: MIDI note (sample loop direction on sample channels) - { - MIDISendFilter(hc, sc, sc->LoopDirection); - } - else if (Byte == 'v'-'a') // Velocity? - { - if (sc->Flags & SF_CHN_MUTED) - { - MIDISendFilter(hc, sc, 0); - } - else - { - uint16_t volume = (sc->VolSet * Song.GlobalVolume * sc->ChnVol) >> 4; - volume = (volume * sc->SmpVol) >> 15; - - if (volume == 0) - volume = 1; - else if (volume >= 128) - volume = 127; - - MIDISendFilter(hc, sc, (uint8_t)volume); - } - } - else if (Byte == 'u'-'a') // Volume? - { - if (sc->Flags & SF_CHN_MUTED) - { - MIDISendFilter(hc, sc, 0); - } - else - { - uint16_t volume = sc->FinalVol7Bit; - - if (volume == 0) - volume = 1; - else if (volume >= 128) - volume = 127; - - MIDISendFilter(hc, sc, (uint8_t)volume); - } - } - else if (Byte == 'h'-'a') // HCN (8bb: host channel number) - { - MIDISendFilter(hc, sc, sc->HostChnNum & 0x7F); - } - else if (Byte == 'x'-'a') // Pan set - { - uint16_t value = sc->Pan * 2; // 8bb: yes sc->Pan, not sc->PS - if (value >= 128) - value--; - - if (value >= 128) - value = 64; - - MIDISendFilter(hc, sc, (uint8_t)value); - } - else if (Byte == 'p'-'a') // Program? - { - MIDISendFilter(hc, sc, sc->MIDIProg); - } - else if (Byte == 'b'-'a') // 8bb: MIDI bank low - { - MIDISendFilter(hc, sc, sc->MIDIBank & 0xFF); - } - else if (Byte == 'a'-'a') // 8bb: MIDI bank high - { - MIDISendFilter(hc, sc, sc->MIDIBank >> 8); - } - } - - MIDIData = 0; - CharsParsed = 0; - } -} - -void InitPlayInstrument(hostChn_t *hc, slaveChn_t *sc, instrument_t *ins) -{ - sc->InsPtr = ins; - - sc->NNA = ins->NNA; - sc->DCT = ins->DCT; - sc->DCA = ins->DCA; - - if (hc->MIDIChn != 0) // 8bb: MIDI? - { - sc->MIDIChn = ins->MIDIChn; - sc->MIDIProg = ins->MIDIProg; - sc->MIDIBank = ins->MIDIBank; - sc->LoopDirection = hc->RawNote; // 8bb: during MIDI, LpD = MIDI note - } - - sc->ChnVol = hc->ChnVol; - - uint8_t pan = (ins->DefPan & 0x80) ? hc->ChnPan : ins->DefPan; - if (hc->Smp != 0) - { - sample_t *s = &Song.Smp[hc->Smp-1]; - if (s->DefPan & 0x80) - pan = s->DefPan & 127; - } - - if (pan != PAN_SURROUND) - { - int16_t newPan = pan + (((int8_t)(hc->RawNote - ins->PitchPanCenter) * (int8_t)ins->PitchPanSep) >> 3); - - if (newPan < 0) - newPan = 0; - else if (newPan > 64) - newPan = 64; - - pan = (uint8_t)newPan; - } - - sc->Pan = sc->PanSet = pan; - - // Envelope init - sc->VolEnvState.Value = 64 << 16; // 8bb: clears fractional part - sc->VolEnvState.Tick = sc->VolEnvState.NextTick = 0; - sc->VolEnvState.CurNode = 0; - - sc->PanEnvState.Value = 0; // 8bb: clears fractional part - sc->PanEnvState.Tick = sc->PanEnvState.NextTick = 0; - sc->PanEnvState.CurNode = 0; - - sc->PitchEnvState.Value = 0; // 8bb: clears fractional part - sc->PitchEnvState.Tick = sc->PitchEnvState.NextTick = 0; - sc->PitchEnvState.CurNode = 0; - - sc->Flags = SF_CHAN_ON + SF_RECALC_PAN + SF_RECALC_VOL + SF_FREQ_CHANGE + SF_NEW_NOTE; - - if (ins->VolEnv.Flags & ENVF_ENABLED) sc->Flags |= SF_VOLENV_ON; - if (ins->PanEnv.Flags & ENVF_ENABLED) sc->Flags |= SF_PANENV_ON; - if (ins->PitchEnv.Flags & ENVF_ENABLED) sc->Flags |= SF_PITCHENV_ON; - - if (LastSlaveChannel != NULL) - { - slaveChn_t *lastSC = LastSlaveChannel; - - if ((ins->VolEnv.Flags & (ENVF_ENABLED|ENVF_CARRY)) == ENVF_ENABLED+ENVF_CARRY) // Transfer volume data - { - sc->VolEnvState.Value = lastSC->VolEnvState.Value; - sc->VolEnvState.Delta = lastSC->VolEnvState.Delta; - sc->VolEnvState.Tick = lastSC->VolEnvState.Tick; - sc->VolEnvState.CurNode = lastSC->VolEnvState.CurNode; - sc->VolEnvState.NextTick = lastSC->VolEnvState.NextTick; - } - - if ((ins->PanEnv.Flags & (ENVF_ENABLED|ENVF_CARRY)) == ENVF_ENABLED+ENVF_CARRY) // Transfer pan data - { - sc->PanEnvState.Value = lastSC->PanEnvState.Value; - sc->PanEnvState.Delta = lastSC->PanEnvState.Delta; - sc->PanEnvState.Tick = lastSC->PanEnvState.Tick; - sc->PanEnvState.CurNode = lastSC->PanEnvState.CurNode; - sc->PanEnvState.NextTick = lastSC->PanEnvState.NextTick; - } - - if ((ins->PitchEnv.Flags & (ENVF_ENABLED|ENVF_CARRY)) == ENVF_ENABLED+ENVF_CARRY) // Transfer pitch data - { - sc->PitchEnvState.Value = lastSC->PitchEnvState.Value; - sc->PitchEnvState.Delta = lastSC->PitchEnvState.Delta; - sc->PitchEnvState.Tick = lastSC->PitchEnvState.Tick; - sc->PitchEnvState.CurNode = lastSC->PitchEnvState.CurNode; - sc->PitchEnvState.NextTick = lastSC->PitchEnvState.NextTick; - } - } - - hc->Flags |= HF_APPLY_RANDOM_VOL; // Apply random volume/pan - - if (hc->MIDIChn == 0) - { - sc->MIDIBank = 0x00FF; // 8bb: reset filter resonance (Q) & cutoff - - if (ins->FilterCutoff & 0x80) // If IFC bit 7 == 1, then set filter cutoff - { - uint8_t filterCutOff = ins->FilterCutoff & 0x7F; - SetFilterCutoff(hc, sc, filterCutOff); - } - - if (ins->FilterResonance & 0x80) // If IFR bit 7 == 1, then set filter resonance - { - const uint8_t filterQ = ins->FilterResonance & 0x7F; - sc->MIDIBank = (filterQ << 8) | (sc->MIDIBank & 0x00FF); - SetFilterResonance(hc, sc, filterQ); - } - } -} - -// 8bb: this function is used in AllocateChannel() -static slaveChn_t *AllocateChannelSample(hostChn_t *hc, uint8_t *hcFlags) -{ - // Sample handler - - slaveChn_t *sc = &sChn[hc->HostChnNum]; - if ((Driver.Flags & DF_USES_VOLRAMP) && (sc->Flags & SF_CHAN_ON)) - { - // copy out channel - sc->Flags |= SF_NOTE_STOP; - sc->HostChnNum |= CHN_DISOWNED; - memcpy(sc + MAX_HOST_CHANNELS, sc, sizeof (slaveChn_t)); - } - - hc->SlaveChnPtr = sc; - sc->HostChnPtr = hc; - sc->HostChnNum = hc->HostChnNum; - - sc->ChnVol = hc->ChnVol; - sc->Pan = sc->PanSet = hc->ChnPan; - sc->FadeOut = 1024; - sc->VolEnvState.Value = (64 << 16) | (sc->VolEnvState.Value & 0xFFFF); // 8bb: keeps frac - sc->MIDIBank = 0x00FF; // Filter cutoff - sc->Note = hc->RawNote; - sc->Ins = hc->Ins; - - sc->Flags = SF_CHAN_ON + SF_RECALC_PAN + SF_RECALC_VOL + SF_FREQ_CHANGE + SF_NEW_NOTE; - - if (hc->Smp > 0) - { - sc->Smp = hc->Smp - 1; - sample_t *s = sc->SmpPtr = &Song.Smp[sc->Smp]; - - sc->SmpBitDepth = 0; // 8bb: 8-bit - sc->AutoVibratoDepth = sc->AutoVibratoPos = 0; - sc->PanEnvState.Value &= 0xFFFF; // No pan deviation (8bb: keeps frac) - sc->PitchEnvState.Value &= 0xFFFF; // No pitch deviation (8bb: keeps frac) - sc->LoopDirection = DIR_FORWARDS; // Reset loop dirn - - if (s->Length == 0 || !(s->Flags & SMPF_ASSOCIATED_WITH_HEADER)) - { - sc->Flags = SF_NOTE_STOP; - *hcFlags &= ~HF_CHAN_ON; - return NULL; - } - - sc->SmpBitDepth = s->Flags & SMPF_16BIT; - sc->SmpVol = s->GlobVol * 2; - return sc; - } - else // No sample! - { - sc->Flags = SF_NOTE_STOP; - *hcFlags &= ~HF_CHAN_ON; - return NULL; - } -} - -// 8bb: this function is used in AllocateChannel() -static slaveChn_t *AllocateChannelInstrument(hostChn_t *hc, slaveChn_t *sc, instrument_t *ins, uint8_t *hcFlags) -{ - assert(hc != NULL && sc != NULL && ins != NULL); - - hc->SlaveChnPtr = sc; - sc->HostChnNum = hc->HostChnNum; - sc->HostChnPtr = hc; - - sc->SmpBitDepth = 0; // 8bb: 8-bit - sc->AutoVibratoDepth = sc->AutoVibratoPos = 0; - sc->LoopDirection = DIR_FORWARDS; // Reset loop dirn - - InitPlayInstrument(hc, sc, ins); - - sc->SmpVol = ins->GlobVol; - sc->FadeOut = 1024; - sc->Note = (hc->Smp == 101) ? hc->TranslatedNote : hc->RawNote; - sc->Ins = hc->Ins; - - if (hc->Smp == 0) - { - // 8bb: shut down channel - sc->Flags = SF_NOTE_STOP; - *hcFlags &= ~HF_CHAN_ON; - return NULL; - } - - sc->Smp = hc->Smp-1; - sample_t *s = sc->SmpPtr = &Song.Smp[sc->Smp]; - - if (s->Length == 0 || !(s->Flags & SMPF_ASSOCIATED_WITH_HEADER)) - { - // 8bb: shut down channel - sc->Flags = SF_NOTE_STOP; - *hcFlags &= ~HF_CHAN_ON; - return NULL; - } - - sc->SmpBitDepth = s->Flags & SMPF_16BIT; - sc->SmpVol = (s->GlobVol * sc->SmpVol) >> 6; // 0->128 - return sc; -} - -// 8bb: this function is used in AllocateChannel() -static bool DuplicateCheck(slaveChn_t **scOut, hostChn_t *hc, uint8_t hostChnNum, instrument_t *ins, uint8_t DCT, uint8_t DCVal) -{ - slaveChn_t *sc = AllocateSlaveOffset; - for (uint32_t i = 0; i < AllocateNumChannels; i++, sc++) - { - *scOut = sc; // 8bb: copy current slave channel pointer to scOut - - if (!(sc->Flags & SF_CHAN_ON) || (hc->Smp != 101 && sc->HostChnNum != hostChnNum) || sc->Ins != hc->Ins) - continue; - - // 8bb: the actual duplicate test - - if (DCT == DCT_NOTE && sc->Note != DCVal) - continue; - - if (DCT == DCT_SAMPLE && sc->Smp != DCVal) - continue; - - if (DCT == DCT_INSTRUMENT && sc->Ins != DCVal) - continue; - - if (hc->Smp == 101) // New note is a MIDI? - { - if (sc->Smp == 100 && sc->MIDIChn == hostChnNum) // Is current channel a MIDI chan - { - sc->Flags |= SF_NOTE_STOP; - if (!(sc->HostChnNum & CHN_DISOWNED)) - { - sc->HostChnNum |= CHN_DISOWNED; - ((hostChn_t *)sc->HostChnPtr)->Flags &= ~HF_CHAN_ON; - } - } - } - else if (sc->DCA == ins->DCA) - { - return true; // 8bb: dupe found - } - } - - return false; // 8bb: dupe not found -} - -// 8bb: are you sure you want to know? ;) -slaveChn_t *AllocateChannel(hostChn_t *hc, uint8_t *hcFlags) -{ - LastSlaveChannel = NULL; - - if (!(Song.Header.Flags & ITF_INSTR_MODE) || hc->Ins == 255) - return AllocateChannelSample(hc, hcFlags); - - // Instrument handler! - - if (hc->Ins == 0) - return NULL; - - if (hc->Smp == 101 && Driver.NumChannels < MAX_SLAVE_CHANNELS) // 8bb: MIDI and below 256 virtual channels in driver? - { - AllocateNumChannels = MAX_SLAVE_CHANNELS - Driver.NumChannels; - AllocateSlaveOffset = &sChn[Driver.NumChannels]; - } - else - { - AllocateNumChannels = Driver.NumChannels; - AllocateSlaveOffset = sChn; // 8bb: points to first virtual channel - } - - // 8bb: some of these are initialized only to prevent compiler warnings - uint8_t NNA = 0; - slaveChn_t *sc = NULL; - uint8_t hostChnNum, DCT, DCVal; - - instrument_t *ins = &Song.Ins[hc->Ins-1]; - - bool scInitialized = false; - if ((*hcFlags) & HF_CHAN_ON) // 8bb: host channel on? - { - sc = (slaveChn_t *)hc->SlaveChnPtr; - if (sc->InsPtr == ins) // 8bb: slave channel has same inst. as host channel? - LastSlaveChannel = sc; - - NNA = sc->NNA; - if (NNA != NNA_NOTE_CUT) // 8bb: not note-cut - sc->HostChnNum |= CHN_DISOWNED; // Disown channel - - scInitialized = true; - } - - while (true) // New note action handling... - { - bool skipMIDITest = false; - if (scInitialized) - { - if (NNA != NNA_NOTE_CUT && sc->VolSet > 0 && sc->ChnVol > 0 && sc->SmpVol > 0) - { - if (NNA == NNA_NOTE_OFF) - { - sc->Flags |= SF_NOTE_OFF; - GetLoopInformation(sc); // 8bb: update sample loop (sustain released) - } - else if (NNA >= NNA_NOTE_FADE) - { - sc->Flags |= SF_FADEOUT; - } - // 8bb: else: NNA_CONTINUE - } - else - { - // 8bb: NNA=Note Cut (or volumes are zero) - if (sc->Smp == 100) // MIDI? - { - sc->Flags |= SF_NOTE_STOP; - sc->HostChnNum |= CHN_DISOWNED; // Disown channel - - if (hc->Smp != 101) - break; // Sample.. (8bb: find available voice now) - } - else - { - if (Driver.Flags & DF_USES_VOLRAMP) - { - sc->Flags |= SF_NOTE_STOP; - sc->HostChnNum |= CHN_DISOWNED; // Disown channel - break; // 8bb: find available voice now - } - - sc->Flags = SF_NOTE_STOP; - if (ins->DCT == DCT_DISABLED) - return AllocateChannelInstrument(hc, sc, ins, hcFlags); - - skipMIDITest = true; - } - } - } - - hostChnNum = DCT = DCVal = 0; // 8bb: prevent stupid compiler warning... - - bool doDupeCheck = false; - if (!skipMIDITest && hc->Smp == 101) - { - // 8bb: MIDI note, do a "duplicate note" check regardless of instrument's DCT setting - hostChnNum = hc->MIDIChn; - DCT = DCT_NOTE; - DCVal = hc->TranslatedNote; - - doDupeCheck = true; - } - else if (ins->DCT != DCT_DISABLED) - { - hostChnNum = hc->HostChnNum | CHN_DISOWNED; // 8bb: only search disowned host channels - DCT = ins->DCT; - - if (ins->DCT == DCT_NOTE) - { - DCVal = hc->RawNote; // 8bb: not the translated note! - } - else if (ins->DCT == DCT_INSTRUMENT) - { - DCVal = hc->Ins; - } - else - { - /* 8bb: - ** .ITs from OpenMPT can have DCT=4, which tests for duplicate instrument plugins. - ** This will be handled as DCT_SAMPLE in Impulse Tracker. Oops... - */ - DCVal = hc->Smp - 1; - if ((int8_t)DCVal < 0) - break; // 8bb: illegal (or no) sample, ignore dupe test and find available voice now - } - - doDupeCheck = true; - } - - if (doDupeCheck) // 8bb: NNA Duplicate Check - { - sc = AllocateSlaveOffset; - if (DuplicateCheck(&sc, hc, hostChnNum, ins, DCT, DCVal)) - { - // 8bb: dupe found! - - scInitialized = true; // 8bb: we have an sc pointer now (we could come from a shutdown host channel) - if (ins->DCA == DCA_NOTE_CUT) - { - NNA = NNA_NOTE_CUT; - } - else - { - sc->DCT = DCT_DISABLED; // 8bb: turn of dupe check so that we don't do infinite NNA tests :) - sc->DCA = DCA_NOTE_CUT; - NNA = ins->DCA + 1; - } - - continue; // 8bb: do another NNA test with the new NNA type - } - } - - break; // NNA handling done, find available voice now - } - - // 8bb: search for inactive channels - - sc = AllocateSlaveOffset; - if (hc->Smp != 101) - { - // 8bb: no MIDI - for (uint32_t i = 0; i < AllocateNumChannels; i++, sc++) - { - if (!(sc->Flags & SF_CHAN_ON)) - return AllocateChannelInstrument(hc, sc, ins, hcFlags); - } - } - else - { - // MIDI 'slave channels' have to be maintained if still referenced - for (uint32_t i = 0; i < AllocateNumChannels; i++, sc++) - { - if (!(sc->Flags & SF_CHAN_ON)) - { - // Have a channel.. check that it's host's slave isn't SI (8bb: SI = sc) - hostChn_t *hcTmp = (hostChn_t *)sc->HostChnPtr; - - if (hcTmp == NULL || hcTmp->SlaveChnPtr != sc) - return AllocateChannelInstrument(hc, sc, ins, hcFlags); - } - } - } - - // Common sample search - - memset(ChannelCountTable, 0, sizeof (ChannelCountTable)); - memset(ChannelVolumeTable, 255, sizeof (ChannelVolumeTable)); - memset(ChannelLocationTable, 0, sizeof (ChannelLocationTable)); - - sc = AllocateSlaveOffset; - for (uint32_t i = 0; i < AllocateNumChannels; i++, sc++) - { - if (sc->Smp > 99) // Just for safety - continue; - - ChannelCountTable[sc->Smp]++; - if ((sc->HostChnNum & CHN_DISOWNED) && sc->FinalVol7Bit < ChannelVolumeTable[sc->Smp]) - { - ChannelLocationTable[sc->Smp] = sc; - ChannelVolumeTable[sc->Smp] = sc->FinalVol7Bit; - } - } - - // OK.. now search table for maximum occurrence of sample... - - sc = NULL; - uint8_t count = 2; // Find maximum count, has to be greater than 2 channels - for (int32_t i = 0; i < 100; i++) - { - if (count < ChannelCountTable[i]) - { - count = ChannelCountTable[i]; - sc = ChannelLocationTable[i]; - } - } - - if (sc != NULL) - return AllocateChannelInstrument(hc, sc, ins, hcFlags); - - /* - ** Find out which host channel has the most (disowned) slave channels. - ** Then find the softest non-single sample in that channel. - */ - - memset(ChannelCountTable, 0, MAX_HOST_CHANNELS); - - sc = AllocateSlaveOffset; - for (uint32_t i = 0; i < AllocateNumChannels; i++, sc++) - ChannelCountTable[sc->HostChnNum & 63]++; - - // OK.. search through and find the most heavily used channel - uint8_t lowestVol; - while (true) - { - hostChnNum = 0; - count = 1; - for (uint8_t i = 0; i < MAX_HOST_CHANNELS; i++) - { - if (count < ChannelCountTable[i]) - { - count = ChannelCountTable[i]; - hostChnNum = i; - } - } - - if (count <= 1) - { - // Now search for softest disowned sample (not non-single) - - sc = NULL; - slaveChn_t *scTmp = AllocateSlaveOffset; - - lowestVol = 255; - for (uint32_t i = 0; i < AllocateNumChannels; i++, scTmp++) - { - if ((scTmp->HostChnNum & CHN_DISOWNED) && scTmp->FinalVol7Bit <= lowestVol) - { - sc = scTmp; - lowestVol = scTmp->FinalVol7Bit; - } - } - - if (sc == NULL) - { - *hcFlags &= ~HF_CHAN_ON; - return NULL; - } - - return AllocateChannelInstrument(hc, sc, ins, hcFlags); - } - - hostChnNum |= CHN_DISOWNED; // Search for disowned only - sc = NULL; // Offset - - lowestVol = 255; - uint8_t targetSmp = hc->Smp-1; - - slaveChn_t *scTmp = AllocateSlaveOffset; - for (uint32_t i = 0; i < AllocateNumChannels; i++, scTmp++) - { - if (scTmp->HostChnNum != hostChnNum || scTmp->FinalVol7Bit >= lowestVol) - continue; - - // Now check if any other channel contains this sample - - if (scTmp->Smp == targetSmp) - { - sc = scTmp; - lowestVol = scTmp->FinalVol7Bit; - continue; - } - - slaveChn_t *scTmp2 = AllocateSlaveOffset; - - uint8_t scSmp = scTmp->Smp; - scTmp->Smp = 255; - for (uint32_t j = 0; j < AllocateNumChannels; j++, scTmp2++) - { - if (scTmp2->Smp == targetSmp || scTmp2->Smp == scSmp) - { - // OK found a second sample. - sc = scTmp; - lowestVol = scTmp->FinalVol7Bit; - break; - } - } - scTmp->Smp = scSmp; - } - - if (sc != NULL) - break; // 8bb: done - - ChannelCountTable[hostChnNum & 63] = 0; // Next cycle... - } - - // 8bb: we have a slave channel in sc at this point - - lowestVol = 255; - - slaveChn_t *scTmp = AllocateSlaveOffset; - for (uint32_t i = 0; i < AllocateNumChannels; i++, scTmp++) - { - if (scTmp->Smp == sc->Smp && (scTmp->HostChnNum & CHN_DISOWNED) && scTmp->FinalVol7Bit < lowestVol) - { - sc = scTmp; - lowestVol = scTmp->FinalVol7Bit; - } - } - - return AllocateChannelInstrument(hc, sc, ins, hcFlags); -} - -uint8_t Random(void) // 8bb: verified to be ported correctly -{ - uint16_t r1, r2, r3, r4; - - r1 = RandSeed1; - r2 = r3 = r4 = RandSeed2; - - r1 += r2; - r1 = (r1 << (r3 & 15)) | (r1 >> ((16-r3) & 15)); - r1 ^= r4; - r3 = (r3 >> 8) | (r3 << 8); - r2 += r3; - r4 += r2; - r3 += r1; - r1 -= r4 + (r2 & 1); - r2 = (r2 << 15) | (r2 >> 1); - - RandSeed2 = r4; - RandSeed1 = r1; - - return (uint8_t)r1; -} - -void GetLoopInformation(slaveChn_t *sc) -{ - uint8_t LoopMode; - int32_t LoopBegin, LoopEnd; - - assert(sc->SmpPtr != NULL); - sample_t *s = sc->SmpPtr; - - bool LoopEnabled = !!(s->Flags & (SMPF_USE_LOOP | SMPF_USE_SUSTAINLOOP)); - bool SustainLoopOnlyAndNoteOff = (s->Flags & SMPF_USE_SUSTAINLOOP) && (sc->Flags & SF_NOTE_OFF) && !(s->Flags & SMPF_USE_LOOP); - - if (!LoopEnabled || SustainLoopOnlyAndNoteOff) - { - LoopBegin = 0; - LoopEnd = s->Length; - LoopMode = 0; - } - else - { - LoopBegin = s->LoopBegin; - LoopEnd = s->LoopEnd; - LoopMode = s->Flags; - - if (s->Flags & SMPF_USE_SUSTAINLOOP) - { - if (!(sc->Flags & SF_NOTE_OFF)) // 8bb: sustain on (note not released)? - { - LoopBegin = s->SustainLoopBegin; - LoopEnd = s->SustainLoopEnd; - LoopMode >>= 1; // 8bb: loop mode = sustain loop mode - } - } - - LoopMode = (LoopMode & SMPF_LOOP_PINGPONG) ? LOOP_PINGPONG : LOOP_FORWARDS; // 8bb: set loop type (Ping-Pong or Forwards) - } - - // 8bb: if any parameter changed, update all - if (sc->LoopMode != LoopMode || sc->LoopBegin != LoopBegin || sc->LoopEnd != LoopEnd) - { - sc->LoopMode = LoopMode; - sc->LoopBegin = LoopBegin; - sc->LoopEnd = LoopEnd; - sc->Flags |= SF_LOOP_CHANGED; - } -} - -void ApplyRandomValues(hostChn_t *hc) -{ - slaveChn_t *sc = (slaveChn_t *)hc->SlaveChnPtr; - instrument_t *ins = sc->InsPtr; - - hc->Flags &= ~HF_APPLY_RANDOM_VOL; - - int8_t value = Random(); // -128->+127 - if (ins->RandVol != 0) // Random volume, 0->100 - { - int16_t vol = (((int8_t)ins->RandVol * value) >> 6) + 1; - vol = sc->SmpVol + ((vol * (int16_t)sc->SmpVol) / 199); - - if (vol < 0) - vol = 0; - else if (vol > 128) - vol = 128; - - sc->SmpVol = (uint8_t)vol; - } - - value = Random(); // -128->+127 - if (ins->RandPan != 0 && sc->Pan != PAN_SURROUND) // Random pan, 0->64 - { - int16_t pan = sc->Pan + (((int8_t)ins->RandPan * value) >> 7); - - if (pan < 0) - pan = 0; - else if (pan > 64) - pan = 64; - - sc->Pan = sc->PanSet = (uint8_t)pan; - } -} - -void PitchSlideUp(hostChn_t *hc, slaveChn_t *sc, int16_t SlideValue) -{ - assert(sc != NULL); - assert(hc != NULL); - - if (Song.Header.Flags & ITF_LINEAR_FRQ) - { - // 8bb: linear frequencies - PitchSlideUpLinear(hc, sc, SlideValue); - } - else - { - // 8bb: Amiga frequencies - -#if USEFPUCODE // 8bb: IT2.15 - - const double InitFreq = sc->Frequency; - - double dFreqDiv = (8363.0 * 1712.0) - (InitFreq * SlideValue); - - // 8bb: added this, needed to make it work in extreme edge cases - if (dFreqDiv <= 0.0) - dFreqDiv = 1e-9; // 8bb: any tiny positive number - - sc->Flags |= SF_FREQ_CHANGE; // recalculate pitch! - - double dNewFreq = ((8363.0 * 1712.0) * InitFreq) / dFreqDiv; - if (dNewFreq >= INT32_MAX) - { - sc->Flags |= SF_NOTE_STOP; // Turn off channel - hc->Flags &= ~HF_CHAN_ON; - return; - } - - sc->Frequency = (int32_t)dNewFreq; // 8bb: Do not round here! Truncate. - -#else // 8bb: IT2.14 and older - - sc->Flags |= SF_FREQ_CHANGE; // recalculate pitch! - - const uint32_t PeriodBase = 1712 * 8363; - - if (SlideValue < 0) - { - SlideValue = -SlideValue; - - // 8bb: slide down - - uint64_t FreqSlide64 = (uint64_t)sc->Frequency * (uint32_t)SlideValue; - if (FreqSlide64 > UINT32_MAX) - { - sc->Flags |= SF_NOTE_STOP; // Turn off channel - hc->Flags &= ~HF_CHAN_ON; - return; - } - - FreqSlide64 += PeriodBase; - - uint32_t ShitValue = 0; - while (FreqSlide64 > UINT32_MAX) - { - FreqSlide64 >>= 1; - ShitValue++; - } - - uint32_t Temp32 = (uint32_t)FreqSlide64; - uint64_t Temp64 = (uint64_t)sc->Frequency * (uint32_t)PeriodBase; - - if (ShitValue > 0) - Temp64 >>= ShitValue; - - if (Temp32 <= Temp64>>32) - { - sc->Flags |= SF_NOTE_STOP; - hc->Flags &= ~HF_CHAN_ON; - return; - } - - sc->Frequency = (uint32_t)(Temp64 / Temp32); - } - else - { - // 8bb: slide up - - uint64_t FreqSlide64 = (uint64_t)sc->Frequency * (uint32_t)SlideValue; - if (FreqSlide64 > UINT32_MAX) - { - sc->Flags |= SF_NOTE_STOP; // Turn off channel - hc->Flags &= ~HF_CHAN_ON; - return; - } - - uint32_t FreqSlide32 = (uint32_t)FreqSlide64; - - uint32_t Temp32 = PeriodBase - FreqSlide32; - if ((int32_t)Temp32 <= 0) - { - sc->Flags |= SF_NOTE_STOP; - hc->Flags &= ~HF_CHAN_ON; - return; - } - - uint64_t Temp64 = (uint64_t)sc->Frequency * (uint32_t)PeriodBase; - if (Temp32 <= Temp64>>32) - { - sc->Flags |= SF_NOTE_STOP; - hc->Flags &= ~HF_CHAN_ON; - return; - } - - sc->Frequency = (uint32_t)(Temp64 / Temp32); - } -#endif - } -} - -void PitchSlideUpLinear(hostChn_t *hc, slaveChn_t *sc, int16_t SlideValue) -{ - assert(sc != NULL); - assert(hc != NULL); - assert(SlideValue >= -1024 && SlideValue <= 1024); - -#ifdef USEFPUCODE // 8bb: IT2.15 (registered) - - sc->Flags |= SF_FREQ_CHANGE; // recalculate pitch! - - // 8bb: yes, IT2 really uses 24-bit (float) precision here - const float fMultiplier = powf(2.0f, SlideValue * (1.0f / 768.0f)); - - double dNewFreq = sc->Frequency * (double)fMultiplier; - if (dNewFreq >= INT32_MAX) - { - sc->Flags |= SF_NOTE_STOP; // Turn off channel - hc->Flags &= ~HF_CHAN_ON; - return; - } - - sc->Frequency = (int32_t)nearbyint(dNewFreq); // 8bb: rounded ( nearbyint() is needed over round() ) - -#else // 8bb: IT2.14 and older (what the vast majority of IT2 users had) - - sc->Flags |= SF_FREQ_CHANGE; // recalculate pitch! - - if (SlideValue < 0) - { - // 8bb: slide down - - SlideValue = -SlideValue; - - const uint16_t *SlideTable; - if (SlideValue <= 15) - { - SlideTable = FineLinearSlideDownTable; - } - else - { - SlideTable = LinearSlideDownTable; - SlideValue >>= 2; - } - - sc->Frequency = ((uint64_t)sc->Frequency * SlideTable[SlideValue]) >> 16; - } - else - { - // 8bb: slide up - - const uint32_t *SlideTable; - if (SlideValue <= 15) - { - SlideTable = FineLinearSlideUpTable; - } - else - { - SlideTable = LinearSlideUpTable; - SlideValue >>= 2; - } - - uint64_t Frequency = ((uint64_t)sc->Frequency * SlideTable[SlideValue]) >> 16; - if (Frequency & 0xFFFF000000000000) - { - sc->Flags |= SF_NOTE_STOP; // Turn off channel - hc->Flags &= ~HF_CHAN_ON; - } - else - { - sc->Frequency = (uint32_t)Frequency; - } - } -#endif -} - -void PitchSlideDown(hostChn_t *hc, slaveChn_t *sc, int16_t SlideValue) -{ - PitchSlideUp(hc, sc, -SlideValue); -} - -static uint8_t *Music_GetPattern(uint32_t pattern, uint16_t *numRows) -{ - assert(pattern < MAX_PATTERNS); - pattern_t *p = &Song.Patt[pattern]; - - if (p->PackedData == NULL) - { - *numRows = 64; - return EmptyPattern; - } - - *numRows = p->Rows; - return p->PackedData; -} - -static void PreInitCommand(hostChn_t *hc) -{ - if (hc->NotePackMask & 0x33) - { - if (!(Song.Header.Flags & ITF_INSTR_MODE) || hc->RawNote >= 120 || hc->Ins == 0) - { - hc->TranslatedNote = hc->RawNote; - hc->Smp = hc->Ins; - } - else - { - instrument_t *ins = &Song.Ins[hc->Ins-1]; - - hc->TranslatedNote = ins->SmpNoteTable[hc->RawNote] & 0xFF; - - /* 8bb: - ** Added >128 check to prevent instruments with ModPlug/OpenMPT plugins - ** from being handled as MIDI (would result in silence, and crash IT2). - */ - if (ins->MIDIChn == 0 || ins->MIDIChn > 128) - { - hc->Smp = ins->SmpNoteTable[hc->RawNote] >> 8; - } - else // 8bb: MIDI - { - hc->MIDIChn = (ins->MIDIChn == 17) ? (hc->HostChnNum & 0x0F) + 1 : ins->MIDIChn; - hc->MIDIProg = ins->MIDIProg; - hc->Smp = 101; - } - - if (hc->Smp == 0) // No sample? - return; - } - } - - InitCommandTable[hc->Cmd & 31](hc); // Init note - - hc->Flags |= HF_ROW_UPDATED; - - bool ChannelMuted = !!(Song.Header.ChnlPan[hc->HostChnNum] & 128); - if (ChannelMuted && !(hc->Flags & HF_FREEPLAY_NOTE) && (hc->Flags & HF_CHAN_ON)) - ((slaveChn_t *)hc->SlaveChnPtr)->Flags |= SF_CHN_MUTED; -} - -static void UpdateGOTONote(void) // Get offset -{ - Song.DecodeExpectedPattern = Song.CurrentPattern; - - uint8_t *p = Music_GetPattern(Song.DecodeExpectedPattern, &Song.NumberOfRows); - if (Song.ProcessRow >= Song.NumberOfRows) - Song.ProcessRow = 0; - - Song.DecodeExpectedRow = Song.CurrentRow = Song.ProcessRow; - - uint16_t rowsTodo = Song.ProcessRow; - if (rowsTodo > 0) - { - while (true) - { - uint8_t chnNum = *p++; - if (chnNum == 0) - { - rowsTodo--; - if (rowsTodo == 0) - break; - - continue; - } - - hostChn_t *hc = &hChn[(chnNum & 0x7F) - 1]; - if (chnNum & 0x80) - hc->NotePackMask = *p++; - - if (hc->NotePackMask & 1) - hc->RawNote = *p++; - - if (hc->NotePackMask & 2) - hc->Ins = *p++; - - if (hc->NotePackMask & 4) - hc->Vol = *p++; - - if (hc->NotePackMask & 8) - { - hc->OldCmd = *p++; - hc->OldCmdVal = *p++; - } - } - } - - Song.PatternOffset = p; -} - -static void UpdateNoteData(void) -{ - hostChn_t *hc; - - Song.PatternLooping = false; - if (Song.CurrentPattern != Song.DecodeExpectedPattern || ++Song.DecodeExpectedRow != Song.CurrentRow) - UpdateGOTONote(); - - // First clear all old command&value. - hc = hChn; - for (int32_t i = 0; i < MAX_HOST_CHANNELS; i++, hc++) - hc->Flags &= ~(HF_UPDATE_EFX_IF_CHAN_ON | HF_ALWAYS_UPDATE_EFX | HF_ROW_UPDATED | HF_UPDATE_VOLEFX_IF_CHAN_ON); - - uint8_t *p = Song.PatternOffset; - while (true) - { - uint8_t chnNum = *p++; - if (chnNum == 0) // No more! else... go through decoding - break; - - hc = &hChn[(chnNum & 0x7F) - 1]; - if (chnNum & 0x80) - hc->NotePackMask = *p++; - - if (hc->NotePackMask & 1) - hc->RawNote = *p++; - - if (hc->NotePackMask & 2) - hc->Ins = *p++; - - if (hc->NotePackMask & 4) - hc->Vol = *p++; - - if (hc->NotePackMask & 8) - { - hc->Cmd = hc->OldCmd = *p++; - hc->CmdVal = hc->OldCmdVal = *p++; - } - else if (hc->NotePackMask & 128) - { - hc->Cmd = hc->OldCmd; - hc->CmdVal = hc->OldCmdVal; - } - else - { - hc->Cmd = 0; - hc->CmdVal = 0; - } - - PreInitCommand(hc); - } - - Song.PatternOffset = p; -} - -static void UpdateData(void) -{ - // 8bb: I only added the logic for "Play Song" (2) mode - - Song.ProcessTick--; - Song.CurrentTick--; - - if (Song.CurrentTick == 0) - { - Song.ProcessTick = Song.CurrentTick = Song.CurrentSpeed; - - Song.RowDelay--; - if (Song.RowDelay == 0) - { - Song.RowDelay = 1; - Song.RowDelayOn = false; - - uint16_t NewRow = Song.ProcessRow + 1; - if (NewRow >= Song.NumberOfRows) - { - uint16_t NewOrder = Song.ProcessOrder + 1; - while (true) - { - if (NewOrder >= 256) - { - NewOrder = 0; - continue; - } - - uint8_t NewPattern = Song.Orders[NewOrder]; // next pattern - if (NewPattern >= 200) - { - if (NewPattern == 0xFE) // 8bb: skip pattern separator - { - NewOrder++; - } - else - { - NewOrder = 0; - Song.StopSong = true; // 8bb: for WAV rendering - } - } - else - { - Song.CurrentPattern = NewPattern; - break; - } - } - - Song.CurrentOrder = Song.ProcessOrder = NewOrder; - NewRow = Song.BreakRow; - Song.BreakRow = 0; - } - - Song.CurrentRow = Song.ProcessRow = NewRow; - UpdateNoteData(); - } - else - { - hostChn_t *hc = hChn; - for (int32_t i = 0; i < MAX_HOST_CHANNELS; i++, hc++) - { - if (!(hc->Flags & HF_ROW_UPDATED) || !(hc->NotePackMask & 0x88)) - continue; - - uint8_t OldNotePackMask = hc->NotePackMask; - hc->NotePackMask &= 0x88; - InitCommandTable[hc->Cmd & 31](hc); - hc->NotePackMask = OldNotePackMask; - } - } - } - else - { - // OK. call update command. - - hostChn_t *hc = hChn; - for (int32_t i = 0; i < MAX_HOST_CHANNELS; i++, hc++) - { - if ((hc->Flags & HF_CHAN_ON) && (hc->Flags & HF_UPDATE_VOLEFX_IF_CHAN_ON)) - VolumeEffectTable[hc->VolCmd & 7](hc); - - if ((hc->Flags & (HF_UPDATE_EFX_IF_CHAN_ON | HF_ALWAYS_UPDATE_EFX)) && - ((hc->Flags & HF_ALWAYS_UPDATE_EFX) || (hc->Flags & HF_CHAN_ON))) - { - CommandTable[hc->Cmd & 31](hc); - } - } - } -} - -static void UpdateAutoVibrato(slaveChn_t *sc) // 8bb: renamed from UpdateVibrato() to UpdateAutoVibrato() for clarity -{ - assert(sc->SmpPtr != NULL); - sample_t *smp = sc->SmpPtr; - - if (smp->AutoVibratoDepth == 0) - return; - - sc->AutoVibratoDepth += smp->AutoVibratoRate; - if (sc->AutoVibratoDepth>>8 > smp->AutoVibratoDepth) - sc->AutoVibratoDepth = (smp->AutoVibratoDepth << 8) | (sc->AutoVibratoDepth & 0xFF); - - if (smp->AutoVibratoSpeed == 0) - return; - - int16_t VibratoData; - if (smp->AutoVibratoWaveform == 3) - { - VibratoData = (Random() & 127) - 64; - } - else - { - sc->AutoVibratoPos += smp->AutoVibratoSpeed; // Update pointer. - - assert(smp->AutoVibratoWaveform < 3); - VibratoData = FineSineData[(smp->AutoVibratoWaveform << 8) + sc->AutoVibratoPos]; - } - - VibratoData = (VibratoData * (int16_t)(sc->AutoVibratoDepth >> 8)) >> 6; - if (VibratoData != 0) - PitchSlideUpLinear(sc->HostChnPtr, sc, VibratoData); -} - -static bool UpdateEnvelope(env_t *env, envState_t *envState, bool SustainReleased) -{ - if (envState->Tick < envState->NextTick) - { - envState->Tick++; - envState->Value += envState->Delta; - return false; // 8bb: last node not reached - } - - envNode_t *Nodes = env->NodePoints; - envState->Value = Nodes[envState->CurNode & 0x00FF].Magnitude << 16; - int16_t NextNode = (envState->CurNode & 0x00FF) + 1; - - if (env->Flags & 6) // 8bb: any loop at all? - { - uint8_t LoopBegin = env->LoopBegin; - uint8_t LoopEnd = env->LoopEnd; - - bool HasLoop = !!(env->Flags & ENVF_LOOP); - bool HasSustainLoop = !!(env->Flags & ENVF_SUSTAINLOOP); - - bool Looping = true; - if (HasSustainLoop) - { - if (!SustainReleased) - { - LoopBegin = env->SustainLoopBegin; - LoopEnd = env->SustainLoopEnd; - } - else if (!HasLoop) - { - Looping = false; - } - } - - if (Looping && NextNode > LoopEnd) - { - envState->CurNode = (envState->CurNode & 0xFF00) | LoopBegin; - envState->Tick = envState->NextTick = Nodes[envState->CurNode & 0x00FF].Tick; - return false; // 8bb: last node not reached - } - } - - if (NextNode >= env->Num) - return true; // 8bb: last node reached - - // 8bb: new node - - envState->NextTick = Nodes[NextNode].Tick; - envState->Tick = Nodes[envState->CurNode & 0x00FF].Tick + 1; - - int16_t TickDelta = envState->NextTick - Nodes[envState->CurNode & 0x00FF].Tick; - if (TickDelta == 0) - TickDelta = 1; - - int16_t Delta = Nodes[NextNode].Magnitude - Nodes[envState->CurNode & 0x00FF].Magnitude; - envState->Delta = (Delta << 16) / TickDelta; - envState->CurNode = (envState->CurNode & 0xFF00) | (uint8_t)NextNode; - - return false; // 8bb: last node not reached -} - -static void UpdateInstruments(void) -{ - slaveChn_t *sc = sChn; - for (int32_t i = 0; i < MAX_SLAVE_CHANNELS; i++, sc++) - { - if (!(sc->Flags & SF_CHAN_ON)) - continue; - - if (sc->Ins != 0xFF) // 8bb: got an instrument? - { - int16_t EnvVal; - instrument_t *ins = sc->InsPtr; - bool SustainReleased = !!(sc->Flags & SF_NOTE_OFF); - - // 8bb: handle pitch/filter envelope - - if (sc->Flags & SF_PITCHENV_ON) - { - if (UpdateEnvelope(&ins->PitchEnv, &sc->PitchEnvState, SustainReleased)) // 8bb: last node reached? - sc->Flags &= ~SF_PITCHENV_ON; - } - - if (!(ins->PitchEnv.Flags & ENVF_TYPE_FILTER)) // 8bb: pitch envelope - { - EnvVal = (int16_t)((uint32_t)sc->PitchEnvState.Value >> 8); - EnvVal >>= 3; // 8bb: arithmetic shift - - if (EnvVal != 0) - { - PitchSlideUpLinear(sc->HostChnPtr, sc, EnvVal); - sc->Flags |= SF_FREQ_CHANGE; - } - } - else if (sc->Smp != 100) // 8bb: filter envelope - { - EnvVal = (int16_t)((uint32_t)sc->PitchEnvState.Value >> 8); - EnvVal >>= 6; // 8bb: arithmetic shift, -128..128 (though -512..511 is in theory possible) - - /* - ** 8bb: Some annoying logic. - ** - ** Original asm code: - ** add bx,128 - ** cmp bh,1 - ** adc bl,-1 - ** - ** The code below is confirmed to be correct - ** for the whole -512..511 range. - ** - ** However, EnvVal should only be -128..128 - ** (0..256 after +128 add) unless something - ** nasty is going on. - */ - EnvVal += 128; - if (EnvVal & 0xFF00) - EnvVal--; - - sc->MIDIBank = (sc->MIDIBank & 0xFF00) | (uint8_t)EnvVal; // 8bb: don't mess with upper byte! - sc->Flags |= SF_RECALC_FINALVOL; - } - - if (sc->Flags & SF_PANENV_ON) - { - sc->Flags |= SF_RECALC_PAN; - if (UpdateEnvelope(&ins->PanEnv, &sc->PanEnvState, SustainReleased)) // 8bb: last node reached? - sc->Flags &= ~SF_PANENV_ON; - } - - bool HandleNoteFade = false; - bool TurnOffCh = false; - - if (sc->Flags & SF_VOLENV_ON) // Volume envelope on? - { - sc->Flags |= SF_RECALC_VOL; - - if (UpdateEnvelope(&ins->VolEnv, &sc->VolEnvState, SustainReleased)) // 8bb: last node reached? - { - // Envelope turned off... - - sc->Flags &= ~SF_VOLENV_ON; - - if ((sc->VolEnvState.Value & 0x00FF0000) == 0) // Turn off if end of loop is reached (8bb: last env. point is zero?) - { - TurnOffCh = true; - } - else - { - sc->Flags |= SF_FADEOUT; - HandleNoteFade = true; - } - } - else - { - if (!(sc->Flags & SF_FADEOUT)) // Note fade on? - { - // Now, check if loop + sustain off - if (SustainReleased && (ins->VolEnv.Flags & ENVF_LOOP)) // Normal vol env loop? - { - sc->Flags |= SF_FADEOUT; - HandleNoteFade = true; - } - } - else - { - HandleNoteFade = true; - } - } - } - else if (sc->Flags & SF_FADEOUT) // Note fade?? - { - HandleNoteFade = true; - } - else if (sc->Flags & SF_NOTE_OFF) // Note off issued? - { - sc->Flags |= SF_FADEOUT; - HandleNoteFade = true; - } - - if (HandleNoteFade) - { - sc->FadeOut -= ins->FadeOut; - if ((int16_t)sc->FadeOut <= 0) - { - sc->FadeOut = 0; - TurnOffCh = true; - } - - sc->Flags |= SF_RECALC_VOL; - } - - if (TurnOffCh) - { - if (!(sc->HostChnNum & CHN_DISOWNED)) - { - sc->HostChnNum |= CHN_DISOWNED; // Host channel exists - ((hostChn_t *)sc->HostChnPtr)->Flags &= ~HF_CHAN_ON; - } - - sc->Flags |= (SF_RECALC_VOL | SF_NOTE_STOP); - } - } - - if (sc->Flags & SF_RECALC_VOL) // Calculate volume - { - sc->Flags &= ~SF_RECALC_VOL; - sc->Flags |= SF_RECALC_FINALVOL; - - uint16_t volume = (sc->Vol * sc->ChnVol * sc->FadeOut) >> 7; - volume = (volume * sc->SmpVol) >> 7; - volume = (volume * (uint16_t)((uint32_t)sc->VolEnvState.Value >> 8)) >> 14; - volume = (volume * Song.GlobalVolume) >> 7; - assert(volume <= 32768); - - sc->FinalVol15Bit = volume; // 8bb: 0..32768 - sc->FinalVol7Bit = volume >> 8; // 8bb: 0..128 - } - - if (sc->Flags & SF_RECALC_PAN) // Change in panning? - { - sc->Flags &= ~SF_RECALC_PAN; - sc->Flags |= SF_PAN_CHANGED; - - if (sc->Pan == PAN_SURROUND) - { - sc->FinalPan = sc->Pan; - } - else - { - int8_t PanVal = 32 - sc->Pan; - if (PanVal < 0) - { - PanVal ^= 255; - PanVal -= 255; - } - PanVal = -PanVal; - PanVal += 32; - - const int8_t PanEnvVal = (int8_t)(sc->PanEnvState.Value >> 16); - PanVal = sc->Pan + ((PanVal * PanEnvVal) >> 5); - PanVal -= 32; - - sc->FinalPan = (int8_t)(((PanVal * (int8_t)(Song.Header.PanSep >> 1)) >> 6) + 32); // 8bb: 0..64 - assert(sc->FinalPan <= 64); - } - } - - UpdateAutoVibrato(sc); - } -} - -static void UpdateSamples(void) // 8bb: for songs without instruments -{ - slaveChn_t *sc = sChn; - for (uint32_t i = 0; i < Driver.NumChannels; i++, sc++) - { - if (!(sc->Flags & SF_CHAN_ON)) - continue; - - if (sc->Flags & SF_RECALC_VOL) // 8bb: recalculate volume - { - sc->Flags &= ~SF_RECALC_VOL; - sc->Flags |= SF_RECALC_FINALVOL; - - uint16_t volume = (((sc->Vol * sc->ChnVol * sc->SmpVol) >> 4) * Song.GlobalVolume) >> 7; - assert(volume <= 32768); - - sc->FinalVol15Bit = volume; // 8bb: 0..32768 - sc->FinalVol7Bit = volume >> 8; // 8bb: 0..128 - } - - if (sc->Flags & SF_RECALC_PAN) // 8bb: recalculate panning - { - sc->Flags &= ~SF_RECALC_PAN; - sc->Flags |= SF_PAN_CHANGED; - - if (sc->Pan == PAN_SURROUND) - { - sc->FinalPan = sc->Pan; - } - else - { - sc->FinalPan = ((((int8_t)sc->Pan - 32) * (int8_t)(Song.Header.PanSep >> 1)) >> 6) + 32; // 8bb: 0..64 - assert(sc->FinalPan <= 64); - } - } - - UpdateAutoVibrato(sc); - } -} - -void Update(void) -{ - slaveChn_t *sc = sChn; - for (uint32_t i = 0; i < MAX_SLAVE_CHANNELS; i++, sc++) - { - if (!(sc->Flags & SF_CHAN_ON)) - continue; - - if (sc->Vol != sc->VolSet) - { - sc->Vol = sc->VolSet; - sc->Flags |= SF_RECALC_VOL; - } - - if (sc->Frequency != sc->FrequencySet) - { - sc->Frequency = sc->FrequencySet; - sc->Flags |= SF_FREQ_CHANGE; - } - } - - UpdateData(); - - if (Song.Header.Flags & ITF_INSTR_MODE) - UpdateInstruments(); - else - UpdateSamples(); -} - -void Music_FillAudioBuffer(int16_t *buffer, int32_t numSamples) -{ - if (!Song.Playing) - { - memset(buffer, 0, numSamples * 2 * sizeof (int16_t)); - return; - } - - if (DriverMix != NULL) - DriverMix(numSamples, buffer); -} - -void Music_FillAudioBufferFloat(float *buffer, int32_t numSamples) -{ - if (!Song.Playing) - { - memset(buffer, 0, numSamples * 2 * sizeof (float)); - return; - } - - if (DriverMixFloat != NULL) - DriverMixFloat(numSamples, buffer); -} - -bool Music_Init(int32_t mixingFrequency, int32_t mixingBufferSize) -{ - if (FirstTimeInit) - { - memset(&Driver, 0, sizeof (Driver)); - FirstTimeInit = false; - } - else - { - Music_Close(); - } - - if (!SB16_InitDriver(mixingFrequency)) - return false; - - return true; -} - -void Music_Close(void) // 8bb: added this -{ - if (DriverClose != NULL) - DriverClose(); -} - -void Music_InitTempo(void) -{ - if (DriverSetTempo != NULL) - DriverSetTempo((uint8_t)Song.Tempo); -} - -void Music_Stop(void) -{ - Song.Playing = false; - - MIDITranslate(NULL, sChn, MIDICOMMAND_STOP); - - Song.DecodeExpectedPattern = 0xFFFE; - Song.DecodeExpectedRow = 0xFFFE; - Song.RowDelay = 1; - Song.RowDelayOn = false; - Song.CurrentRow = 0; - Song.CurrentOrder = 0; - Song.CurrentTick = 1; - Song.BreakRow = 0; - - // 8bb: clear host/slave channels - memset(hChn, 0, sizeof (hChn)); - memset(sChn, 0, sizeof (sChn)); - - hostChn_t *hc = hChn; - for (uint8_t i = 0; i < MAX_HOST_CHANNELS; i++, hc++) - { - hc->HostChnNum = i; - - // 8bb: set initial channel pan and channel vol - hc->ChnPan = Song.Header.ChnlPan[i] & 0x7F; - hc->ChnVol = Song.Header.ChnlVol[i]; - } - - slaveChn_t *sc = sChn; - for (uint32_t i = 0; i < MAX_SLAVE_CHANNELS; i++, sc++) - sc->Flags = SF_NOTE_STOP; - - if (Song.Loaded) - { - Song.GlobalVolume = Song.Header.GlobalVol; - Song.ProcessTick = Song.CurrentSpeed = Song.Header.InitialSpeed; - Song.Tempo = Song.Header.InitialTempo; - - Music_InitTempo(); - } -} - -void Music_StopChannels(void) -{ - hostChn_t *hc = hChn; - for (int32_t i = 0; i < MAX_HOST_CHANNELS; i++, hc++) - { - hc->Flags = 0; - hc->PattLoopStartRow = hc->PattLoopCount = 0; - } - - slaveChn_t *sc = sChn; - for (int32_t i = 0; i < MAX_SLAVE_CHANNELS; i++, sc++) - sc->Flags = SF_NOTE_STOP; -} - -void Music_PreviousOrder(void) -{ - if (!Song.Playing) - return; - - if (Song.CurrentOrder > 0) - { - Music_StopChannels(); - - Song.CurrentOrder -= 2; - Song.ProcessOrder = Song.CurrentOrder; - Song.ProcessRow = 0xFFFE; - Song.CurrentTick = 1; - Song.RowDelay = 1; - Song.RowDelayOn = false; - } -} - -void Music_NextOrder(void) -{ - if (!Song.Playing) - return; - - if (Song.CurrentOrder < 255) - { - Music_StopChannels(); - - Song.ProcessRow = 0xFFFE; - Song.CurrentTick = 1; - Song.RowDelay = 1; - Song.RowDelayOn = false; - } -} - -void Music_PlaySong(uint16_t order) -{ - if (!Song.Loaded) - return; - - Music_Stop(); - - MIDITranslate(NULL, sChn, MIDICOMMAND_START); // 8bb: this will reset channel filters - - Song.CurrentOrder = order; - Song.ProcessOrder = order - 1; - Song.ProcessRow = 0xFFFE; - - // 8bb: reset seed (IT2 only does this at tracker startup, but let's do it here) - RandSeed1 = 0x1234; - RandSeed2 = 0x5678; - - MIDIInterpretState = MIDIInterpretType = 0; // 8bb: clear MIDI filter interpretor state - - if (DriverResetMixer != NULL) - DriverResetMixer(); - - Song.Playing = true; -} - -void Music_ReleaseSample(uint32_t sample) -{ - assert(sample < MAX_SAMPLES); - sample_t *smp = &Song.Smp[sample]; - - if (smp->OrigData != NULL) free(smp->OrigData); - if (smp->OrigDataR != NULL) free(smp->OrigDataR); - - smp->Data = smp->OrigData = NULL; - smp->DataR = smp->OrigDataR = NULL; -} - -bool Music_AllocatePattern(uint32_t pattern, uint32_t length) -{ - assert(pattern < MAX_PATTERNS); - pattern_t *p = &Song.Patt[pattern]; - - if (p->PackedData != NULL) - return true; - - p->PackedData = (uint8_t *)malloc(length); - if (p->PackedData == NULL) - return false; - - return true; -} - -bool Music_AllocateSample(uint32_t sample, uint32_t length) -{ - assert(sample < MAX_SAMPLES); - sample_t *s = &Song.Smp[sample]; - - // 8bb: done a little differently than IT2 - - s->OrigData = (int8_t *)malloc(length + SAMPLE_PAD_LENGTH); // 8bb: extra bytes for interpolation taps, filled later - if (s->OrigData == NULL) - return false; - - memset((int8_t *)s->OrigData, 0, SMP_DAT_OFFSET); - memset((int8_t *)s->OrigData + length, 0, 32); - - // 8bb: offset sample so that we can fix negative interpolation taps - s->Data = (int8_t *)s->OrigData + SMP_DAT_OFFSET; - - s->Length = length; - s->Flags |= SMPF_ASSOCIATED_WITH_HEADER; - - return true; -} - -bool Music_AllocateRightSample(uint32_t sample, uint32_t length) // 8bb: added this -{ - assert(sample < MAX_SAMPLES); - sample_t *s = &Song.Smp[sample]; - - s->OrigDataR = (int8_t *)malloc(length + SAMPLE_PAD_LENGTH); // 8bb: extra bytes for interpolation taps, filled later - if (s->OrigDataR == NULL) - return false; - - memset((int8_t *)s->OrigDataR, 0, SMP_DAT_OFFSET); - memset((int8_t *)s->OrigDataR + length, 0, 32); - - // 8bb: offset sample so that we can fix negative interpolation taps - s->DataR = (int8_t *)s->OrigDataR + SMP_DAT_OFFSET; - - return true; -} - -void Music_ReleasePattern(uint32_t pattern) -{ - assert(pattern < MAX_PATTERNS); - pattern_t *p = &Song.Patt[pattern]; - - if (p->PackedData != NULL) - free(p->PackedData); - - p->Rows = 0; - p->PackedData = NULL; -} - -void Music_ReleaseAllPatterns(void) -{ - for (int32_t i = 0; i < MAX_PATTERNS; i++) - Music_ReleasePattern(i); -} - -void Music_ReleaseAllSamples(void) -{ - for (int32_t i = 0; i < MAX_SAMPLES; i++) - Music_ReleaseSample(i); -} - -void Music_FreeSong(void) // 8bb: added this -{ - Music_Stop(); - - Music_ReleaseAllPatterns(); - Music_ReleaseAllSamples(); - - memset(&Song, 0, sizeof (Song)); - memset(Song.Orders, 255, MAX_ORDERS); - - Song.Loaded = false; -} - -int32_t Music_GetActiveVoices(void) // 8bb: added this -{ - int32_t activeVoices = 0; - - slaveChn_t *sc = sChn; - for (int32_t i = 0; i < MAX_SLAVE_CHANNELS; i++, sc++) - { - if (!(sc->Flags & SF_NOTE_STOP) && (sc->Flags & SF_CHAN_ON)) - activeVoices++; - } - - return activeVoices; -} \ No newline at end of file diff --git a/libraries/m4p/it_music.h b/libraries/m4p/it_music.h deleted file mode 100644 index 1c48949c5..000000000 --- a/libraries/m4p/it_music.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include -#include -#include "it_structs.h" - -enum -{ - MIDICOMMAND_START = 0x0000, - MIDICOMMAND_STOP = 0x0020, - MIDICOMMAND_TICK = 0x0040, - MIDICOMMAND_PLAYNOTE = 0x0060, - MIDICOMMAND_STOPNOTE = 0x0080, - MIDICOMMAND_CHANGEVOLUME = 0x00A0, - MIDICOMMAND_CHANGEPAN = 0x00C0, - MIDICOMMAND_BANKSELECT = 0x00E0, - MIDICOMMAND_PROGRAMSELECT = 0x0100, - MIDICOMMAND_CHANGEPITCH = 0xFFFF -}; - -// 8bb: 31 is possible through initial tempo (but 32 is general minimum) -#define LOWEST_BPM_POSSIBLE 31 - -#define MIX_FRAC_BITS 16 -#define MIX_FRAC_MASK ((1 << MIX_FRAC_BITS)-1) - -/* 8bb: -** Amount of extra bytes to allocate for every instrument sample, -** this is used for a hack for resampling interpolation to be -** branchless in the inner channel mixer loop. -** Warning: Do not change this! -*/ -#define SMP_DAT_OFFSET 16 -#define SAMPLE_PAD_LENGTH (SMP_DAT_OFFSET+16) - -// 8bb: globalized -extern void (*DriverClose)(void); -extern void (*DriverMix)(int32_t, int16_t *); -extern void (*DriverMixFloat)(int32_t, float *); -extern void (*DriverResetMixer)(void); -extern int32_t (*DriverPostMix)(int16_t *, int32_t); -extern int32_t (*DriverPostMixFloat)(float *, int32_t); -extern void (*DriverMixSamples)(void); -extern void (*DriverSetTempo)(uint8_t); -extern void (*DriverSetMixVolume)(uint8_t); -extern void (*DriverFixSamples)(void); -// -------------------------------------------- - -void Music_SetDefaultMIDIDataArea(void); // 8bb: added this -char *Music_GetMIDIDataArea(void); -void RecalculateAllVolumes(void); -void MIDITranslate(hostChn_t *hc, slaveChn_t *sc, uint16_t Input); -void InitPlayInstrument(hostChn_t *hc, slaveChn_t *sc, instrument_t *ins); -slaveChn_t *AllocateChannel(hostChn_t *hc, uint8_t *hcFlags); -uint8_t Random(void); -void GetLoopInformation(slaveChn_t *sc); -void ApplyRandomValues(hostChn_t *hc); - -void PitchSlideUpLinear(hostChn_t *hc, slaveChn_t *sc, int16_t SlideValue); -void PitchSlideUp(hostChn_t *hc, slaveChn_t *sc, int16_t SlideValue); -void PitchSlideDown(hostChn_t *hc, slaveChn_t *sc, int16_t SlideValue); - -void Update(void); -void Music_FillAudioBuffer(int16_t *buffer, int32_t numSamples); -void Music_FillAudioBufferFloat(float *buffer, int32_t numSamples); - -bool Music_Init(int32_t mixingFrequency, int32_t mixingBufferSize); -void Music_Close(void); // 8bb: added this -void Music_Stop(void); -void Music_StopChannels(void); -void Music_PreviousOrder(void); -void Music_NextOrder(void); -void Music_PlaySong(uint16_t order); -void Music_InitTempo(void); - -bool Music_AllocateSample(uint32_t sample, uint32_t length); -bool Music_AllocateRightSample(uint32_t sample, uint32_t length); // 8bb: added this -void Music_ReleaseSample(uint32_t sample); -void Music_ReleaseAllSamples(void); - -bool Music_AllocatePattern(uint32_t pattern, uint32_t length); -void Music_ReleasePattern(uint32_t pattern); -void Music_ReleaseAllPatterns(void); -int32_t Music_GetActiveVoices(void); // 8bb: added this -void Music_FreeSong(void); // 8bb: added this \ No newline at end of file diff --git a/libraries/m4p/it_structs.h b/libraries/m4p/it_structs.h deleted file mode 100644 index 7117cada5..000000000 --- a/libraries/m4p/it_structs.h +++ /dev/null @@ -1,252 +0,0 @@ -#pragma once - -#include -#include - -#define CHN_DISOWNED 128 -#define DIR_FORWARDS 0 -#define DIR_BACKWARDS 1 -#define PAN_SURROUND 100 -#define LOOP_PINGPONG 24 -#define LOOP_FORWARDS 8 - -enum // 8bb: envelope flags -{ - ENVF_ENABLED = 1, - ENVF_LOOP = 2, - ENVF_SUSTAINLOOP = 4, - ENVF_CARRY = 8, - ENVF_TYPE_FILTER = 128 // 8bb: for pitch envelope only -}; - -enum // 8bb: sample flags -{ - SMPF_ASSOCIATED_WITH_HEADER = 1, - SMPF_16BIT = 2, - SMPF_STEREO = 4, - SMPF_COMPRESSED = 8, - SMPF_USE_LOOP = 16, - SMPF_USE_SUSTAINLOOP = 32, - SMPF_LOOP_PINGPONG = 64, - SMPF_SUSTAINLOOP_PINGPONG = 128 -}; - -enum // 8bb: host channel flags -{ - HF_UPDATE_EFX_IF_CHAN_ON = 1, - HF_ALWAYS_UPDATE_EFX = 2, - HF_CHAN_ON = 4, - HF_CHAN_CUT = 8, // No longer implemented - HF_PITCH_SLIDE_ONGOING = 16, - HF_FREEPLAY_NOTE = 32, // 8bb: Only needed for tracker. Logic removed. - HF_ROW_UPDATED = 64, - HF_APPLY_RANDOM_VOL = 128, - HF_UPDATE_VOLEFX_IF_CHAN_ON = 256, - HF_ALWAYS_VOLEFX = 512 -}; - -enum // 8bb: slave channel flags -{ - SF_CHAN_ON = 1, - SF_RECALC_PAN = 2, - SF_NOTE_OFF = 4, - SF_FADEOUT = 8, - SF_RECALC_VOL = 16, - SF_FREQ_CHANGE = 32, - SF_RECALC_FINALVOL = 64, - SF_CENTRAL_PAN = 128, - SF_NEW_NOTE = 256, - SF_NOTE_STOP = 512, - SF_LOOP_CHANGED = 1024, - SF_CHN_MUTED = 2048, - SF_VOLENV_ON = 4096, - SF_PANENV_ON = 8192, - SF_PITCHENV_ON = 16384, - SF_PAN_CHANGED = 32768 -}; - -enum // 8bb: IT header flags -{ - ITF_STEREO = 1, - ITF_VOL0_OPTIMIZATION = 2, // 8bb: not used in IT1.04 and later - ITF_INSTR_MODE = 4, - ITF_LINEAR_FRQ = 8, - ITF_OLD_EFFECTS = 16, - ITF_COMPAT_GXX = 32, - ITF_USE_MIDI_PITCH_CNTRL = 64, - ITF_REQ_MIDI_CFG = 128 -}; - -enum // 8bb: audio driver flags -{ - DF_SUPPORTS_MIDI = 1, - DF_USES_VOLRAMP = 2, // 8bb: aka. "hiqual" - DF_WAVEFORM = 4, // Output waveform data available - DF_HAS_RESONANCE_FILTER = 8 // 8bb: added this -}; - -// 8bb: do NOT change these, it will only mess things up! -#define MAX_PATTERNS 200 -#define MAX_SAMPLES 200 -#define MAX_INSTRUMENTS 200 -#define MAX_ORDERS 256 -#define MAX_ROWS 200 -#define MAX_HOST_CHANNELS 64 -#define MAX_SLAVE_CHANNELS 256 -#define MAX_SONGMSG_LENGTH 8000 - -typedef struct pattern_t -{ - uint16_t Rows; - uint8_t *PackedData; -} pattern_t; - -typedef struct envNode_t -{ - int8_t Magnitude; - uint16_t Tick; -} envNode_t; - -typedef struct env_t -{ - uint8_t Flags, Num, LoopBegin, LoopEnd, SustainLoopBegin, SustainLoopEnd; - envNode_t NodePoints[25]; -} env_t; - -typedef struct instrument_t -{ - char DOSFilename[12+1]; - uint8_t NNA, DCT, DCA; - uint16_t FadeOut; - uint8_t PitchPanSep, PitchPanCenter, GlobVol, DefPan, RandVol, RandPan; - char InstrumentName[26]; - uint8_t FilterCutoff, FilterResonance, MIDIChn, MIDIProg; - uint16_t MIDIBank; - uint16_t SmpNoteTable[120]; - env_t VolEnv, PanEnv, PitchEnv; -} instrument_t; - -typedef struct smp_t -{ - char DOSFilename[12+1]; - uint8_t GlobVol, Flags, Vol; - char SampleName[26]; - uint8_t Cvt, DefPan; - uint32_t Length, LoopBegin, LoopEnd, C5Speed, SustainLoopBegin, SustainLoopEnd, OffsetInFile; - uint8_t AutoVibratoSpeed, AutoVibratoDepth, AutoVibratoRate, AutoVibratoWaveform; - void *Data; - - // 8bb: added this for custom HQ driver - void *OrigData, *DataR, *OrigDataR; -} sample_t; - -typedef struct hostChn_t -{ - uint16_t Flags; - uint8_t NotePackMask, RawNote, Ins, Vol, Cmd, CmdVal, OldCmd, OldCmdVal, VolCmd, VolCmdVal; - uint8_t MIDIChn, MIDIProg, TranslatedNote, Smp; - uint8_t DKL, EFG, O00, I00, J00, M00, N00, P00, Q00, T00, S00, W00, GOE, SFx; - uint8_t HighSmpOffs; - uint8_t HostChnNum, VolSet; - void *SlaveChnPtr; - uint8_t PattLoopStartRow, PattLoopCount; - uint8_t PanbrelloWaveform, PanbrelloPos, PanbrelloDepth, PanbrelloSpeed, LastPanbrelloData; - int8_t LastVibratoData, LastTremoloData; - uint8_t ChnPan, ChnVol; - int8_t VolSlideDelta; - uint8_t TremorCount, TremorOnOff, RetrigCount; - int32_t PortaFreq; - uint8_t VibratoWaveform, VibratoPos, VibratoDepth, VibratoSpeed; - uint8_t TremoloWaveform, TremoloPos, TremoloDepth, TremoloSpeed; - uint8_t MiscEfxData[16]; -} hostChn_t; - -typedef struct envState_t -{ - int32_t Value, Delta; - int16_t Tick, CurNode, NextTick; -} envState_t; - -typedef struct slaveChn_t -{ - uint16_t Flags; - uint32_t MixOffset; // 8bb: which sample mix function to use - uint8_t LoopMode, LoopDirection; - int32_t LeftVolume, RightVolume; - int32_t Frequency, FrequencySet; - uint8_t SmpBitDepth, AutoVibratoPos; - uint16_t AutoVibratoDepth; - int32_t OldLeftVolume, OldRightVolume; - uint8_t FinalVol7Bit, Vol, VolSet, ChnVol, SmpVol, FinalPan; - uint16_t FadeOut; - uint8_t DCT, DCA, Pan, PanSet; - instrument_t *InsPtr; - sample_t *SmpPtr; - uint8_t Note, Ins; - uint8_t Smp; - void *HostChnPtr; - uint8_t HostChnNum, NNA, MIDIChn, MIDIProg; - uint16_t MIDIBank; - int32_t LoopBegin, LoopEnd; - uint32_t Frac32; - uint16_t FinalVol15Bit; - int32_t SamplingPosition; - int32_t filtera, filterb, filterc; - envState_t VolEnvState, PanEnvState, PitchEnvState; - - // 8bb: added these - uint32_t Delta32; - int32_t OldSamples[2]; - int32_t DestVolL, DestVolR, CurrVolL, CurrVolR; // 8bb: ramp - float fOldSamples[4], fFiltera, fFilterb, fFilterc; - - // 8bb: for custom HQ mixer - float fOldLeftVolume, fOldRightVolume, fLeftVolume, fRightVolume; - float fDestVolL, fDestVolR, fCurrVolL, fCurrVolR; - uint64_t Frac64, Delta64; -} slaveChn_t; - -typedef struct it_header_t -{ - char SongName[26]; - uint16_t OrdNum, InsNum, SmpNum, PatNum, Cwtv, Cmwt, Flags, Special; - uint8_t GlobalVol, MixVolume, InitialSpeed, InitialTempo, PanSep; - uint16_t MessageLength; - uint32_t MessageOffset; - uint8_t ChnlPan[MAX_HOST_CHANNELS], ChnlVol[MAX_HOST_CHANNELS]; -} it_header_t; - -typedef struct // 8bb: custom struct -{ - uint32_t NumChannels; - uint8_t Flags, FilterParameters[128]; - uint32_t MixMode, MixSpeed; - int32_t Delta32; - int64_t Delta64; - float QualityFactorTable[128], FreqParameterMultiplier, FreqMultiplier; -} driver_t; - -typedef struct song_t -{ - it_header_t Header; - uint8_t Orders[MAX_ORDERS]; - instrument_t Ins[MAX_INSTRUMENTS]; - sample_t Smp[MAX_SAMPLES]; - pattern_t Patt[MAX_PATTERNS]; - char Message[MAX_SONGMSG_LENGTH+1]; // 8bb: +1 to fit protection-NUL - - volatile bool Playing, Loaded; - uint8_t *PatternOffset, LastMIDIByte; - uint16_t CurrentOrder, CurrentPattern, CurrentRow, ProcessOrder, ProcessRow; - uint16_t BreakRow; - uint8_t RowDelay; - bool RowDelayOn, StopSong, PatternLooping; - uint16_t NumberOfRows, CurrentTick, CurrentSpeed, ProcessTick; - uint16_t Tempo, GlobalVolume; - uint16_t DecodeExpectedPattern, DecodeExpectedRow; -} song_t; - -extern hostChn_t hChn[MAX_HOST_CHANNELS]; -extern slaveChn_t sChn[MAX_SLAVE_CHANNELS]; -extern song_t Song; -extern driver_t Driver; diff --git a/libraries/m4p/it_tables.c b/libraries/m4p/it_tables.c deleted file mode 100644 index 74fec7962..000000000 --- a/libraries/m4p/it_tables.c +++ /dev/null @@ -1,212 +0,0 @@ -/* Bit-accurate IT2 tables. Copied from IT2 source code. -** All of the comments in this file are written by me (8bitbubsy) -*/ - -#include - -/* Formula: -** for (i = 0; i < 10*12; i++) -** LUT[i] = round(pow(2.0, (i - 5*12) / 12.0) * 65536.0); -*/ -const uint32_t PitchTable[120] = -{ - 2048, 2170, 2299, 2435, 2580, 2734, 2896, 3069, 3251, 3444, 3649, 3866, - 4096, 4340, 4598, 4871, 5161, 5468, 5793, 6137, 6502, 6889, 7298, 7732, - 8192, 8679, 9195, 9742, 10321, 10935, 11585, 12274, 13004, 13777, 14596, 15464, - 16384, 17358, 18390, 19484, 20643, 21870, 23170, 24548, 26008, 27554, 29193, 30929, - 32768, 34716, 36781, 38968, 41285, 43740, 46341, 49097, 52016, 55109, 58386, 61858, - 65536, 69433, 73562, 77936, 82570, 87480, 92682, 98193, 104032, 110218, 116772, 123715, - 131072, 138866, 147123, 155872, 165140, 174960, 185364, 196386, 208064, 220436, 233544, 247431, - 262144, 277732, 294247, 311744, 330281, 349920, 370728, 392772, 416128, 440872, 467088, 494862, - 524288, 555464, 588493, 623487, 660561, 699841, 741455, 785544, 832255, 881744, 934175, 989724, - 1048576, 1110928, 1176987, 1246974, 1321123, 1399681, 1482910, 1571089, 1664511, 1763488, 1868350, 1979448 -}; - -/* Formula: -** for (i = 0; i < 256; i++) -** { -** LUT[(256*0)+i] = round(sin(i * (2.0 * M_PI / 256.0)) * 64.0); // sine -** LUT[(256*1)+i] = (128 - i) >> 1; // ramp -** LUT[(256*2)+i] = ((i ^ 255) & 128) >> 1; // square -** } -*/ -const int8_t FineSineData[3 * 256] = // sine/ramp/square -{ - 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, - 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, - 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, - 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, - 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, - 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, - 0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23, - -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, - -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, - -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, - -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, - -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, - -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, - -24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2, - - 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56, - 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, - 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, - 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, - 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, - 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16, - 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, - 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, - 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8, - -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16, - -16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24, - -24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32, - -32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40, - -40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48, - -48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56, - -56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64, - - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 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, 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, 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, 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, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -#ifndef USEFPUCODE - -/* Formula: -** for (i = 0; i < 16; i++) -** LUT[i] = round(pow(2.0, i / 768.0) * 65536.0); -*/ -const uint32_t FineLinearSlideUpTable[16] = -{ - 65536, 65595, 65654, 65714, 65773, 65832, 65892, 65951, - 66011, 66071, 66130, 66190, 66250, 66309, 66369, 66429 -}; - -/* Formula (can't be used to recreate LUT): -** for (i = 0; i < 257; i++) -** LUT[i] = round(pow(2.0, i / 192.0) * 65536.0); -** -** Note: -** The following entries have unexplainable errors: 21,35,108,152,201,243 -*/ -const uint32_t LinearSlideUpTable[257] = -{ - 65536, 65773, 66011, 66250, 66489, 66730, 66971, 67213, - 67456, 67700, 67945, 68191, 68438, 68685, 68933, 69183, - 69433, 69684, 69936, 70189, 70443, 70693, 70953, 71210, - 71468, 71726, 71985, 72246, 72507, 72769, 73032, 73297, - 73562, 73828, 74095, 73563, 74632, 74902, 75172, 75444, - 75717, 75991, 76266, 76542, 76819, 77096, 77375, 77655, - 77936, 78218, 78501, 78785, 79069, 79355, 79642, 79930, - 80220, 80510, 80801, 81093, 81386, 81681, 81976, 82273, - 82570, 82869, 83169, 83469, 83771, 84074, 84378, 84683, - 84990, 85297, 85606, 85915, 86226, 86538, 86851, 87165, - 87480, 87796, 88114, 88433, 88752, 89073, 89396, 89719, - 90043, 90369, 90696, 91024, 91353, 91684, 92015, 92348, - 92682, 93017, 93354, 93691, 94030, 94370, 94711, 95054, - 95398, 95743, 96089, 96436, 96784, 97135, 97487, 97839, - 98193, 98548, 98905, 99262, 99621, 99982, 100343, 100706, - 101070, 101436, 101803, 102171, 102540, 102911, 103283, 103657, - 104032, 104408, 104786, 105165, 105545, 105927, 106310, 106694, - 107080, 107468, 107856, 108246, 108638, 109031, 109425, 109821, - 110218, 110617, 111017, 111418, 111821, 112226, 112631, 113039, - 113453, 113858, 114270, 114683, 115098, 115514, 115932, 116351, - 116772, 117194, 117618, 118043, 118470, 118899, 119329, 119760, - 120194, 120628, 121065, 121502, 121942, 122383, 122825, 123270, - 123715, 124163, 124612, 125063, 125515, 125969, 126425, 126882, - 127341, 127801, 128263, 128727, 129193, 129660, 130129, 130600, - 131072, 131546, 132022, 132499, 132978, 133459, 133942, 134427, - 134913, 135399, 135890, 136382, 136875, 137370, 137867, 138366, - 138866, 139368, 139872, 140378, 140886, 141395, 141907, 142420, - 142935, 143452, 143971, 144491, 145014, 145539, 146065, 146593, - 147123, 147655, 148189, 148725, 149263, 149803, 150345, 150889, - 151434, 151982, 152532, 153083, 153637, 154193, 154750, 155310, - 155872, 156435, 157001, 156569, 158139, 158711, 159285, 159861, - 160439, 161019, 161602, 162186, 162773, 163361, 163952, 164545, - 165140 -}; - -/* Formula (can't be used to recreate LUT): -** for (i = 0; i < 16; i++) -** { -** x = round(pow(2.0, i / -768.0) * 65536.0); -** if (x > 65535) -** x = 65535; -** -** LUT[i] = x; -** } -** -** Note: -** The following entries have unexplainable errors: 7,11,15 -*/ -const uint16_t FineLinearSlideDownTable[16] = -{ - 65535, 65477, 65418, 65359, 65300, 65241, 65182, 65359, - 65065, 65006, 64947, 64888, 64830, 64772, 64713, 64645 -}; - -/* Formula (can't be used to recreate LUT): -** for (i = 0; i < 257; i++) -** { -** x = round(pow(2.0, i / -192.0) * 65536.0); -** if (x > 65535) -** x = 65535; -** -** LUT[i] = x; -** } -** -** Note: -** The following entries have unexplainable errors: 85,132,133,135,214 -*/ -const uint16_t LinearSlideDownTable[257] = -{ - 65535, 65300, 65065, 64830, 64596, 64364, 64132, 63901, - 63670, 63441, 63212, 62984, 62757, 62531, 62306, 62081, - 61858, 61635, 61413, 61191, 60971, 60751, 60532, 60314, - 60097, 59880, 59664, 59449, 59235, 59022, 58809, 58597, - 58386, 58176, 57966, 57757, 57549, 57341, 57135, 56929, - 56724, 56519, 56316, 56113, 55911, 55709, 55508, 55308, - 55109, 54910, 54713, 54515, 54319, 54123, 53928, 53734, - 53540, 53347, 53155, 52963, 52773, 52582, 52393, 52204, - 52016, 51829, 51642, 51456, 51270, 51085, 50901, 50718, - 50535, 50353, 50172, 49991, 49811, 49631, 49452, 49274, - 49097, 48920, 48743, 48568, 48393, 48128, 48044, 47871, - 47699, 47527, 47356, 47185, 47015, 46846, 46677, 46509, - 46341, 46174, 46008, 45842, 45677, 45512, 45348, 45185, - 45022, 44859, 44698, 44537, 44376, 44216, 44057, 43898, - 43740, 43582, 43425, 43269, 43113, 42958, 42803, 42649, - 42495, 42342, 42189, 42037, 41886, 41735, 41584, 41434, - 41285, 41136, 40988, 40840, 40639, 40566, 40400, 40253, - 40110, 39965, 39821, 39678, 39535, 39392, 39250, 39109, - 38968, 38828, 38688, 38548, 38409, 38271, 38133, 37996, - 37859, 37722, 37586, 37451, 37316, 37181, 37047, 36914, - 36781, 36648, 36516, 36385, 36254, 36123, 35993, 35863, - 35734, 35605, 35477, 35349, 35221, 35095, 34968, 34842, - 34716, 34591, 34467, 34343, 34219, 34095, 33973, 33850, - 33728, 33607, 33486, 33365, 33245, 33125, 33005, 32887, - 32768, 32650, 32532, 32415, 32298, 32182, 32066, 31950, - 31835, 31720, 31606, 31492, 31379, 31266, 31153, 31041, - 30929, 30817, 30706, 30596, 30485, 30376, 30226, 30157, - 30048, 29940, 29832, 29725, 29618, 29511, 29405, 29299, - 29193, 29088, 28983, 28879, 28774, 28671, 28567, 28464, - 28362, 28260, 28158, 28056, 27955, 27855, 27754, 27654, - 27554, 27455, 27356, 27258, 27159, 27062, 26964, 26867, - 26770, 26674, 26577, 26482, 26386, 26291, 26196, 26102, - 26008 -}; - -#endif diff --git a/libraries/m4p/it_tables.h b/libraries/m4p/it_tables.h deleted file mode 100644 index 637a3a550..000000000 --- a/libraries/m4p/it_tables.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -extern const uint32_t PitchTable[120]; -extern const int8_t FineSineData[3 * 256]; // 8bb: sine/ramp/square - -#ifndef USEFPUCODE -extern const uint32_t FineLinearSlideUpTable[16]; -extern const uint32_t LinearSlideUpTable[257]; -extern const uint16_t FineLinearSlideDownTable[16]; -extern const uint16_t LinearSlideDownTable[257]; -#endif diff --git a/libraries/m4p/loaders/it.c b/libraries/m4p/loaders/it.c deleted file mode 100644 index ded0e8da2..000000000 --- a/libraries/m4p/loaders/it.c +++ /dev/null @@ -1,744 +0,0 @@ -// all of the comments in this file are written by me (8bitbubsy) - -// for finding memory leaks in debug mode with Visual Studio -#if defined _DEBUG && defined _MSC_VER -#include -#endif - -#include -#include -#include -#include -#include -#include "../it_music.h" -#include "../it_structs.h" -#include "../it_d_rm.h" - -static void Decompress16BitData(int16_t *Dst, const uint8_t *Src, uint32_t BlockLen); -static void Decompress8BitData(int8_t *Dst, const uint8_t *Src, uint32_t BlockLen); -static bool LoadCompressed16BitSample(MEMFILE *m, sample_t *s, bool Stereo, bool DeltaEncoded); -static bool LoadCompressed8BitSample(MEMFILE *m, sample_t *s, bool Stereo, bool DeltaEncoded); - -bool LoadIT(MEMFILE *m) -{ - /* - ** =================================== - ** =========== LOAD HEADER =========== - ** =================================== - */ - - mseek(m, 4, SEEK_CUR); - if (!ReadBytes(m, Song.Header.SongName, 25)) return false; - mseek(m, 1+2, SEEK_CUR); - if (!ReadBytes(m, &Song.Header.OrdNum, 2)) return false; - if (!ReadBytes(m, &Song.Header.InsNum, 2)) return false; - if (!ReadBytes(m, &Song.Header.SmpNum, 2)) return false; - if (!ReadBytes(m, &Song.Header.PatNum, 2)) return false; - if (!ReadBytes(m, &Song.Header.Cwtv, 2)) return false; - if (!ReadBytes(m, &Song.Header.Cmwt, 2)) return false; - if (!ReadBytes(m, &Song.Header.Flags, 2)) return false; - if (!ReadBytes(m, &Song.Header.Special, 2)) return false; - if (!ReadBytes(m, &Song.Header.GlobalVol, 1)) return false; - if (!ReadBytes(m, &Song.Header.MixVolume, 1)) return false; - if (!ReadBytes(m, &Song.Header.InitialSpeed, 1)) return false; - if (!ReadBytes(m, &Song.Header.InitialTempo, 1)) return false; - if (!ReadBytes(m, &Song.Header.PanSep, 1)) return false; - mseek(m, 1, SEEK_CUR); - if (!ReadBytes(m, &Song.Header.MessageLength, 2)) return false; - if (!ReadBytes(m, &Song.Header.MessageOffset, 4)) return false; - mseek(m, 4, SEEK_CUR); // skip unwanted stuff - if (!ReadBytes(m, Song.Header.ChnlPan, MAX_HOST_CHANNELS)) return false; - if (!ReadBytes(m, Song.Header.ChnlVol, MAX_HOST_CHANNELS)) return false; - - // IT2 doesn't do this test, but I do it for safety. - if (Song.Header.OrdNum > MAX_ORDERS+1 || Song.Header.InsNum > MAX_INSTRUMENTS || - Song.Header.SmpNum > MAX_SAMPLES || Song.Header.PatNum > MAX_PATTERNS) - { - return false; - } - - // IT2 doesn't do this, but let's do it for safety - if (Song.Header.MessageLength > MAX_SONGMSG_LENGTH) - Song.Header.MessageLength = MAX_SONGMSG_LENGTH; - - Song.Header.SongName[25] = '\0'; // just in case... - - /* *absolute* lowest possible initial tempo is 31, we need to clamp - ** it for safety reasons (yes, IT2 can do 31 as initial tempo!). - */ - if (Song.Header.InitialTempo < LOWEST_BPM_POSSIBLE) - Song.Header.InitialTempo = LOWEST_BPM_POSSIBLE; - - int32_t PtrListOffset = 192 + Song.Header.OrdNum; - - int16_t OrdersToLoad = Song.Header.OrdNum - 1; // IT2 does this (removes the count for the last 255 terminator) - if (OrdersToLoad > 0) - { - if (!ReadBytes(m, Song.Orders, OrdersToLoad)) - return false; - - // fill rest of order list with 255 - if (OrdersToLoad < MAX_ORDERS) - memset(&Song.Orders[OrdersToLoad], 255, MAX_ORDERS-OrdersToLoad); - } - else - { - memset(Song.Orders, 255, MAX_ORDERS); - } - - mseek(m, 192 + Song.Header.OrdNum + ((Song.Header.InsNum + Song.Header.SmpNum + Song.Header.PatNum) * 4), SEEK_SET); - - // skip time data, if present - if (Song.Header.Special & 2) - { - uint16_t NumTimerData; - ReadBytes(m, &NumTimerData, 2); - mseek(m, NumTimerData * 8, SEEK_CUR); - } - - // read embedded MIDI configuration, if preset (needed for Zxx macros) - char *MIDIDataArea = Music_GetMIDIDataArea(); - if (Song.Header.Special & 8) - ReadBytes(m, MIDIDataArea, (9+16+128)*32); - - // load song message, if present - if ((Song.Header.Special & 1) && Song.Header.MessageLength > 0 && Song.Header.MessageOffset > 0) - { - mseek(m, Song.Header.MessageOffset, SEEK_SET); - mread(Song.Message, 1, Song.Header.MessageLength, m); - Song.Message[MAX_SONGMSG_LENGTH] = '\0'; // just in case - } - - /* - ** =================================== - ** ======== LOAD INSTRUMENTS ========= - ** =================================== - */ - - mseek(m, PtrListOffset, SEEK_SET); - size_t InsPtrOffset = mtell(m); - - instrument_t *ins = Song.Ins; - for (uint32_t i = 0; i < Song.Header.InsNum; i++, ins++) - { - mseek(m, InsPtrOffset + (i * 4), SEEK_SET); - if (meof(m)) return false; - - uint32_t InsOffset; - if (!ReadBytes(m, &InsOffset, 4)) return false; - - if (InsOffset == 0) - continue; - - mseek(m, InsOffset, SEEK_SET); - if (meof(m)) return false; - - if (Song.Header.Cmwt >= 0x200) - { - mseek(m, 4, SEEK_CUR); // skip unwanted stuff - if (!ReadBytes(m, ins->DOSFilename, 13)) return false; - if (!ReadBytes(m, &ins->NNA, 1)) return false; - if (!ReadBytes(m, &ins->DCT, 1)) return false; - if (!ReadBytes(m, &ins->DCA, 1)) return false; - if (!ReadBytes(m, &ins->FadeOut, 2)) return false; - if (!ReadBytes(m, &ins->PitchPanSep, 1)) return false; - if (!ReadBytes(m, &ins->PitchPanCenter, 1)) return false; - if (!ReadBytes(m, &ins->GlobVol, 1)) return false; - if (!ReadBytes(m, &ins->DefPan, 1)) return false; - if (!ReadBytes(m, &ins->RandVol, 1)) return false; - if (!ReadBytes(m, &ins->RandPan, 1)) return false; - mseek(m, 4, SEEK_CUR); // skip unwanted stuff - if (!ReadBytes(m, ins->InstrumentName, 26)) return false; - if (!ReadBytes(m, &ins->FilterCutoff, 1)) return false; - if (!ReadBytes(m, &ins->FilterResonance, 1)) return false; - if (!ReadBytes(m, &ins->MIDIChn, 1)) return false; - if (!ReadBytes(m, &ins->MIDIProg, 1)) return false; - if (!ReadBytes(m, &ins->MIDIBank, 2)) return false; - if (!ReadBytes(m, &ins->SmpNoteTable, 2*120)) return false; - - // just in case - ins->DOSFilename[12] = '\0'; - ins->InstrumentName[25] = '\0'; - - // read envelopes - for (uint32_t j = 0; j < 3; j++) - { - env_t *env; - - if (j == 0) env = &ins->VolEnv; - else if (j == 1) env = &ins->PanEnv; - else env = &ins->PitchEnv; - - if (!ReadBytes(m, &env->Flags, 1)) return false; - if (!ReadBytes(m, &env->Num, 1)) return false; - if (!ReadBytes(m, &env->LoopBegin, 1)) return false; - if (!ReadBytes(m, &env->LoopEnd, 1)) return false; - if (!ReadBytes(m, &env->SustainLoopBegin, 1)) return false; - if (!ReadBytes(m, &env->SustainLoopEnd, 1)) return false; - - envNode_t *node = env->NodePoints; - for (uint32_t k = 0; k < 25; k++, node++) - { - if (!ReadBytes(m, &node->Magnitude, 1)) return false; - if (!ReadBytes(m, &node->Tick, 2)) return false; - } - - mseek(m, 1, SEEK_CUR); // skip unwanted stuff - } - } - else // old instruments (v1.xx) - { - mseek(m, 4, SEEK_CUR); // skip unwanted stuff - if (!ReadBytes(m, ins->DOSFilename, 13)) return false; - if (!ReadBytes(m, &ins->VolEnv.Flags, 1)) return false; - if (!ReadBytes(m, &ins->VolEnv.LoopBegin, 1)) return false; - if (!ReadBytes(m, &ins->VolEnv.LoopEnd, 1)) return false; - if (!ReadBytes(m, &ins->VolEnv.SustainLoopBegin, 1)) return false; - if (!ReadBytes(m, &ins->VolEnv.SustainLoopEnd, 1)) return false; - mseek(m, 2, SEEK_CUR); // skip unwanted stuff - if (!ReadBytes(m, &ins->FadeOut, 2)) return false; - if (!ReadBytes(m, &ins->NNA, 1)) return false; - if (!ReadBytes(m, &ins->DCT, 1)) return false; - mseek(m, 4, SEEK_CUR); // skip unwanted stuff - if (!ReadBytes(m, ins->InstrumentName, 26)) return false; - mseek(m, 6, SEEK_CUR); // skip unwanted stuff - if (!ReadBytes(m, &ins->SmpNoteTable, 2*120)) return false; - - ins->FadeOut *= 2; - - // just in case - ins->DOSFilename[12] = '\0'; - ins->InstrumentName[25] = '\0'; - - // set default values not present in old instrument - ins->PitchPanCenter = 60; - ins->GlobVol = 128; - ins->DefPan = 32 + 128; // center + pan disabled - - mseek(m, 200, SEEK_CUR); - - // read volume envelope - uint8_t j; - for (j = 0; j < 25; j++) - { - uint16_t word; - envNode_t *node = &ins->VolEnv.NodePoints[j]; - - if (!ReadBytes(m, &word, 2)) return false; - if (word == 0xFFFF) - break; // end of envelope - - node->Tick = word & 0xFF; - node->Magnitude = word >> 8; - } - - ins->VolEnv.Num = j; - - ins->PanEnv.Num = 2; - ins->PanEnv.NodePoints[1].Tick = 99; - - ins->PitchEnv.Num = 2; - ins->PitchEnv.NodePoints[1].Tick = 99; - } - } - - /* - ** =================================== - ** ======= LOAD SAMPLE HEADERS ======= - ** =================================== - */ - - mseek(m, PtrListOffset + (Song.Header.InsNum * 4), SEEK_SET); - size_t SmpPtrOffset = mtell(m); - - sample_t *s = Song.Smp; - for (uint32_t i = 0; i < Song.Header.SmpNum; i++, s++) - { - mseek(m, SmpPtrOffset + (i * 4), SEEK_SET); - if (meof(m)) return false; - - uint32_t SmpOffset; - if (!ReadBytes(m, &SmpOffset, 4)) return false; - - if (SmpOffset == 0) - continue; - - mseek(m, SmpOffset, SEEK_SET); - if (meof(m)) return false; - - mseek(m, 4, SEEK_CUR); // skip unwanted stuff - if (!ReadBytes(m, s->DOSFilename, 13)) return false; - if (!ReadBytes(m, &s->GlobVol, 1)) return false; - if (!ReadBytes(m, &s->Flags, 1)) return false; - if (!ReadBytes(m, &s->Vol, 1)) return false; - if (!ReadBytes(m, s->SampleName, 26)) return false; - if (!ReadBytes(m, &s->Cvt, 1)) return false; - if (!ReadBytes(m, &s->DefPan, 1)) return false; - if (!ReadBytes(m, &s->Length, 4)) return false; - if (!ReadBytes(m, &s->LoopBegin, 4)) return false; - if (!ReadBytes(m, &s->LoopEnd, 4)) return false; - if (!ReadBytes(m, &s->C5Speed, 4)) return false; - if (!ReadBytes(m, &s->SustainLoopBegin, 4)) return false; - if (!ReadBytes(m, &s->SustainLoopEnd, 4)) return false; - if (!ReadBytes(m, &s->OffsetInFile, 4)) return false; - if (!ReadBytes(m, &s->AutoVibratoSpeed, 1)) return false; - if (!ReadBytes(m, &s->AutoVibratoDepth, 1)) return false; - if (!ReadBytes(m, &s->AutoVibratoRate, 1)) return false; - if (!ReadBytes(m, &s->AutoVibratoWaveform, 1)) return false; - - // just in case - s->DOSFilename[12] = '\0'; - s->SampleName[25] = '\0'; - } - - /* =================================== - ** ======== LOAD SAMPLE DATA ========= - ** =================================== - */ - - s = Song.Smp; - for (uint32_t i = 0; i < Song.Header.SmpNum; i++, s++) - { - if (s->OffsetInFile == 0 || !(s->Flags & SMPF_ASSOCIATED_WITH_HEADER)) - continue; - - mseek(m, s->OffsetInFile, SEEK_SET); - if (meof(m)) - continue; // This WAS a return false...will I regret this? - Dasho - - bool Stereo = !!(s->Flags & SMPF_STEREO); // added stereo support - bool Compressed = !!(s->Flags & SMPF_COMPRESSED); - bool Sample16Bit = !!(s->Flags & SMPF_16BIT); - bool SignedSamples = !!(s->Cvt & 1); - bool DeltaEncoded = !!(s->Cvt & 4); - - if (DeltaEncoded && !Compressed) - continue; - - if (s->Length == 0 || !(s->Flags & SMPF_ASSOCIATED_WITH_HEADER)) - continue; // safely skip this sample - - if (s->Cvt & 0b11111010) - continue; // not supported - - if (!Music_AllocateSample(i, s->Length << Sample16Bit)) - return false; - - // added stereo support - if (Stereo) - { - if (!Music_AllocateRightSample(i, s->Length << Sample16Bit)) - return false; - } - - if (Compressed) - { - if (Sample16Bit) - { - if (!LoadCompressed16BitSample(m, s, Stereo, DeltaEncoded)) - return false; - } - else - { - if (!LoadCompressed8BitSample(m, s, Stereo, DeltaEncoded)) - return false; - } - } - else - { - mread(s->Data, 1, s->Length, m); - - // added stereo support for custom HQ driver - if (Stereo) - mread(s->DataR, 1, s->Length, m); - } - - // convert unsigned sample to signed - if (!SignedSamples) - { - if (Sample16Bit) - { - int16_t *Ptr16 = (int16_t *)s->Data; - for (uint32_t j = 0; j < s->Length; j++) - Ptr16[j] ^= 0x8000; - } - else - { - int8_t *Ptr8 = (int8_t *)s->Data; - for (uint32_t j = 0; j < s->Length; j++) - Ptr8[j] ^= 0x80; - } - } - - if (Sample16Bit) // Music_AllocateSample() also set s->Length, divide by two if 16-bit - s->Length >>= 1; - } - - /* - ** =================================== - ** ========== LOAD PATTERNS ========== - ** =================================== - */ - - mseek(m, PtrListOffset + (Song.Header.InsNum * 4) + (Song.Header.SmpNum * 4), SEEK_SET); - size_t PatPtrOffset = mtell(m); - - pattern_t *p = Song.Patt; - for (uint32_t i = 0; i < Song.Header.PatNum; i++, p++) - { - mseek(m, PatPtrOffset + (i * 4), SEEK_SET); - if (meof(m)) - return false; - - uint32_t PatOffset; - if (!ReadBytes(m, &PatOffset, 4)) return false; - - if (PatOffset == 0) - continue; - - mseek(m, PatOffset, SEEK_SET); - if (meof(m)) - return false; - - uint16_t PatLength; - if (!ReadBytes(m, &PatLength, 2)) return false; - if (!ReadBytes(m, &p->Rows, 2)) return false; - - if (PatLength == 0 || p->Rows == 0) - continue; - - mseek(m, 4, SEEK_CUR); - - if (!Music_AllocatePattern(i, PatLength)) return false; - if (!ReadBytes(m, p->PackedData, PatLength)) return false; - } - - return true; -} - -static void Decompress16BitData(int16_t *Dst, const uint8_t *Src, uint32_t BlockLength) -{ - uint8_t Byte8, BitDepth, BitDepthInv, BitsRead; - uint16_t Bytes16, LastVal; - uint32_t Bytes32; - - LastVal = 0; - BitDepth = 17; - BitDepthInv = BitsRead = 0; - - BlockLength >>= 1; - while (BlockLength != 0) - { - Bytes32 = (*(uint32_t *)Src) >> BitsRead; - - BitsRead += BitDepth; - Src += BitsRead >> 3; - BitsRead &= 7; - - if (BitDepth <= 6) - { - Bytes32 <<= BitDepthInv & 0x1F; - - Bytes16 = (uint16_t)Bytes32; - if (Bytes16 != 0x8000) - { - LastVal += (int16_t)Bytes16 >> (BitDepthInv & 0x1F); // arithmetic shift - *Dst++ = LastVal; - BlockLength--; - } - else - { - Byte8 = ((Bytes32 >> 16) & 0xF) + 1; - if (Byte8 >= BitDepth) - Byte8++; - BitDepth = Byte8; - - BitDepthInv = 16; - if (BitDepthInv < BitDepth) - BitDepthInv++; - BitDepthInv -= BitDepth; - - BitsRead += 4; - } - - continue; - } - - Bytes16 = (uint16_t)Bytes32; - - if (BitDepth <= 16) - { - uint16_t DX = 0xFFFF >> (BitDepthInv & 0x1F); - Bytes16 &= DX; - DX = (DX >> 1) - 8; - - if (Bytes16 > DX+16 || Bytes16 <= DX) - { - Bytes16 <<= BitDepthInv & 0x1F; - Bytes16 = (int16_t)Bytes16 >> (BitDepthInv & 0x1F); // arithmetic shift - LastVal += Bytes16; - *Dst++ = LastVal; - BlockLength--; - continue; - } - - Byte8 = (uint8_t)(Bytes16 - DX); - if (Byte8 >= BitDepth) - Byte8++; - BitDepth = Byte8; - - BitDepthInv = 16; - if (BitDepthInv < BitDepth) - BitDepthInv++; - BitDepthInv -= BitDepth; - continue; - } - - if (Bytes32 & 0x10000) - { - BitDepth = (uint8_t)(Bytes16 + 1); - BitDepthInv = 16 - BitDepth; - } - else - { - LastVal += Bytes16; - *Dst++ = LastVal; - BlockLength--; - } - } -} - -static void Decompress8BitData(int8_t *Dst, const uint8_t *Src, uint32_t BlockLength) -{ - uint8_t LastVal, Byte8, BitDepth, BitDepthInv, BitsRead; - uint16_t Bytes16; - - LastVal = 0; - BitDepth = 9; - BitDepthInv = BitsRead = 0; - - while (BlockLength != 0) - { - Bytes16 = (*(uint16_t *)Src) >> BitsRead; - - BitsRead += BitDepth; - Src += (BitsRead >> 3); - BitsRead &= 7; - - Byte8 = Bytes16 & 0xFF; - - if (BitDepth <= 6) - { - Bytes16 <<= (BitDepthInv & 0x1F); - Byte8 = Bytes16 & 0xFF; - - if (Byte8 != 0x80) - { - LastVal += (int8_t)Byte8 >> (BitDepthInv & 0x1F); // arithmetic shift - *Dst++ = LastVal; - BlockLength--; - continue; - } - - Byte8 = (Bytes16 >> 8) & 7; - BitsRead += 3; - Src += (BitsRead >> 3); - BitsRead &= 7; - } - else - { - if (BitDepth == 8) - { - if (Byte8 < 0x7C || Byte8 > 0x83) - { - LastVal += Byte8; - *Dst++ = LastVal; - BlockLength--; - continue; - } - Byte8 -= 0x7C; - } - else if (BitDepth < 8) - { - Byte8 <<= 1; - if (Byte8 < 0x78 || Byte8 > 0x86) - { - LastVal += (int8_t)Byte8 >> (BitDepthInv & 0x1F); // arithmetic shift - *Dst++ = LastVal; - BlockLength--; - continue; - } - Byte8 = (Byte8 >> 1) - 0x3C; - } - else - { - Bytes16 &= 0x1FF; - if ((Bytes16 & 0x100) == 0) - { - LastVal += Byte8; - *Dst++ = LastVal; - BlockLength--; - continue; - } - } - } - - Byte8++; - if (Byte8 >= BitDepth) - Byte8++; - BitDepth = Byte8; - - BitDepthInv = 8; - if (BitDepthInv < BitDepth) - BitDepthInv++; - BitDepthInv -= BitDepth; - } -} - -static bool LoadCompressed16BitSample(MEMFILE *m, sample_t *s, bool Stereo, bool DeltaEncoded) -{ - int8_t *DstPtr = (int8_t *)s->Data; - - uint8_t *DecompBuffer = (uint8_t *)malloc(65536); - if (DecompBuffer == NULL) - return false; - - uint32_t i = s->Length; - while (i > 0) - { - uint32_t BytesToUnpack = 32768; - if (BytesToUnpack > i) - BytesToUnpack = i; - - uint16_t PackedLen; - mread(&PackedLen, sizeof (uint16_t), 1, m); - mread(DecompBuffer, 1, PackedLen, m); - - Decompress16BitData((int16_t *)DstPtr, DecompBuffer, BytesToUnpack); - - if (DeltaEncoded) // convert from delta values to PCM - { - int16_t *Ptr16 = (int16_t *)DstPtr; - int16_t LastSmp16 = 0; // yes, reset this every block! - - const uint32_t Length = BytesToUnpack >> 1; - for (uint32_t j = 0; j < Length; j++) - { - LastSmp16 += Ptr16[j]; - Ptr16[j] = LastSmp16; - } - } - - DstPtr += BytesToUnpack; - i -= BytesToUnpack; - } - - if (Stereo) // added stereo support for custom HQ driver - { - DstPtr = (int8_t *)s->DataR; - - i = s->Length; - while (i > 0) - { - uint32_t BytesToUnpack = 32768; - if (BytesToUnpack > i) - BytesToUnpack = i; - - uint16_t PackedLen; - mread(&PackedLen, sizeof (uint16_t), 1, m); - mread(DecompBuffer, 1, PackedLen, m); - - Decompress16BitData((int16_t *)DstPtr, DecompBuffer, BytesToUnpack); - - if (DeltaEncoded) // convert from delta values to PCM - { - int16_t *Ptr16 = (int16_t *)DstPtr; - int16_t LastSmp16 = 0; // yes, reset this every block! - - const uint32_t Length = BytesToUnpack >> 1; - for (uint32_t j = 0; j < Length; j++) - { - LastSmp16 += Ptr16[j]; - Ptr16[j] = LastSmp16; - } - } - - DstPtr += BytesToUnpack; - i -= BytesToUnpack; - } - } - - free(DecompBuffer); - return true; -} - -static bool LoadCompressed8BitSample(MEMFILE *m, sample_t *s, bool Stereo, bool DeltaEncoded) -{ - int8_t *DstPtr = (int8_t *)s->Data; - - uint8_t *DecompBuffer = (uint8_t *)malloc(65536); - if (DecompBuffer == NULL) - return false; - - uint32_t i = s->Length; - while (i > 0) - { - uint32_t BytesToUnpack = 32768; - if (BytesToUnpack > i) - BytesToUnpack = i; - - uint16_t PackedLen; - mread(&PackedLen, sizeof (uint16_t), 1, m); - mread(DecompBuffer, 1, PackedLen, m); - - Decompress8BitData(DstPtr, DecompBuffer, BytesToUnpack); - - if (DeltaEncoded) // convert from delta values to PCM - { - int8_t LastSmp8 = 0; // yes, reset this every block! - for (uint32_t j = 0; j < BytesToUnpack; j++) - { - LastSmp8 += DstPtr[j]; - DstPtr[j] = LastSmp8; - } - } - - DstPtr += BytesToUnpack; - i -= BytesToUnpack; - } - - if (Stereo) // added stereo support for custom HQ driver - { - DstPtr = (int8_t *)s->DataR; - - i = s->Length; - while (i > 0) - { - uint32_t BytesToUnpack = 32768; - if (BytesToUnpack > i) - BytesToUnpack = i; - - uint16_t PackedLen; - mread(&PackedLen, sizeof (uint16_t), 1, m); - mread(DecompBuffer, 1, PackedLen, m); - - Decompress8BitData(DstPtr, DecompBuffer, BytesToUnpack); - - if (DeltaEncoded) // convert from delta values to PCM - { - int8_t LastSmp8 = 0; // yes, reset this every block! - for (uint32_t j = 0; j < BytesToUnpack; j++) - { - LastSmp8 += DstPtr[j]; - DstPtr[j] = LastSmp8; - } - } - - DstPtr += BytesToUnpack; - i -= BytesToUnpack; - } - } - - free(DecompBuffer); - return true; -} diff --git a/libraries/m4p/loaders/it.h b/libraries/m4p/loaders/it.h deleted file mode 100644 index e39388024..000000000 --- a/libraries/m4p/loaders/it.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include -#include -#include "../it_d_rm.h" - -bool LoadIT(MEMFILE *m); diff --git a/libraries/m4p/loaders/mmcmp/mmcmp.c b/libraries/m4p/loaders/mmcmp/mmcmp.c deleted file mode 100644 index a816a5b9c..000000000 --- a/libraries/m4p/loaders/mmcmp/mmcmp.c +++ /dev/null @@ -1,307 +0,0 @@ -/* MMCMP (Zirconia) decompressor. -** Taken from Mmcmp.cpp (ModPlug Tracker source code) and converted -** from C++ to C. libmodplug is public domain, so this file should be -** able to go under BSD 3-clause. -*/ - -#include -#include -#include -#include - -#define MMCMP_COMP 0x0001 -#define MMCMP_DELTA 0x0002 -#define MMCMP_16BIT 0x0004 -#define MMCMP_ABS16 0x0200 - -#ifdef _MSC_VER -#pragma pack(push) -#pragma pack(1) -#endif -typedef struct MMCMPFILEHEADER -{ - uint32_t id_ziRC; // "ziRC" - uint32_t id_ONia; // "ONia" - uint16_t hdrsize; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -MMCMPFILEHEADER, *LPMMCMPFILEHEADER; - -typedef struct MMCMPHEADER -{ - uint16_t version; - uint16_t nblocks; - uint32_t filesize; - uint32_t blktable; - uint8_t glb_comp; - uint8_t fmt_comp; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -MMCMPHEADER, *LPMMCMPHEADER; - -typedef struct MMCMPBLOCK -{ - uint32_t unpk_size; - uint32_t pk_size; - uint32_t xor_chk; - uint16_t sub_blk; - uint16_t flags; - uint16_t tt_entries; - uint16_t num_bits; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -MMCMPBLOCK, *LPMMCMPBLOCK; - -typedef struct MMCMPSUBBLOCK -{ - uint32_t unpk_pos; - uint32_t unpk_size; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -MMCMPSUBBLOCK, *LPMMCMPSUBBLOCK; -#ifdef _MSC_VER -#pragma pack(pop) -#endif - -typedef struct MMCMPBITBUFFER -{ - uint32_t bitcount; - uint32_t bitbuffer; - const uint8_t *pSrc; - const uint8_t *pEnd; -} MMCMPBITBUFFER; - -static uint32_t GetBits(MMCMPBITBUFFER *bb, uint32_t nBits) -{ - if (!nBits) - return 0; - - while (bb->bitcount < 24) - { - bb->bitbuffer |= ((bb->pSrc < bb->pEnd) ? *bb->pSrc++ : 0) << bb->bitcount; - bb->bitcount += 8; - } - - uint32_t d = bb->bitbuffer & ((1 << nBits) - 1); - - bb->bitbuffer >>= nBits; - bb->bitcount -= nBits; - - return d; -} - -static const uint8_t MMCMP8BitCommands[8] = { 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8 }; -static const uint8_t MMCMP16BitFetch[16] = { 4, 4, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static const uint8_t MMCMP8BitFetch[8] = { 3, 3, 3, 3, 2, 1, 0, 0 }; -static const uint16_t MMCMP16BitCommands[16] = -{ - 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0, - 0x1F0, 0x3F0, 0x7F0, 0xFF0, 0x1FF0, 0x3FF0, 0x7FF0, 0xFFF0 -}; - -bool unpackMMCMP(uint8_t **ppMemFile, uint32_t *pdwMemLength) -{ - uint32_t dwMemLength = *pdwMemLength; - const uint8_t *lpMemFile = *ppMemFile; - uint8_t *pBuffer; - LPMMCMPFILEHEADER pmfh = (LPMMCMPFILEHEADER)lpMemFile; - LPMMCMPHEADER pmmh = (LPMMCMPHEADER)(lpMemFile+10); - uint32_t *pblk_table; - uint32_t dwFileSize; - - if ((dwMemLength < 256) || !pmfh || pmfh->id_ziRC != 0x4352697A || pmfh->id_ONia != 0x61694e4f || pmfh->hdrsize < 14 - || !pmmh->nblocks || pmmh->filesize < 16 || pmmh->filesize > 0x8000000 || pmmh->blktable >= dwMemLength - || pmmh->blktable + 4*pmmh->nblocks > dwMemLength) - { - return false; - } - - dwFileSize = pmmh->filesize; - - pBuffer = (uint8_t *)malloc((dwFileSize + 31) & ~15); - if (pBuffer == NULL) - return false; - - pblk_table = (uint32_t *)(lpMemFile+pmmh->blktable); - for (uint32_t nBlock = 0; nBlock < pmmh->nblocks; nBlock++) - { - uint32_t dwMemPos = pblk_table[nBlock]; - LPMMCMPBLOCK pblk = (LPMMCMPBLOCK)(lpMemFile+dwMemPos); - LPMMCMPSUBBLOCK psubblk = (LPMMCMPSUBBLOCK)(lpMemFile+dwMemPos+20); - - if (dwMemPos+20 >= dwMemLength || dwMemPos+20+pblk->sub_blk*8 >= dwMemLength) - break; - - dwMemPos += 20+pblk->sub_blk*8; - - // Data is not packed - if (!(pblk->flags & MMCMP_COMP)) - { - for (uint32_t i = 0; isub_blk; i++) - { - if (psubblk->unpk_pos > dwFileSize || psubblk->unpk_pos+psubblk->unpk_size > dwFileSize) - break; - - memcpy(pBuffer+psubblk->unpk_pos, lpMemFile+dwMemPos, psubblk->unpk_size); - dwMemPos += psubblk->unpk_size; - psubblk++; - } - } - else if (pblk->flags & MMCMP_16BIT) // Data is 16-bit packed - { - MMCMPBITBUFFER bb; - uint16_t *pDest = (uint16_t *)(pBuffer + psubblk->unpk_pos); - uint32_t dwSize = psubblk->unpk_size >> 1; - uint32_t dwPos = 0; - uint32_t numbits = pblk->num_bits; - uint32_t subblk = 0, oldval = 0; - - bb.bitcount = 0; - bb.bitbuffer = 0; - bb.pSrc = lpMemFile+dwMemPos+pblk->tt_entries; - bb.pEnd = lpMemFile+dwMemPos+pblk->pk_size; - - while (subblk < pblk->sub_blk) - { - uint32_t newval = 0x10000; - uint32_t d = GetBits(&bb, numbits+1); - - if (d >= MMCMP16BitCommands[numbits]) - { - uint32_t nFetch = MMCMP16BitFetch[numbits]; - uint32_t newbits = GetBits(&bb, nFetch) + ((d - MMCMP16BitCommands[numbits]) << nFetch); - - if (newbits != numbits) - { - numbits = newbits & 0x0F; - } - else - { - d = GetBits(&bb, 4); - if (d == 0x0F) - { - if (GetBits(&bb, 1)) - break; - - newval = 0xFFFF; - } - else - { - newval = 0xFFF0 + d; - } - } - } - else - { - newval = d; - } - - if (newval < 0x10000) - { - newval = (newval & 1) ? (uint32_t)(-(int32_t)((newval+1) >> 1)) : (uint32_t)(newval >> 1); - if (pblk->flags & MMCMP_DELTA) - { - newval += oldval; - oldval = newval; - } - else if (!(pblk->flags & MMCMP_ABS16)) - { - newval ^= 0x8000; - } - - pDest[dwPos++] = (uint16_t)newval; - } - - if (dwPos >= dwSize) - { - subblk++; - dwPos = 0; - dwSize = psubblk[subblk].unpk_size >> 1; - pDest = (uint16_t *)(pBuffer + psubblk[subblk].unpk_pos); - } - } - } - else // Data is 8-bit packed - { - MMCMPBITBUFFER bb; - uint8_t *pDest = pBuffer + psubblk->unpk_pos; - uint32_t dwSize = psubblk->unpk_size; - uint32_t dwPos = 0; - uint32_t numbits = pblk->num_bits; - uint32_t subblk = 0, oldval = 0; - const uint8_t *ptable = lpMemFile+dwMemPos; - - bb.bitcount = 0; - bb.bitbuffer = 0; - bb.pSrc = lpMemFile+dwMemPos+pblk->tt_entries; - bb.pEnd = lpMemFile+dwMemPos+pblk->pk_size; - while (subblk < pblk->sub_blk) - { - uint32_t newval = 0x100; - uint32_t d = GetBits(&bb, numbits+1); - - if (d >= MMCMP8BitCommands[numbits]) - { - uint32_t nFetch = MMCMP8BitFetch[numbits]; - uint32_t newbits = GetBits(&bb, nFetch) + ((d - MMCMP8BitCommands[numbits]) << nFetch); - if (newbits != numbits) - { - numbits = newbits & 0x07; - } - else - { - d = GetBits(&bb, 3); - if (d == 7) - { - if (GetBits(&bb, 1)) - break; - - newval = 0xFF; - } - else - { - newval = 0xF8 + d; - } - } - } - else - { - newval = d; - } - - if (newval < 0x100) - { - int32_t n = ptable[newval]; - if (pblk->flags & MMCMP_DELTA) - { - n += oldval; - oldval = n; - } - - pDest[dwPos++] = (uint8_t)n; - } - - if (dwPos >= dwSize) - { - subblk++; - dwPos = 0; - dwSize = psubblk[subblk].unpk_size; - pDest = pBuffer + psubblk[subblk].unpk_pos; - } - } - } - } - - *ppMemFile = pBuffer; - *pdwMemLength = dwFileSize; - - return true; -} diff --git a/libraries/m4p/loaders/mmcmp/mmcmp.h b/libraries/m4p/loaders/mmcmp/mmcmp.h deleted file mode 100644 index c4f4b73d2..000000000 --- a/libraries/m4p/loaders/mmcmp/mmcmp.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include - -bool unpackMMCMP(uint8_t **ppMemFile, uint32_t *pdwMemLength); diff --git a/libraries/m4p/loaders/s3m.c b/libraries/m4p/loaders/s3m.c deleted file mode 100644 index e75a0a806..000000000 --- a/libraries/m4p/loaders/s3m.c +++ /dev/null @@ -1,610 +0,0 @@ -// for finding memory leaks in debug mode with Visual Studio -#if defined _DEBUG && defined _MSC_VER -#include -#endif - -#include -#include -#include -#include -#include -#include "../it_music.h" -#include "../it_structs.h" -#include "../it_d_rm.h" - -#define S3M_ROWS 64 - -static void ClearEncodingInfo(void); -static bool GetPatternLength(uint16_t Rows, uint16_t *LengthOut); -static void EncodePattern(pattern_t *p, uint8_t Rows); -static bool StorePattern(uint8_t NumRows, int32_t Pattern); -static bool TranslateS3MPattern(uint8_t *Src, int32_t Pattern); - -static uint8_t *PatternDataArea, EncodingInfo[MAX_HOST_CHANNELS*6]; - -bool LoadS3M(MEMFILE *m) -{ - uint8_t DefPan; - uint16_t Flags, SmpPtrs[100], PatPtrs[100]; - - if (!ReadBytes(m, Song.Header.SongName, 25)) return false; - mseek(m, 0x20, SEEK_SET); - if (!ReadBytes(m, &Song.Header.OrdNum, 2)) return false; - if (!ReadBytes(m, &Song.Header.SmpNum, 2)) return false; - if (!ReadBytes(m, &Song.Header.PatNum, 2)) return false; - if (!ReadBytes(m, &Flags, 2)) return false; - - mseek(m, 0x30, SEEK_SET); - if (!ReadBytes(m, &Song.Header.GlobalVol, 1)) return false; - if (!ReadBytes(m, &Song.Header.InitialSpeed, 1)) return false; - if (!ReadBytes(m, &Song.Header.InitialTempo, 1)) return false; - if (!ReadBytes(m, &Song.Header.MixVolume, 1)) return false; - mseek(m, 1, SEEK_CUR); - if (!ReadBytes(m, &DefPan, 1)) return false; - - if (Song.Header.SmpNum > 100) - Song.Header.SmpNum = 100; - - if (Song.Header.PatNum > 100) - Song.Header.PatNum = 100; - - Song.Header.Flags = ITF_OLD_EFFECTS; - if (Flags & 8) - Song.Header.Flags = ITF_VOL0_OPTIMIZATION; - - Song.Header.PanSep = 128; - Song.Header.GlobalVol *= 2; - - if (Song.Header.MixVolume & 128) - { - Song.Header.Flags |= ITF_STEREO; - Song.Header.MixVolume &= 127; - } - - // OK, panning now... - mseek(m, 64, SEEK_SET); - for (int32_t i = 0; i < 32; i++) - { - uint8_t Pan; - if (!ReadBytes(m, &Pan, 1)) return false; - - if (Pan >= 128) - { - Song.Header.ChnlPan[i] = 32 | 128; // 8bb: center + channel off - } - else - { - Pan &= 127; - if (Pan <= 7) - Song.Header.ChnlPan[i] = 0; - else if (Pan <= 15) - Song.Header.ChnlPan[i] = 64; - else - Song.Header.ChnlPan[i] = 32; - } - } - - // 8bb: set rest of channels to "off" - for (int32_t i = 32; i < MAX_HOST_CHANNELS; i++) - Song.Header.ChnlPan[i] = 32 | 128; - - for (int32_t i = 0; i < MAX_HOST_CHANNELS; i++) - Song.Header.ChnlVol[i] = 64; - - memset(Song.Orders, 255, MAX_ORDERS); - if (!ReadBytes(m, Song.Orders, Song.Header.OrdNum)) return false; // Order list loaded. - - if (!ReadBytes(m, SmpPtrs, Song.Header.SmpNum * 2)) return false; - if (!ReadBytes(m, PatPtrs, Song.Header.PatNum * 2)) return false; - - if (DefPan == 252) // 8bb: load custom channel pans, if present - { - for (int32_t i = 0; i < 32; i++) - { - uint8_t Pan; - if (!ReadBytes(m, &Pan, 1)) return false; - - if (Pan & 32) - { - uint8_t ChannelOffFlag = Song.Header.ChnlPan[i] & 128; - Song.Header.ChnlPan[i] = (((Pan & 15) << 2) + 2) | ChannelOffFlag; - } - } - } - - // Load instruments (8bb: and data) - sample_t *s = Song.Smp; - for (int32_t i = 0; i < Song.Header.SmpNum; i++, s++) - { - const uint32_t HeaderOffset = SmpPtrs[i] << 4; - if (HeaderOffset == 0) // 8bb: added this check - continue; - - mseek(m, HeaderOffset, SEEK_SET); - - uint8_t Type; - ReadBytes(m, &Type, 1); - - ReadBytes(m, &s->DOSFilename, 12); - - uint8_t MemSegH; - ReadBytes(m, &MemSegH, 1); - uint16_t MemSegL; - ReadBytes(m, &MemSegL, 2); - - ReadBytes(m, &s->Length, 4); - ReadBytes(m, &s->LoopBegin, 4); - ReadBytes(m, &s->LoopEnd, 4); - ReadBytes(m, &s->Vol, 1); - - mseek(m, 2, SEEK_CUR); - - uint8_t SmpFlags; - ReadBytes(m, &SmpFlags, 1); - - ReadBytes(m, &s->C5Speed, 4); - - mseek(m, 12, SEEK_CUR); - ReadBytes(m, &s->SampleName, 25); - - if (Type == 1) - { - if (SmpFlags & 2) - s->Flags |= SMPF_STEREO; - - if ((s->Length & 0xFFFF) > 0) - s->Flags |= SMPF_ASSOCIATED_WITH_HEADER; - - s->OffsetInFile = ((MemSegH << 16) | MemSegL) << 4; - } - - if (SmpFlags & 1) - s->Flags |= SMPF_USE_LOOP; - - if (SmpFlags & 4) - s->Flags |= SMPF_16BIT; - - s->GlobVol = 64; - s->DefPan = 32; - - if (s->Flags & SMPF_ASSOCIATED_WITH_HEADER) - { - if (s->OffsetInFile != 0) // 8bb: added this check - { - bool Stereo = !!(s->Flags & SMPF_STEREO); // 8bb: added stereo support - bool Sample16Bit = !!(s->Flags & SMPF_16BIT); - - uint32_t SampleBytes = s->Length << Sample16Bit; - - if (!Music_AllocateSample(i, SampleBytes)) - return false; - - if (Stereo) - { - if (!Music_AllocateRightSample(i, SampleBytes)) - return false; - } - - mseek(m, s->OffsetInFile, SEEK_SET); - if (!ReadBytes(m, s->Data, SampleBytes)) - return false; - - if (Stereo) - { - if (!ReadBytes(m, s->DataR, SampleBytes)) - return false; - } - - if (!Sample16Bit) - { - // 8bb: convert from unsigned to signed - int8_t *Ptr8 = (int8_t *)s->Data; - for (uint32_t j = 0; j < s->Length; j++) - Ptr8[j] ^= 0x80; - - if (Stereo) - { - Ptr8 = (int8_t *)s->DataR; - for (uint32_t j = 0; j < s->Length; j++) - Ptr8[j] ^= 0x80; - } - } - else - { - // 8bb: Music_AllocateSample() also set s->Length, divide by two if 16-bit - s->Length >>= 1; - - // 8bb: convert from unsigned to signed - int16_t *Ptr16 = (int16_t *)s->Data; - for (uint32_t j = 0; j < s->Length; j++) - Ptr16[j] ^= 0x8000; - - if (Stereo) - { - Ptr16 = (int16_t *)s->DataR; - for (uint32_t j = 0; j < s->Length; j++) - Ptr16[j] ^= 0x8000; - } - } - } - } - } - - // Load patterns.... - pattern_t *p = Song.Patt; - for (int32_t i = 0; i < Song.Header.PatNum; i++, p++) - { - const uint32_t PatternOffset = PatPtrs[i] << 4; - if (PatternOffset == 0) - continue; - - mseek(m, PatternOffset, SEEK_SET); - - uint16_t PackedPatLength; - if (!ReadBytes(m, &PackedPatLength, 2)) - return false; - - uint8_t *PackedData = (uint8_t *)malloc(PackedPatLength); - if (PackedData == NULL) - return false; - - if (!ReadBytes(m, PackedData, PackedPatLength)) - return false; - - if (!TranslateS3MPattern(PackedData, i)) - { - free(PackedData); - return false; - } - - free(PackedData); - } - - return true; -} - -static bool TranslateS3MPattern(uint8_t *Src, int32_t Pattern) -{ - PatternDataArea = (uint8_t *)malloc(MAX_HOST_CHANNELS * MAX_ROWS * 5); - if (PatternDataArea == NULL) - return false; - - // 8bb: clear destination pattern - uint8_t *Ptr8 = PatternDataArea; - for (int32_t i = 0; i < 200; i++) - { - for (int32_t j = 0; j < MAX_HOST_CHANNELS; j++, Ptr8 += 5) - { - Ptr8[0] = 253; // note - Ptr8[1] = 0; // ins - Ptr8[2] = 255; // vol - Ptr8[3] = 0; // cmd - Ptr8[4] = 0; // value - } - } - - uint8_t *OrigDst = PatternDataArea; - for (int32_t i = 0; i < S3M_ROWS; i++) - { - while (true) - { - uint8_t Byte, Mask = *Src++; - if (Mask == 0) - { - OrigDst += MAX_HOST_CHANNELS * 5; // 8bb: end of channels, go to next row - break; - } - - uint8_t *Dst = OrigDst + ((Mask & 31) * 5); // 8bb: aligned to current channel to write into - - // 8bb: Note and sample - if (Mask & 32) - { - Byte = *Src++; - if (Byte == 254) - Dst[0] = 254; - else if (Byte <= 127) - Dst[0] = 12 + (((Byte >> 4) * 12) + (Byte & 0x0F)); // C5 is now central octave - - // Instrument - Byte = *Src++; - if (Byte <= 99) - Dst[1] = Byte; - else - Dst[1] = 0; - } - - // Volume - if (Mask & 64) - { - Byte = *Src++; - if (Byte != 255) - { - if (Byte <= 64) - Dst[2] = Byte; - else - Dst[2] = 64; - } - } - - // 8bb: Effect + parameter - if (Mask & 128) - { - Dst[3] = *Src++; - Dst[4] = *Src++; - - if (Dst[3] == 'C'-'@') - { - // 8bb: IT2's broken (?) way of converting between decimal/hex - Dst[4] = (Dst[4] & 0x0F) + ((Dst[4] & 0xF0) >> 1) + ((Dst[4] & 0xF0) >> 3); - } - else if (Dst[3] == 'V'-'@') - { - if (Dst[4] < 128) - Dst[4] <<= 1; - else - Dst[4] = 255; - } - else if (Dst[3] == 'X'-'@') - { - if (Dst[4] == 0xA4) // 8bb: surround - { - Dst[3] = 'S'-'@'; - Dst[4] = 0x91; - } - else - { - if (Dst[4] < 128) - Dst[4] <<= 1; - else - Dst[4] = 255; - } - } - else if (Dst[3] == 'D'-'@') - { - uint8_t lo = Dst[4] & 0x0F; - uint8_t hi = Dst[4] & 0xF0; - - if (lo != 0 && hi != 0) - { - if (lo != 0x0F && hi != 0xF0) - Dst[4] &= 0x0F; - } - } - } - } - } - - bool result = StorePattern(S3M_ROWS, Pattern); - - free(PatternDataArea); - return result; -} - -static void ClearEncodingInfo(void) -{ - uint8_t *Enc = EncodingInfo; - for (int32_t i = 0; i < MAX_HOST_CHANNELS; i++, Enc += 6) - { - Enc[0] = 0; // mask - Enc[1] = 253; // note - Enc[2] = 0; // ins - Enc[3] = 255; // vol - Enc[4] = 0; // cmd - Enc[5] = 0; // value - } -} - -static bool GetPatternLength(uint16_t Rows, uint16_t *LengthOut) -{ - ClearEncodingInfo(); - - uint8_t *Src = PatternDataArea; - uint32_t Bytes = Rows; // End of row bytes added. - - for (int32_t i = 0; i < Rows; i++) - { - uint8_t *Enc = EncodingInfo; - for (int32_t j = 0; j < MAX_HOST_CHANNELS; j++, Src += 5, Enc += 6) - { - if (Src[0] == 253 && Src[1] == 0 && Src[2] == 255 && Src[3] == 0 && Src[4] == 0) - continue; - - Bytes++; // 1 byte for channel indication - - uint8_t Mask = 0; - - uint8_t Note = Src[0]; - if (Note != 253) - { - if (Enc[1] != Note) - { - Enc[1] = Note; - Bytes++; - Mask |= 1; - } - else - { - Mask |= 16; - } - } - - uint8_t Instr = Src[1]; - if (Instr != 0) - { - if (Enc[2] != Instr) - { - Enc[2] = Instr; - Bytes++; - Mask |= 2; - } - else - { - Mask |= 32; - } - } - - uint8_t Vol = Src[2]; - if (Vol != 255) - { - if (Enc[3] != Vol) - { - Enc[3] = Vol; - Bytes++; - Mask |= 4; - } - else - { - Mask |= 64; - } - } - - uint16_t EfxAndParam = *(uint16_t *)&Src[3]; - if (EfxAndParam != 0) - { - if (*(uint16_t *)&Enc[4] != EfxAndParam) - { - *(uint16_t *)&Enc[4] = EfxAndParam; - Bytes += 2; - Mask |= 8; - } - else - { - Mask |= 128; - } - } - - if (Mask != Enc[0]) - { - Enc[0] = Mask; - Bytes++; - } - } - } - - if (Bytes > 65535) - return false; - - *LengthOut = (uint16_t)Bytes; - return true; -} - -static void EncodePattern(pattern_t *p, uint8_t Rows) -{ - ClearEncodingInfo(); - - p->Rows = Rows; - - uint8_t *Src = PatternDataArea; - uint8_t *Dst = p->PackedData; - - for (int32_t i = 0; i < Rows; i++) - { - uint8_t *Enc = EncodingInfo; - for (uint8_t ch = 0; ch < MAX_HOST_CHANNELS; ch++, Src += 5, Enc += 6) - { - if (Src[0] == 253 && Src[1] == 0 && Src[2] == 255 && Src[3] == 0 && Src[4] == 0) - continue; - - uint8_t Mask = 0; - - uint8_t Note = Src[0]; - if (Note != 253) - { - if (Enc[1] != Note) - { - Enc[1] = Note; - Mask |= 1; - } - else - { - Mask |= 16; - } - } - - uint8_t Ins = Src[1]; - if (Src[1] != 0) - { - if (Enc[2] != Ins) - { - Enc[2] = Ins; - Mask |= 2; - } - else - { - Mask |= 32; - } - } - - uint8_t Vol = Src[2]; - if (Vol != 255) - { - if (Enc[3] != Vol) - { - Enc[3] = Vol; - Mask |= 4; - } - else - { - Mask |= 64; - } - } - - uint16_t EfxAndParam = *(uint16_t *)&Src[3]; - if (EfxAndParam != 0) - { - if (EfxAndParam != *(uint16_t *)&Enc[4]) - { - *(uint16_t *)&Enc[4] = EfxAndParam; - Mask |= 8; - } - else - { - Mask |= 128; - } - } - - if (Enc[0] != Mask) - { - Enc[0] = Mask; - - *Dst++ = (ch + 1) | 128; // read another mask... - *Dst++ = Mask; - } - else - { - *Dst++ = ch + 1; - } - - if (Mask & 1) - *Dst++ = Note; - - if (Mask & 2) - *Dst++ = Ins; - - if (Mask & 4) - *Dst++ = Vol; - - if (Mask & 8) - { - *(uint16_t *)Dst = EfxAndParam; - Dst += 2; - } - } - - *Dst++ = 0; - } -} - -static bool StorePattern(uint8_t NumRows, int32_t Pattern) -{ - uint16_t PackedLength; - if (!GetPatternLength(NumRows, &PackedLength)) - return false; - - if (!Music_AllocatePattern(Pattern, PackedLength)) - return false; - - EncodePattern(&Song.Patt[Pattern], NumRows); - return true; -} diff --git a/libraries/m4p/loaders/s3m.h b/libraries/m4p/loaders/s3m.h deleted file mode 100644 index 87d304c6a..000000000 --- a/libraries/m4p/loaders/s3m.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include -#include -#include "../it_d_rm.h" - -bool LoadS3M(MEMFILE *m); diff --git a/libraries/m4p/m4p.c b/libraries/m4p/m4p.c deleted file mode 100644 index 313c8173b..000000000 --- a/libraries/m4p/m4p.c +++ /dev/null @@ -1,139 +0,0 @@ -#include "m4p.h" -#include - -// IT/S3M -extern bool Music_Init(int32_t mixingFrequency, int32_t mixingBufferSize); -extern bool Music_LoadFromData(uint8_t *Data, uint32_t DataLen); -extern void Music_PlaySong(uint16_t order); -extern void Music_FillAudioBuffer(int16_t *buffer, int32_t numSamples); -extern void Music_FillAudioBufferFloat(float *buffer, int32_t numSamples); -extern void Music_Close(void); -extern void Music_Stop(void); -extern void Music_FreeSong(void); - -// XM/MOD/FT -extern bool initMusic(int32_t audioFrequency, int32_t audioBufferSize, bool interpolation, bool volumeRamping); -extern bool loadMusicFromData(const uint8_t *data, uint32_t dataLength); -extern void startPlaying(void); -extern void stopPlaying(void); -extern void mix_UpdateBuffer(int16_t *buffer, int32_t numSamples); -extern void mix_UpdateBufferFloat(float *buffer, int32_t numSamples); -extern void stopMusic(); -extern void freeMusic(void); - -extern const char *MODSig[16]; // For format checking - -enum -{ - FORMAT_UNKNOWN = 0, - FORMAT_IT_S3M = 1, - FORMAT_XM_MOD = 2 -}; - -int current_format = FORMAT_UNKNOWN; - -int m4p_TestFromData(uint8_t *Data, uint32_t DataLen) -{ - if ((DataLen >= 4 && (Data[0] == 'I' && Data[1] == 'M' && - Data[2] == 'P' && Data[3] == 'M')) || (DataLen >= 48 && - (Data[44] == 'S' && Data[45] == 'C' && - Data[46] == 'R' && Data[47] == 'M'))) - { - return FORMAT_IT_S3M; - } - if (DataLen >= 17) - { - bool is_xm_mod = true; - const char *hdrtxt = "Extended Module:"; - for (int i = 0; i < 16; i++) - { - if (Data[i] != *hdrtxt++) - { - is_xm_mod = false; - break; - } - } - if (is_xm_mod) return FORMAT_XM_MOD; - } - if (DataLen >= 1084) - { - for (uint8_t i = 0; i < 16; i++) - { - if (Data[1080] == MODSig[i][0] && Data[1081] == MODSig[i][1] && - Data[1082] == MODSig[i][2] && Data[1083] == MODSig[i][3]) - return FORMAT_XM_MOD; - } - } - - return FORMAT_UNKNOWN; -} - -bool m4p_LoadFromData(uint8_t *Data, uint32_t DataLen, int32_t mixingFrequency, int32_t mixingBufferSize) -{ - current_format = m4p_TestFromData(Data, DataLen); - - if (current_format == FORMAT_IT_S3M) - { - if (Music_Init(mixingFrequency, mixingBufferSize)) - return Music_LoadFromData(Data, DataLen); - else - return false; - } - else if (current_format == FORMAT_XM_MOD) - { - if (initMusic(mixingFrequency, mixingBufferSize, true, true)) - return loadMusicFromData(Data, DataLen); - else - return false; - } - - return false; -} - -void m4p_PlaySong(void) -{ - if (current_format == FORMAT_IT_S3M) - Music_PlaySong(0); - else - startPlaying(); -} - -void m4p_GenerateSamples(int16_t *buffer, int32_t numSamples) -{ - if (current_format == FORMAT_IT_S3M) - Music_FillAudioBuffer(buffer, numSamples); - else - mix_UpdateBuffer(buffer, numSamples); -} - -void m4p_GenerateFloatSamples(float *buffer, int32_t numSamples) -{ - if (current_format == FORMAT_IT_S3M) - Music_FillAudioBufferFloat(buffer, numSamples); - else - mix_UpdateBufferFloat(buffer, numSamples); -} - -void m4p_Stop(void) -{ - if (current_format == FORMAT_IT_S3M) - Music_Stop(); - else - stopPlaying(); -} - -void m4p_Close(void) -{ - if (current_format == FORMAT_IT_S3M) - Music_Close(); - else - stopMusic(); -} - -void m4p_FreeSong(void) -{ - if (current_format == FORMAT_IT_S3M) - Music_FreeSong(); - else - freeMusic(); -} \ No newline at end of file diff --git a/libraries/m4p/m4p.h b/libraries/m4p/m4p.h deleted file mode 100644 index 4f2f4aa3b..000000000 --- a/libraries/m4p/m4p.h +++ /dev/null @@ -1,35 +0,0 @@ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -// See if song in memory is a supported type (IT/S3M/XM/MOD) -int m4p_TestFromData(uint8_t *Data, uint32_t DataLen); - -// Load song from memory and initialize appropriate replayer -bool m4p_LoadFromData(uint8_t *Data, uint32_t DataLen, int32_t mixingFrequency, int32_t mixingBufferSize); - -// Set replayer status to Play (does not generate output) -void m4p_PlaySong(void); - -// Generate samples and fill buffer -void m4p_GenerateSamples(int16_t *buffer, int32_t numSamples); - -// Generate samples and fill buffer -void m4p_GenerateFloatSamples(float *buffer, int32_t numSamples); - -// Set replayer status to Stop -void m4p_Stop(void); - -// De-initialize replayer -void m4p_Close(void); - -// Free song memfile -void m4p_FreeSong(void); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/libraries/m4p/pmp_main.c b/libraries/m4p/pmp_main.c deleted file mode 100644 index 72f9d4af1..000000000 --- a/libraries/m4p/pmp_main.c +++ /dev/null @@ -1,1853 +0,0 @@ -/* - main XM replayer - -** -** NOTE: Effect handling is slightly different because -** I've removed the channel muting logic. -** Muted channels would only process *some* effects, but -** since we can't mute channels, we don't care about this. -** -** In FT2, the only way to mute a channel is through the -** tracker itself, so this is not really needed in a replayer. -*/ - -#include -#include -#include -#include "pmplay.h" -#include "pmp_mix.h" -#include "snd_masm.h" -#include "ft_tables.h" - -#define MAX_FRQ 32000 -#define MAX_NOTES (10*12*16+16) - -static tonTyp nilPatternLine[32]; // 8bb: used for non-allocated (empty) patterns - -typedef void (*volKolEfxRoutine)(stmTyp *ch); -typedef void (*volKolEfxRoutine2)(stmTyp *ch, uint8_t *volKol); -typedef void (*efxRoutine)(stmTyp *ch, uint8_t param); - -static void retrigVolume(stmTyp *ch) -{ - ch->realVol = ch->oldVol; - ch->outVol = ch->oldVol; - ch->outPan = ch->oldPan; - ch->status |= IS_Vol + IS_Pan + IS_QuickVol; -} - -static void retrigEnvelopeVibrato(stmTyp *ch) -{ - // 8bb: reset vibrato position - if (!(ch->waveCtrl & 0x04)) - ch->vibPos = 0; - - /* - ** 8bb: - ** In FT2.00 .. FT2.09, if the sixth bit of "ch->waveCtrl" is set - ** (from effect E7x where x is $4..$7 or $C..$F) and you trigger a note, - ** the replayer interrupt will freeze / lock up. This is because of a - ** label bug in the original code, causing it to jump back to itself - ** indefinitely. - */ - - // 8bb: safely reset tremolo position - if (!(ch->waveCtrl & 0x40)) - ch->tremPos = 0; - - ch->retrigCnt = 0; - ch->tremorPos = 0; - - ch->envSustainActive = true; - - instrTyp *ins = ch->instrSeg; - - if (ins->envVTyp & ENV_ENABLED) - { - ch->envVCnt = 65535; - ch->envVPos = 0; - } - - if (ins->envPTyp & ENV_ENABLED) - { - ch->envPCnt = 65535; - ch->envPPos = 0; - } - - ch->fadeOutSpeed = ins->fadeOut; // 8bb: ranges 0..4095 (FT2 doesn't check if it's higher than 4095!) - - // 8bb: final fadeout range is in fact 0..32768, and not 0..65536 like the XM format doc says - ch->fadeOutAmp = 32768; - - if (ins->vibDepth > 0) - { - ch->eVibPos = 0; - - if (ins->vibSweep > 0) - { - ch->eVibAmp = 0; - ch->eVibSweep = (ins->vibDepth << 8) / ins->vibSweep; - } - else - { - ch->eVibAmp = ins->vibDepth << 8; - ch->eVibSweep = 0; - } - } -} - -static void keyOff(stmTyp *ch) -{ - instrTyp *ins = ch->instrSeg; - - if (!(ins->envPTyp & ENV_ENABLED)) // 8bb: probably an FT2 bug - { - if (ch->envPCnt >= (uint16_t)ins->envPP[ch->envPPos][0]) - ch->envPCnt = ins->envPP[ch->envPPos][0]-1; - } - - if (ins->envVTyp & ENV_ENABLED) - { - if (ch->envVCnt >= (uint16_t)ins->envVP[ch->envVPos][0]) - ch->envVCnt = ins->envVP[ch->envVPos][0]-1; - } - else - { - ch->realVol = 0; - ch->outVol = 0; - ch->status |= IS_Vol + IS_QuickVol; - } - - ch->envSustainActive = false; -} - -uint32_t getFrequenceValue(uint16_t period) // 8bb: converts period to 16.16fp resampling delta -{ - uint32_t delta; - - if (period == 0) - return 0; - - if (linearFrqTab) - { - const uint16_t invPeriod = (12 * 192 * 4) - period; // 8bb: this intentionally underflows uint16_t to be accurate to FT2 - - const uint32_t quotient = invPeriod / 768; - const uint32_t remainder = invPeriod % 768; - - const int32_t octShift = 14 - quotient; - - delta = (uint32_t)(((int64_t)logTab[remainder] * (int32_t)frequenceMulFactor) >> 24); - delta >>= (octShift & 31); // 8bb: added needed 32-bit bitshift mask - } - else - { - delta = frequenceDivFactor / (uint32_t)period; - } - - return delta; -} - -static void startTone(uint8_t ton, uint8_t effTyp, uint8_t eff, stmTyp *ch) -{ - if (ton == NOTE_KEYOFF) - { - keyOff(ch); - return; - } - - // 8bb: if we came from Rxy (retrig), we didn't check note (Ton) yet - if (ton == 0) - { - ton = ch->tonNr; - if (ton == 0) - return; // 8bb: if still no note, return - } - - ch->tonNr = ton; - - instrTyp *ins = instr[ch->instrNr]; - if (ins == NULL) - ins = instr[0]; - - ch->instrSeg = ins; - ch->mute = ins->mute; - - uint8_t smp = ins->ta[ton-1] & 0xF; // 8bb: added for safety - ch->sampleNr = smp; - - sampleTyp *s = &ins->samp[smp]; - ch->relTonNr = s->relTon; - - ton += ch->relTonNr; - if (ton >= 10*12) - return; - - ch->oldVol = s->vol; - ch->oldPan = s->pan; - - if (effTyp == 0x0E && (eff & 0xF0) == 0x50) // 8bb: EFx - Set Finetune - ch->fineTune = ((eff & 0x0F) << 4) - 128; - else - ch->fineTune = s->fine; - - if (ton != 0) - { - const uint16_t tmpTon = ((ton-1) << 4) + (((int8_t)ch->fineTune >> 3) + 16); // 8bb: 0..1935 - if (tmpTon < MAX_NOTES) // 8bb: tmpTon is *always* below MAX_NOTES here, this check is not needed - ch->outPeriod = ch->realPeriod = note2Period[tmpTon]; - } - - ch->status |= IS_Period + IS_Vol + IS_Pan + IS_NyTon + IS_QuickVol; - - if (effTyp == 9) // 8bb: 9xx - Set Sample Offset - { - if (eff) - ch->smpOffset = ch->eff; - - ch->smpStartPos = ch->smpOffset << 8; - } - else - { - ch->smpStartPos = 0; - } - - P_StartTone(s, ch->smpStartPos); -} - -static void volume(stmTyp *ch, uint8_t param); // 8bb: volume slide -static void vibrato2(stmTyp *ch); -static void tonePorta(stmTyp *ch, uint8_t param); - -static void dummy(stmTyp *ch, uint8_t param) -{ - return; - - (void)ch; - (void)param; -} - -static void finePortaUp(stmTyp *ch, uint8_t param) -{ - if (param == 0) - param = ch->fPortaUpSpeed; - - ch->fPortaUpSpeed = param; - - ch->realPeriod -= param << 2; - if ((int16_t)ch->realPeriod < 1) - ch->realPeriod = 1; - - ch->outPeriod = ch->realPeriod; - ch->status |= IS_Period; -} - -static void finePortaDown(stmTyp *ch, uint8_t param) -{ - if (param == 0) - param = ch->fPortaDownSpeed; - - ch->fPortaDownSpeed = param; - - ch->realPeriod += param << 2; - if ((int16_t)ch->realPeriod > MAX_FRQ-1) // 8bb: FT2 bug, should've been unsigned comparison! - ch->realPeriod = MAX_FRQ-1; - - ch->outPeriod = ch->realPeriod; - ch->status |= IS_Period; -} - -static void setGlissCtrl(stmTyp *ch, uint8_t param) -{ - ch->glissFunk = param; -} - -static void setVibratoCtrl(stmTyp *ch, uint8_t param) -{ - ch->waveCtrl = (ch->waveCtrl & 0xF0) | param; -} - -static void jumpLoop(stmTyp *ch, uint8_t param) -{ - if (param == 0) - { - ch->pattPos = song.pattPos & 0xFF; - } - else if (ch->loopCnt == 0) - { - ch->loopCnt = param; - - song.pBreakPos = ch->pattPos; - song.pBreakFlag = true; - } - else if (--ch->loopCnt > 0) - { - song.pBreakPos = ch->pattPos; - song.pBreakFlag = true; - } -} - -static void setTremoloCtrl(stmTyp *ch, uint8_t param) -{ - ch->waveCtrl = (param << 4) | (ch->waveCtrl & 0x0F); -} - -static void volFineUp(stmTyp *ch, uint8_t param) -{ - if (param == 0) - param = ch->fVolSlideUpSpeed; - - ch->fVolSlideUpSpeed = param; - - ch->realVol += param; - if (ch->realVol > 64) - ch->realVol = 64; - - ch->outVol = ch->realVol; - ch->status |= IS_Vol; -} - -static void volFineDown(stmTyp *ch, uint8_t param) -{ - if (param == 0) - param = ch->fVolSlideDownSpeed; - - ch->fVolSlideDownSpeed = param; - - ch->realVol -= param; - if ((int8_t)ch->realVol < 0) - ch->realVol = 0; - - ch->outVol = ch->realVol; - ch->status |= IS_Vol; -} - -static void noteCut0(stmTyp *ch, uint8_t param) -{ - if (param == 0) // 8bb: only a parameter of zero is handled here - { - ch->realVol = 0; - ch->outVol = 0; - ch->status |= IS_Vol + IS_QuickVol; - } -} - -static void pattDelay(stmTyp *ch, uint8_t param) -{ - if (song.pattDelTime2 == 0) - song.pattDelTime = param + 1; - - (void)ch; -} - -static const efxRoutine EJumpTab_TickZero[16] = -{ - dummy, // 0 - finePortaUp, // 1 - finePortaDown, // 2 - setGlissCtrl, // 3 - setVibratoCtrl, // 4 - dummy, // 5 - jumpLoop, // 6 - setTremoloCtrl, // 7 - dummy, // 8 - dummy, // 9 - volFineUp, // A - volFineDown, // B - noteCut0, // C - dummy, // D - pattDelay, // E - dummy // F -}; - -static void E_Effects_TickZero(stmTyp *ch, uint8_t param) -{ - EJumpTab_TickZero[param >> 4](ch, param & 0x0F); -} - -static void posJump(stmTyp *ch, uint8_t param) -{ - song.songPos = (int16_t)param - 1; - song.pBreakPos = 0; - song.posJumpFlag = true; - - (void)ch; -} - -static void pattBreak(stmTyp *ch, uint8_t param) -{ - song.posJumpFlag = true; - - param = ((param >> 4) * 10) + (param & 0x0F); - if (param <= 63) - song.pBreakPos = param; - else - song.pBreakPos = 0; - - (void)ch; -} - -static void setSpeed(stmTyp *ch, uint8_t param) -{ - if (param >= 32) - { - song.speed = param; - P_SetSpeed(song.speed); - } - else - { - song.timer = song.tempo = param; - } - - (void)ch; -} - -static void setGlobaVol(stmTyp *ch, uint8_t param) -{ - if (param > 64) - param = 64; - - song.globVol = param; - - stmTyp *c = stm; - for (int32_t i = 0; i < song.antChn; i++, c++) // 8bb: this updates the volume for all voices - c->status |= IS_Vol; - - (void)ch; -} - -static void setEnvelopePos(stmTyp *ch, uint8_t param) -{ - int8_t envPos; - bool envUpdate; - int16_t newEnvPos; - - instrTyp *ins = ch->instrSeg; - - // *** VOLUME ENVELOPE *** - if (ins->envVTyp & ENV_ENABLED) - { - ch->envVCnt = param - 1; - - envPos = 0; - envUpdate = true; - newEnvPos = param; - - if (ins->envVPAnt > 1) - { - envPos++; - for (int32_t i = 0; i < ins->envVPAnt-1; i++) - { - if (newEnvPos < ins->envVP[envPos][0]) - { - envPos--; - - newEnvPos -= ins->envVP[envPos][0]; - if (newEnvPos == 0) - { - envUpdate = false; - break; - } - - if (ins->envVP[envPos+1][0] <= ins->envVP[envPos][0]) - { - envUpdate = true; - break; - } - - ch->envVIPValue = ((ins->envVP[envPos+1][1] - ins->envVP[envPos][1]) & 0xFF) << 8; - ch->envVIPValue /= (ins->envVP[envPos+1][0] - ins->envVP[envPos][0]); - - ch->envVAmp = (ch->envVIPValue * (newEnvPos - 1)) + ((ins->envVP[envPos][1] & 0xFF) << 8); - - envPos++; - - envUpdate = false; - break; - } - - envPos++; - } - - if (envUpdate) - envPos--; - } - - if (envUpdate) - { - ch->envVIPValue = 0; - ch->envVAmp = (ins->envVP[envPos][1] & 0xFF) << 8; - } - - if (envPos >= ins->envVPAnt) - { - envPos = ins->envVPAnt - 1; - if (envPos < 0) - envPos = 0; - } - - ch->envVPos = envPos; - } - - // *** PANNING ENVELOPE *** - if (ins->envVTyp & ENV_SUSTAIN) // 8bb: FT2 bug? (should probably have been "ins->envPTyp & ENV_ENABLED") - { - ch->envPCnt = param - 1; - - envPos = 0; - envUpdate = true; - newEnvPos = param; - - if (ins->envPPAnt > 1) - { - envPos++; - for (int32_t i = 0; i < ins->envPPAnt-1; i++) - { - if (newEnvPos < ins->envPP[envPos][0]) - { - envPos--; - - newEnvPos -= ins->envPP[envPos][0]; - if (newEnvPos == 0) - { - envUpdate = false; - break; - } - - if (ins->envPP[envPos + 1][0] <= ins->envPP[envPos][0]) - { - envUpdate = true; - break; - } - - ch->envPIPValue = ((ins->envPP[envPos+1][1] - ins->envPP[envPos][1]) & 0xFF) << 8; - ch->envPIPValue /= (ins->envPP[envPos+1][0] - ins->envPP[envPos][0]); - - ch->envPAmp = (ch->envPIPValue * (newEnvPos - 1)) + ((ins->envPP[envPos][1] & 0xFF) << 8); - - envPos++; - - envUpdate = false; - break; - } - - envPos++; - } - - if (envUpdate) - envPos--; - } - - if (envUpdate) - { - ch->envPIPValue = 0; - ch->envPAmp = (ins->envPP[envPos][1] & 0xFF) << 8; - } - - if (envPos >= ins->envPPAnt) - { - envPos = ins->envPPAnt - 1; - if (envPos < 0) - envPos = 0; - } - - ch->envPPos = envPos; - } -} - -/* -- tick-zero volume column effects -- -** 2nd parameter is used for a volume column quirk with the Rxy command (multiretrig) -*/ - -static void v_SetVibSpeed(stmTyp *ch, uint8_t *volKol) -{ - *volKol = (ch->volKolVol & 0x0F) << 2; - if (*volKol != 0) - ch->vibSpeed = *volKol; -} - -static void v_Volume(stmTyp *ch, uint8_t *volKol) -{ - *volKol -= 16; - if (*volKol > 64) // 8bb: no idea why FT2 has this check, this can't happen... - *volKol = 64; - - ch->outVol = ch->realVol = *volKol; - ch->status |= IS_Vol + IS_QuickVol; -} - -static void v_FineSlideDown(stmTyp *ch, uint8_t *volKol) -{ - *volKol = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->realVol; - if ((int8_t)*volKol < 0) - *volKol = 0; - - ch->outVol = ch->realVol = *volKol; - ch->status |= IS_Vol; -} - -static void v_FineSlideUp(stmTyp *ch, uint8_t *volKol) -{ - *volKol = (ch->volKolVol & 0x0F) + ch->realVol; - if (*volKol > 64) - *volKol = 64; - - ch->outVol = ch->realVol = *volKol; - ch->status |= IS_Vol; -} - -static void v_SetPan(stmTyp *ch, uint8_t *volKol) -{ - *volKol <<= 4; - - ch->outPan = *volKol; - ch->status |= IS_Pan; -} - -// -- non-tick-zero volume column effects -- - -static void v_SlideDown(stmTyp *ch) -{ - uint8_t newVol = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->realVol; - if ((int8_t)newVol < 0) - newVol = 0; - - ch->outVol = ch->realVol = newVol; - ch->status |= IS_Vol; -} - -static void v_SlideUp(stmTyp *ch) -{ - uint8_t newVol = (ch->volKolVol & 0x0F) + ch->realVol; - if (newVol > 64) - newVol = 64; - - ch->outVol = ch->realVol = newVol; - ch->status |= IS_Vol; -} - -static void v_Vibrato(stmTyp *ch) -{ - const uint8_t param = ch->volKolVol & 0xF; - if (param > 0) - ch->vibDepth = param; - - vibrato2(ch); -} - -static void v_PanSlideLeft(stmTyp *ch) -{ - uint16_t tmp16 = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->outPan; - if (tmp16 < 256) // 8bb: includes an FT2 bug: pan-slide-left of 0 = set pan to 0 - tmp16 = 0; - - ch->outPan = (uint8_t)tmp16; - ch->status |= IS_Pan; -} - -static void v_PanSlideRight(stmTyp *ch) -{ - uint16_t tmp16 = (ch->volKolVol & 0x0F) + ch->outPan; - if (tmp16 > 255) - tmp16 = 255; - - ch->outPan = (uint8_t)tmp16; - ch->status |= IS_Pan; -} - -static void v_TonePorta(stmTyp *ch) -{ - tonePorta(ch, 0); // 8bb: the last parameter is actually not used in tonePorta() -} - -static void v_dummy(stmTyp *ch) -{ - (void)ch; - return; -} - -static void v_dummy2(stmTyp *ch, uint8_t *volKol) -{ - (void)ch; - (void)volKol; - return; -} - -static const volKolEfxRoutine VJumpTab_TickNonZero[16] = -{ - v_dummy, v_dummy, v_dummy, v_dummy, - v_dummy, v_dummy, v_SlideDown, v_SlideUp, - v_dummy, v_dummy, v_dummy, v_Vibrato, - v_dummy, v_PanSlideLeft, v_PanSlideRight, v_TonePorta -}; - -static const volKolEfxRoutine2 VJumpTab_TickZero[16] = -{ - v_dummy2, v_Volume, v_Volume, v_Volume, - v_Volume, v_Volume, v_dummy2, v_dummy2, - v_FineSlideDown, v_FineSlideUp, v_SetVibSpeed, v_dummy2, - v_SetPan, v_dummy2, v_dummy2, v_dummy2 -}; - -static void setPan(stmTyp *ch, uint8_t param) -{ - ch->outPan = param; - ch->status |= IS_Pan; -} - -static void setVol(stmTyp *ch, uint8_t param) -{ - if (param > 64) - param = 64; - - ch->outVol = ch->realVol = param; - ch->status |= IS_Vol + IS_QuickVol; -} - -static void xFinePorta(stmTyp *ch, uint8_t param) -{ - const uint8_t type = param >> 4; - param &= 0x0F; - - if (type == 0x1) // extra fine porta up - { - if (param == 0) - param = ch->ePortaUpSpeed; - - ch->ePortaUpSpeed = param; - - uint16_t newPeriod = ch->realPeriod; - - newPeriod -= param; - if ((int16_t)newPeriod < 1) - newPeriod = 1; - - ch->outPeriod = ch->realPeriod = newPeriod; - ch->status |= IS_Period; - } - else if (type == 0x2) // extra fine porta down - { - if (param == 0) - param = ch->ePortaDownSpeed; - - ch->ePortaDownSpeed = param; - - uint16_t newPeriod = ch->realPeriod; - - newPeriod += param; - if ((int16_t)newPeriod > MAX_FRQ-1) // 8bb: FT2 bug, should've been unsigned comparison! - newPeriod = MAX_FRQ-1; - - ch->outPeriod = ch->realPeriod = newPeriod; - ch->status |= IS_Period; - } -} - -static void doMultiRetrig(stmTyp *ch, uint8_t param) // 8bb: "param" is never used (needed for efx jumptable structure) -{ - uint8_t cnt = ch->retrigCnt + 1; - if (cnt < ch->retrigSpeed) - { - ch->retrigCnt = cnt; - return; - } - - ch->retrigCnt = 0; - - int16_t vol = ch->realVol; - switch (ch->retrigVol) - { - case 0x1: vol -= 1; break; - case 0x2: vol -= 2; break; - case 0x3: vol -= 4; break; - case 0x4: vol -= 8; break; - case 0x5: vol -= 16; break; - case 0x6: vol = (vol >> 1) + (vol >> 3) + (vol >> 4); break; - case 0x7: vol >>= 1; break; - case 0x8: break; // 8bb: does not change the volume - case 0x9: vol += 1; break; - case 0xA: vol += 2; break; - case 0xB: vol += 4; break; - case 0xC: vol += 8; break; - case 0xD: vol += 16; break; - case 0xE: vol = (vol >> 1) + vol; break; - case 0xF: vol += vol; break; - default: break; - } - vol = CLAMP(vol, 0, 64); - - ch->realVol = (uint8_t)vol; - ch->outVol = ch->realVol; - - if (ch->volKolVol >= 0x10 && ch->volKolVol <= 0x50) // 8bb: Set Volume (volume column) - { - ch->outVol = ch->volKolVol - 0x10; - ch->realVol = ch->outVol; - } - else if (ch->volKolVol >= 0xC0 && ch->volKolVol <= 0xCF) // 8bb: Set Panning (volume column) - { - ch->outPan = (ch->volKolVol & 0x0F) << 4; - } - - startTone(0, 0, 0, ch); - - (void)param; -} - -static void multiRetrig(stmTyp *ch, uint8_t param, uint8_t volumeColumnData) -{ - uint8_t tmpParam; - - tmpParam = param & 0x0F; - if (tmpParam == 0) - tmpParam = ch->retrigSpeed; - - ch->retrigSpeed = tmpParam; - - tmpParam = param >> 4; - if (tmpParam == 0) - tmpParam = ch->retrigVol; - - ch->retrigVol = tmpParam; - - if (volumeColumnData == 0) - doMultiRetrig(ch, 0); // 8bb: the second parameter is never used (needed for efx jumptable structure) -} - -static const efxRoutine JumpTab_TickZero[36] = -{ - dummy, // 0 - dummy, // 1 - dummy, // 2 - dummy, // 3 - dummy, // 4 - dummy, // 5 - dummy, // 6 - dummy, // 7 - setPan, // 8 - dummy, // 9 - dummy, // A - posJump, // B - setVol, // C - pattBreak, // D - E_Effects_TickZero, // E - setSpeed, // F - setGlobaVol, // G - dummy, // H - dummy, // I - dummy, // J - dummy, // K - setEnvelopePos, // L - dummy, // M - dummy, // N - dummy, // O - dummy, // P - dummy, // Q - dummy, // R - dummy, // S - dummy, // T - dummy, // U - dummy, // V - dummy, // W - xFinePorta, // X - dummy, // Y - dummy // Z -}; - -static void checkEffects(stmTyp *ch) // tick0 effect handling -{ - // volume column effects - uint8_t newVolKol = ch->volKolVol; // 8bb: manipulated by vol. column effects, then used for multiretrig check (FT2 quirk) - VJumpTab_TickZero[ch->volKolVol >> 4](ch, &newVolKol); - - // normal effects - const uint8_t param = ch->eff; - - if ((ch->effTyp == 0 && param == 0) || ch->effTyp > 35) - return; - - // 8bb: this one has to be done here instead of in the jumptable, as it needs the "newVolKol" parameter (FT2 quirk) - if (ch->effTyp == 27) // 8bb: Rxy - Multi Retrig - { - multiRetrig(ch, param, newVolKol); - return; - } - - JumpTab_TickZero[ch->effTyp](ch, ch->eff); -} - -static void fixTonePorta(stmTyp *ch, const tonTyp *p, uint8_t inst) -{ - if (p->ton > 0) - { - if (p->ton == NOTE_KEYOFF) - { - keyOff(ch); - } - else - { - const uint16_t portaTmp = (((p->ton-1) + ch->relTonNr) << 4) + (((int8_t)ch->fineTune >> 3) + 16); - if (portaTmp < MAX_NOTES) - { - ch->wantPeriod = note2Period[portaTmp]; - - if (ch->wantPeriod == ch->realPeriod) - ch->portaDir = 0; - else if (ch->wantPeriod > ch->realPeriod) - ch->portaDir = 1; - else - ch->portaDir = 2; - } - } - } - - if (inst > 0) - { - retrigVolume(ch); - - if (p->ton != NOTE_KEYOFF) - retrigEnvelopeVibrato(ch); - } -} - -static void getNewNote(stmTyp *ch, const tonTyp *p) -{ - ch->volKolVol = p->vol; - - if (ch->effTyp == 0) - { - if (ch->eff != 0) // 8bb: we have an arpeggio (0xy) running, set period back - { - ch->outPeriod = ch->realPeriod; - ch->status |= IS_Period; - } - } - else - { - // 8bb: if we have a vibrato (4xy/6xy) on previous row (ch) that ends at current row (p), set period back - if ((ch->effTyp == 4 || ch->effTyp == 6) && (p->effTyp != 4 && p->effTyp != 6)) - { - ch->outPeriod = ch->realPeriod; - ch->status |= IS_Period; - } - } - - ch->effTyp = p->effTyp; - ch->eff = p->eff; - ch->tonTyp = (p->instr << 8) | p->ton; - - // 8bb: 'inst' var is used for later if-checks - uint8_t inst = p->instr; - if (inst > 0) - { - if (inst <= 128) - ch->instrNr = inst; - else - inst = 0; - } - - bool checkEfx = true; - if (p->effTyp == 0x0E) // 8bb: check for EDx (Note Delay) and E90 (Retrigger Note) - { - if (p->eff >= 0xD1 && p->eff <= 0xDF) // 8bb: ED1..EDF (Note Delay) - return; - else if (p->eff == 0x90) // 8bb: E90 (Retrigger Note) - checkEfx = false; - } - - if (checkEfx) - { - if ((ch->volKolVol & 0xF0) == 0xF0) // 8bb: Portamento (volume column) - { - const uint8_t volKolParam = ch->volKolVol & 0x0F; - if (volKolParam > 0) - ch->portaSpeed = volKolParam << 6; - - fixTonePorta(ch, p, inst); - checkEffects(ch); - return; - } - - if (p->effTyp == 3 || p->effTyp == 5) // 8bb: Portamento (3xx/5xx) - { - if (p->effTyp != 5 && p->eff != 0) - ch->portaSpeed = p->eff << 2; - - fixTonePorta(ch, p, inst); - checkEffects(ch); - return; - } - - if (p->effTyp == 0x14 && p->eff == 0) // 8bb: K00 (Key Off - only handle tick 0 here) - { - keyOff(ch); - - if (inst) - retrigVolume(ch); - - checkEffects(ch); - return; - } - - if (p->ton == 0) - { - if (inst > 0) - { - retrigVolume(ch); - retrigEnvelopeVibrato(ch); - } - - checkEffects(ch); - return; - } - } - - if (p->ton == NOTE_KEYOFF) - keyOff(ch); - else - startTone(p->ton, p->effTyp, p->eff, ch); - - if (inst > 0) - { - retrigVolume(ch); - if (p->ton != NOTE_KEYOFF) - retrigEnvelopeVibrato(ch); - } - - checkEffects(ch); -} - -static void fixaEnvelopeVibrato(stmTyp *ch) -{ - bool envInterpolateFlag, envDidInterpolate; - uint8_t envPos; - int16_t autoVibVal; - uint16_t autoVibAmp, envVal; - uint32_t vol; - - instrTyp *ins = ch->instrSeg; - - // *** FADEOUT *** - if (!ch->envSustainActive) - { - ch->status |= IS_Vol; - - if (ch->fadeOutAmp >= ch->fadeOutSpeed) - { - ch->fadeOutAmp -= ch->fadeOutSpeed; - } - else - { - ch->fadeOutAmp = 0; - ch->fadeOutSpeed = 0; - } - } - - if (!ch->mute) - { - // *** VOLUME ENVELOPE *** - envVal = 0; - if (ins->envVTyp & ENV_ENABLED) - { - envDidInterpolate = false; - envPos = ch->envVPos; - - if (++ch->envVCnt == ins->envVP[envPos][0]) - { - ch->envVAmp = ins->envVP[envPos][1] << 8; - - envPos++; - if (ins->envVTyp & ENV_LOOP) - { - envPos--; - - if (envPos == ins->envVRepE) - { - if (!(ins->envVTyp & ENV_SUSTAIN) || envPos != ins->envVSust || ch->envSustainActive) - { - envPos = ins->envVRepS; - - ch->envVCnt = ins->envVP[envPos][0]; - ch->envVAmp = ins->envVP[envPos][1] << 8; - } - } - - envPos++; - } - - if (envPos < ins->envVPAnt) - { - envInterpolateFlag = true; - if ((ins->envVTyp & ENV_SUSTAIN) && ch->envSustainActive) - { - if (envPos-1 == ins->envVSust) - { - envPos--; - ch->envVIPValue = 0; - envInterpolateFlag = false; - } - } - - if (envInterpolateFlag) - { - ch->envVPos = envPos; - - ch->envVIPValue = 0; - if (ins->envVP[envPos][0] > ins->envVP[envPos-1][0]) - { - ch->envVIPValue = (ins->envVP[envPos][1] - ins->envVP[envPos-1][1]) << 8; - ch->envVIPValue /= (ins->envVP[envPos][0] - ins->envVP[envPos-1][0]); - - envVal = ch->envVAmp; - envDidInterpolate = true; - } - } - } - else - { - ch->envVIPValue = 0; - } - } - - if (!envDidInterpolate) - { - ch->envVAmp += ch->envVIPValue; - - envVal = ch->envVAmp; - if (envVal > 64*256) - { - if (envVal > 128*256) - envVal = 64*256; - else - envVal = 0; - - ch->envVIPValue = 0; - } - } - - envVal >>= 8; - - vol = (envVal * ch->outVol * ch->fadeOutAmp) >> (16+2); - vol = (vol * song.globVol) >> 7; - - ch->status |= IS_Vol; // 8bb: this updates vol on every tick (because vol envelope is enabled) - } - else - { - vol = ((ch->outVol << 4) * ch->fadeOutAmp) >> 16; - vol = (vol * song.globVol) >> 7; - } - - ch->finalVol = (uint16_t)vol; // 0..256 - } - else - { - ch->finalVol = 0; - } - - // *** PANNING ENVELOPE *** - - envVal = 0; - if (ins->envPTyp & ENV_ENABLED) - { - envDidInterpolate = false; - envPos = ch->envPPos; - - if (++ch->envPCnt == ins->envPP[envPos][0]) - { - ch->envPAmp = ins->envPP[envPos][1] << 8; - - envPos++; - if (ins->envPTyp & ENV_LOOP) - { - envPos--; - - if (envPos == ins->envPRepE) - { - if (!(ins->envPTyp & ENV_SUSTAIN) || envPos != ins->envPSust || ch->envSustainActive) - { - envPos = ins->envPRepS; - - ch->envPCnt = ins->envPP[envPos][0]; - ch->envPAmp = ins->envPP[envPos][1] << 8; - } - } - - envPos++; - } - - if (envPos < ins->envPPAnt) - { - envInterpolateFlag = true; - if ((ins->envPTyp & ENV_SUSTAIN) && ch->envSustainActive) - { - if (envPos-1 == ins->envPSust) - { - envPos--; - ch->envPIPValue = 0; - envInterpolateFlag = false; - } - } - - if (envInterpolateFlag) - { - ch->envPPos = envPos; - - ch->envPIPValue = 0; - if (ins->envPP[envPos][0] > ins->envPP[envPos-1][0]) - { - ch->envPIPValue = (ins->envPP[envPos][1] - ins->envPP[envPos-1][1]) << 8; - ch->envPIPValue /= (ins->envPP[envPos][0] - ins->envPP[envPos-1][0]); - - envVal = ch->envPAmp; - envDidInterpolate = true; - } - } - } - else - { - ch->envPIPValue = 0; - } - } - - if (!envDidInterpolate) - { - ch->envPAmp += ch->envPIPValue; - - envVal = ch->envPAmp; - if (envVal > 64*256) - { - if (envVal > 128*256) - envVal = 64*256; - else - envVal = 0; - - ch->envPIPValue = 0; - } - } - - int16_t panTmp = ch->outPan - 128; - if (panTmp > 0) - panTmp = 0 - panTmp; - panTmp += 128; - - panTmp <<= 3; - envVal -= 32*256; - - ch->finalPan = ch->outPan + (uint8_t)(((int16_t)envVal * panTmp) >> 16); - ch->status |= IS_Pan; - } - else - { - ch->finalPan = ch->outPan; - } - - // *** AUTO VIBRATO *** - if (ins->vibDepth > 0) - { - if (ch->eVibSweep > 0) - { - autoVibAmp = ch->eVibSweep; - if (ch->envSustainActive) - { - autoVibAmp += ch->eVibAmp; - if ((autoVibAmp >> 8) > ins->vibDepth) - { - autoVibAmp = ins->vibDepth << 8; - ch->eVibSweep = 0; - } - - ch->eVibAmp = autoVibAmp; - } - } - else - { - autoVibAmp = ch->eVibAmp; - } - - ch->eVibPos += ins->vibRate; - - if (ins->vibTyp == 1) autoVibVal = (ch->eVibPos > 127) ? 64 : -64; // square - else if (ins->vibTyp == 2) autoVibVal = (((ch->eVibPos >> 1) + 64) & 127) - 64; // ramp up - else if (ins->vibTyp == 3) autoVibVal = ((-(ch->eVibPos >> 1) + 64) & 127) - 64; // ramp down - else autoVibVal = vibSineTab[ch->eVibPos]; // sine - - autoVibVal <<= 2; - uint16_t tmpPeriod = (autoVibVal * (int16_t)autoVibAmp) >> 16; - - tmpPeriod += ch->outPeriod; - if (tmpPeriod >= MAX_FRQ) - tmpPeriod = 0; // 8bb: yes, FT2 does this (!) - - ch->finalPeriod = tmpPeriod; - ch->status |= IS_Period; - } - else - { - ch->finalPeriod = ch->outPeriod; - } -} - -// 8bb: converts period to note number, for arpeggio and portamento (in semitone-slide mode) -static uint16_t relocateTon(uint16_t period, uint8_t arpNote, stmTyp *ch) -{ - int32_t tmpPeriod; - - const int32_t fineTune = ((int8_t)ch->fineTune >> 3) + 16; - - /* 8bb: FT2 bug, should've been 10*12*16. Notes above B-7 (95) will have issues. - ** You can only achieve such high notes by having a high relative note value - ** in the sample. - */ - int32_t hiPeriod = 8*12*16; - - int32_t loPeriod = 0; - - for (int32_t i = 0; i < 8; i++) - { - tmpPeriod = (((loPeriod + hiPeriod) >> 1) & ~15) + fineTune; - - int32_t lookUp = tmpPeriod - 8; - if (lookUp < 0) - lookUp = 0; // 8bb: safety fix (C-0 w/ ftune <= -65). This buggy read seems to return 0 in FT2 (TODO: verify) - - if (period >= note2Period[lookUp]) - hiPeriod = (tmpPeriod - fineTune) & ~15; - else - loPeriod = (tmpPeriod - fineTune) & ~15; - } - - tmpPeriod = loPeriod + fineTune + (arpNote << 4); - if (tmpPeriod >= (8*12*16+15)-1) // 8bb: FT2 bug, should've been 10*12*16+16 (also notice the +2 difference) - tmpPeriod = (8*12*16+16)-1; - - return note2Period[tmpPeriod]; -} - -static void vibrato2(stmTyp *ch) -{ - uint8_t tmpVib = (ch->vibPos >> 2) & 0x1F; - - switch (ch->waveCtrl & 3) - { - // 0: sine - case 0: tmpVib = vibTab[tmpVib]; break; - - // 1: ramp - case 1: - { - tmpVib <<= 3; - if ((int8_t)ch->vibPos < 0) - tmpVib = ~tmpVib; - } - break; - - // 2/3: square - default: tmpVib = 255; break; - } - - tmpVib = (tmpVib * ch->vibDepth) >> 5; - - if ((int8_t)ch->vibPos < 0) - ch->outPeriod = ch->realPeriod - tmpVib; - else - ch->outPeriod = ch->realPeriod + tmpVib; - - ch->status |= IS_Period; - ch->vibPos += ch->vibSpeed; -} - -static void arp(stmTyp *ch, uint8_t param) -{ - /* 8bb: The original arpTab table only supports 16 ticks, so it can and will overflow. - ** I have added overflown values to the table so that we can handle up to 256 ticks. - ** The added overflow entries are accurate to the overflow-read in FT2.08/FT2.09. - */ - const uint8_t tick = arpTab[song.timer & 0xFF]; - - if (tick == 0) - { - ch->outPeriod = ch->realPeriod; - } - else - { - const uint8_t note = (tick == 1) ? (param >> 4) : (param & 0x0F); - ch->outPeriod = relocateTon(ch->realPeriod, note, ch); - } - - ch->status |= IS_Period; -} - -static void portaUp(stmTyp *ch, uint8_t param) -{ - if (param == 0) - param = ch->portaUpSpeed; - - ch->portaUpSpeed = param; - - ch->realPeriod -= param << 2; - if ((int16_t)ch->realPeriod < 1) - ch->realPeriod = 1; - - ch->outPeriod = ch->realPeriod; - ch->status |= IS_Period; -} - -static void portaDown(stmTyp *ch, uint8_t param) -{ - if (param == 0) - param = ch->portaDownSpeed; - - ch->portaDownSpeed = param; - - ch->realPeriod += param << 2; - if ((int16_t)ch->realPeriod > MAX_FRQ-1) // 8bb: FT2 bug, should've been unsigned comparison! - ch->realPeriod = MAX_FRQ-1; - - ch->outPeriod = ch->realPeriod; - ch->status |= IS_Period; -} - -static void tonePorta(stmTyp *ch, uint8_t param) // 8bb: param is a placeholder (not used) -{ - if (ch->portaDir == 0) - return; - - if (ch->portaDir > 1) - { - ch->realPeriod -= ch->portaSpeed; - if ((int16_t)ch->realPeriod <= (int16_t)ch->wantPeriod) - { - ch->portaDir = 1; - ch->realPeriod = ch->wantPeriod; - } - } - else - { - ch->realPeriod += ch->portaSpeed; - if (ch->realPeriod >= ch->wantPeriod) - { - ch->portaDir = 1; - ch->realPeriod = ch->wantPeriod; - } - } - - if (ch->glissFunk) // 8bb: semitone-slide flag - ch->outPeriod = relocateTon(ch->realPeriod, 0, ch); - else - ch->outPeriod = ch->realPeriod; - - ch->status |= IS_Period; - - (void)param; -} - -static void vibrato(stmTyp *ch, uint8_t param) -{ - uint8_t tmp8; - - if (ch->eff > 0) - { - tmp8 = param & 0x0F; - if (tmp8 > 0) - ch->vibDepth = tmp8; - - tmp8 = (param & 0xF0) >> 2; - if (tmp8 > 0) - ch->vibSpeed = tmp8; - } - - vibrato2(ch); -} - -static void tonePlusVol(stmTyp *ch, uint8_t param) -{ - tonePorta(ch, 0); // 8bb: the last parameter is not used in tonePorta() - volume(ch, param); - - (void)param; -} - -static void vibratoPlusVol(stmTyp *ch, uint8_t param) -{ - vibrato2(ch); - volume(ch, param); - - (void)param; -} - -static void tremolo(stmTyp *ch, uint8_t param) -{ - uint8_t tmp8; - int16_t tremVol; - - const uint8_t tmpEff = param; - if (tmpEff > 0) - { - tmp8 = tmpEff & 0x0F; - if (tmp8 > 0) - ch->tremDepth = tmp8; - - tmp8 = (tmpEff & 0xF0) >> 2; - if (tmp8 > 0) - ch->tremSpeed = tmp8; - } - - uint8_t tmpTrem = (ch->tremPos >> 2) & 0x1F; - switch ((ch->waveCtrl >> 4) & 3) - { - // 0: sine - case 0: tmpTrem = vibTab[tmpTrem]; break; - - // 1: ramp - case 1: - { - tmpTrem <<= 3; - if ((int8_t)ch->vibPos < 0) // 8bb: FT2 bug, should've been ch->tremPos - tmpTrem = ~tmpTrem; - } - break; - - // 2/3: square - default: tmpTrem = 255; break; - } - tmpTrem = (tmpTrem * ch->tremDepth) >> 6; - - if ((int8_t)ch->tremPos < 0) - { - tremVol = ch->realVol - tmpTrem; - if (tremVol < 0) - tremVol = 0; - } - else - { - tremVol = ch->realVol + tmpTrem; - if (tremVol > 64) - tremVol = 64; - } - - ch->outVol = (uint8_t)tremVol; - ch->status |= IS_Vol; - ch->tremPos += ch->tremSpeed; -} - -static void volume(stmTyp *ch, uint8_t param) // 8bb: volume slide -{ - if (param == 0) - param = ch->volSlideSpeed; - - ch->volSlideSpeed = param; - - uint8_t newVol = ch->realVol; - if ((param & 0xF0) == 0) - { - newVol -= param; - if ((int8_t)newVol < 0) - newVol = 0; - } - else - { - param >>= 4; - - newVol += param; - if (newVol > 64) - newVol = 64; - } - - ch->outVol = ch->realVol = newVol; - ch->status |= IS_Vol; -} - -static void globalVolSlide(stmTyp *ch, uint8_t param) -{ - if (param == 0) - param = ch->globVolSlideSpeed; - - ch->globVolSlideSpeed = param; - - uint8_t newVol = (uint8_t)song.globVol; - if ((param & 0xF0) == 0) - { - newVol -= param; - if ((int8_t)newVol < 0) - newVol = 0; - } - else - { - param >>= 4; - - newVol += param; - if (newVol > 64) - newVol = 64; - } - - song.globVol = newVol; - - stmTyp *c = stm; - for (int32_t i = 0; i < song.antChn; i++, c++) // 8bb: this updates the volume for all voices - c->status |= IS_Vol; -} - -static void keyOffCmd(stmTyp *ch, uint8_t param) -{ - if ((uint8_t)(song.tempo-song.timer) == (param & 31)) - keyOff(ch); -} - -static void panningSlide(stmTyp *ch, uint8_t param) -{ - if (param == 0) - param = ch->panningSlideSpeed; - - ch->panningSlideSpeed = param; - - int16_t newPan = (int16_t)ch->outPan; - if ((param & 0xF0) == 0) - { - newPan -= param; - if (newPan < 0) - newPan = 0; - } - else - { - param >>= 4; - - newPan += param; - if (newPan > 255) - newPan = 255; - } - - ch->outPan = (uint8_t)newPan; - ch->status |= IS_Pan; -} - -static void tremor(stmTyp *ch, uint8_t param) -{ - if (param == 0) - param = ch->tremorSave; - - ch->tremorSave = param; - - uint8_t tremorSign = ch->tremorPos & 0x80; - uint8_t tremorData = ch->tremorPos & 0x7F; - - tremorData--; - if ((int8_t)tremorData < 0) - { - if (tremorSign == 0x80) - { - tremorSign = 0x00; - tremorData = param & 0x0F; - } - else - { - tremorSign = 0x80; - tremorData = param >> 4; - } - } - - ch->tremorPos = tremorSign | tremorData; - ch->outVol = (tremorSign == 0x80) ? ch->realVol : 0; - ch->status |= IS_Vol + IS_QuickVol; -} - -static void retrigNote(stmTyp *ch, uint8_t param) -{ - if (param == 0) // 8bb: E9x with a param of zero is handled in getNewNote() - return; - - if ((song.tempo-song.timer) % param == 0) - { - startTone(0, 0, 0, ch); - retrigEnvelopeVibrato(ch); - } -} - -static void noteCut(stmTyp *ch, uint8_t param) -{ - if ((uint8_t)(song.tempo-song.timer) == param) - { - ch->outVol = ch->realVol = 0; - ch->status |= IS_Vol + IS_QuickVol; - } -} - -static void noteDelay(stmTyp *ch, uint8_t param) -{ - if ((uint8_t)(song.tempo-song.timer) == param) - { - startTone(ch->tonTyp & 0xFF, 0, 0, ch); - - if ((ch->tonTyp & 0xFF00) > 0) // 8bb: do we have an instrument number? - retrigVolume(ch); - - retrigEnvelopeVibrato(ch); - - if (ch->volKolVol >= 0x10 && ch->volKolVol <= 0x50) // 8bb: Set Volume (volume column) - { - ch->outVol = ch->volKolVol - 16; - ch->realVol = ch->outVol; - } - else if (ch->volKolVol >= 0xC0 && ch->volKolVol <= 0xCF) // 8bb: Set Panning (volume column) - { - ch->outPan = (ch->volKolVol & 0x0F) << 4; - } - } -} - -static const efxRoutine EJumpTab_TickNonZero[16] = -{ - dummy, // 0 - dummy, // 1 - dummy, // 2 - dummy, // 3 - dummy, // 4 - dummy, // 5 - dummy, // 6 - dummy, // 7 - dummy, // 8 - retrigNote, // 9 - dummy, // A - dummy, // B - noteCut, // C - noteDelay, // D - dummy, // E - dummy // F -}; - -static void E_Effects_TickNonZero(stmTyp *ch, uint8_t param) -{ - EJumpTab_TickNonZero[param >> 4](ch, param & 0xF); -} - -static const efxRoutine JumpTab_TickNonZero[36] = -{ - arp, // 0 - portaUp, // 1 - portaDown, // 2 - tonePorta, // 3 - vibrato, // 4 - tonePlusVol, // 5 - vibratoPlusVol, // 6 - tremolo, // 7 - dummy, // 8 - dummy, // 9 - volume, // A - dummy, // B - dummy, // C - dummy, // D - E_Effects_TickNonZero, // E - dummy, // F - dummy, // G - globalVolSlide, // H - dummy, // I - dummy, // J - keyOffCmd, // K - dummy, // L - dummy, // M - dummy, // N - dummy, // O - panningSlide, // P - dummy, // Q - doMultiRetrig, // R - dummy, // S - tremor, // T - dummy, // U - dummy, // V - dummy, // W - dummy, // X - dummy, // Y - dummy // Z -}; - -static void doEffects(stmTyp *ch) // tick>0 effect handling -{ - const uint8_t volKolEfx = ch->volKolVol >> 4; - if (volKolEfx > 0) - VJumpTab_TickNonZero[volKolEfx](ch); - - if ((ch->eff == 0 && ch->effTyp == 0) || ch->effTyp > 35) - return; - - JumpTab_TickNonZero[ch->effTyp](ch, ch->eff); -} - -static void getNextPos(void) -{ - song.pattPos++; - - if (song.pattDelTime > 0) - { - song.pattDelTime2 = song.pattDelTime; - song.pattDelTime = 0; - } - - if (song.pattDelTime2 > 0) - { - song.pattDelTime2--; - if (song.pattDelTime2 > 0) - song.pattPos--; - } - - if (song.pBreakFlag) - { - song.pBreakFlag = false; - song.pattPos = song.pBreakPos; - } - - if (song.pattPos >= song.pattLen || song.posJumpFlag) - { - song.pattPos = song.pBreakPos; - song.pBreakPos = 0; - song.posJumpFlag = false; - - song.songPos++; - if (song.songPos >= song.len) - song.songPos = song.repS; - - song.pattNr = song.songTab[(uint8_t)song.songPos]; - song.pattLen = pattLens[(uint8_t)song.pattNr]; - } -} - -void mainPlayer(void) -{ - if (musicPaused) - return; - - bool tickZero = false; - - song.timer--; - if (song.timer == 0) - { - song.timer = song.tempo; - tickZero = true; - } - - const bool readNewNote = tickZero && (song.pattDelTime2 == 0); - if (readNewNote) - { - const tonTyp *pattPtr = nilPatternLine; - if (patt[song.pattNr] != NULL) - pattPtr = &patt[song.pattNr][song.pattPos * song.antChn]; - - stmTyp *c = stm; - for (uint8_t i = 0; i < song.antChn; i++, c++, pattPtr++) - { - PMPTmpActiveChannel = i; // 8bb: for P_StartTone() - getNewNote(c, pattPtr); - fixaEnvelopeVibrato(c); - } - } - else - { - stmTyp *c = stm; - for (uint8_t i = 0; i < song.antChn; i++, c++) - { - PMPTmpActiveChannel = i; // 8bb: for P_StartTone() - doEffects(c); - fixaEnvelopeVibrato(c); - } - } - - if (song.timer == 1) - getNextPos(); -} diff --git a/libraries/m4p/pmp_main.h b/libraries/m4p/pmp_main.h deleted file mode 100644 index 37b85df27..000000000 --- a/libraries/m4p/pmp_main.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include -#include - -void mainPlayer(void); -uint32_t getFrequenceValue(uint16_t period); diff --git a/libraries/m4p/pmp_mix.c b/libraries/m4p/pmp_mix.c deleted file mode 100644 index 7f7075e85..000000000 --- a/libraries/m4p/pmp_mix.c +++ /dev/null @@ -1,468 +0,0 @@ -#include -#include -#include -#include -#include -#include "pmplay.h" -#include "pmp_main.h" -#include "snd_masm.h" -#include "ft_tables.h" - -// fast 32-bit -> 16-bit clamp -#define CLAMP16(i) if ((int16_t)(i) != i) i = 0x7FFF ^ (i >> 31) - -static bool dump_Flag; -static int32_t oldReplayRate; - -// globalized -int16_t chnReloc[32]; -int32_t *CDA_MixBuffer = NULL; -CIType CI[32 * 2]; -// ------------ - -static void mix_UpdateChannel(int32_t nr, WaveChannelInfoType *WCI); - -void P_SetSpeed(uint16_t bpm) -{ - // 8bb: added this - if (bpm == 0) - bpm = 125; - - speedVal = ((realReplayRate + realReplayRate) + (realReplayRate >> 1)) / bpm; // 8bb: same as doing "((realReplayRate * 5) / 2) / bpm" -} - -void P_StartTone(sampleTyp *s, int32_t smpStartPos) -{ - WaveChannelInfoType WCI; - - WCI.SStartPos = smpStartPos; - WCI.SBase = s->pek; - WCI.SLen = s->len; - WCI.SRepS = s->repS; - WCI.SRepL = s->repL; - WCI.SType = s->typ; - WCI.Status = Status_StartTone+Status_StopTone; - - mix_UpdateChannel(PMPTmpActiveChannel, &WCI); -} - -// 8bb: added these two -bool mix_Init(int32_t audioBufferSize) -{ - CDA_MixBuffer = (int32_t *)malloc(audioBufferSize * 2 * sizeof (int32_t)); - if (CDA_MixBuffer == NULL) - return false; - - PMPLeft = 0; - return true; -} - -void mix_Free(void) -{ - if (CDA_MixBuffer != NULL) - { - free(CDA_MixBuffer); - CDA_MixBuffer = NULL; - } -} -// -------------------- - -static void updateVolume(CIType *v, int32_t volIPLen) -{ - const uint32_t vol = v->SVol * CDA_Amp; - - v->SLVol1 = (vol * panningTab[256-v->SPan]) >> (32-28); - v->SRVol1 = (vol * panningTab[ v->SPan]) >> (32-28); - - if (volumeRampingFlag) - { - v->SLVolIP = (v->SLVol1 - v->SLVol2) / volIPLen; - v->SRVolIP = (v->SRVol1 - v->SRVol2) / volIPLen; - v->SVolIPLen = volIPLen; - } -} - -static void mix_UpdateChannel(int32_t nr, WaveChannelInfoType *WCI) -{ - CIType *v = &CI[chnReloc[nr]]; - const uint8_t status = WCI->Status; - - if (status & Status_StopTone) - { - if (volumeRampingFlag) - { - // 8bb: fade out current voice - v->SType |= SType_Fadeout; - v->SVol = 0; - updateVolume(v, quickVolSizeVal); - - // 8bb: swap current voice with neighbor - chnReloc[nr] ^= 1; - v = &CI[chnReloc[nr]]; - } - - v->SType = SType_Off; - } - - if (status & Status_SetPan) - v->SPan = (uint8_t)WCI->SPan; - - if (status & Status_SetVol) - { - uint16_t vol = WCI->SVol; - if (vol > 0) vol--; // 8bb: 0..256 -> 0..255 ( FT2 does this to prevent mul overflow in updateVolume() ) - v->SVol = (uint8_t)vol; - } - - if (status & (Status_SetVol+Status_SetPan)) - updateVolume(v, (status & Status_QuickVol) ? quickVolSizeVal : speedVal); - - if (status & Status_SetFrq) - v->SFrq = WCI->SFrq; - - if (status & Status_StartTone) - { - int32_t len; - - uint8_t type = WCI->SType; - const bool sample16Bit = (type >> 4) & 1; - - if (type & (SType_Fwd+SType_Rev)) - { - int32_t repL = WCI->SRepL; - int32_t repS = WCI->SRepS; - - if (sample16Bit) - { - repL >>= 1; - repS >>= 1; - - v->SRevBase = (int16_t *)WCI->SBase + (repS+repS+repL); - } - else - { - v->SRevBase = (int8_t *)WCI->SBase + (repS+repS+repL); - } - - v->SRepL = repL; - v->SRepS = repS; - - len = repS + repL; - } - else - { - type &= ~(SType_Fwd+SType_Rev); // 8bb: keep loop flags only - - len = WCI->SLen; - if (sample16Bit) - len >>= 1; - - if (len == 0) - return; - } - - // 8bb: overflown 9xx (set sample offset), cut voice (voice got ended earlier in "if (status & Status_StopTone)") - if (WCI->SStartPos >= len) - return; - - v->SLen = len; - v->SPos = WCI->SStartPos; - v->SPosDec = 0; - v->SBase = WCI->SBase; - v->SMixType = (sample16Bit * 4) + (volumeRampingFlag * 2) + interpolationFlag; - v->SType = type; - } -} - -static void mix_UpdateChannelVolPanFrq(void) -{ - WaveChannelInfoType WCI; - - stmTyp *ch = stm; - for (int32_t i = 0; i < song.antChn; i++, ch++) - { - uint8_t newStatus = 0; - - const uint8_t status = ch->status; - ch->status = 0; - - if (status == 0) - continue; - - if (status & IS_Vol) - { - WCI.SVol = ch->finalVol; - newStatus |= Status_SetVol; - } - - if (status & IS_QuickVol) - newStatus |= Status_QuickVol; - - if (status & IS_Pan) - { - WCI.SPan = ch->finalPan; - newStatus |= Status_SetPan; - } - - if (status & IS_Period) - { - WCI.SFrq = getFrequenceValue(ch->finalPeriod); - newStatus |= Status_SetFrq; - } - - WCI.Status = newStatus; - mix_UpdateChannel(i, &WCI); - } -} - -void mix_ClearChannels(void) // 8bb: rewritten to handle all voices instead of song.antChn -{ - memset(CI, 0, sizeof (CI)); - - CIType *v = CI; - for (int16_t i = 0; i < 32*2; i++, v++) - { - v->SPan = 128; - v->SType = SType_Off; - } - - for (int16_t i = 0; i < 32; i++) - chnReloc[i] = i+i; -} - -static void mix_SaveIPVolumes(void) -{ - CIType *v = CI; - for (int32_t i = 0; i < song.antChn*2; i++, v++) - { - // 8bb: this cuts any active fade-out voices (volume ramping) - if (v->SType & SType_Fadeout) - v->SType = SType_Off; - - v->SLVol2 = v->SLVol1; - v->SRVol2 = v->SRVol1; - v->SVolIPLen = 0; - } -} - -void mix_UpdateBuffer(int16_t *buffer, int32_t numSamples) -{ - if (numSamples <= 0) - return; - - if (musicPaused) // silence output - { - memset(buffer, 0, numSamples * (2 * sizeof (int16_t))); - return; - } - - memset(CDA_MixBuffer, 0, numSamples * (2 * sizeof (int32_t))); - - int32_t c = 0; - int32_t a = numSamples; - - while (a > 0) - { - if (PMPLeft == 0) - { - mix_SaveIPVolumes(); - mainPlayer(); - mix_UpdateChannelVolPanFrq(); - PMPLeft = speedVal; - } - - int32_t b = a; - if (b > PMPLeft) - b = PMPLeft; - - CIType *v = CI; - for (int32_t i = 0; i < song.antChn*2; i++, v++) - PMPMix32Proc(v, b, c); - - c += b; - a -= b; - PMPLeft -= b; - } - - numSamples *= 2; // 8bb: stereo - - /* 8bb: Done a bit differently since we don't use a - ** Sound Blaster with its master volume setting. - ** Instead we change the amplitude here. - */ - - if (masterVol == 256) // 8bb: max master volume, no need to change amp - { - for (int32_t i = 0; i < numSamples; i++) - { - int32_t out32 = CDA_MixBuffer[i] >> 8; - CLAMP16(out32); - buffer[i] = (int16_t)out32; - } - } - else - { - for (int32_t i = 0; i < numSamples; i++) - { - int32_t out32 = CDA_MixBuffer[i] >> 8; - CLAMP16(out32); - out32 = (out32 * masterVol) >> 8; - buffer[i] = (int16_t)out32; - } - } -} - -void mix_UpdateBufferFloat(float *buffer, int32_t numSamples) -{ - if (numSamples <= 0) - return; - - if (musicPaused) // silence output - { - memset(buffer, 0, numSamples * (2 * sizeof (int16_t))); - return; - } - - memset(CDA_MixBuffer, 0, numSamples * (2 * sizeof (int32_t))); - - int32_t c = 0; - int32_t a = numSamples; - - while (a > 0) - { - if (PMPLeft == 0) - { - mix_SaveIPVolumes(); - mainPlayer(); - mix_UpdateChannelVolPanFrq(); - PMPLeft = speedVal; - } - - int32_t b = a; - if (b > PMPLeft) - b = PMPLeft; - - CIType *v = CI; - for (int32_t i = 0; i < song.antChn*2; i++, v++) - PMPMix32Proc(v, b, c); - - c += b; - a -= b; - PMPLeft -= b; - } - - numSamples *= 2; // 8bb: stereo - - /* 8bb: Done a bit differently since we don't use a - ** Sound Blaster with its master volume setting. - ** Instead we change the amplitude here. - */ - -#if defined _MSC_VER || (defined __SIZEOF_FLOAT__ && __SIZEOF_FLOAT__ == 4) - if (masterVol == 256) // 8bb: max master volume, no need to change amp - { - for (int32_t i = 0; i < numSamples; i++) - { - int32_t out32 = CDA_MixBuffer[i] >> 8; - CLAMP16(out32); - *(uint32_t *)buffer = 0x43818000^((uint16_t)out32); - *buffer++ -= 259.0f; - } - } - else - { - for (int32_t i = 0; i < numSamples; i++) - { - int32_t out32 = CDA_MixBuffer[i] >> 8; - CLAMP16(out32); - out32 = (out32 * masterVol) >> 8; - *(uint32_t *)buffer = 0x43818000^((uint16_t)out32); - *buffer++ -= 259.0f; - } - } -#else - if (masterVol == 256) // 8bb: max master volume, no need to change amp - { - for (int32_t i = 0; i < numSamples; i++) - { - int32_t out32 = CDA_MixBuffer[i] >> 8; - CLAMP16(out32); - *buffer++ = (float)out32 * 0.000030517578125f; - } - } - else - { - for (int32_t i = 0; i < numSamples; i++) - { - int32_t out32 = CDA_MixBuffer[i] >> 8; - CLAMP16(out32); - out32 = (out32 * masterVol) >> 8; - *buffer++ = (float)out32 * 0.000030517578125f; - } - } -#endif -} - -bool dump_Init(int32_t frq, int32_t amp, int16_t songPos) -{ - setPos(songPos, 0); - - oldReplayRate = realReplayRate; - - realReplayRate = frq; - updateReplayRate(); - CDA_Amp = 8 * amp; - - mix_ClearChannels(); - stopVoices(); - song.globVol = 64; - speedVal = (frq*5 / 2) / song.speed; - quickVolSizeVal = frq / 200; - - dump_Flag = false; - return true; -} - -void dump_Close(void) -{ - stopVoices(); - realReplayRate = oldReplayRate; - updateReplayRate(); -} - -bool dump_EndOfTune(int32_t endSongPos) -{ - bool returnValue = (dump_Flag && song.pattPos == 0 && song.timer == 1) || (song.tempo == 0); - - // 8bb: FT2 bugfix for EEx (pattern delay) on first row of a pattern - if (song.pattDelTime2 > 0) - returnValue = false; - - if (song.songPos == endSongPos && song.pattPos == 0 && song.timer == 1) - dump_Flag = true; - - return returnValue; -} - -int32_t dump_GetFrame(int16_t *p) // 8bb: returns bytes mixed to 16-bit stereo buffer -{ - mix_SaveIPVolumes(); - mainPlayer(); - mix_UpdateChannelVolPanFrq(); - - memset(CDA_MixBuffer, 0, speedVal * (2 * sizeof (int32_t))); - - CIType *v = CI; - for (int32_t i = 0; i < song.antChn*2; i++, v++) - PMPMix32Proc(v, speedVal, 0); - - const int32_t numSamples = speedVal * 2; // 8bb: *2 for stereo - for (int32_t i = 0; i < numSamples; i++) - { - int32_t out32 = CDA_MixBuffer[i] >> 8; - CLAMP16(out32); - p[i] = (int16_t)out32; - } - - return speedVal * (2 * sizeof (int16_t)); -} diff --git a/libraries/m4p/pmp_mix.h b/libraries/m4p/pmp_mix.h deleted file mode 100644 index f0e026ced..000000000 --- a/libraries/m4p/pmp_mix.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include -#include -#include "pmplay.h" - -enum -{ - Status_SetVol = 1, - Status_SetPan = 2, - Status_SetFrq = 4, - Status_StartTone = 8, - Status_StopTone = 16, - Status_QuickVol = 32, - - SType_Fwd = 1, - SType_Rev = 2, - SType_RevDir = 4, - SType_Off = 8, - SType_16 = 16, - SType_Fadeout = 32 -}; - -typedef struct -{ - const void *SBase, *SRevBase; - uint8_t SType, SPan, SVol; - int32_t SLVol1, SRVol1, SLVol2, SRVol2, SLVolIP, SRVolIP, SVolIPLen; - int32_t SLen, SRepS, SRepL, SPos, SMixType; - uint32_t SPosDec, SFrq; -} CIType; - -typedef struct -{ - const void *SBase; - uint8_t Status, SType; - int16_t SVol, SPan; - int32_t SFrq, SLen, SRepS, SRepL, SStartPos; -} WaveChannelInfoType; - -extern int16_t chnReloc[32]; -extern int32_t *CDA_MixBuffer; -extern CIType CI[32 * 2]; - -void P_SetSpeed(uint16_t bpm); -void P_StartTone(sampleTyp *s, int32_t smpStartPos); - -// 8bb: added these two -bool mix_Init(int32_t audioBufferSize); -void mix_Free(void); -// ------------------- - -void mix_ClearChannels(void); -void mix_UpdateBuffer(int16_t *buffer, int32_t numSamples); -void mix_UpdateBufferFloat(float *buffer, int32_t numSamples); - -bool dump_Init(int32_t frq, int32_t amp, int16_t songPos); -void dump_Close(void); -bool dump_EndOfTune(int32_t endSongPos); -int32_t dump_GetFrame(int16_t *p); diff --git a/libraries/m4p/pmplay.c b/libraries/m4p/pmplay.c deleted file mode 100644 index 7c0ec74a7..000000000 --- a/libraries/m4p/pmplay.c +++ /dev/null @@ -1,1254 +0,0 @@ -/* -** - loaders and replayer handlers - -*/ - -#define DEFAULT_AMP 4 -#define DEFAULT_MASTER_VOL 256 - -#include -#include -#include -#include -#include -#include -#include -#include "pmplay.h" -#include "pmp_mix.h" -#include "snd_masm.h" -#include "ft_tables.h" - -#define INSTR_HEADER_SIZE 263 - -#define SWAP16(value) \ -( \ - (((uint16_t)((value) & 0x00FF)) << 8) | \ - (((uint16_t)((value) & 0xFF00)) >> 8) \ -) - -#ifdef _MSC_VER -#pragma pack(push) -#pragma pack(1) -#endif -typedef struct songHeaderTyp_t -{ - char sig[17], name[21], progName[20]; - uint16_t ver; - int32_t headerSize; - uint16_t len, repS, antChn, antPtn, antInstrs, flags, defTempo, defSpeed; - uint8_t songTab[256]; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -songHeaderTyp; - -typedef struct modSampleTyp -{ - char name[22]; - uint16_t len; - uint8_t fine, vol; - uint16_t repS, repL; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -modSampleTyp; - -typedef struct songMOD31HeaderTyp -{ - char name[20]; - modSampleTyp sample[31]; - uint8_t len, repS, songTab[128]; - char Sig[4]; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -songMOD31HeaderTyp; - -typedef struct songMOD15HeaderTyp -{ - char name[20]; - modSampleTyp sample[15]; - uint8_t len, repS, songTab[128]; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -songMOD15HeaderTyp; - -typedef struct sampleHeaderTyp_t -{ - int32_t len, repS, repL; - uint8_t vol; - int8_t fine; - uint8_t typ, pan; - int8_t relTon; - uint8_t skrap; - char name[22]; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -sampleHeaderTyp; - -typedef struct instrHeaderTyp_t -{ - int32_t instrSize; - char name[22]; - uint8_t typ; - uint16_t antSamp; - int32_t sampleSize; - uint8_t ta[96]; - int16_t envVP[12][2], envPP[12][2]; - uint8_t envVPAnt, envPPAnt, envVSust, envVRepS, envVRepE, envPSust, envPRepS; - uint8_t envPRepE, envVTyp, envPTyp, vibTyp, vibSweep, vibDepth, vibRate; - uint16_t fadeOut; - uint8_t midiOn, midiChannel; - int16_t midiProgram, midiBend; - int8_t mute; - uint8_t reserved[15]; - sampleHeaderTyp samp[32]; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -instrHeaderTyp; - -typedef struct patternHeaderTyp_t -{ - int32_t patternHeaderSize; - uint8_t typ; - uint16_t pattLen, dataLen; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -patternHeaderTyp; -#ifdef _MSC_VER -#pragma pack(pop) -#endif - -static int32_t soundBufferSize; - -// globalized -volatile bool interpolationFlag, volumeRampingFlag, moduleLoaded, musicPaused; -bool linearFrqTab; -volatile const uint16_t *note2Period; -uint16_t pattLens[256]; -int16_t PMPTmpActiveChannel, boostLevel = DEFAULT_AMP; -int32_t masterVol = DEFAULT_MASTER_VOL, PMPLeft = 0; -int32_t realReplayRate, quickVolSizeVal, speedVal; -uint32_t frequenceDivFactor, frequenceMulFactor, CDA_Amp = 8*DEFAULT_AMP; -tonTyp *patt[256]; -instrTyp *instr[1+128]; -songTyp song; -stmTyp stm[32]; -// ------------------ - -// 8bb: added these for loader -typedef struct -{ - uint8_t *_ptr, *_base; - bool _eof; - size_t _cnt, _bufsiz; -} MEMFILE; - -static MEMFILE *mopen(const uint8_t *src, uint32_t length); -static void mclose(MEMFILE **buf); -static size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf); -static bool meof(MEMFILE *buf); -static void mseek(MEMFILE *buf, int32_t offset, int32_t whence); -static void mrewind(MEMFILE *buf); -// -------------------------- - -static void resetMusic(void); -static void freeAllPatterns(void); -static void setFrqTab(bool linear); - -static CIType *getVoice(int32_t ch) // 8bb: added this -{ - if (ch < 0 || ch > 31) - return NULL; - - return &CI[chnReloc[ch]]; -} - -/*************************************************************************** - * ROUTINES FOR SAMPLE HANDLING ETC. * - ***************************************************************************/ - -// 8bb: modifies wrapped sample after loop/end (for branchless mixer interpolation) -static void fixSample(sampleTyp *s) -{ - if (s->pek == NULL) - return; // empty sample - - const bool sample16Bit = !!(s->typ & SAMPLE_16BIT); - uint8_t loopType = s->typ & 3; - int16_t *ptr16 = (int16_t *)s->pek; - int32_t len = s->len; - int32_t loopStart = s->repS; - int32_t loopEnd = s->repS + s->repL; - - if (sample16Bit) - { - len >>= 1; - loopStart >>= 1; - loopEnd >>= 1; - } - - if (len < 1) - return; - - /* 8bb: - ** This is the exact bit test order of which FT2 handles - ** the sample tap fix. - ** - ** This order is important for rare cases where both the - ** "forward" and "pingpong" loop bits are set at once. - ** - ** This means that if both flags are set, the mixer will - ** play the sample with pingpong looping, but the sample fix - ** is handled as if it was a forward loop. This results in - ** the wrong interpolation tap sample being written after the - ** loop end point. - */ - - if (loopType & LOOP_FORWARD) - { - if (sample16Bit) - ptr16[loopEnd] = ptr16[loopStart]; - else - s->pek[loopEnd] = s->pek[loopStart]; - - return; - } - else if (loopType & LOOP_PINGPONG) - { - if (sample16Bit) - ptr16[loopEnd] = ptr16[loopEnd-1]; - else - s->pek[loopEnd] = s->pek[loopEnd-1]; - } - else // no loop - { - if (sample16Bit) - ptr16[len] = 0; - else - s->pek[len] = 0; - } -} - -static void checkSampleRepeat(int32_t nr, int32_t nr2) -{ - instrTyp *i = instr[nr]; - if (i == NULL) - return; - - sampleTyp *s = &i->samp[nr2]; - - if (s->repS < 0) s->repS = 0; - if (s->repL < 0) s->repL = 0; - if (s->repS > s->len) s->repS = s->len; - if (s->repS+s->repL > s->len) s->repL = s->len - s->repS; -} - -static void upDateInstrs(void) -{ - for (int32_t i = 0; i <= 128; i++) - { - instrTyp *ins = instr[i]; - if (ins == NULL) - continue; - - sampleTyp *s = ins->samp; - for (int32_t j = 0; j < 16; j++, s++) - { - checkSampleRepeat(i, j); - fixSample(s); - - if (s->pek == NULL) - { - s->len = 0; - s->repS = 0; - s->repL = 0; - } - } - } -} - -static bool patternEmpty(uint16_t nr) -{ - if (patt[nr] == NULL) - return true; - - const uint8_t *scanPtr = (const uint8_t *)patt[nr]; - const int32_t scanLen = pattLens[nr] * song.antChn * sizeof (tonTyp); - - for (int32_t i = 0; i < scanLen; i++) - { - if (scanPtr[i] != 0) - return false; - } - - return true; -} - -static bool allocateInstr(uint16_t i) -{ - if (instr[i] != NULL) - return true; - - instrTyp *p = (instrTyp *)calloc(1, sizeof (instrTyp)); - if (p == NULL) - return false; - - sampleTyp *s = p->samp; - for (int32_t j = 0; j < 16; j++, s++) - { - s->pan = 128; - s->vol = 64; - } - - instr[i] = p; - return true; -} - -static void freeInstr(uint16_t nr) -{ - if (nr > 128) - return; - - instrTyp *ins = instr[nr]; - if (ins == NULL) - return; - - sampleTyp *s = ins->samp; - for (uint8_t i = 0; i < 16; i++, s++) - { - if (s->pek != NULL) - free(s->pek); - } - - free(ins); - instr[nr] = NULL; -} - -static void freeAllInstr(void) -{ - for (uint16_t i = 0; i <= 128; i++) - freeInstr(i); -} - -static void freeAllPatterns(void) // 8bb: added this one, since it's handy -{ - for (int32_t i = 0; i < 256; i++) - { - if (patt[i] != NULL) - { - free(patt[i]); - patt[i] = NULL; - } - - pattLens[i] = 64; - } -} - -static void delta2Samp(int8_t *p, uint32_t len, bool sample16Bit) -{ - if (sample16Bit) - { - len >>= 1; - - int16_t *p16 = (int16_t *)p; - - int16_t olds16 = 0; - for (uint32_t i = 0; i < len; i++) - { - const int16_t news16 = p16[i] + olds16; - p16[i] = news16; - olds16 = news16; - } - } - else - { - int8_t *p8 = (int8_t *)p; - - int8_t olds8 = 0; - for (uint32_t i = 0; i < len; i++) - { - const int8_t news8 = p8[i] + olds8; - p8[i] = news8; - olds8 = news8; - } - } -} - -static void unpackPatt(uint8_t *dst, uint16_t inn, uint16_t len, uint8_t antChn) -{ - if (dst == NULL) - return; - - const uint8_t *src = dst + inn; - const int32_t srcEnd = len * (sizeof (tonTyp) * antChn); - - int32_t srcIdx = 0; - for (int32_t i = 0; i < len; i++) - { - for (int32_t j = 0; j < antChn; j++) - { - if (srcIdx >= srcEnd) - return; // error! - - const uint8_t note = *src++; - if (note & 0x80) - { - *dst++ = (note & 0x01) ? *src++ : 0; - *dst++ = (note & 0x02) ? *src++ : 0; - *dst++ = (note & 0x04) ? *src++ : 0; - *dst++ = (note & 0x08) ? *src++ : 0; - *dst++ = (note & 0x10) ? *src++ : 0; - } - else - { - *dst++ = note; - *dst++ = *src++; - *dst++ = *src++; - *dst++ = *src++; - *dst++ = *src++; - } - - // 8bb: added this. If note >97, remove it (prevents out-of-range read in note->sample LUT) - if (*(dst-5) > 97) - *(dst-5) = 0; - - srcIdx += sizeof (tonTyp); - } - } -} - -void freeMusic(void) -{ - stopMusic(); - freeAllInstr(); - freeAllPatterns(); - - song.tempo = 6; - song.speed = 125; - song.timer = 1; - - setFrqTab(true); - resetMusic(); -} - -void stopVoices(void) -{ - stmTyp *ch = stm; - for (uint8_t i = 0; i < 32; i++, ch++) - { - ch->tonTyp = 0; - ch->relTonNr = 0; - ch->instrNr = 0; - ch->instrSeg = instr[0]; // 8bb: placeholder instrument - ch->status = IS_Vol; - - ch->realVol = 0; - ch->outVol = 0; - ch->oldVol = 0; - ch->finalVol = 0; - ch->oldPan = 128; - ch->outPan = 128; - ch->finalPan = 128; - ch->vibDepth = 0; - } -} - -static void resetMusic(void) -{ - song.timer = 1; - stopVoices(); - setPos(0, 0); -} - -void setPos(int32_t pos, int32_t row) // -1 = don't change -{ - if (pos != -1) - { - song.songPos = (int16_t)pos; - if (song.len > 0 && song.songPos >= song.len) - song.songPos = song.len - 1; - - song.pattNr = song.songTab[song.songPos]; - song.pattLen = pattLens[song.pattNr]; - } - - if (row != -1) - { - song.pattPos = (int16_t)row; - if (song.pattPos >= song.pattLen) - song.pattPos = song.pattLen - 1; - } - - song.timer = 1; -} - -/*************************************************************************** - * MODULE LOADING ROUTINES * - ***************************************************************************/ - -static bool loadInstrHeader(MEMFILE *f, uint16_t i) -{ - instrHeaderTyp ih; - - memset(&ih, 0, INSTR_HEADER_SIZE); - mread(&ih.instrSize, 4, 1, f); - if (ih.instrSize > INSTR_HEADER_SIZE) ih.instrSize = INSTR_HEADER_SIZE; - - if (ih.instrSize < 4) // 8bb: added protection - return false; - - mread(ih.name, ih.instrSize-4, 1, f); - - if (ih.antSamp > 16) - return false; - - if (ih.antSamp > 0) - { - if (!allocateInstr(i)) - return false; - - instrTyp *ins = instr[i]; - - memcpy(ins->name, ih.name, 22); - ins->name[22] = '\0'; - - // 8bb: copy instrument header elements to our instrument struct - memcpy(ins->ta, ih.ta, 96); - memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t)); - memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t)); - ins->envVPAnt = ih.envVPAnt; - ins->envPPAnt = ih.envPPAnt; - ins->envVSust = ih.envVSust; - ins->envVRepS = ih.envVRepS; - ins->envVRepE = ih.envVRepE; - ins->envPSust = ih.envPSust; - ins->envPRepS = ih.envPRepS; - ins->envPRepE = ih.envPRepE; - ins->envVTyp = ih.envVTyp; - ins->envPTyp = ih.envPTyp; - ins->vibTyp = ih.vibTyp; - ins->vibSweep = ih.vibSweep; - ins->vibDepth = ih.vibDepth; - ins->vibRate = ih.vibRate; - ins->fadeOut = ih.fadeOut; - ins->mute = (ih.mute == 1) ? true : false; // 8bb: correct logic! - ins->antSamp = ih.antSamp; - - if (mread(ih.samp, ih.antSamp * sizeof (sampleHeaderTyp), 1, f) != 1) - return false; - - sampleTyp *s = instr[i]->samp; - sampleHeaderTyp *src = ih.samp; - for (int32_t j = 0; j < ih.antSamp; j++, s++, src++) - { - memcpy(s->name, src->name, 22); - s->name[22] = '\0'; - - s->len = src->len; - s->repS = src->repS; - s->repL = src->repL; - s->vol = src->vol; - s->fine = src->fine; - s->typ = src->typ; - s->pan = src->pan; - s->relTon = src->relTon; - } - } - - return true; -} - -static bool loadInstrSample(MEMFILE *f, uint16_t i) -{ - if (instr[i] == NULL) - return true; // empty instrument - - sampleTyp *s = instr[i]->samp; - for (uint16_t j = 0; j < instr[i]->antSamp; j++, s++) - { - if (s->len > 0) - { - bool sample16Bit = !!(s->typ & SAMPLE_16BIT); - - s->pek = (int8_t *)malloc(s->len+2); // 8bb: +2 for fixed interpolation tap sample - if (s->pek == NULL) - return false; - - mread(s->pek, 1, s->len, f); - delta2Samp(s->pek, s->len, sample16Bit); - } - - checkSampleRepeat(i, j); - } - - return true; -} - -static bool loadPatterns(MEMFILE *f, uint16_t antPtn) -{ - uint8_t tmpLen; - patternHeaderTyp ph; - - for (uint16_t i = 0; i < antPtn; i++) - { - mread(&ph.patternHeaderSize, 4, 1, f); - mread(&ph.typ, 1, 1, f); - - ph.pattLen = 0; - if (song.ver == 0x0102) - { - mread(&tmpLen, 1, 1, f); - mread(&ph.dataLen, 2, 1, f); - ph.pattLen = (uint16_t)tmpLen + 1; // 8bb: +1 in v1.02 - - if (ph.patternHeaderSize > 8) - mseek(f, ph.patternHeaderSize - 8, SEEK_CUR); - } - else - { - mread(&ph.pattLen, 2, 1, f); - mread(&ph.dataLen, 2, 1, f); - - if (ph.patternHeaderSize > 9) - mseek(f, ph.patternHeaderSize - 9, SEEK_CUR); - } - - if (meof(f)) - { - mclose(&f); - return false; - } - - pattLens[i] = ph.pattLen; - if (ph.dataLen) - { - const uint16_t a = ph.pattLen * song.antChn * sizeof (tonTyp); - - patt[i] = (tonTyp *)malloc(a); - if (patt[i] == NULL) - return false; - - uint8_t *pattPtr = (uint8_t *)patt[i]; - - memset(pattPtr, 0, a); - mread(&pattPtr[a - ph.dataLen], 1, ph.dataLen, f); - unpackPatt(pattPtr, a - ph.dataLen, ph.pattLen, song.antChn); - } - - if (patternEmpty(i)) - { - if (patt[i] != NULL) - { - free(patt[i]); - patt[i] = NULL; - } - - pattLens[i] = 64; - } - } - - return true; -} - -static bool loadMusicMOD(MEMFILE *f) -{ - uint8_t ha[sizeof (songMOD31HeaderTyp)]; - songMOD31HeaderTyp *h_MOD31 = (songMOD31HeaderTyp *)ha; - songMOD15HeaderTyp *h_MOD15 = (songMOD15HeaderTyp *)ha; - - mread(ha, sizeof (ha), 1, f); - if (meof(f)) - goto loadError2; - - memcpy(song.name, h_MOD31->name, 20); - song.name[20] = '\0'; - - uint8_t j = 0; - for (uint8_t i = 1; i <= 16; i++) - { - if (memcmp(h_MOD31->Sig, MODSig[i-1], 4) == 0) - j = i + i; - } - - if (memcmp(h_MOD31->Sig, "M!K!", 4) == 0 || memcmp(h_MOD31->Sig, "FLT4", 4) == 0) - j = 4; - - if (memcmp(h_MOD31->Sig, "OCTA", 4) == 0) - j = 8; - - uint8_t typ; - if (j > 0) - { - typ = 1; - song.antChn = j; - } - else - { - typ = 2; - song.antChn = 4; - } - - int16_t ai; - if (typ == 1) - { - mseek(f, sizeof (songMOD31HeaderTyp), SEEK_SET); - song.len = h_MOD31->len; - song.repS = h_MOD31->repS; - memcpy(song.songTab, h_MOD31->songTab, 128); - ai = 31; - } - else - { - mseek(f, sizeof (songMOD15HeaderTyp), SEEK_SET); - song.len = h_MOD15->len; - song.repS = h_MOD15->repS; - memcpy(song.songTab, h_MOD15->songTab, 128); - ai = 15; - } - - song.antInstrs = ai; // 8bb: added this - - if (meof(f)) - goto loadError2; - - int32_t b = 0; - for (int32_t a = 0; a < 128; a++) - { - if (song.songTab[a] > b) - b = song.songTab[a]; - } - - uint8_t pattBuf[32 * 4 * 64]; // 8bb: max pattern size (32 channels, 64 rows) - for (uint16_t a = 0; a <= b; a++) - { - patt[a] = (tonTyp *)calloc(song.antChn * 64, sizeof (tonTyp)); - if (patt[a] == NULL) - goto loadError; - - pattLens[a] = 64; - - mread(pattBuf, 1, song.antChn * 4 * 64, f); - if (meof(f)) - goto loadError; - - // convert pattern - uint8_t *bytes = pattBuf; - tonTyp *ton = patt[a]; - for (int32_t i = 0; i < 64 * song.antChn; i++, bytes += 4, ton++) - { - const uint16_t period = ((bytes[0] & 0x0F) << 8) | bytes[1]; - for (uint8_t k = 0; k < 96; k++) - { - if (period >= amigaPeriod[k]) - { - ton->ton = k+1; - break; - } - } - - ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4); - ton->effTyp = bytes[2] & 0x0F; - ton->eff = bytes[3]; - - switch (ton->effTyp) - { - case 0xC: - { - if (ton->eff > 64) - ton->eff = 64; - } - break; - - case 0x1: - case 0x2: - { - if (ton->eff == 0) - ton->effTyp = 0; - } - break; - - case 0x5: - { - if (ton->eff == 0) - ton->effTyp = 3; - } - break; - - case 0x6: - { - if (ton->eff == 0) - ton->effTyp = 4; - } - break; - - case 0xA: - { - if (ton->eff == 0) - ton->effTyp = 0; - } - break; - - case 0xE: - { - const uint8_t effTyp = ton->effTyp >> 4; - const uint8_t eff = ton->effTyp & 15; - - if (eff == 0 && (effTyp == 0x1 || effTyp == 0x2 || effTyp == 0xA || effTyp == 0xB)) - { - ton->eff = 0; - ton->effTyp = 0; - } - } - break; - - default: break; - } - } - - if (patternEmpty(a)) - { - free(patt[a]); - patt[a] = NULL; - pattLens[a] = 64; - } - } - - for (uint16_t a = 1; a <= ai; a++) - { - modSampleTyp *modSmp = &h_MOD31->sample[a-1]; - - uint32_t len = 2 * SWAP16(modSmp->len); - if (len == 0) - continue; - - if (!allocateInstr(a)) - goto loadError; - - sampleTyp *xmSmp = &instr[a]->samp[0]; - - memcpy(xmSmp->name, modSmp->name, 22); - xmSmp->name[22] = '\0'; - - uint32_t repS = 2 * SWAP16(modSmp->repS); - uint32_t repL = 2 * SWAP16(modSmp->repL); - - if (repL <= 2) - { - repS = 0; - repL = 0; - } - - if (repS+repL > len) - { - if (repS >= len) - { - repS = 0; - repL = 0; - } - else - { - repL = len-repS; - } - } - - xmSmp->typ = (repL > 2) ? 1 : 0; - xmSmp->len = len; - xmSmp->vol = (modSmp->vol <= 64) ? modSmp->vol : 64; - xmSmp->fine = 8 * ((2 * ((modSmp->fine & 15) ^ 8)) - 16); - xmSmp->repL = repL; - xmSmp->repS = repS; - - xmSmp->pek = (int8_t *)malloc(len + 2); - if (xmSmp->pek == NULL) - goto loadError; - - mread(xmSmp->pek, 1, len, f); - } - - mclose(&f); - - if (song.repS > song.len) - song.repS = 0; - - resetMusic(); - upDateInstrs(); - - moduleLoaded = true; - return true; -loadError: - freeAllInstr(); - freeAllPatterns(); -loadError2: - mclose(&f); - return false; -} - -bool loadMusicFromData(const uint8_t *data, uint32_t dataLength) // .XM/.MOD/.FT -{ - uint16_t i; - songHeaderTyp h; - - freeMusic(); - setFrqTab(false); - - moduleLoaded = false; - - MEMFILE *f = mopen(data, dataLength); - if (f == NULL) - return false; - - // 8bb: instr 0 is a placeholder for empty instruments - allocateInstr(0); - instr[0]->samp[0].vol = 0; - - mread(&h, sizeof (h), 1, f); - if (meof(f)) - goto loadError2; - - if (memcmp(h.sig, "Extended Module: ", 17) != 0) - { - mrewind(f); - return loadMusicMOD(f); - } - - if (h.ver < 0x0102 || h.ver > 0x104 || h.antChn < 2 || h.antChn > 32 || (h.antChn & 1) != 0 || - h.antPtn > 256 || h.antInstrs > 128) - { - goto loadError2; - } - - mseek(f, 60+h.headerSize, SEEK_SET); - if (meof(f)) - goto loadError2; - - memcpy(song.name, h.name, 20); - song.name[20] = '\0'; - - song.len = h.len; - song.repS = h.repS; - song.antChn = (uint8_t)h.antChn; - bool linearFrequencies = !!(h.flags & LINEAR_FREQUENCIES); - setFrqTab(linearFrequencies); - memcpy(song.songTab, h.songTab, 256); - - song.antInstrs = h.antInstrs; // 8bb: added this - if (h.defSpeed == 0) h.defSpeed = 125; // 8bb: (BPM) FT2 doesn't do this, but we do it for safety - song.speed = h.defSpeed; - song.tempo = h.defTempo; - song.ver = h.ver; - - // 8bb: bugfixes... - if (song.speed < 1) song.speed = 1; - if (song.tempo < 1) song.tempo = 1; - // ---------------- - - if (song.ver < 0x0104) // old FT2 XM format - { - for (i = 1; i <= h.antInstrs; i++) - { - if (!loadInstrHeader(f, i)) - goto loadError; - } - - if (!loadPatterns(f, h.antPtn)) - goto loadError; - - for (i = 1; i <= h.antInstrs; i++) - { - if (!loadInstrSample(f, i)) - goto loadError; - } - } - else // latest FT2 XM format - { - if (!loadPatterns(f, h.antPtn)) - goto loadError; - - for (i = 1; i <= h.antInstrs; i++) - { - if (!loadInstrHeader(f, i)) - goto loadError; - - if (!loadInstrSample(f, i)) - goto loadError; - } - } - - mclose(&f); - - if (song.repS > song.len) - song.repS = 0; - - resetMusic(); - upDateInstrs(); - - moduleLoaded = true; - return true; - -loadError: - freeAllInstr(); - freeAllPatterns(); -loadError2: - mclose(&f); - return false; -} - -/*************************************************************************** - * PROCESS HANDLING * - ***************************************************************************/ - -bool startMusic(void) -{ - if (!moduleLoaded || song.speed == 0) - return false; - - mix_ClearChannels(); - stopVoices(); - song.globVol = 64; - - speedVal = ((realReplayRate * 5) / 2) / song.speed; - quickVolSizeVal = realReplayRate / 200; - - if (!mix_Init(soundBufferSize)) - return false; - - musicPaused = false; - return true; -} - -void stopMusic(void) -{ - pauseMusic(); - - mix_Free(); - song.globVol = 64; - - resumeMusic(); -} - -void startPlaying(void) -{ - stopMusic(); - song.pattDelTime = song.pattDelTime2 = 0; // 8bb: added these - setPos(0, 0); - startMusic(); -} - -void stopPlaying(void) -{ - stopMusic(); - stopVoices(); -} - -void pauseMusic(void) -{ - musicPaused = true; -} - -void resumeMusic(void) -{ - musicPaused = false; -} - -// 8bb: added these three, handy -void toggleMusic(void) -{ - musicPaused ^= 1; -} - -void setInterpolation(bool on) -{ - interpolationFlag = on; - mix_ClearChannels(); -} - -void setVolumeRamping(bool on) -{ - volumeRampingFlag = on; - mix_ClearChannels(); -} - -/*************************************************************************** - * CONFIGURATION ROUTINES * - ***************************************************************************/ - -void setMasterVol(int32_t v) // 0..256 -{ - masterVol = CLAMP(v, 0, 256); - - stmTyp *ch = stm; - for (int32_t i = 0; i < 32; i++, ch++) - ch->status |= IS_Vol; -} - -void setAmp(int32_t level) // 1..32 -{ - boostLevel = (int16_t)CLAMP(level, 1, 32); - CDA_Amp = boostLevel * 8; -} - -int32_t getMasterVol(void) // 8bb: added this -{ - return masterVol; -} - -int32_t getAmp(void) // 8bb: added this -{ - return boostLevel; -} - -uint8_t getNumActiveVoices(void) // 8bb: added this -{ - uint8_t activeVoices = 0; - for (int32_t i = 0; i < song.antChn; i++) - { - CIType *v = getVoice(i); - if (!(v->SType & SType_Off) && v->SVol > 0) - activeVoices++; - } - - return activeVoices; -} - -static void setFrqTab(bool linear) -{ - linearFrqTab = linear; - note2Period = linear ? linearPeriods : amigaPeriods; -} - -void updateReplayRate(void) -{ - // 8bb: bit-exact to FT2 - frequenceDivFactor = (uint32_t)round(65536.0*1712.0/realReplayRate*8363.0); - frequenceMulFactor = (uint32_t)round(256.0*65536.0/realReplayRate*8363.0); -} - -/*************************************************************************** - * INITIALIZATION ROUTINES * - ***************************************************************************/ - -bool initMusic(int32_t audioFrequency, int32_t audioBufferSize, bool interpolation, bool volumeRamping) -{ - freeMusic(); - memset(stm, 0, sizeof (stm)); - - realReplayRate = CLAMP(audioFrequency, 8000, 96000); - updateReplayRate(); - - soundBufferSize = audioBufferSize; - interpolationFlag = interpolation; - volumeRampingFlag = volumeRamping; - - song.tempo = 6; - song.speed = 125; - setFrqTab(true); - resetMusic(); - - return true; -} - -/*************************************************************************** - * MEMORY READ ROUTINES (8bb: added these) * - ***************************************************************************/ - -static MEMFILE *mopen(const uint8_t *src, uint32_t length) -{ - if (src == NULL || length == 0) - return NULL; - - MEMFILE *b = (MEMFILE *)malloc(sizeof (MEMFILE)); - if (b == NULL) - return NULL; - - b->_base = (uint8_t *)src; - b->_ptr = (uint8_t *)src; - b->_cnt = length; - b->_bufsiz = length; - b->_eof = false; - - return b; -} - -static void mclose(MEMFILE **buf) -{ - if (*buf != NULL) - { - free(*buf); - *buf = NULL; - } -} - -static size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf) -{ - if (buf == NULL || buf->_ptr == NULL) - return 0; - - size_t wrcnt = size * count; - if (size == 0 || buf->_eof) - return 0; - - int32_t pcnt = (buf->_cnt > wrcnt) ? (int32_t)wrcnt : (int32_t)buf->_cnt; - memcpy(buffer, buf->_ptr, pcnt); - - buf->_cnt -= pcnt; - buf->_ptr += pcnt; - - if (buf->_cnt <= 0) - { - buf->_ptr = buf->_base + buf->_bufsiz; - buf->_cnt = 0; - buf->_eof = true; - } - - return pcnt / size; -} - -static bool meof(MEMFILE *buf) -{ - if (buf == NULL) - return true; - - return buf->_eof; -} - -static void mseek(MEMFILE *buf, int32_t offset, int32_t whence) -{ - if (buf == NULL) - return; - - if (buf->_base) - { - switch (whence) - { - case SEEK_SET: buf->_ptr = buf->_base + offset; break; - case SEEK_CUR: buf->_ptr += offset; break; - case SEEK_END: buf->_ptr = buf->_base + buf->_bufsiz + offset; break; - default: break; - } - - buf->_eof = false; - if (buf->_ptr >= buf->_base+buf->_bufsiz) - { - buf->_ptr = buf->_base + buf->_bufsiz; - buf->_eof = true; - } - - buf->_cnt = (buf->_base + buf->_bufsiz) - buf->_ptr; - } -} - -static void mrewind(MEMFILE *buf) -{ - mseek(buf, 0, SEEK_SET); -} diff --git a/libraries/m4p/pmplay.h b/libraries/m4p/pmplay.h deleted file mode 100644 index 38e14b7ed..000000000 --- a/libraries/m4p/pmplay.h +++ /dev/null @@ -1,153 +0,0 @@ -#pragma once - -#include -#include - -enum // voice flags -{ - IS_Vol = 1, - IS_Period = 2, - IS_NyTon = 4, - IS_Pan = 8, - IS_QuickVol = 16 -}; - -enum // note -{ - NOTE_KEYOFF = 97 -}; - -enum // header flags -{ - LINEAR_FREQUENCIES = 1 -}; - -enum // sample flags -{ - LOOP_OFF = 0, - LOOP_FORWARD = 1, - LOOP_PINGPONG = 2, - SAMPLE_16BIT = 16 -}; - -enum // envelope flags -{ - ENV_ENABLED = 1, - ENV_SUSTAIN = 2, - ENV_LOOP = 4 -}; - -typedef struct songTyp_t -{ - char name[20+1]; - uint8_t antChn, pattDelTime, pattDelTime2, pBreakPos, songTab[256]; - bool pBreakFlag, posJumpFlag; - int16_t songPos, pattNr, pattPos, pattLen; - uint16_t len, repS, speed, tempo, globVol, timer, ver; - - uint16_t antInstrs; // 8bb: added this -} songTyp; - -typedef struct sampleTyp_t -{ - char name[22+1]; - int32_t len, repS, repL; - uint8_t vol; - int8_t fine; - uint8_t typ, pan; - int8_t relTon; - int8_t *pek; -} sampleTyp; - -typedef struct instrTyp_t -{ - char name[22+1]; - uint8_t ta[96]; - int16_t envVP[12][2], envPP[12][2]; - uint8_t envVPAnt, envPPAnt; - uint8_t envVSust, envVRepS, envVRepE; - uint8_t envPSust, envPRepS, envPRepE; - uint8_t envVTyp, envPTyp; - uint8_t vibTyp, vibSweep, vibDepth, vibRate; - uint16_t fadeOut; - uint8_t mute; - int16_t antSamp; - sampleTyp samp[16]; -} instrTyp; - -typedef struct stmTyp_t -{ - volatile uint8_t status; - int8_t relTonNr, fineTune; - uint8_t sampleNr, instrNr, effTyp, eff, smpOffset, tremorSave, tremorPos; - uint8_t globVolSlideSpeed, panningSlideSpeed, mute, waveCtrl, portaDir; - uint8_t glissFunk, vibPos, tremPos, vibSpeed, vibDepth, tremSpeed, tremDepth; - uint8_t pattPos, loopCnt, volSlideSpeed, fVolSlideUpSpeed, fVolSlideDownSpeed; - uint8_t fPortaUpSpeed, fPortaDownSpeed, ePortaUpSpeed, ePortaDownSpeed; - uint8_t portaUpSpeed, portaDownSpeed, retrigSpeed, retrigCnt, retrigVol; - uint8_t volKolVol, tonNr, envPPos, eVibPos, envVPos, realVol, oldVol, outVol; - uint8_t oldPan, outPan, finalPan; - bool envSustainActive; - int16_t envVIPValue, envPIPValue; - uint16_t outPeriod, realPeriod, finalPeriod, finalVol, tonTyp, wantPeriod, portaSpeed; - uint16_t envVCnt, envVAmp, envPCnt, envPAmp, eVibAmp, eVibSweep; - uint16_t fadeOutAmp, fadeOutSpeed; - int32_t smpStartPos; - instrTyp *instrSeg; -} stmTyp; - -#ifdef _MSC_VER -#pragma pack(push) -#pragma pack(1) -#endif -typedef struct tonTyp_t // this one must be packed on some systems -{ - uint8_t ton, instr, vol, effTyp, eff; -} -#ifdef __GNUC__ -__attribute__ ((packed)) -#endif -tonTyp; -#ifdef _MSC_VER -#pragma pack(pop) -#endif - -// globalized -extern volatile bool interpolationFlag, volumeRampingFlag, moduleLoaded, musicPaused; -extern bool linearFrqTab; -extern volatile const uint16_t *note2Period; -extern uint16_t pattLens[256]; -extern int16_t PMPTmpActiveChannel, boostLevel; -extern int32_t masterVol, PMPLeft; -extern int32_t realReplayRate, quickVolSizeVal, speedVal; -extern uint32_t frequenceDivFactor, frequenceMulFactor; -extern uint32_t CDA_Amp; -extern tonTyp *patt[256]; -extern instrTyp *instr[1+128]; -extern songTyp song; -extern stmTyp stm[32]; - -#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) - -bool initMusic(int32_t audioFrequency, int32_t audioBufferSize, bool interpolation, bool volumeRamping); -bool loadMusicFromData(const uint8_t *data, uint32_t dataLength); // .XM/.MOD/.FT -void freeMusic(void); -bool startMusic(void); -void stopMusic(); -void pauseMusic(void); -void resumeMusic(void); -void setMasterVol(int32_t v); // 0..256 -void setAmp(int32_t level); // 1..32 -void setPos(int32_t pos, int32_t row); // input of -1 = don't change -void stopVoices(void); -void updateReplayRate(void); -void startPlaying(void); -void stopPlaying(void); - -// 8bb: added these three, handy -int32_t getMasterVol(void); -int32_t getAmp(void); -uint8_t getNumActiveVoices(void); -void toggleMusic(void); -void setInterpolation(bool on); -void setVolumeRamping(bool on); diff --git a/libraries/m4p/snd_masm.c b/libraries/m4p/snd_masm.c deleted file mode 100644 index c7273bfef..000000000 --- a/libraries/m4p/snd_masm.c +++ /dev/null @@ -1,676 +0,0 @@ -#include -#include -#include "snd_masm.h" -#include "pmplay.h" - -/* 8bb: This is done in a slightly different way, but the result -** is the same (bit-accurate to FT2.08/FT2.09 w/ SB16, and WAV-writer). -** -** Mixer macros are stored in snd_masm.h -*/ - -void PMPMix32Proc(CIType *v, int32_t numSamples, int32_t bufferPos) -{ - if (v->SType & SType_Off) - return; // voice is not active - - uint32_t volStatus = v->SLVol1 | v->SRVol1; - if (volumeRampingFlag) - volStatus |= v->SLVol2 | v->SRVol2; - - if (volStatus == 0) // silence mix - { - const uint64_t samplesToMix = (uint64_t)v->SFrq * (uint32_t)numSamples; // 16.16fp - - const int32_t samples = (int32_t)(samplesToMix >> 16); - const int32_t samplesFrac = (samplesToMix & 0xFFFF) + (v->SPosDec >> 16); - - int32_t realPos = v->SPos + samples + (samplesFrac >> 16); - int32_t posFrac = samplesFrac & 0xFFFF; - - if (realPos >= v->SLen) - { - uint8_t SType = v->SType; - if (SType & (SType_Fwd+SType_Rev)) - { - do - { - SType ^= SType_RevDir; - realPos -= v->SRepL; - } - while (realPos >= v->SLen); - v->SType = SType; - } - else - { - v->SType = SType_Off; - return; - } - } - - v->SPosDec = posFrac << 16; - v->SPos = realPos; - } - else // normal mixing - { - bool mixInCenter; - if (volumeRampingFlag) - mixInCenter = (v->SLVol2 == v->SRVol2) && (v->SLVolIP == v->SRVolIP); - else - mixInCenter = v->SLVol1 == v->SRVol1; - - mixRoutineTable[(mixInCenter * 8) + v->SMixType](v, numSamples, bufferPos); - } -} - -static void mix8b(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample; - - GET_VOL - GET_MIXER_VARS - SET_BASE8 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - CDA_BytesLeft -= samplesToMix; - - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_8BIT - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_8BIT - MIX_8BIT - MIX_8BIT - MIX_8BIT - } - HANDLE_POS_END - } - - SET_BACK_MIXER_POS -} - -static void mix8bIntrp(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample, sample2; - - GET_VOL - GET_MIXER_VARS - SET_BASE8 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - CDA_BytesLeft -= samplesToMix; - - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_8BIT_INTRP - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_8BIT_INTRP - MIX_8BIT_INTRP - MIX_8BIT_INTRP - MIX_8BIT_INTRP - } - HANDLE_POS_END - } - - SET_BACK_MIXER_POS -} - -static void mix8bRamp(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample; - - GET_MIXER_VARS - GET_RAMP_VARS - SET_BASE8 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - LIMIT_MIX_NUM_RAMP - CDA_BytesLeft -= samplesToMix; - - GET_VOL_RAMP - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_8BIT - VOL_RAMP - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_8BIT - VOL_RAMP - MIX_8BIT - VOL_RAMP - MIX_8BIT - VOL_RAMP - MIX_8BIT - VOL_RAMP - } - HANDLE_POS_END - SET_VOL_BACK - } - - SET_BACK_MIXER_POS -} - -static void mix8bRampIntrp(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample, sample2; - - GET_MIXER_VARS - GET_RAMP_VARS - SET_BASE8 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - LIMIT_MIX_NUM_RAMP - CDA_BytesLeft -= samplesToMix; - - GET_VOL_RAMP - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_8BIT_INTRP - VOL_RAMP - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_8BIT_INTRP - VOL_RAMP - MIX_8BIT_INTRP - VOL_RAMP - MIX_8BIT_INTRP - VOL_RAMP - MIX_8BIT_INTRP - VOL_RAMP - } - HANDLE_POS_END - SET_VOL_BACK - } - - SET_BACK_MIXER_POS -} - -static void mix16b(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample; - - GET_VOL - GET_MIXER_VARS - SET_BASE16 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - CDA_BytesLeft -= samplesToMix; - - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_16BIT - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_16BIT - MIX_16BIT - MIX_16BIT - MIX_16BIT - } - HANDLE_POS_END - } - - SET_BACK_MIXER_POS -} - -static void mix16bIntrp(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample, sample2; - - GET_VOL - GET_MIXER_VARS - SET_BASE16 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - CDA_BytesLeft -= samplesToMix; - - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_16BIT_INTRP - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_16BIT_INTRP - MIX_16BIT_INTRP - MIX_16BIT_INTRP - MIX_16BIT_INTRP - } - HANDLE_POS_END - } - - SET_BACK_MIXER_POS -} - -static void mix16bRamp(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample; - - GET_MIXER_VARS - GET_RAMP_VARS - SET_BASE16 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - LIMIT_MIX_NUM_RAMP - CDA_BytesLeft -= samplesToMix; - - GET_VOL_RAMP - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_16BIT - VOL_RAMP - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_16BIT - VOL_RAMP - MIX_16BIT - VOL_RAMP - MIX_16BIT - VOL_RAMP - MIX_16BIT - VOL_RAMP - } - HANDLE_POS_END - SET_VOL_BACK - } - - SET_BACK_MIXER_POS -} - -static void mix16bRampIntrp(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample, sample2; - - GET_MIXER_VARS - GET_RAMP_VARS - SET_BASE16 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - LIMIT_MIX_NUM_RAMP - CDA_BytesLeft -= samplesToMix; - - GET_VOL_RAMP - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_16BIT_INTRP - VOL_RAMP - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_16BIT_INTRP - VOL_RAMP - MIX_16BIT_INTRP - VOL_RAMP - MIX_16BIT_INTRP - VOL_RAMP - MIX_16BIT_INTRP - VOL_RAMP - } - HANDLE_POS_END - SET_VOL_BACK - } - - SET_BACK_MIXER_POS -} - -static void mix8bCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample; - - GET_VOL_CENTER - GET_MIXER_VARS - SET_BASE8 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - CDA_BytesLeft -= samplesToMix; - - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_8BIT_M - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_8BIT_M - MIX_8BIT_M - MIX_8BIT_M - MIX_8BIT_M - } - HANDLE_POS_END - } - - SET_BACK_MIXER_POS -} - -static void mix8bIntrpCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample, sample2; - - GET_VOL_CENTER - GET_MIXER_VARS - SET_BASE8 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - CDA_BytesLeft -= samplesToMix; - - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_8BIT_INTRP_M - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_8BIT_INTRP_M - MIX_8BIT_INTRP_M - MIX_8BIT_INTRP_M - MIX_8BIT_INTRP_M - } - HANDLE_POS_END - } - - SET_BACK_MIXER_POS -} - -static void mix8bRampCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample; - - GET_MIXER_VARS - GET_RAMP_VARS - SET_BASE8 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - LIMIT_MIX_NUM_RAMP - CDA_BytesLeft -= samplesToMix; - - GET_VOL_RAMP - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_8BIT_M - VOL_RAMP - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_8BIT_M - VOL_RAMP - MIX_8BIT_M - VOL_RAMP - MIX_8BIT_M - VOL_RAMP - MIX_8BIT_M - VOL_RAMP - } - HANDLE_POS_END - SET_VOL_BACK - } - - SET_BACK_MIXER_POS -} - -static void mix8bRampIntrpCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample, sample2; - - GET_MIXER_VARS - GET_RAMP_VARS - SET_BASE8 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - LIMIT_MIX_NUM_RAMP - CDA_BytesLeft -= samplesToMix; - - GET_VOL_RAMP - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_8BIT_INTRP_M - VOL_RAMP - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_8BIT_INTRP_M - VOL_RAMP - MIX_8BIT_INTRP_M - VOL_RAMP - MIX_8BIT_INTRP_M - VOL_RAMP - MIX_8BIT_INTRP_M - VOL_RAMP - } - HANDLE_POS_END - SET_VOL_BACK - } - - SET_BACK_MIXER_POS -} - -static void mix16bCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample; - - GET_VOL_CENTER - GET_MIXER_VARS - SET_BASE16 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - CDA_BytesLeft -= samplesToMix; - - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_16BIT_M - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_16BIT_M - MIX_16BIT_M - MIX_16BIT_M - MIX_16BIT_M - } - HANDLE_POS_END - } - - SET_BACK_MIXER_POS -} - -static void mix16bIntrpCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample, sample2; - - GET_VOL_CENTER - GET_MIXER_VARS - SET_BASE16 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - CDA_BytesLeft -= samplesToMix; - - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_16BIT_INTRP_M - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_16BIT_INTRP_M - MIX_16BIT_INTRP_M - MIX_16BIT_INTRP_M - MIX_16BIT_INTRP_M - } - HANDLE_POS_END - } - - SET_BACK_MIXER_POS -} - -static void mix16bRampCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample; - - GET_MIXER_VARS - GET_RAMP_VARS - SET_BASE16 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - LIMIT_MIX_NUM_RAMP - CDA_BytesLeft -= samplesToMix; - - GET_VOL_RAMP - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_16BIT_M - VOL_RAMP - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_16BIT_M - VOL_RAMP - MIX_16BIT_M - VOL_RAMP - MIX_16BIT_M - VOL_RAMP - MIX_16BIT_M - VOL_RAMP - } - HANDLE_POS_END - SET_VOL_BACK - } - - SET_BACK_MIXER_POS -} - -static void mix16bRampIntrpCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) -{ - int32_t sample, sample2; - - GET_MIXER_VARS - GET_RAMP_VARS - SET_BASE16 - - int32_t CDA_BytesLeft = numSamples; - while (CDA_BytesLeft > 0) - { - LIMIT_MIX_NUM - LIMIT_MIX_NUM_RAMP - CDA_BytesLeft -= samplesToMix; - - GET_VOL_RAMP - HANDLE_POS_START - for (i = 0; i < (samplesToMix & 3); i++) - { - MIX_16BIT_INTRP_M - VOL_RAMP - } - samplesToMix >>= 2; - for (i = 0; i < samplesToMix; i++) - { - MIX_16BIT_INTRP_M - VOL_RAMP - MIX_16BIT_INTRP_M - VOL_RAMP - MIX_16BIT_INTRP_M - VOL_RAMP - MIX_16BIT_INTRP_M - VOL_RAMP - } - HANDLE_POS_END - SET_VOL_BACK - } - - SET_BACK_MIXER_POS -} - -mixRoutine mixRoutineTable[16] = -{ - (mixRoutine)mix8b, - (mixRoutine)mix8bIntrp, - (mixRoutine)mix8bRamp, - (mixRoutine)mix8bRampIntrp, - (mixRoutine)mix16b, - (mixRoutine)mix16bIntrp, - (mixRoutine)mix16bRamp, - (mixRoutine)mix16bRampIntrp, - (mixRoutine)mix8bCenter, - (mixRoutine)mix8bIntrpCenter, - (mixRoutine)mix8bRampCenter, - (mixRoutine)mix8bRampIntrpCenter, - (mixRoutine)mix16bCenter, - (mixRoutine)mix16bIntrpCenter, - (mixRoutine)mix16bRampCenter, - (mixRoutine)mix16bRampIntrpCenter -}; diff --git a/libraries/m4p/snd_masm.h b/libraries/m4p/snd_masm.h deleted file mode 100644 index 436332024..000000000 --- a/libraries/m4p/snd_masm.h +++ /dev/null @@ -1,227 +0,0 @@ -#pragma once - -#include -#include -#include "pmp_mix.h" - -#define GET_VOL \ - const int32_t CDA_LVol = v->SLVol1; \ - const int32_t CDA_RVol = v->SRVol1; \ - -#define GET_VOL_CENTER \ - const int32_t CDA_LVol = v->SLVol1; \ - -#define GET_VOL_RAMP \ - int32_t CDA_LVol = v->SLVol2; \ - int32_t CDA_RVol = v->SRVol2; \ - -#define SET_VOL_BACK \ - v->SLVol2 = CDA_LVol; \ - v->SRVol2 = CDA_RVol; \ - -#define GET_MIXER_VARS \ - int32_t *audioMix = CDA_MixBuffer + (bufferPos << 1); \ - int32_t realPos = v->SPos; \ - uint32_t pos = v->SPosDec; \ - uint16_t CDA_MixBuffPos = (32768+96)-8; /* address of FT2 mix buffer minus mix sample size (used for quirky LERP) */ \ - -#define GET_RAMP_VARS \ - int32_t CDA_LVolIP = v->SLVolIP; \ - int32_t CDA_RVolIP = v->SRVolIP; \ - -#define SET_BASE8 \ - const int8_t *CDA_LinearAdr = (int8_t *)v->SBase; \ - const int8_t *CDA_LinAdrRev = (int8_t *)v->SRevBase; \ - const int8_t *smpPtr = CDA_LinearAdr + realPos; \ - -#define SET_BASE16 \ - const int16_t *CDA_LinearAdr = (int16_t *)v->SBase; \ - const int16_t *CDA_LinAdrRev = (int16_t *)v->SRevBase; \ - const int16_t *smpPtr = CDA_LinearAdr + realPos; \ - -#define INC_POS \ - smpPtr += CDA_IPValH; \ - smpPtr += (CDA_IPValL > (uint32_t)~pos); /* if pos would 32-bit overflow after CDA_IPValL add, add one to smpPtr (branchless) */ \ - pos += CDA_IPValL; \ - -#define SET_BACK_MIXER_POS \ - v->SPosDec = pos & 0xFFFF0000; \ - v->SPos = realPos; \ - -#define VOL_RAMP \ - CDA_LVol += CDA_LVolIP; \ - CDA_RVol += CDA_RVolIP; \ - -// stereo mixing without interpolation - -#define MIX_8BIT \ - sample = (*smpPtr) << (28-8); \ - *audioMix++ += ((int64_t)sample * (int32_t)CDA_LVol) >> 32; \ - *audioMix++ += ((int64_t)sample * (int32_t)CDA_RVol) >> 32; \ - INC_POS \ - -#define MIX_16BIT \ - sample = (*smpPtr) << (28-16); \ - *audioMix++ += ((int64_t)sample * (int32_t)CDA_LVol) >> 32; \ - *audioMix++ += ((int64_t)sample * (int32_t)CDA_RVol) >> 32; \ - INC_POS \ - -// center mixing without interpolation - -#define MIX_8BIT_M \ - sample = (*smpPtr) << (28-8); \ - sample = ((int64_t)sample * (int32_t)CDA_LVol) >> 32; \ - *audioMix++ += sample; \ - *audioMix++ += sample; \ - INC_POS \ - -#define MIX_16BIT_M \ - sample = (*smpPtr) << (28-16); \ - sample = ((int64_t)sample * (int32_t)CDA_LVol) >> 32; \ - *audioMix++ += sample; \ - *audioMix++ += sample; \ - INC_POS \ - -// linear interpolation with bit-accurate results to FT2.08/FT2.09 -#define LERP(s1, s2, f) \ -{ \ - s2 -= s1; \ - f >>= 1; \ - s2 = ((int64_t)s2 * (int32_t)f) >> 32; \ - f += f; \ - s2 += s2; \ - s2 += s1; \ -} \ - -// stereo mixing w/ linear interpolation - -#define MIX_8BIT_INTRP \ - sample = smpPtr[0] << 8; \ - sample2 = smpPtr[1] << 8; \ - LERP(sample, sample2, pos) \ - sample2 <<= (28-16); \ - *audioMix++ += ((int64_t)sample2 * (int32_t)CDA_LVol) >> 32; \ - *audioMix++ += ((int64_t)sample2 * (int32_t)CDA_RVol) >> 32; \ - INC_POS \ - -#define MIX_16BIT_INTRP \ - sample = smpPtr[0]; \ - sample2 = smpPtr[1]; \ - LERP(sample, sample2, pos) \ - sample2 <<= (28-16); \ - *audioMix++ += ((int64_t)sample2 * (int32_t)CDA_LVol) >> 32; \ - *audioMix++ += ((int64_t)sample2 * (int32_t)CDA_RVol) >> 32; \ - INC_POS \ - -// center mixing w/ linear interpolation - -#define MIX_8BIT_INTRP_M \ - sample = smpPtr[0] << 8; \ - sample2 = smpPtr[1] << 8; \ - LERP(sample, sample2, pos) \ - sample2 <<= (28-16); \ - sample = ((int64_t)sample2 * (int32_t)CDA_LVol) >> 32; \ - *audioMix++ += sample; \ - *audioMix++ += sample; \ - INC_POS \ - -#define MIX_16BIT_INTRP_M \ - sample = smpPtr[0]; \ - sample2 = smpPtr[1]; \ - LERP(sample, sample2, pos) \ - sample2 <<= (28-16); \ - sample = ((int64_t)sample2 * (int32_t)CDA_LVol) >> 32; \ - *audioMix++ += sample; \ - *audioMix++ += sample; \ - INC_POS \ - -// ------------------------ - -#define LIMIT_MIX_NUM \ - int32_t samplesToMix; \ - int32_t SFrq = v->SFrq; \ - int32_t i = (v->SLen-1) - realPos; \ - if (i > UINT16_MAX) i = UINT16_MAX; /* 8bb: added this to prevent 64-bit div (still bit-accurate mixing results) */ \ - if (SFrq != 0) \ - { \ - const uint32_t tmp32 = (i << 16) | ((0xFFFF0000 - pos) >> 16); \ - samplesToMix = (tmp32 / (uint32_t)SFrq) + 1; \ - } \ - else \ - { \ - samplesToMix = 65535; \ - } \ - \ - if (samplesToMix > CDA_BytesLeft) \ - samplesToMix = CDA_BytesLeft; \ - -#define LIMIT_MIX_NUM_RAMP \ - if (v->SVolIPLen == 0) \ - { \ - CDA_LVolIP = 0; \ - CDA_RVolIP = 0; \ - } \ - else \ - { \ - if (samplesToMix > v->SVolIPLen) \ - samplesToMix = v->SVolIPLen; \ - \ - v->SVolIPLen -= samplesToMix; \ - } \ - -#define HANDLE_POS_START \ - const bool backwards = (v->SType & (SType_Rev+SType_RevDir)) == SType_Rev+SType_RevDir; \ - if (backwards) \ - { \ - SFrq = 0 - SFrq; \ - realPos = ~realPos; \ - smpPtr = CDA_LinAdrRev + realPos; \ - pos ^= 0xFFFF0000; \ - } \ - else \ - { \ - smpPtr = CDA_LinearAdr + realPos; \ - } \ - \ - pos += CDA_MixBuffPos; \ - const int32_t CDA_IPValH = (int32_t)SFrq >> 16; \ - const uint32_t CDA_IPValL = ((uint32_t)(SFrq & 0xFFFF) << 16) + 8; /* 8 = mixer buffer increase (for LERP to be bit-accurate to FT2) */ \ - -#define HANDLE_POS_END \ - if (backwards) \ - { \ - pos ^= 0xFFFF0000; \ - realPos = ~(int32_t)(smpPtr - CDA_LinAdrRev); \ - } \ - else \ - { \ - realPos = (int32_t)(smpPtr - CDA_LinearAdr); \ - } \ - CDA_MixBuffPos = pos & 0xFFFF; \ - pos &= 0xFFFF0000; \ - \ - if (realPos >= v->SLen) \ - { \ - uint8_t SType = v->SType; \ - if (SType & (SType_Fwd+SType_Rev)) \ - { \ - do \ - { \ - realPos -= v->SRepL; \ - SType ^= SType_RevDir; \ - } \ - while (realPos >= v->SLen); \ - v->SType = SType; \ - } \ - else \ - { \ - v->SType = SType_Off; \ - return; \ - } \ - } \ - -typedef void (*mixRoutine)(void *, int32_t, int32_t); - -extern mixRoutine mixRoutineTable[16]; - -void PMPMix32Proc(CIType *v, int32_t numSamples, int32_t bufferPos); diff --git a/source_files/ddf/ddf_playlist.cc b/source_files/ddf/ddf_playlist.cc index a007df454..f2d391276 100644 --- a/source_files/ddf/ddf_playlist.cc +++ b/source_files/ddf/ddf_playlist.cc @@ -32,7 +32,7 @@ PlaylistEntryContainer playlist; static void DDFMusicParseInfo(const char *info) { static const char *const musstrtype[] = {"UNKNOWN", "MIDI", "MUS", "OGG", "MP3", "SID", "FLAC", - "M4P", "RAD", "IMF280", "IMF560", "IMF700", nullptr}; + "IBXM", "RAD", "IMF280", "IMF560", "IMF700", nullptr}; static const char *const musinftype[] = {"UNKNOWN", "LUMP", "FILE", "PACK", nullptr}; char charbuff[256]; diff --git a/source_files/ddf/ddf_playlist.h b/source_files/ddf/ddf_playlist.h index 77dada9c5..20d61b3d8 100644 --- a/source_files/ddf/ddf_playlist.h +++ b/source_files/ddf/ddf_playlist.h @@ -35,7 +35,7 @@ enum DDFMusicType kDDFMusicMP3, kDDFMusicSID, kDDFMusicFLAC, - kDDFMusicM4P, + kDDFMusicIBXM, kDDFMusicRAD, kDDFMusicIMF280, kDDFMusicIMF560, diff --git a/source_files/edge/CMakeLists.txt b/source_files/edge/CMakeLists.txt index 40c742a84..46e266a21 100644 --- a/source_files/edge/CMakeLists.txt +++ b/source_files/edge/CMakeLists.txt @@ -88,8 +88,8 @@ set (EDGE_SOURCE_FILES s_emidi.cc s_fluid.cc s_fmm.cc - s_m4p.cc - s_opl.cc + s_ibxm.cc + s_imf.cc s_rad.cc s_sid.cc s_wav.cc @@ -144,11 +144,11 @@ set (EDGE_LINK_LIBRARIES fluidlite fmmidi HandmadeMath + ibxm libemidi libRAD libvwad lua - m4p miniz pl_mpeg prns diff --git a/source_files/edge/i_sound.cc b/source_files/edge/i_sound.cc index 1b02c05ba..a2fea62dc 100644 --- a/source_files/edge/i_sound.cc +++ b/source_files/edge/i_sound.cc @@ -30,7 +30,6 @@ #include "s_blit.h" #include "s_cache.h" #include "s_fluid.h" -#include "s_opl.h" #include "s_sound.h" #include "w_wad.h" @@ -266,9 +265,6 @@ void StartupMusic(void) if (!StartupFluid()) fluid_disabled = true; - if (!StartupOpal()) - opl_disabled = true; - return; } diff --git a/source_files/edge/m_option.cc b/source_files/edge/m_option.cc index 1fd613a78..adcff07d7 100644 --- a/source_files/edge/m_option.cc +++ b/source_files/edge/m_option.cc @@ -111,7 +111,6 @@ #include "s_fluid.h" #include "s_fmm.h" #include "s_music.h" -#include "s_opl.h" #include "s_sound.h" #include "stb_sprintf.h" #include "w_wad.h" @@ -527,7 +526,7 @@ static OptionMenuItem soundoptions[] = { {kOptionMenuItemTypePlain, "", nullptr, 0, nullptr, nullptr, nullptr}, {kOptionMenuItemTypeSwitch, "Stereo", "Off/On/Swapped", 3, &var_sound_stereo, nullptr, "NeedRestart"}, {kOptionMenuItemTypePlain, "", nullptr, 0, nullptr, nullptr, nullptr}, - {kOptionMenuItemTypeSwitch, "MIDI Player", "Fluidlite/Opal/FMMIDI/Emu de MIDI (OPLL Mode)/Emu de MIDI (SCC-PSG Mode)", 5, &var_midi_player, OptionMenuChangeMidiPlayer, + {kOptionMenuItemTypeSwitch, "MIDI Player", "Fluidlite/FMMIDI/Emu de MIDI (OPLL Mode)/Emu de MIDI (SCC-PSG Mode)", 4, &var_midi_player, OptionMenuChangeMidiPlayer, nullptr}, {kOptionMenuItemTypeFunction, "Fluidlite Soundfont", nullptr, 0, nullptr, OptionMenuChangeSoundfont, nullptr}, {kOptionMenuItemTypeBoolean, "PC Speaker Mode", YesNo, 2, &pc_speaker_mode, OptionMenuChangePCSpeakerMode, @@ -2037,13 +2036,9 @@ static void OptionMenuChangeLanguage(int key_pressed, ConsoleVariable *console_v // static void OptionMenuChangeMidiPlayer(int key_pressed, ConsoleVariable *console_variable) { - PlaylistEntry *playing = playlist.Find(entry_playing); - if (var_midi_player == 1 || (playing && (playing->type_ == kDDFMusicIMF280 || playing->type_ == kDDFMusicIMF560 || - playing->type_ == kDDFMusicIMF700))) - RestartOpal(); - else if (var_midi_player == 0) + if (var_midi_player == 0) RestartFluid(); - else if (var_midi_player == 2) + else if (var_midi_player == 1) RestartFMM(); else RestartEMIDI(); diff --git a/source_files/edge/s_emidi.cc b/source_files/edge/s_emidi.cc index ebf0a56ea..05d929b88 100644 --- a/source_files/edge/s_emidi.cc +++ b/source_files/edge/s_emidi.cc @@ -318,7 +318,7 @@ AbstractMusicPlayer *PlayEMIDIMusic(uint8_t *data, int length, bool loop) return nullptr; } - player->emidi_synth_ = new dsa::CSMFPlay(sound_device_frequency, var_midi_player == 3 ? dsa::CSMFPlay::OPLL_MODE : dsa::CSMFPlay::SCC_PSG_MODE); + player->emidi_synth_ = new dsa::CSMFPlay(sound_device_frequency, var_midi_player == 2 ? dsa::CSMFPlay::OPLL_MODE : dsa::CSMFPlay::SCC_PSG_MODE); if (!player->emidi_synth_) { LogDebug("Emu de MIDI player: error initializing!\n"); diff --git a/source_files/edge/s_m4p.cc b/source_files/edge/s_ibxm.cc similarity index 53% rename from source_files/edge/s_m4p.cc rename to source_files/edge/s_ibxm.cc index e7fa3aa3e..f6a0a028d 100644 --- a/source_files/edge/s_m4p.cc +++ b/source_files/edge/s_ibxm.cc @@ -1,8 +1,8 @@ //---------------------------------------------------------------------------- -// EDGE MOD4PLAY Music Player +// EDGE IBXM (Tracker Module) Music Player //---------------------------------------------------------------------------- // -// Copyright (c) 2023 - The EDGE Team. +// Copyright (c) 2022-2024 - The EDGE Team. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -22,7 +22,7 @@ #include "epi_file.h" #include "epi_filesystem.h" #include "i_movie.h" -#include "m4p.h" +#include "ibxm.h" #include "s_blit.h" #include "s_cache.h" #include "s_music.h" @@ -31,11 +31,39 @@ extern int sound_device_frequency; -class M4PPlayer : public AbstractMusicPlayer +bool CheckIBXMFormat (uint8_t *data, int length) +{ + ibxm_data mod_check; + mod_check.buffer = (char *)data; + mod_check.length = length; + bool is_mod_music = false; + // Check for MOD format + switch( ibxm_data_u16be(&mod_check, 1082)) + { + case 0x4b2e: /* M.K. */ + case 0x4b21: /* M!K! */ + case 0x5434: /* FLT4 */ + case 0x484e: /* xCHN */ + case 0x4348: /* xxCH */ + is_mod_music = true; + break; + default: + break; + } + // Check for XM format + if( ibxm_data_u16le(&mod_check, 58) == 0x0104 ) + is_mod_music = true; + // Check for S3M format + if( ibxm_data_u32le(&mod_check, 44) == 0x4d524353 ) + is_mod_music = true; + return is_mod_music; +} + +class IBXMPlayer : public AbstractMusicPlayer { public: - M4PPlayer(); - ~M4PPlayer(); + IBXMPlayer(); + ~IBXMPlayer(); private: enum Status @@ -49,6 +77,11 @@ class M4PPlayer : public AbstractMusicPlayer int status_; bool looping_; + ibxm_module *ibxm_track_; + ibxm_replay *ibxm_replayer_; + ibxm_data *ibxm_raw_track_; + int ibxm_buffer_length_; + public: bool OpenMemory(uint8_t *data, int length); @@ -70,48 +103,77 @@ class M4PPlayer : public AbstractMusicPlayer //---------------------------------------------------------------------------- -M4PPlayer::M4PPlayer() : status_(kNotLoaded) +IBXMPlayer::IBXMPlayer() : status_(kNotLoaded) { } -M4PPlayer::~M4PPlayer() +IBXMPlayer::~IBXMPlayer() { Close(); } -void M4PPlayer::PostOpen() +void IBXMPlayer::PostOpen() { // Loaded, but not playing + ibxm_buffer_length_ = ibxm_calculate_mix_buf_len(sound_device_frequency); status_ = kStopped; } -bool M4PPlayer::StreamIntoBuffer(SoundData *buf) +bool IBXMPlayer::StreamIntoBuffer(SoundData *buf) { bool song_done = false; - m4p_GenerateSamples(buf->data_, kMusicBuffer / sizeof(int16_t)); + int got_size = ibxm_replay_get_audio(ibxm_replayer_, (int *)buf->data_, 0); - buf->length_ = kMusicBuffer / sizeof(int16_t); + if (got_size < 0) // ERROR + { + LogDebug("[ibxmplayer_c::StreamIntoBuffer] Failed\n"); + return false; + } - if (song_done) /* EOF */ - { - if (!looping_) - return false; - m4p_Stop(); - m4p_PlaySong(); - return true; - } + if (got_size == 0) + song_done = true; + + buf->length_ = got_size * 2; + + if (song_done) /* EOF */ + { + if (!looping_) + return false; + ibxm_replay_set_sequence_pos(ibxm_replayer_, 0); + } return true; } -bool M4PPlayer::OpenMemory(uint8_t *data, int length) +bool IBXMPlayer::OpenMemory(uint8_t *data, int length) { EPI_ASSERT(data); - if (!m4p_LoadFromData(data, length, sound_device_frequency, kMusicBuffer)) + ibxm_raw_track_ = (ibxm_data *)calloc(1, sizeof(ibxm_data)); + + ibxm_raw_track_->length = length; + ibxm_raw_track_->buffer = (char *)data; + std::string load_error; + load_error.resize(64); + ibxm_track_ = ibxm_module_load(ibxm_raw_track_, load_error.data()); + + if (!ibxm_track_) { - LogWarning("M4P: failure to load song!\n"); + LogWarning("IBXMPlayer: failure to load module: %s\n", load_error.c_str()); + delete[] data; + free(ibxm_raw_track_); + return false; + } + + ibxm_replayer_ = ibxm_new_replay(ibxm_track_, sound_device_frequency / 2, 0); + + if (!ibxm_replayer_) + { + LogWarning("IBXMPlayer::OpenMemory failed!\n"); + ibxm_dispose_module(ibxm_track_); + delete[] data; + free(ibxm_raw_track_); return false; } @@ -119,7 +181,7 @@ bool M4PPlayer::OpenMemory(uint8_t *data, int length) return true; } -void M4PPlayer::Close() +void IBXMPlayer::Close() { if (status_ == kNotLoaded) return; @@ -128,13 +190,15 @@ void M4PPlayer::Close() if (status_ != kStopped) Stop(); - m4p_Close(); - m4p_FreeSong(); + ibxm_dispose_replay(ibxm_replayer_); + ibxm_dispose_module(ibxm_track_); + delete[] ibxm_raw_track_->buffer; + free(ibxm_raw_track_); status_ = kNotLoaded; } -void M4PPlayer::Pause() +void IBXMPlayer::Pause() { if (status_ != kPlaying) return; @@ -142,7 +206,7 @@ void M4PPlayer::Pause() status_ = kPaused; } -void M4PPlayer::Resume() +void IBXMPlayer::Resume() { if (status_ != kPaused) return; @@ -150,7 +214,7 @@ void M4PPlayer::Resume() status_ = kPlaying; } -void M4PPlayer::Play(bool loop) +void IBXMPlayer::Play(bool loop) { if (status_ != kNotLoaded && status_ != kStopped) return; @@ -158,29 +222,25 @@ void M4PPlayer::Play(bool loop) status_ = kPlaying; looping_ = loop; - m4p_PlaySong(); - // Load up initial buffer data Ticker(); } -void M4PPlayer::Stop() +void IBXMPlayer::Stop() { if (status_ != kPlaying && status_ != kPaused) return; SoundQueueStop(); - m4p_Stop(); - status_ = kStopped; } -void M4PPlayer::Ticker() +void IBXMPlayer::Ticker() { while (status_ == kPlaying && !pc_speaker_mode && !playing_movie) { - SoundData *buf = SoundQueueGetFreeBuffer(kMusicBuffer); + SoundData *buf = SoundQueueGetFreeBuffer(ibxm_buffer_length_); if (!buf) break; @@ -207,9 +267,9 @@ void M4PPlayer::Ticker() //---------------------------------------------------------------------------- -AbstractMusicPlayer *PlayM4PMusic(uint8_t *data, int length, bool looping) +AbstractMusicPlayer *PlayIBXMMusic(uint8_t *data, int length, bool looping) { - M4PPlayer *player = new M4PPlayer(); + IBXMPlayer *player = new IBXMPlayer(); if (!player->OpenMemory(data, length)) { @@ -218,8 +278,6 @@ AbstractMusicPlayer *PlayM4PMusic(uint8_t *data, int length, bool looping) return nullptr; } - delete[] data; - player->Play(looping); return player; diff --git a/source_files/edge/s_m4p.h b/source_files/edge/s_ibxm.h similarity index 79% rename from source_files/edge/s_m4p.h rename to source_files/edge/s_ibxm.h index ebba45ce6..48b323186 100644 --- a/source_files/edge/s_m4p.h +++ b/source_files/edge/s_ibxm.h @@ -1,8 +1,8 @@ //---------------------------------------------------------------------------- -// EDGE MOD4PLAY Music Player (HEADER) +// EDGE IBXM (Tracker Module) Music Player //---------------------------------------------------------------------------- // -// Copyright (c) 2023 - The EDGE Team. +// Copyright (c) 2022-2024 - The EDGE Team. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -20,7 +20,9 @@ #include "s_music.h" -AbstractMusicPlayer *PlayM4PMusic(uint8_t *data, int length, bool looping); +bool CheckIBXMFormat (uint8_t *data, int length); + +AbstractMusicPlayer *PlayIBXMMusic(uint8_t *data, int length, bool looping); //--- editor settings --- // vi:ts=4:sw=4:noexpandtab diff --git a/source_files/edge/s_opl.cc b/source_files/edge/s_imf.cc similarity index 61% rename from source_files/edge/s_opl.cc rename to source_files/edge/s_imf.cc index 26bd65109..fa364b033 100644 --- a/source_files/edge/s_opl.cc +++ b/source_files/edge/s_imf.cc @@ -1,5 +1,5 @@ //---------------------------------------------------------------------------- -// EDGE Opal Music Player +// EDGE IMF Music Player //---------------------------------------------------------------------------- // // Copyright (c) 2022-2024 The EDGE Team. @@ -16,7 +16,7 @@ // //---------------------------------------------------------------------------- -#include "s_opl.h" +#include "s_imf.h" #include "dm_state.h" #include "epi_file.h" @@ -25,72 +25,26 @@ #include "i_system.h" #include "m_misc.h" // clang-format off -#define MidiFraction OPLFraction -#define MidiSequencer OPLSequencer -typedef struct MidiRealTimeInterface OPLInterface; +#define MidiFraction IMFFraction +#define MidiSequencer IMFSequencer +typedef struct MidiRealTimeInterface IMFInterface; #include "midi_sequencer_impl.hpp" // clang-format on #include "ddf_playlist.h" #include "epi_str_compare.h" #include "epi_str_util.h" -#include "radmidi.h" +#include "opal.h" #include "s_blit.h" #include "s_music.h" #include "snd_types.h" #include "w_files.h" #include "w_wad.h" -OPLPlayer *edge_opl = nullptr; - extern int sound_device_frequency; -bool opl_disabled = false; +static Opal *imf_opl = nullptr; -bool StartupOpal(void) -{ - LogPrint("Initializing OPL player...\n"); - - if (edge_opl) - delete edge_opl; - - edge_opl = new OPLPlayer(sound_device_frequency); - - if (!edge_opl) - return false; - - if (!edge_opl->loadPatches()) - { - LogWarning("StartupOpal: Error loading instruments!\n"); - return false; - } - - // OK - return true; -} - -// Should only be invoked when changing MIDI player options -void RestartOpal(void) -{ - if (opl_disabled) - return; - - int old_entry = entry_playing; - - StopMusic(); - - if (!StartupOpal()) - { - opl_disabled = true; - return; - } - - ChangeMusic(old_entry, - true); // Restart track that was playing when switched - - return; // OK! -} - -class OpalPlayer : public AbstractMusicPlayer +class IMFPlayer : public AbstractMusicPlayer { private: enum Status @@ -104,30 +58,35 @@ class OpalPlayer : public AbstractMusicPlayer int status_; bool looping_; - OPLInterface *opl_interface_; + IMFInterface *imf_interface_; public: - OpalPlayer(bool looping) : status_(kNotLoaded), looping_(looping) + IMFPlayer(bool looping) : status_(kNotLoaded), looping_(looping) { SequencerInit(); } - ~OpalPlayer() + ~IMFPlayer() { Close(); } public: - OPLSequencer *opl_sequencer_; + IMFSequencer *imf_sequencer__; static void rtNoteOn(void *userdata, uint8_t channel, uint8_t note, uint8_t velocity) { - edge_opl->midiNoteOn(channel, note, velocity); + (void)userdata; + (void)channel; + (void)note; + (void)velocity; } static void rtNoteOff(void *userdata, uint8_t channel, uint8_t note) { - edge_opl->midiNoteOff(channel, note); + (void)userdata; + (void)channel; + (void)note; } static void rtNoteAfterTouch(void *userdata, uint8_t channel, uint8_t note, uint8_t atVal) @@ -147,22 +106,32 @@ class OpalPlayer : public AbstractMusicPlayer static void rtControllerChange(void *userdata, uint8_t channel, uint8_t type, uint8_t value) { - edge_opl->midiControlChange(channel, type, value); + (void)userdata; + (void)channel; + (void)type; + (void)value; } static void rtPatchChange(void *userdata, uint8_t channel, uint8_t patch) { - edge_opl->midiProgramChange(channel, patch); + (void)userdata; + (void)channel; + (void)patch; } static void rtPitchBend(void *userdata, uint8_t channel, uint8_t msb, uint8_t lsb) { - edge_opl->midiPitchControl(channel, (msb - 64) / 127.0); + (void)userdata; + (void)channel; + (void)msb; + (void)lsb; } static void rtSysEx(void *userdata, const uint8_t *msg, size_t size) { - edge_opl->midiSysEx(msg, size); + (void)userdata; + (void)msg; + (void)size; } static void rtDeviceSwitch(void *userdata, size_t track, const char *data, size_t length) @@ -182,47 +151,51 @@ class OpalPlayer : public AbstractMusicPlayer static void rtRawOPL(void *userdata, uint8_t reg, uint8_t value) { - edge_opl->midiRawOPL(reg, value); + (void)userdata; + if ((reg & 0xF0) == 0xC0) + value |= 0x30; + imf_opl->Port(reg, value); } static void playSynth(void *userdata, uint8_t *stream, size_t length) { (void)userdata; - edge_opl->generate((int16_t *)(stream), length / (2 * sizeof(int16_t))); + for (size_t i = 0; i < length / 2; i += 2) + imf_opl->Sample((int16_t *)stream + i, (int16_t *)stream + i + 1); } void SequencerInit() { - opl_sequencer_ = new OPLSequencer; - opl_interface_ = new OPLInterface; - memset(opl_interface_, 0, sizeof(MidiRealTimeInterface)); - - opl_interface_->rtUserData = this; - opl_interface_->rt_noteOn = rtNoteOn; - opl_interface_->rt_noteOff = rtNoteOff; - opl_interface_->rt_noteAfterTouch = rtNoteAfterTouch; - opl_interface_->rt_channelAfterTouch = rtChannelAfterTouch; - opl_interface_->rt_controllerChange = rtControllerChange; - opl_interface_->rt_patchChange = rtPatchChange; - opl_interface_->rt_pitchBend = rtPitchBend; - opl_interface_->rt_systemExclusive = rtSysEx; - - opl_interface_->onPcmRender = playSynth; - opl_interface_->onPcmRender_userdata = this; - - opl_interface_->pcmSampleRate = sound_device_frequency; - opl_interface_->pcmFrameSize = 2 /*channels*/ * sizeof(int16_t) /*size of one sample*/; - - opl_interface_->rt_deviceSwitch = rtDeviceSwitch; - opl_interface_->rt_currentDevice = rtCurrentDevice; - opl_interface_->rt_rawOPL = rtRawOPL; - - opl_sequencer_->SetInterface(opl_interface_); + imf_sequencer__ = new IMFSequencer; + imf_interface_ = new IMFInterface; + memset(imf_interface_, 0, sizeof(MidiRealTimeInterface)); + + imf_interface_->rtUserData = this; + imf_interface_->rt_noteOn = rtNoteOn; + imf_interface_->rt_noteOff = rtNoteOff; + imf_interface_->rt_noteAfterTouch = rtNoteAfterTouch; + imf_interface_->rt_channelAfterTouch = rtChannelAfterTouch; + imf_interface_->rt_controllerChange = rtControllerChange; + imf_interface_->rt_patchChange = rtPatchChange; + imf_interface_->rt_pitchBend = rtPitchBend; + imf_interface_->rt_systemExclusive = rtSysEx; + + imf_interface_->onPcmRender = playSynth; + imf_interface_->onPcmRender_userdata = this; + + imf_interface_->pcmSampleRate = sound_device_frequency; + imf_interface_->pcmFrameSize = 2 /*channels*/ * sizeof(int16_t) /*size of one sample*/; + + imf_interface_->rt_deviceSwitch = rtDeviceSwitch; + imf_interface_->rt_currentDevice = rtCurrentDevice; + imf_interface_->rt_rawOPL = rtRawOPL; + + imf_sequencer__->SetInterface(imf_interface_); } bool LoadTrack(const uint8_t *data, int length, uint16_t rate) { - return opl_sequencer_->LoadMidi(data, length, rate); + return imf_sequencer__->LoadMidi(data, length, rate); } void Close(void) @@ -234,15 +207,20 @@ class OpalPlayer : public AbstractMusicPlayer if (status_ != kStopped) Stop(); - if (opl_sequencer_) + if (imf_opl) { - delete opl_sequencer_; - opl_sequencer_ = nullptr; + delete imf_opl; + imf_opl = nullptr; } - if (opl_interface_) + if (imf_sequencer__) { - delete opl_interface_; - opl_interface_ = nullptr; + delete imf_sequencer__; + imf_sequencer__ = nullptr; + } + if (imf_interface_) + { + delete imf_interface_; + imf_interface_ = nullptr; } music_player_gain = 1.0f; @@ -269,8 +247,6 @@ class OpalPlayer : public AbstractMusicPlayer if (!(status_ == kPlaying || status_ == kPaused)) return; - edge_opl->reset(); - SoundQueueStop(); status_ = kStopped; @@ -320,9 +296,9 @@ class OpalPlayer : public AbstractMusicPlayer { bool song_done = false; - int played = opl_sequencer_->PlayStream((uint8_t *)(buf->data_), kMusicBuffer); + int played = imf_sequencer__->PlayStream((uint8_t *)(buf->data_), kMusicBuffer); - if (opl_sequencer_->PositionAtEnd()) + if (imf_sequencer__->PositionAtEnd()) song_done = true; buf->length_ = played / 2 / sizeof(int16_t); @@ -331,7 +307,7 @@ class OpalPlayer : public AbstractMusicPlayer { if (!looping_) return false; - opl_sequencer_->Rewind(); + imf_sequencer__->Rewind(); return true; } @@ -339,19 +315,21 @@ class OpalPlayer : public AbstractMusicPlayer } }; -AbstractMusicPlayer *PlayOPLMusic(uint8_t *data, int length, bool loop, int type) +AbstractMusicPlayer *PlayIMFMusic(uint8_t *data, int length, bool loop, int type) { - if (opl_disabled) - { - delete[] data; - return nullptr; - } - - OpalPlayer *player = new OpalPlayer(loop); + imf_opl = new Opal(sound_device_frequency); + IMFPlayer *player = new IMFPlayer(loop); - if (!player) + if (!imf_opl || !player) { - LogDebug("OPL player: error initializing!\n"); + LogDebug("IMF player: error initializing!\n"); + if (imf_opl) + { + delete imf_opl; + imf_opl = nullptr; + } + if (player) + delete player; delete[] data; return nullptr; } @@ -374,11 +352,23 @@ AbstractMusicPlayer *PlayOPLMusic(uint8_t *data, int length, bool loop, int type break; } + if (rate == 0) + { + LogDebug("IMF player: no IMF sample rate provided!\n"); + delete[] data; + delete imf_opl; + imf_opl = nullptr; + delete player; + return nullptr; + } + if (!player->LoadTrack(data, length, rate)) // Lobo: quietly log it instead of completely exiting EDGE { - LogDebug("OPL player: failed to load MIDI file!\n"); + LogDebug("IMF player: failed to load IMF file!\n"); delete[] data; + delete imf_opl; + imf_opl = nullptr; delete player; return nullptr; } diff --git a/source_files/edge/s_opl.h b/source_files/edge/s_imf.h similarity index 84% rename from source_files/edge/s_opl.h rename to source_files/edge/s_imf.h index 80250bb4e..ce234d877 100644 --- a/source_files/edge/s_opl.h +++ b/source_files/edge/s_imf.h @@ -1,5 +1,5 @@ //---------------------------------------------------------------------------- -// EDGE Opal Music Player +// EDGE IMF Music Player //---------------------------------------------------------------------------- // // Copyright (c) 2022-2024 The EDGE Team. @@ -22,13 +22,7 @@ #include "s_music.h" -extern bool opl_disabled; - -bool StartupOpal(void); - -void RestartOpal(void); - -AbstractMusicPlayer *PlayOPLMusic(uint8_t *data, int length, bool loop, int type); +AbstractMusicPlayer *PlayIMFMusic(uint8_t *data, int length, bool loop, int type); //--- editor settings --- // vi:ts=4:sw=4:noexpandtab diff --git a/source_files/edge/s_music.cc b/source_files/edge/s_music.cc index cfdf2ec1d..948ade3ac 100644 --- a/source_files/edge/s_music.cc +++ b/source_files/edge/s_music.cc @@ -35,10 +35,10 @@ #include "s_flac.h" #include "s_fluid.h" #include "s_fmm.h" -#include "s_m4p.h" +#include "s_ibxm.h" #include "s_mp3.h" #include "s_ogg.h" -#include "s_opl.h" +#include "s_imf.h" #include "s_rad.h" #include "s_sid.h" #include "s_sound.h" @@ -188,9 +188,9 @@ void ChangeMusic(int entry_number, bool loop) music_player = PlayFLACMusic(data, length, loop); break; - case kSoundM4P: + case kSoundIBXM: delete F; - music_player = PlayM4PMusic(data, length, loop); + music_player = PlayIBXMMusic(data, length, loop); break; case kSoundRAD: @@ -203,11 +203,9 @@ void ChangeMusic(int entry_number, bool loop) music_player = PlaySIDMusic(data, length, loop); break; - // IMF writes raw OPL registers, so must use the OPL player - // unconditionally case kSoundIMF: delete F; - music_player = PlayOPLMusic(data, length, loop, play->type_); + music_player = PlayIMFMusic(data, length, loop, play->type_); break; case kSoundMIDI: @@ -219,10 +217,6 @@ void ChangeMusic(int entry_number, bool loop) music_player = PlayFluidMusic(data, length, loop); } else if (var_midi_player == 1) - { - music_player = PlayOPLMusic(data, length, loop, play->type_); - } - else if (var_midi_player == 2) { music_player = PlayFMMMusic(data, length, loop); } diff --git a/source_files/edge/snd_types.cc b/source_files/edge/snd_types.cc index f84b4d235..5ea51b9c1 100644 --- a/source_files/edge/snd_types.cc +++ b/source_files/edge/snd_types.cc @@ -21,7 +21,7 @@ #include "epi.h" #include "epi_filesystem.h" #include "epi_str_util.h" -#include "m4p.h" +#include "s_ibxm.h" SoundFormat DetectSoundFormat(uint8_t *data, int song_len) { @@ -98,9 +98,9 @@ SoundFormat DetectSoundFormat(uint8_t *data, int song_len) // Moving on to more specialized or less reliable detections - if (m4p_TestFromData(data, song_len)) + if (CheckIBXMFormat(data, song_len)) { - return kSoundM4P; + return kSoundIBXM; } if ((data[0] == 'I' && data[1] == 'D' && data[2] == '3') || (data[0] == 0xFF && ((data[1] >> 4 & 0xF) == 0xF))) @@ -149,8 +149,8 @@ SoundFormat SoundFilenameToFormat(std::string_view filename) if (ext == ".mid" || ext == ".midi" || ext == ".xmi" || ext == ".rmi" || ext == ".rmid") return kSoundMIDI; - if (ext == ".mod" || ext == ".s3m" || ext == ".xm" || ext == ".it") - return kSoundM4P; + if (ext == ".mod" || ext == ".s3m" || ext == ".xm") + return kSoundIBXM; if (ext == ".rad") return kSoundRAD; diff --git a/source_files/edge/snd_types.h b/source_files/edge/snd_types.h index 3cc39cc7c..4ca604547 100644 --- a/source_files/edge/snd_types.h +++ b/source_files/edge/snd_types.h @@ -29,7 +29,7 @@ enum SoundFormat kSoundFLAC, kSoundOGG, kSoundMP3, - kSoundM4P, + kSoundIBXM, kSoundSID, kSoundRAD, kSoundMUS, From b94787c21cb5c01372be504311cac645cecfc6a7 Mon Sep 17 00:00:00 2001 From: dashodanger <> Date: Thu, 19 Dec 2024 13:15:55 -0700 Subject: [PATCH 2/3] TSF stuff --- libraries/CMakeLists.txt | 2 +- libraries/fluidlite/CMakeLists.txt | 24 - libraries/fluidlite/LICENSE | 15 - libraries/fluidlite/include/fluidlite.h | 79 - libraries/fluidlite/include/fluidlite/gen.h | 135 - libraries/fluidlite/include/fluidlite/log.h | 83 - libraries/fluidlite/include/fluidlite/misc.h | 65 - libraries/fluidlite/include/fluidlite/mod.h | 112 - .../fluidlite/include/fluidlite/settings.h | 202 - libraries/fluidlite/include/fluidlite/sfont.h | 252 -- libraries/fluidlite/include/fluidlite/synth.h | 720 ---- libraries/fluidlite/include/fluidlite/types.h | 67 - libraries/fluidlite/include/fluidlite/voice.h | 97 - libraries/fluidlite/src/fluid_chan.c | 455 --- libraries/fluidlite/src/fluid_chan.h | 114 - libraries/fluidlite/src/fluid_chorus.c | 606 --- libraries/fluidlite/src/fluid_chorus.h | 56 - libraries/fluidlite/src/fluid_config.h | 35 - libraries/fluidlite/src/fluid_conv.c | 320 -- libraries/fluidlite/src/fluid_conv.h | 63 - libraries/fluidlite/src/fluid_defsfont.c | 3432 ---------------- libraries/fluidlite/src/fluid_defsfont.h | 603 --- libraries/fluidlite/src/fluid_dsp_float.c | 685 ---- libraries/fluidlite/src/fluid_gen.c | 149 - libraries/fluidlite/src/fluid_gen.h | 44 - libraries/fluidlite/src/fluid_hash.c | 388 -- libraries/fluidlite/src/fluid_hash.h | 64 - libraries/fluidlite/src/fluid_list.c | 257 -- libraries/fluidlite/src/fluid_list.h | 61 - libraries/fluidlite/src/fluid_midi.h | 247 -- libraries/fluidlite/src/fluid_mod.c | 434 -- libraries/fluidlite/src/fluid_mod.h | 40 - libraries/fluidlite/src/fluid_phase.h | 115 - libraries/fluidlite/src/fluid_rev.c | 561 --- libraries/fluidlite/src/fluid_rev.h | 67 - libraries/fluidlite/src/fluid_settings.c | 822 ---- libraries/fluidlite/src/fluid_settings.h | 55 - libraries/fluidlite/src/fluid_sfont.h | 77 - libraries/fluidlite/src/fluid_synth.c | 3575 ----------------- libraries/fluidlite/src/fluid_synth.h | 206 - libraries/fluidlite/src/fluid_sys.c | 364 -- libraries/fluidlite/src/fluid_sys.h | 141 - libraries/fluidlite/src/fluid_tuning.c | 144 - libraries/fluidlite/src/fluid_tuning.h | 65 - libraries/fluidlite/src/fluid_voice.c | 1996 --------- libraries/fluidlite/src/fluid_voice.h | 291 -- libraries/fluidlite/src/fluidsynth_priv.h | 228 -- libraries/tsf/CMakeLists.txt | 7 + libraries/tsf/tsf.cc | 3 + libraries/tsf/tsf.h | 2073 ++++++++++ source_files/edge/CMakeLists.txt | 4 +- source_files/edge/i_sound.cc | 6 +- source_files/edge/m_option.cc | 10 +- source_files/edge/s_fluid.cc | 438 -- source_files/edge/s_music.cc | 4 +- source_files/edge/s_tsf.cc | 400 ++ source_files/edge/{s_fluid.h => s_tsf.h} | 10 +- 57 files changed, 2501 insertions(+), 19067 deletions(-) delete mode 100644 libraries/fluidlite/CMakeLists.txt delete mode 100644 libraries/fluidlite/LICENSE delete mode 100644 libraries/fluidlite/include/fluidlite.h delete mode 100644 libraries/fluidlite/include/fluidlite/gen.h delete mode 100644 libraries/fluidlite/include/fluidlite/log.h delete mode 100644 libraries/fluidlite/include/fluidlite/misc.h delete mode 100644 libraries/fluidlite/include/fluidlite/mod.h delete mode 100644 libraries/fluidlite/include/fluidlite/settings.h delete mode 100644 libraries/fluidlite/include/fluidlite/sfont.h delete mode 100644 libraries/fluidlite/include/fluidlite/synth.h delete mode 100644 libraries/fluidlite/include/fluidlite/types.h delete mode 100644 libraries/fluidlite/include/fluidlite/voice.h delete mode 100644 libraries/fluidlite/src/fluid_chan.c delete mode 100644 libraries/fluidlite/src/fluid_chan.h delete mode 100644 libraries/fluidlite/src/fluid_chorus.c delete mode 100644 libraries/fluidlite/src/fluid_chorus.h delete mode 100644 libraries/fluidlite/src/fluid_config.h delete mode 100644 libraries/fluidlite/src/fluid_conv.c delete mode 100644 libraries/fluidlite/src/fluid_conv.h delete mode 100644 libraries/fluidlite/src/fluid_defsfont.c delete mode 100644 libraries/fluidlite/src/fluid_defsfont.h delete mode 100644 libraries/fluidlite/src/fluid_dsp_float.c delete mode 100644 libraries/fluidlite/src/fluid_gen.c delete mode 100644 libraries/fluidlite/src/fluid_gen.h delete mode 100644 libraries/fluidlite/src/fluid_hash.c delete mode 100644 libraries/fluidlite/src/fluid_hash.h delete mode 100644 libraries/fluidlite/src/fluid_list.c delete mode 100644 libraries/fluidlite/src/fluid_list.h delete mode 100644 libraries/fluidlite/src/fluid_midi.h delete mode 100644 libraries/fluidlite/src/fluid_mod.c delete mode 100644 libraries/fluidlite/src/fluid_mod.h delete mode 100644 libraries/fluidlite/src/fluid_phase.h delete mode 100644 libraries/fluidlite/src/fluid_rev.c delete mode 100644 libraries/fluidlite/src/fluid_rev.h delete mode 100644 libraries/fluidlite/src/fluid_settings.c delete mode 100644 libraries/fluidlite/src/fluid_settings.h delete mode 100644 libraries/fluidlite/src/fluid_sfont.h delete mode 100644 libraries/fluidlite/src/fluid_synth.c delete mode 100644 libraries/fluidlite/src/fluid_synth.h delete mode 100644 libraries/fluidlite/src/fluid_sys.c delete mode 100644 libraries/fluidlite/src/fluid_sys.h delete mode 100644 libraries/fluidlite/src/fluid_tuning.c delete mode 100644 libraries/fluidlite/src/fluid_tuning.h delete mode 100644 libraries/fluidlite/src/fluid_voice.c delete mode 100644 libraries/fluidlite/src/fluid_voice.h delete mode 100644 libraries/fluidlite/src/fluidsynth_priv.h create mode 100644 libraries/tsf/CMakeLists.txt create mode 100644 libraries/tsf/tsf.cc create mode 100644 libraries/tsf/tsf.h delete mode 100644 source_files/edge/s_fluid.cc create mode 100644 source_files/edge/s_tsf.cc rename source_files/edge/{s_fluid.h => s_tsf.h} (82%) diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 569894572..f50121238 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -1,7 +1,6 @@ add_subdirectory(almostequals) add_subdirectory(crsid) add_subdirectory(dr_libs) -add_subdirectory(fluidlite) add_subdirectory(fmmidi) if (NOT EDGE_GL_ES2) add_subdirectory(glad) @@ -19,4 +18,5 @@ add_subdirectory(pl_mpeg) add_subdirectory(prns) add_subdirectory(stb) add_subdirectory(tracy) +add_subdirectory(tsf) add_subdirectory(xxHash) \ No newline at end of file diff --git a/libraries/fluidlite/CMakeLists.txt b/libraries/fluidlite/CMakeLists.txt deleted file mode 100644 index 775133715..000000000 --- a/libraries/fluidlite/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -########################################## -# fluidlite -########################################## - -add_library(fluidlite - src/fluid_chan.c - src/fluid_chorus.c - src/fluid_conv.c - src/fluid_defsfont.c - src/fluid_dsp_float.c - src/fluid_gen.c - src/fluid_hash.c - src/fluid_list.c - src/fluid_mod.c - src/fluid_rev.c - src/fluid_settings.c - src/fluid_synth.c - src/fluid_sys.c - src/fluid_tuning.c - src/fluid_voice.c -) - -target_include_directories(fluidlite PRIVATE ./src) -target_include_directories(fluidlite PUBLIC ./include) diff --git a/libraries/fluidlite/LICENSE b/libraries/fluidlite/LICENSE deleted file mode 100644 index 4622093b4..000000000 --- a/libraries/fluidlite/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -FluidLite (c) 2016 Robin Lobel - -This library 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 3 of the License, or (at your option) any later version. - -This library 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 library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/libraries/fluidlite/include/fluidlite.h b/libraries/fluidlite/include/fluidlite.h deleted file mode 100644 index 62c5984c2..000000000 --- a/libraries/fluidlite/include/fluidlite.h +++ /dev/null @@ -1,79 +0,0 @@ -/* FluidLite - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUIDLITE_H -#define _FLUIDLITE_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define FLUIDSYNTH_API - -/** - * @file fluidlite.h - * @brief FluidLite is a real-time synthesizer designed for SoundFont(R) files. - * - * This is the header of the fluidlite library and contains the - * synthesizer's public API. - * - * Depending on how you want to use or extend the synthesizer you - * will need different API functions. You probably do not need all - * of them. Here is what you might want to do: - * - * o Embedded synthesizer: create a new synthesizer and send MIDI - * events to it. The sound goes directly to the audio output of - * your system. - * - * o Plugin synthesizer: create a synthesizer and send MIDI events - * but pull the audio back into your application. - * - * o SoundFont plugin: create a new type of "SoundFont" and allow - * the synthesizer to load your type of SoundFonts. - * - * o MIDI input: Create a MIDI handler to read the MIDI input on your - * machine and send the MIDI events directly to the synthesizer. - * - * o MIDI files: Open MIDI files and send the MIDI events to the - * synthesizer. - * - * o Command lines: You can send textual commands to the synthesizer. - * - * SoundFont(R) is a registered trademark of E-mu Systems, Inc. - */ - -#include "fluidlite/types.h" -#include "fluidlite/settings.h" -#include "fluidlite/synth.h" -#include "fluidlite/sfont.h" -#include "fluidlite/log.h" -#include "fluidlite/misc.h" -#include "fluidlite/mod.h" -#include "fluidlite/gen.h" -#include "fluidlite/voice.h" - - -#ifdef __cplusplus -} -#endif - -#endif /* _FLUIDLITE_H */ diff --git a/libraries/fluidlite/include/fluidlite/gen.h b/libraries/fluidlite/include/fluidlite/gen.h deleted file mode 100644 index 5cc647b79..000000000 --- a/libraries/fluidlite/include/fluidlite/gen.h +++ /dev/null @@ -1,135 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUIDSYNTH_GEN_H -#define _FLUIDSYNTH_GEN_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @file gen.h - * @brief Functions and defines for SoundFont generator effects. - */ - -/** - * Generator (effect) numbers (Soundfont 2.01 specifications section 8.1.3) - */ -enum fluid_gen_type { - GEN_STARTADDROFS, /**< Sample start address offset (0-32767) */ - GEN_ENDADDROFS, /**< Sample end address offset (-32767-0) */ - GEN_STARTLOOPADDROFS, /**< Sample loop start address offset (-32767-32767) */ - GEN_ENDLOOPADDROFS, /**< Sample loop end address offset (-32767-32767) */ - GEN_STARTADDRCOARSEOFS, /**< Sample start address coarse offset (X 32768) */ - GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */ - GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */ - GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */ - GEN_FILTERFC, /**< Filter cutoff */ - GEN_FILTERQ, /**< Filter Q */ - GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */ - GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */ - GEN_ENDADDRCOARSEOFS, /**< Sample end address coarse offset (X 32768) */ - GEN_MODLFOTOVOL, /**< Modulation LFO to volume */ - GEN_UNUSED1, /**< Unused */ - GEN_CHORUSSEND, /**< Chorus send amount */ - GEN_REVERBSEND, /**< Reverb send amount */ - GEN_PAN, /**< Stereo panning */ - GEN_UNUSED2, /**< Unused */ - GEN_UNUSED3, /**< Unused */ - GEN_UNUSED4, /**< Unused */ - GEN_MODLFODELAY, /**< Modulation LFO delay */ - GEN_MODLFOFREQ, /**< Modulation LFO frequency */ - GEN_VIBLFODELAY, /**< Vibrato LFO delay */ - GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */ - GEN_MODENVDELAY, /**< Modulation envelope delay */ - GEN_MODENVATTACK, /**< Modulation envelope attack */ - GEN_MODENVHOLD, /**< Modulation envelope hold */ - GEN_MODENVDECAY, /**< Modulation envelope decay */ - GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */ - GEN_MODENVRELEASE, /**< Modulation envelope release */ - GEN_KEYTOMODENVHOLD, /**< Key to modulation envelope hold */ - GEN_KEYTOMODENVDECAY, /**< Key to modulation envelope decay */ - GEN_VOLENVDELAY, /**< Volume envelope delay */ - GEN_VOLENVATTACK, /**< Volume envelope attack */ - GEN_VOLENVHOLD, /**< Volume envelope hold */ - GEN_VOLENVDECAY, /**< Volume envelope decay */ - GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */ - GEN_VOLENVRELEASE, /**< Volume envelope release */ - GEN_KEYTOVOLENVHOLD, /**< Key to volume envelope hold */ - GEN_KEYTOVOLENVDECAY, /**< Key to volume envelope decay */ - GEN_INSTRUMENT, /**< Instrument ID (shouldn't be set by user) */ - GEN_RESERVED1, /**< Reserved */ - GEN_KEYRANGE, /**< MIDI note range */ - GEN_VELRANGE, /**< MIDI velocity range */ - GEN_STARTLOOPADDRCOARSEOFS, /**< Sample start loop address coarse offset (X 32768) */ - GEN_KEYNUM, /**< Fixed MIDI note number */ - GEN_VELOCITY, /**< Fixed MIDI velocity value */ - GEN_ATTENUATION, /**< Initial volume attenuation */ - GEN_RESERVED2, /**< Reserved */ - GEN_ENDLOOPADDRCOARSEOFS, /**< Sample end loop address coarse offset (X 32768) */ - GEN_COARSETUNE, /**< Coarse tuning */ - GEN_FINETUNE, /**< Fine tuning */ - GEN_SAMPLEID, /**< Sample ID (shouldn't be set by user) */ - GEN_SAMPLEMODE, /**< Sample mode flags */ - GEN_RESERVED3, /**< Reserved */ - GEN_SCALETUNE, /**< Scale tuning */ - GEN_EXCLUSIVECLASS, /**< Exclusive class number */ - GEN_OVERRIDEROOTKEY, /**< Sample root note override */ - - /* the initial pitch is not a "standard" generator. It is not - * mentioned in the list of generator in the SF2 specifications. It - * is used, however, as the destination for the default pitch wheel - * modulator. */ - GEN_PITCH, /**< Pitch (NOTE: Not a real SoundFont generator) */ - GEN_LAST /**< Value defines the count of generators (#fluid_gen_type) */ -}; - - -/** - * SoundFont generator structure. - */ -typedef struct _fluid_gen_t -{ - unsigned char flags; /**< Is the generator set or not (#fluid_gen_flags) */ - double val; /**< The nominal value */ - double mod; /**< Change by modulators */ - double nrpn; /**< Change by NRPN messages */ -} fluid_gen_t; - -/** - * Enum value for 'flags' field of #_fluid_gen_t (not really flags). - */ -enum fluid_gen_flags -{ - GEN_UNUSED, /**< Generator value is not set */ - GEN_SET, /**< Generator value is set */ - GEN_ABS_NRPN /**< DOCME */ -}; - -FLUIDSYNTH_API int fluid_gen_set_default_values(fluid_gen_t* gen); - - - -#ifdef __cplusplus -} -#endif -#endif /* _FLUIDSYNTH_GEN_H */ - diff --git a/libraries/fluidlite/include/fluidlite/log.h b/libraries/fluidlite/include/fluidlite/log.h deleted file mode 100644 index 98691ab5c..000000000 --- a/libraries/fluidlite/include/fluidlite/log.h +++ /dev/null @@ -1,83 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUIDSYNTH_LOG_H -#define _FLUIDSYNTH_LOG_H - - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * @file log.h - * @brief Logging interface - * - * The default logging function of the fluidsynth prints its messages - * to the stderr. The synthesizer uses five level of messages: #FLUID_PANIC, - * #FLUID_ERR, #FLUID_WARN, #FLUID_INFO, and #FLUID_DBG. - * - * A client application can install a new log function to handle the - * messages differently. In the following example, the application - * sets a callback function to display #FLUID_PANIC messages in a dialog, - * and ignores all other messages by setting the log function to - * NULL: - * - * DOCME (formatting) - * fluid_set_log_function(FLUID_PANIC, show_dialog, (void*) root_window); - * fluid_set_log_function(FLUID_ERR, NULL, NULL); - * fluid_set_log_function(FLUID_WARN, NULL, NULL); - * fluid_set_log_function(FLUID_DBG, NULL, NULL); - */ - -/** - * FluidSynth log levels. - */ -enum fluid_log_level { - FLUID_PANIC, /**< The synth can't function correctly any more */ - FLUID_ERR, /**< Serious error occurred */ - FLUID_WARN, /**< Warning */ - FLUID_INFO, /**< Verbose informational messages */ - FLUID_DBG, /**< Debugging messages */ - LAST_LOG_LEVEL -}; - -/** - * Log function handler callback type used by fluid_set_log_function(). - * @param level Log level (#fluid_log_level) - * @param message Log message text - * @param data User data pointer supplied to fluid_set_log_function(). - */ -typedef void (*fluid_log_function_t)(int level, char* message, void* data); - -FLUIDSYNTH_API -fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void* data); - -FLUIDSYNTH_API void fluid_default_log_function(int level, char* message, void* data); - -FLUIDSYNTH_API int fluid_log(int level, char * fmt, ...); - - -#ifdef __cplusplus -} -#endif - -#endif /* _FLUIDSYNTH_LOG_H */ diff --git a/libraries/fluidlite/include/fluidlite/misc.h b/libraries/fluidlite/include/fluidlite/misc.h deleted file mode 100644 index 263064dd4..000000000 --- a/libraries/fluidlite/include/fluidlite/misc.h +++ /dev/null @@ -1,65 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUIDSYNTH_MISC_H -#define _FLUIDSYNTH_MISC_H - - -#ifdef __cplusplus -extern "C" { -#endif - - -/* - * - * Utility functions - */ - -/** - * fluid_is_soundfont returns 1 if the specified filename is a - * soundfont. It retuns 0 otherwise. The current implementation only - * checks for the "RIFF" header in the file. It is useful only to - * distinguish between SoundFonts and MIDI files. - */ -FLUIDSYNTH_API int fluid_is_soundfont(char* filename); - -/** - * fluid_is_midifile returns 1 if the specified filename is a MIDI - * file. It retuns 0 otherwise. The current implementation only checks - * for the "MThd" header in the file. - */ -FLUIDSYNTH_API int fluid_is_midifile(char* filename); - - - - -#ifdef WIN32 -/** Set the handle to the instance of the application on the Windows - platform. The handle is needed to open DirectSound. */ -FLUIDSYNTH_API void* fluid_get_hinstance(void); -FLUIDSYNTH_API void fluid_set_hinstance(void* hinstance); -#endif - - -#ifdef __cplusplus -} -#endif - -#endif /* _FLUIDSYNTH_MISC_H */ diff --git a/libraries/fluidlite/include/fluidlite/mod.h b/libraries/fluidlite/include/fluidlite/mod.h deleted file mode 100644 index d61d184bd..000000000 --- a/libraries/fluidlite/include/fluidlite/mod.h +++ /dev/null @@ -1,112 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUIDSYNTH_MOD_H -#define _FLUIDSYNTH_MOD_H - -#ifdef __cplusplus -extern "C" { -#endif - - /* Modulator-related definitions */ - - /* Maximum number of modulators in a voice */ -#define FLUID_NUM_MOD 64 - - /* - * fluid_mod_t - */ -struct _fluid_mod_t -{ - unsigned char dest; - unsigned char src1; - unsigned char flags1; - unsigned char src2; - unsigned char flags2; - double amount; - /* The 'next' field allows to link modulators into a list. It is - * not used in fluid_voice.c, there each voice allocates memory for a - * fixed number of modulators. Since there may be a huge number of - * different zones, this is more efficient. - */ - fluid_mod_t * next; -}; - -/* Flags telling the polarity of a modulator. Compare with SF2.01 - section 8.2. Note: The numbers of the bits are different! (for - example: in the flags of a SF modulator, the polarity bit is bit - nr. 9) */ -enum fluid_mod_flags -{ - FLUID_MOD_POSITIVE = 0, - FLUID_MOD_NEGATIVE = 1, - FLUID_MOD_UNIPOLAR = 0, - FLUID_MOD_BIPOLAR = 2, - FLUID_MOD_LINEAR = 0, - FLUID_MOD_CONCAVE = 4, - FLUID_MOD_CONVEX = 8, - FLUID_MOD_SWITCH = 12, - FLUID_MOD_GC = 0, - FLUID_MOD_CC = 16 -}; - -/* Flags telling the source of a modulator. This corresponds to - * SF2.01 section 8.2.1 */ -enum fluid_mod_src -{ - FLUID_MOD_NONE = 0, - FLUID_MOD_VELOCITY = 2, - FLUID_MOD_KEY = 3, - FLUID_MOD_KEYPRESSURE = 10, - FLUID_MOD_CHANNELPRESSURE = 13, - FLUID_MOD_PITCHWHEEL = 14, - FLUID_MOD_PITCHWHEELSENS = 16 -}; - -/* Allocates memory for a new modulator */ -FLUIDSYNTH_API fluid_mod_t * fluid_mod_new(void); - -/* Frees the modulator */ -FLUIDSYNTH_API void fluid_mod_delete(fluid_mod_t * mod); - - -FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags); -FLUIDSYNTH_API void fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags); -FLUIDSYNTH_API void fluid_mod_set_dest(fluid_mod_t* mod, int dst); -FLUIDSYNTH_API void fluid_mod_set_amount(fluid_mod_t* mod, double amount); - -FLUIDSYNTH_API int fluid_mod_get_source1(fluid_mod_t* mod); -FLUIDSYNTH_API int fluid_mod_get_flags1(fluid_mod_t* mod); -FLUIDSYNTH_API int fluid_mod_get_source2(fluid_mod_t* mod); -FLUIDSYNTH_API int fluid_mod_get_flags2(fluid_mod_t* mod); -FLUIDSYNTH_API int fluid_mod_get_dest(fluid_mod_t* mod); -FLUIDSYNTH_API double fluid_mod_get_amount(fluid_mod_t* mod); - - -/* Determines, if two modulators are 'identical' (all parameters - except the amount match) */ -FLUIDSYNTH_API int fluid_mod_test_identity(fluid_mod_t * mod1, fluid_mod_t * mod2); - - -#ifdef __cplusplus -} -#endif -#endif /* _FLUIDSYNTH_MOD_H */ - diff --git a/libraries/fluidlite/include/fluidlite/settings.h b/libraries/fluidlite/include/fluidlite/settings.h deleted file mode 100644 index ea5463f5d..000000000 --- a/libraries/fluidlite/include/fluidlite/settings.h +++ /dev/null @@ -1,202 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUIDSYNTH_SETTINGS_H -#define _FLUIDSYNTH_SETTINGS_H - -#ifdef __cplusplus -extern "C" { -#endif - - /** - * - * Synthesizer settings - * - * - * The create a synthesizer object you will have to specify its - * settings. These settings are stored in the structure below. - - * void my_synthesizer() - * { - * fluid_settings_t* settings; - * fluid_synth_t* synth; - * fluid_audio_driver_t* adriver; - * - * - * settings = new_fluid_settings(); - * fluid_settings_setstr(settings, "audio.driver", "alsa"); - * // ... change settings ... - * synth = new_fluid_synth(settings); - * adriver = new_fluid_audio_driver(settings, synth); - * - * ... - * - * } - * - * - */ - - - - -/* Hint FLUID_HINT_BOUNDED_BELOW indicates that the LowerBound field - of the FLUID_PortRangeHint should be considered meaningful. The - value in this field should be considered the (inclusive) lower - bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also - specified then the value of LowerBound should be multiplied by the - sample rate. */ -#define FLUID_HINT_BOUNDED_BELOW 0x1 - -/* Hint FLUID_HINT_BOUNDED_ABOVE indicates that the UpperBound field - of the FLUID_PortRangeHint should be considered meaningful. The - value in this field should be considered the (inclusive) upper - bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also - specified then the value of UpperBound should be multiplied by the - sample rate. */ -#define FLUID_HINT_BOUNDED_ABOVE 0x2 - -/* Hint FLUID_HINT_TOGGLED indicates that the data item should be - considered a Boolean toggle. Data less than or equal to zero should - be considered `off' or `false,' and data above zero should be - considered `on' or `true.' FLUID_HINT_TOGGLED may not be used in - conjunction with any other hint except FLUID_HINT_DEFAULT_0 or - FLUID_HINT_DEFAULT_1. */ -#define FLUID_HINT_TOGGLED 0x4 - -/* Hint FLUID_HINT_SAMPLE_RATE indicates that any bounds specified - should be interpreted as multiples of the sample rate. For - instance, a frequency range from 0Hz to the Nyquist frequency (half - the sample rate) could be requested by this hint in conjunction - with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds - at all must support this hint to retain meaning. */ -#define FLUID_HINT_SAMPLE_RATE 0x8 - -/* Hint FLUID_HINT_LOGARITHMIC indicates that it is likely that the - user will find it more intuitive to view values using a logarithmic - scale. This is particularly useful for frequencies and gains. */ -#define FLUID_HINT_LOGARITHMIC 0x10 - -/* Hint FLUID_HINT_INTEGER indicates that a user interface would - probably wish to provide a stepped control taking only integer - values. Any bounds set should be slightly wider than the actual - integer range required to avoid floating point rounding errors. For - instance, the integer set {0,1,2,3} might be described as [-0.1, - 3.1]. */ -#define FLUID_HINT_INTEGER 0x20 - - -#define FLUID_HINT_FILENAME 0x01 -#define FLUID_HINT_OPTIONLIST 0x02 - - - -enum fluid_types_enum { - FLUID_NO_TYPE = -1, - FLUID_NUM_TYPE, - FLUID_INT_TYPE, - FLUID_STR_TYPE, - FLUID_SET_TYPE -}; - - -FLUIDSYNTH_API fluid_settings_t* new_fluid_settings(void); -FLUIDSYNTH_API void delete_fluid_settings(fluid_settings_t* settings); - - - -FLUIDSYNTH_API -int fluid_settings_get_type(fluid_settings_t* settings, const char* name); - -FLUIDSYNTH_API -int fluid_settings_get_hints(fluid_settings_t* settings, const char* name); - -/** Returns whether the setting is changeable in real-time. */ -FLUIDSYNTH_API int fluid_settings_is_realtime(fluid_settings_t* settings, const char* name); - - -/** returns 1 if the value has been set, 0 otherwise */ -FLUIDSYNTH_API -int fluid_settings_setstr(fluid_settings_t* settings, const char* name, const char* str); - -/** - Get the value of a string setting. If the value does not exists, - 'str' is set to NULL. Otherwise, 'str' will point to the - value. The application does not own the returned value. Instead, - the application should make a copy of the value if it needs it - later. - - \returns 1 if the value exists, 0 otherwise -*/ -FLUIDSYNTH_API -int fluid_settings_getstr(fluid_settings_t* settings, const char* name, char** str); - -/** Get the default value of a string setting. */ -FLUIDSYNTH_API -char* fluid_settings_getstr_default(fluid_settings_t* settings, const char* name); - -/** Get the value of a numeric setting. - - \returns 1 if the value exists and is equal to 'value', 0 - otherwise -*/ -FLUIDSYNTH_API -int fluid_settings_str_equal(fluid_settings_t* settings, const char* name, char* value); - - -/** returns 1 if the value has been set, 0 otherwise */ -FLUIDSYNTH_API -int fluid_settings_setnum(fluid_settings_t* settings, const char* name, double val); - -/** returns 1 if the value exists, 0 otherwise */ -FLUIDSYNTH_API -int fluid_settings_getnum(fluid_settings_t* settings, const char* name, double* val); - -/** Get the default value of a string setting. */ -FLUIDSYNTH_API -double fluid_settings_getnum_default(fluid_settings_t* settings, const char* name); - -/** Get the range of values of a numeric settings. */ -FLUIDSYNTH_API -void fluid_settings_getnum_range(fluid_settings_t* settings, const char* name, - double* min, double* max); - - -/** returns 1 if the value has been set, 0 otherwise */ -FLUIDSYNTH_API -int fluid_settings_setint(fluid_settings_t* settings, const char* name, int val); - -/** returns 1 if the value exists, 0 otherwise */ -FLUIDSYNTH_API -int fluid_settings_getint(fluid_settings_t* settings, const char* name, int* val); - -/** Get the default value of a string setting. */ -FLUIDSYNTH_API -int fluid_settings_getint_default(fluid_settings_t* settings, const char* name); - -/** Get the range of values of a numeric settings. */ -FLUIDSYNTH_API -void fluid_settings_getint_range(fluid_settings_t* settings, const char* name, - int* min, int* max); - -#ifdef __cplusplus -} -#endif - -#endif /* _FLUIDSYNTH_SETTINGS_H */ diff --git a/libraries/fluidlite/include/fluidlite/sfont.h b/libraries/fluidlite/include/fluidlite/sfont.h deleted file mode 100644 index f2824edf9..000000000 --- a/libraries/fluidlite/include/fluidlite/sfont.h +++ /dev/null @@ -1,252 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUIDSYNTH_SFONT_H -#define _FLUIDSYNTH_SFONT_H - -#ifdef __cplusplus -extern "C" { -#endif - - - - /** - * - * SoundFont plugins - * - * It is possible to add new SoundFont loaders to the - * synthesizer. The API uses a couple of "interfaces" (structures - * with callback functions): fluid_sfloader_t, fluid_sfont_t, and - * fluid_preset_t. - * - * To add a new SoundFont loader to the synthesizer, call - * fluid_synth_add_sfloader() and pass a pointer to an - * fluid_sfloader_t structure. The important callback function in - * this structure is "load", which should try to load a file and - * returns a fluid_sfont_t structure, or NULL if it fails. - * - * The fluid_sfont_t structure contains a callback to obtain the - * name of the soundfont. It contains two functions to iterate - * though the contained presets, and one function to obtain a - * preset corresponding to a bank and preset number. This - * function should return an fluid_preset_t structure. - * - * The fluid_preset_t structure contains some functions to obtain - * information from the preset (name, bank, number). The most - * important callback is the noteon function. The noteon function - * should call fluid_synth_alloc_voice() for every sample that has - * to be played. fluid_synth_alloc_voice() expects a pointer to a - * fluid_sample_t structure and returns a pointer to the opaque - * fluid_voice_t structure. To set or increments the values of a - * generator, use fluid_voice_gen_{set,incr}. When you are - * finished initializing the voice call fluid_voice_start() to - * start playing the synthesis voice. - * */ - - enum { - FLUID_PRESET_SELECTED, - FLUID_PRESET_UNSELECTED, - FLUID_SAMPLE_DONE - }; - - -/* - * fluid_sfloader_t - */ - -struct _fluid_sfloader_t { - /** Private data */ - void* data; - - /** The free must free the memory allocated for the loader in - * addition to any private data. It should return 0 if no error - * occured, non-zero otherwise.*/ - int (*free)(fluid_sfloader_t* loader); - - /** Load a file. Returns NULL if an error occured. */ - fluid_sfont_t* (*load)(fluid_sfloader_t* loader, const char* filename); - - /** Callback structure specifying file operations used during soundfont loading to allow custom loading, such as from memory */ - fluid_fileapi_t* fileapi; -}; - -/** - * File callback structure to enable custom soundfont loading (e.g. from memory). - */ -struct _fluid_fileapi_t { - /** Private data */ - void* data; - - /** - * The free must free the memory allocated for the loader in - * addition to any private data. It should return 0 if no error - * occured, non-zero otherwise. - */ - int (*free)(fluid_fileapi_t* fileapi); - - /** - * Opens the file or memory indicated by \c filename in binary read mode. - * \c filename matches the one provided during the fluid_synth_sfload() call. - * - * @return returns a file handle on success, NULL otherwise - */ - void *(*fopen)(fluid_fileapi_t* fileapi, const char * filename); - - /** - * Reads \c count bytes to the specified buffer \c buf. - * - * @return returns #FLUID_OK if exactly \c count bytes were successfully read, else #FLUID_FAILED - */ - int (*fread)(void *buf, int count, void* handle); - - /** - * Same purpose and behaviour as fseek. - * - * @param origin either \c SEEK_SET, \c SEEK_CUR or \c SEEK_END - * - * @return returns #FLUID_OK if the seek was successfully performed while not seeking beyond a buffer or file, #FLUID_FAILED otherwise */ - int (*fseek)(void* handle, long offset, int origin); - - /** - * Closes the handle and frees used ressources. - * - * @return returns #FLUID_OK on success, #FLUID_FAILED on error */ - int (*fclose)(void* handle); - - /** @return returns current file offset or #FLUID_FAILED on error */ - long (*ftell)(void* handle); -}; - -FLUIDSYNTH_API void fluid_init_default_fileapi(fluid_fileapi_t* fileapi); - -FLUIDSYNTH_API void fluid_set_default_fileapi(fluid_fileapi_t* fileapi); - -FLUIDSYNTH_API fluid_sfloader_t* new_fluid_defsfloader(); - -FLUIDSYNTH_API int delete_fluid_defsfloader(fluid_sfloader_t* loader); - -/* - * fluid_sfont_t - */ - -struct _fluid_sfont_t { - void* data; - unsigned int id; - - /** The 'free' callback function should return 0 when it was able to - free all resources. It should return a non-zero value if some of - the samples could not be freed because they are still in use. */ - int (*free)(fluid_sfont_t* sfont); - - /** Return the name of the sfont */ - char* (*get_name)(fluid_sfont_t* sfont); - - /** Return the preset with the specified bank and preset number. All - * the fields, including the 'sfont' field, should * be filled - * in. If the preset cannot be found, the function returns NULL. */ - fluid_preset_t* (*get_preset)(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); - - void (*iteration_start)(fluid_sfont_t* sfont); - - /* return 0 when no more presets are available, 1 otherwise */ - int (*iteration_next)(fluid_sfont_t* sfont, fluid_preset_t* preset); -}; - -#define fluid_sfont_get_id(_sf) ((_sf)->id) - - -/* - * fluid_preset_t - */ - -struct _fluid_preset_t { - void* data; - fluid_sfont_t* sfont; - int (*free)(fluid_preset_t* preset); - char* (*get_name)(fluid_preset_t* preset); - int (*get_banknum)(fluid_preset_t* preset); - int (*get_num)(fluid_preset_t* preset); - - /** handle a noteon event. Returns 0 if no error occured. */ - int (*noteon)(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); - - /** Implement this function if the preset needs to be notified about - preset select and unselect events. */ - int (*notify)(fluid_preset_t* preset, int reason, int chan); -}; - - -/* - * fluid_sample_t - */ - -struct _fluid_sample_t -{ - char name[21]; - unsigned int start; - unsigned int end; /* Note: Index of last valid sample point (contrary to SF spec) */ - unsigned int loopstart; - unsigned int loopend; /* Note: first point following the loop (superimposed on loopstart) */ - unsigned int samplerate; - int origpitch; - int pitchadj; - int sampletype; - int valid; - short* data; - - /** The amplitude, that will lower the level of the sample's loop to - the noise floor. Needed for note turnoff optimization, will be - filled out automatically */ - /* Set this to zero, when submitting a new sample. */ - int amplitude_that_reaches_noise_floor_is_valid; - double amplitude_that_reaches_noise_floor; - - /** Count the number of playing voices that use this sample. */ - unsigned int refcount; - - /** Implement this function if the sample or SoundFont needs to be - notified when the sample is no longer used. */ - int (*notify)(fluid_sample_t* sample, int reason); - - /** Pointer to SoundFont specific data */ - void* userdata; -}; - - -#define fluid_sample_refcount(_sample) ((_sample)->refcount) - - -/** Sample types */ - -#define FLUID_SAMPLETYPE_MONO 1 -#define FLUID_SAMPLETYPE_RIGHT 2 -#define FLUID_SAMPLETYPE_LEFT 4 -#define FLUID_SAMPLETYPE_LINKED 8 -#define FLUID_SAMPLETYPE_OGG_VORBIS 0x10 /**< Flag for #fluid_sample_t \a sampletype field for Ogg Vorbis compressed samples */ -#define FLUID_SAMPLETYPE_OGG_VORBIS_UNPACKED 0x20 -#define FLUID_SAMPLETYPE_ROM 0x8000 - - - -#ifdef __cplusplus -} -#endif - -#endif /* _FLUIDSYNTH_SFONT_H */ diff --git a/libraries/fluidlite/include/fluidlite/synth.h b/libraries/fluidlite/include/fluidlite/synth.h deleted file mode 100644 index bfa442e55..000000000 --- a/libraries/fluidlite/include/fluidlite/synth.h +++ /dev/null @@ -1,720 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUIDSYNTH_SYNTH_H -#define _FLUIDSYNTH_SYNTH_H - - -#ifdef __cplusplus -extern "C" { -#endif - - - /** Embedded synthesizer - * - * You create a new synthesizer with new_fluid_synth() and you destroy - * if with delete_fluid_synth(). Use the settings structure to specify - * the synthesizer characteristics. - * - * You have to load a SoundFont in order to hear any sound. For that - * you use the fluid_synth_sfload() function. - * - * You can use the audio driver functions described below to open - * the audio device and create a background audio thread. - * - * The API for sending MIDI events is probably what you expect: - * fluid_synth_noteon(), fluid_synth_noteoff(), ... - * - */ - - - /** Creates a new synthesizer object. - * - * Creates a new synthesizer object. As soon as the synthesizer is - * created, it will start playing. - * - * \param settings a pointer to a settings structure - * \return a newly allocated synthesizer or NULL in case of error - */ -FLUIDSYNTH_API fluid_synth_t* new_fluid_synth(fluid_settings_t* settings); - -FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t* synth, float sample_rate); - - - /** - * Deletes the synthesizer previously created with new_fluid_synth. - * - * \param synth the synthesizer object - * \return 0 if no error occured, -1 otherwise - */ -FLUIDSYNTH_API int delete_fluid_synth(fluid_synth_t* synth); - - - /** Get a reference to the settings of the synthesizer. - * - * \param synth the synthesizer object - * \return pointer to the settings - */ -FLUIDSYNTH_API fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth); - - - /* - * - * MIDI channel messages - * - */ - - /** Send a noteon message. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel); - - /** Send a noteoff message. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key); - - /** Send a control change message. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t* synth, int chan, int ctrl, int val); - - /** Get a control value. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int ctrl, int* pval); - - /** Send a pitch bend message. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val); - - /** Get the pitch bend value. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API -int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend); - - /** Set the pitch wheel sensitivity. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val); - - /** Get the pitch wheel sensitivity. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval); - - /** Send a program change message. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t* synth, int chan, int program); - -FLUIDSYNTH_API int fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val); -FLUIDSYNTH_API int fluid_synth_key_pressure(fluid_synth_t* synth, int chan, int key, int val); -FLUIDSYNTH_API int fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, - char *response, int *response_len, int *handled, int dryrun); - - /** Select a bank. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API -int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank); - - /** Select a sfont. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API -int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id); - - /** Select a preset for a channel. The preset is specified by the - SoundFont ID, the bank number, and the preset number. This - allows any preset to be selected and circumvents preset masking - due to previously loaded SoundFonts on the SoundFont stack. - - \param synth The synthesizer - \param chan The channel on which to set the preset - \param sfont_id The ID of the SoundFont - \param bank_num The bank number - \param preset_num The preset number - \return 0 if no errors occured, -1 otherwise - */ -FLUIDSYNTH_API -int fluid_synth_program_select(fluid_synth_t* synth, int chan, - unsigned int sfont_id, - unsigned int bank_num, - unsigned int preset_num); - - /** Returns the program, bank, and SoundFont number of the preset on - a given channel. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API -int fluid_synth_get_program(fluid_synth_t* synth, int chan, - unsigned int* sfont_id, - unsigned int* bank_num, - unsigned int* preset_num); - - /** Send a bank select and a program change to every channel to - * reinitialize the preset of the channel. This function is useful - * mainly after a SoundFont has been loaded, unloaded or - * reloaded. . Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t* synth); - - /** Send a reset. A reset turns all the notes off and resets the - controller values. */ -FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t* synth); - -/** Stop all voices without any other modifications to the synth */ -FLUIDSYNTH_API int fluid_synth_all_voices_stop(fluid_synth_t* synth); - -/** Stop all notes without any other modifications to the synth */ -FLUIDSYNTH_API int fluid_synth_all_voices_pause(fluid_synth_t* synth); - - /* - * - * Low level access - * - */ - - /** Create and start voices using a preset. The id passed as - * argument will be used as the voice group id. */ -FLUIDSYNTH_API int fluid_synth_start(fluid_synth_t* synth, unsigned int id, - fluid_preset_t* preset, int audio_chan, - int midi_chan, int key, int vel); - - /** Stop the voices in the voice group defined by id. */ -FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t* synth, unsigned int id); - - /** Change the value of a generator of the voices in the voice group - * defined by id. */ -/* FLUIDSYNTH_API int fluid_synth_ctrl(fluid_synth_t* synth, int id, */ -/* int gen, float value, */ -/* int absolute, int normalized); */ - - - /* - * - * SoundFont management - * - */ - - /** Set an optional function callback each time a preset has finished loading. - This can be useful when calling fluid_synth_sfload asynchronously. - The function must be formatted like this: - void my_callback_function(int bank, int num, char* name) - - \param callback Pointer to the function - */ -FLUIDSYNTH_API -void fluid_synth_set_preset_callback(void* callback); - - /** Loads a SoundFont file and creates a new SoundFont. The newly - loaded SoundFont will be put on top of the SoundFont - stack. Presets are searched starting from the SoundFont on the - top of the stack, working the way down the stack until a preset - is found. - - \param synth The synthesizer object - \param filename The file name - \param reset_presets If non-zero, the presets on the channels will be reset - \returns The ID of the loaded SoundFont, or -1 in case of error - */ -FLUIDSYNTH_API -int fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets); - - /** Reload a SoundFont. The reloaded SoundFont retains its ID and - index on the stack. - - \param synth The synthesizer object - \param id The id of the SoundFont - \returns The ID of the loaded SoundFont, or -1 in case of error - */ -FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id); - - /** Removes a SoundFont from the stack and deallocates it. - - \param synth The synthesizer object - \param id The id of the SoundFont - \param reset_presets If TRUE then presets will be reset for all channels - \returns 0 if no error, -1 otherwise - */ -FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets); - - /** Add a SoundFont. The SoundFont will be put on top of - the SoundFont stack. - - \param synth The synthesizer object - \param sfont The SoundFont - \returns The ID of the loaded SoundFont, or -1 in case of error - */ -FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont); - - /** Remove a SoundFont that was previously added using - * fluid_synth_add_sfont(). The synthesizer does not delete the - * SoundFont; this is responsability of the caller. - - \param synth The synthesizer object - \param sfont The SoundFont - */ -FLUIDSYNTH_API void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont); - - /** Count the number of loaded SoundFonts. - - \param synth The synthesizer object - \returns The number of loaded SoundFonts - */ -FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t* synth); - - /** Get a SoundFont. The SoundFont is specified by its index on the - stack. The top of the stack has index zero. - - \param synth The synthesizer object - \param num The number of the SoundFont (0 <= num < sfcount) - \returns A pointer to the SoundFont - */ -FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num); - - /** Get a SoundFont. The SoundFont is specified by its ID. - - \param synth The synthesizer object - \param id The id of the sfont - \returns A pointer to the SoundFont - */ -FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id); - - - /** Get the preset of a channel */ -FLUIDSYNTH_API fluid_preset_t* fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan); - - /** Offset the bank numbers in a SoundFont. Returns -1 if an error - * occured (out of memory or negative offset) */ -FLUIDSYNTH_API int fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset); - - /** Get the offset of the bank numbers in a SoundFont. */ -FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id); - - - - /* - * - * Reverb - * - */ - - /** Set the parameters for the built-in reverb unit */ -FLUIDSYNTH_API void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, - double damping, double width, double level); - - /** Turn on (1) / off (0) the built-in reverb unit */ -FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on); - - - /** Query the current state of the reverb. */ -FLUIDSYNTH_API double fluid_synth_get_reverb_roomsize(fluid_synth_t* synth); -FLUIDSYNTH_API double fluid_synth_get_reverb_damp(fluid_synth_t* synth); -FLUIDSYNTH_API double fluid_synth_get_reverb_level(fluid_synth_t* synth); -FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t* synth); - - /* Those are the default settings for the reverb */ -#define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f -#define FLUID_REVERB_DEFAULT_DAMP 0.0f -#define FLUID_REVERB_DEFAULT_WIDTH 0.5f -#define FLUID_REVERB_DEFAULT_LEVEL 0.9f - - - - /* - * - * Chorus - * - */ - -enum fluid_chorus_mod { - FLUID_CHORUS_MOD_SINE = 0, - FLUID_CHORUS_MOD_TRIANGLE = 1 -}; - - /** Set up the chorus. It should be turned on with fluid_synth_set_chorus_on. - * If faulty parameters are given, all new settings are discarded. - * Keep in mind, that the needed CPU time is proportional to 'nr'. - */ -FLUIDSYNTH_API void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, - double speed, double depth_ms, int type); - - /** Turn on (1) / off (0) the built-in chorus unit */ -FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on); - - /** Query the current state of the chorus. */ -FLUIDSYNTH_API int fluid_synth_get_chorus_nr(fluid_synth_t* synth); -FLUIDSYNTH_API double fluid_synth_get_chorus_level(fluid_synth_t* synth); -FLUIDSYNTH_API double fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth); -FLUIDSYNTH_API double fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth); -FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t* synth); /* see fluid_chorus_mod */ - - /* Those are the default settings for the chorus. */ -#define FLUID_CHORUS_DEFAULT_N 3 -#define FLUID_CHORUS_DEFAULT_LEVEL 2.0f -#define FLUID_CHORUS_DEFAULT_SPEED 0.3f -#define FLUID_CHORUS_DEFAULT_DEPTH 8.0f -#define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE - - - - /* - * - * Audio and MIDI channels - * - */ - - /** Returns the number of MIDI channels that the synthesizer uses - internally */ -FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t* synth); - - /** Returns the number of audio channels that the synthesizer uses - internally */ -FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t* synth); - - /** Returns the number of audio groups that the synthesizer uses - internally. This is usually identical to audio_channels. */ -FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t* synth); - - /** Returns the number of effects channels that the synthesizer uses - internally */ -FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t* synth); - - - - /* - * - * Synthesis parameters - * - */ - - /** Set the master gain */ -FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t* synth, float gain); - - /** Get the master gain */ -FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t* synth); - - /** Set the polyphony limit (FluidSynth >= 1.0.6) */ -FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony); - - /** Get the polyphony limit (FluidSynth >= 1.0.6) */ -FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_t* synth); - - /** Get the internal buffer size. The internal buffer size if not the - same thing as the buffer size specified in the - settings. Internally, the synth *always* uses a specific buffer - size independent of the buffer size used by the audio driver. The - internal buffer size is normally 64 samples. The reason why it - uses an internal buffer size is to allow audio drivers to call the - synthesizer with a variable buffer length. The internal buffer - size is useful for client who want to optimize their buffer sizes. - */ -FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_t* synth); - - /** Set the interpolation method for one channel or all channels (chan = -1) */ -FLUIDSYNTH_API -int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method); - - /* Flags to choose the interpolation method */ -enum fluid_interp { - /* no interpolation: Fastest, but questionable audio quality */ - FLUID_INTERP_NONE = 0, - /* Straight-line interpolation: A bit slower, reasonable audio quality */ - FLUID_INTERP_LINEAR = 1, - /* Fourth-order interpolation: Requires 50 % of the whole DSP processing time, good quality - * Default. */ - FLUID_INTERP_DEFAULT = 4, - FLUID_INTERP_4THORDER = 4, - FLUID_INTERP_7THORDER = 7, - FLUID_INTERP_HIGHEST=7 -}; - - - - - /* - * - * Generator interface - * - */ - - /** Change the value of a generator. This function allows to control - all synthesis parameters in real-time. The changes are additive, - i.e. they add up to the existing parameter value. This function is - similar to sending an NRPN message to the synthesizer. The - function accepts a float as the value of the parameter. The - parameter numbers and ranges are described in the SoundFont 2.01 - specification, paragraph 8.1.3, page 48. See also 'fluid_gen_type'. - - \param synth The synthesizer object. - \param chan The MIDI channel number. - \param param The parameter number. - \param value The parameter value. - \returns Your favorite dish. - */ -FLUIDSYNTH_API -int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value); - - - /** Retreive the value of a generator. This function returns the value - set by a previous call 'fluid_synth_set_gen' or by an NRPN message. - - \param synth The synthesizer object. - \param chan The MIDI channel number. - \param param The generator number. - \returns The value of the generator. - */ -FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param); - - - - - /* - * - * Tuning - * - */ - - /** Create a new key-based tuning with given name, number, and - pitches. The array 'pitches' should have length 128 and contains - the pitch in cents of every key in cents. However, if 'pitches' is - NULL, a new tuning is created with the well-tempered scale. - - \param synth The synthesizer object - \param tuning_bank The tuning bank number [0-127] - \param tuning_prog The tuning program number [0-127] - \param name The name of the tuning - \param pitch The array of pitch values. The array length has to be 128. - */ -FLUIDSYNTH_API -int fluid_synth_create_key_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog, - const char* name, double* pitch); - - /** Create a new octave-based tuning with given name, number, and - pitches. The array 'pitches' should have length 12 and contains - derivation in cents from the well-tempered scale. For example, if - pitches[0] equals -33, then the C-keys will be tuned 33 cents - below the well-tempered C. - - \param synth The synthesizer object - \param tuning_bank The tuning bank number [0-127] - \param tuning_prog The tuning program number [0-127] - \param name The name of the tuning - \param pitch The array of pitch derivations. The array length has to be 12. - */ -FLUIDSYNTH_API -int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog, - const char* name, const double* pitch); - -FLUIDSYNTH_API -int fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog, - const char* name, const double* pitch, int apply); - - /** Request a note tuning changes. Both they 'keys' and 'pitches' - arrays should be of length 'num_pitches'. If 'apply' is non-zero, - the changes should be applied in real-time, i.e. sounding notes - will have their pitch updated. 'APPLY' IS CURRENTLY IGNORED. The - changes will be available for newly triggered notes only. - - \param synth The synthesizer object - \param tuning_bank The tuning bank number [0-127] - \param tuning_prog The tuning program number [0-127] - \param len The length of the keys and pitch arrays - \param keys The array of keys values. - \param pitch The array of pitch values. - \param apply Flag to indicate whether to changes should be applied in real-time. - */ -FLUIDSYNTH_API -int fluid_synth_tune_notes(fluid_synth_t* synth, int tuning_bank, int tuning_prog, - int len, int *keys, double* pitch, int apply); - - /** Select a tuning for a channel. - - \param synth The synthesizer object - \param chan The channel number [0-max channels] - \param tuning_bank The tuning bank number [0-127] - \param tuning_prog The tuning program number [0-127] - */ -FLUIDSYNTH_API -int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int tuning_bank, int tuning_prog); - -int fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog, int apply); - - /** Set the tuning to the default well-tempered tuning on a channel. - - \param synth The synthesizer object - \param chan The channel number [0-max channels] - */ -FLUIDSYNTH_API int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan); - - /** Start the iteration throught the list of available tunings. - - \param synth The synthesizer object - */ -FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t* synth); - - - /** Get the next tuning in the iteration. This functions stores the - bank and program number of the next tuning in the pointers given as - arguments. - - \param synth The synthesizer object - \param bank Pointer to an int to store the bank number - \param prog Pointer to an int to store the program number - \returns 1 if there is a next tuning, 0 otherwise - */ -FLUIDSYNTH_API -int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog); - - - /** Dump the data of a tuning. This functions stores the name and - pitch values of a tuning in the pointers given as arguments. Both - name and pitch can be NULL is the data is not needed. - - \param synth The synthesizer object - \param bank The tuning bank number [0-127] - \param prog The tuning program number [0-127] - \param name Pointer to a buffer to store the name - \param len The length of the name buffer - \param pitch Pointer to buffer to store the pitch values - */ -FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, - char* name, int len, double* pitch); - - - - - /* - * - * Misc - * - */ - - /** Get a textual representation of the last error */ -FLUIDSYNTH_API char* fluid_synth_error(fluid_synth_t* synth); - - - /* - * - * Synthesizer plugin - * - * - * To create a synthesizer plugin, create the synthesizer as - * explained above. Once the synthesizer is created you can call - * any of the functions below to get the audio. - * - */ - - /** Generate a number of samples. This function expects two signed - * 16bits buffers (left and right channel) that will be filled with - * samples. - * - * \param synth The synthesizer - * \param len The number of samples to generate - * \param lout The sample buffer for the left channel - * \param loff The offset, in samples, in the left buffer where the writing pointer starts - * \param lincr The increment, in samples, of the writing pointer in the left buffer - * \param rout The sample buffer for the right channel - * \param roff The offset, in samples, in the right buffer where the writing pointer starts - * \param rincr The increment, in samples, of the writing pointer in the right buffer - * \returns 0 if no error occured, non-zero otherwise - */ - -FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t* synth, int len, - void* lout, int loff, int lincr, - void* rout, int roff, int rincr); - - - /** Generate a number of samples. This function expects two floating - * point buffers (left and right channel) that will be filled with - * samples. - * - * \param synth The synthesizer - * \param len The number of samples to generate - * \param lout The sample buffer for the left channel - * \param loff The offset, in samples, in the left buffer where the writing pointer starts - * \param lincr The increment, in samples, of the writing pointer in the left buffer - * \param rout The sample buffer for the right channel - * \param roff The offset, in samples, in the right buffer where the writing pointer starts - * \param rincr The increment, in samples, of the writing pointer in the right buffer - * \returns 0 if no error occured, non-zero otherwise - */ - -FLUIDSYNTH_API int fluid_synth_write_float(fluid_synth_t* synth, int len, - void* lout, int loff, int lincr, - void* rout, int roff, int rincr); - -FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t* synth, int len, - float** left, float** right, - float** fx_left, float** fx_right); - - /** Generate a number of samples. This function implements the - * default interface defined in fluidsynth/audio.h. This function - * ignores the input buffers and expects at least two output - * buffer. - * - * \param synth The synthesizer - * \param len The number of samples to generate - * \param nin The number of input buffers - * \param in The array of input buffers - * \param nout The number of output buffers - * \param out The array of output buffers - * \returns 0 if no error occured, non-zero otherwise - */ - -FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t* synth, int len, - int nin, float** in, - int nout, float** out); - - - - /* Type definition of the synthesizer's audio callback function. */ -typedef int (*fluid_audio_callback_t)(fluid_synth_t* synth, int len, - void* out1, int loff, int lincr, - void* out2, int roff, int rincr); - - - - - - /* - * Synthesizer's interface to handle SoundFont loaders - */ - - - /** Add a SoundFont loader to the synthesizer. Note that SoundFont - loader don't necessarily load SoundFonts. They can load any type - of wavetable data but export a SoundFont interface. */ -FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader); - - /** Allocate a synthesis voice. This function is called by a - soundfont's preset in response to a noteon event. - The returned voice comes with default modulators installed (velocity-to-attenuation, - velocity to filter, ...) - Note: A single noteon event may create any number of voices, when the preset is layered. - Typically 1 (mono) or 2 (stereo).*/ -FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, - int channum, int key, int vel); - - /** Start a synthesis voice. This function is called by a - soundfont's preset in response to a noteon event after the voice - has been allocated with fluid_synth_alloc_voice() and - initialized. - Exclusive classes are processed here.*/ -FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice); - - - /** Write a list of all voices matching ID into buf, but not more than bufsize voices. - * If ID <0, return all voices. */ -FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t* synth, - fluid_voice_t* buf[], int bufsize, int ID); - - -/* midi router disabled */ -#if 0 - /** This is a hack to get command handlers working */ -FLUIDSYNTH_API void fluid_synth_set_midi_router(fluid_synth_t* synth, - fluid_midi_router_t* router); -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* _FLUIDSYNTH_SYNTH_H */ diff --git a/libraries/fluidlite/include/fluidlite/types.h b/libraries/fluidlite/include/fluidlite/types.h deleted file mode 100644 index c015bbf93..000000000 --- a/libraries/fluidlite/include/fluidlite/types.h +++ /dev/null @@ -1,67 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUIDSYNTH_TYPES_H -#define _FLUIDSYNTH_TYPES_H - - - -#ifdef __cplusplus -extern "C" { -#endif - - -/* - - Forward declarations - -*/ -typedef struct _fluid_hashtable_t fluid_settings_t; -typedef struct _fluid_synth_t fluid_synth_t; -typedef struct _fluid_voice_t fluid_voice_t; -typedef struct _fluid_sfloader_t fluid_sfloader_t; -typedef struct _fluid_sfont_t fluid_sfont_t; -typedef struct _fluid_preset_t fluid_preset_t; -typedef struct _fluid_sample_t fluid_sample_t; -typedef struct _fluid_mod_t fluid_mod_t; -typedef struct _fluid_audio_driver_t fluid_audio_driver_t; -typedef struct _fluid_player_t fluid_player_t; -typedef struct _fluid_midi_event_t fluid_midi_event_t; -typedef struct _fluid_midi_driver_t fluid_midi_driver_t; -typedef struct _fluid_midi_router_t fluid_midi_router_t; -typedef struct _fluid_midi_router_rule_t fluid_midi_router_rule_t; -typedef struct _fluid_hashtable_t fluid_cmd_handler_t; -typedef struct _fluid_shell_t fluid_shell_t; -typedef struct _fluid_server_t fluid_server_t; -typedef struct _fluid_event_t fluid_event_t; -typedef struct _fluid_sequencer_t fluid_sequencer_t; -typedef struct _fluid_ramsfont_t fluid_ramsfont_t; -typedef struct _fluid_rampreset_t fluid_rampreset_t; -typedef struct _fluid_fileapi_t fluid_fileapi_t; - -typedef int fluid_istream_t; -typedef int fluid_ostream_t; - - -#ifdef __cplusplus -} -#endif - -#endif /* _FLUIDSYNTH_TYPES_H */ diff --git a/libraries/fluidlite/include/fluidlite/voice.h b/libraries/fluidlite/include/fluidlite/voice.h deleted file mode 100644 index 157039653..000000000 --- a/libraries/fluidlite/include/fluidlite/voice.h +++ /dev/null @@ -1,97 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUIDSYNTH_VOICE_H -#define _FLUIDSYNTH_VOICE_H - -#ifdef __cplusplus -extern "C" { -#endif - - - /* - * The interface to the synthesizer's voices - * Examples on using them can be found in fluid_defsfont.c - */ - - /** Update all the synthesis parameters, which depend on generator gen. - This is only necessary after changing a generator of an already operating voice. - Most applications will not need this function.*/ - -FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t* voice, int gen); - - - /* for fluid_voice_add_mod */ -enum fluid_voice_add_mod{ - FLUID_VOICE_OVERWRITE, - FLUID_VOICE_ADD, - FLUID_VOICE_DEFAULT -}; - - /* Add a modulator to a voice (SF2.1 only). */ -FLUIDSYNTH_API void fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode); - - /** Set the value of a generator */ -FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t* voice, int gen, float val); - - /** Get the value of a generator */ -FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t* voice, int gen); - - /** Modify the value of a generator by val */ -FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t* voice, int gen, float val); - - - /** Return the unique ID of the noteon-event. A sound font loader - * may store the voice processes it has created for * real-time - * control during the operation of a voice (for example: parameter - * changes in sound font editor). The synth uses a pool of - * voices, which are 'recycled' and never deallocated. - * - * Before modifying an existing voice, check - * - that its state is still 'playing' - * - that the ID is still the same - * Otherwise the voice has finished playing. - */ -FLUIDSYNTH_API unsigned int fluid_voice_get_id(fluid_voice_t* voice); - - -FLUIDSYNTH_API int fluid_voice_is_playing(fluid_voice_t* voice); - - /** If the peak volume during the loop is known, then the voice can - * be released earlier during the release phase. Otherwise, the - * voice will operate (inaudibly), until the envelope is at the - * nominal turnoff point. In many cases the loop volume is many dB - * below the maximum volume. For example, the loop volume for a - * typical acoustic piano is 20 dB below max. Taking that into - * account in the turn-off algorithm we can save 20 dB / 100 dB => - * 1/5 of the total release time. - * So it's a good idea to call fluid_voice_optimize_sample - * on each sample once. - */ - -FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t* s); - - - -#ifdef __cplusplus -} -#endif -#endif /* _FLUIDSYNTH_VOICE_H */ - diff --git a/libraries/fluidlite/src/fluid_chan.c b/libraries/fluidlite/src/fluid_chan.c deleted file mode 100644 index 334e9821a..000000000 --- a/libraries/fluidlite/src/fluid_chan.c +++ /dev/null @@ -1,455 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#include "fluid_chan.h" -#include "fluid_mod.h" -#include "fluid_synth.h" -#include "fluid_sfont.h" - -#define SETCC(_c,_n,_v) _c->cc[_n] = _v - -/* - * new_fluid_channel - */ -fluid_channel_t* -new_fluid_channel(fluid_synth_t* synth, int num) -{ - fluid_channel_t* chan; - - chan = FLUID_NEW(fluid_channel_t); - if (chan == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - chan->synth = synth; - chan->channum = num; - chan->preset = NULL; - - fluid_channel_init(chan); - fluid_channel_init_ctrl(chan,0); - - return chan; -} - -void -fluid_channel_init(fluid_channel_t* chan) -{ - chan->prognum = 0; - chan->banknum = 0; - chan->sfontnum = 0; - - if (chan->preset) delete_fluid_preset (chan->preset); - chan->preset = fluid_synth_find_preset(chan->synth, chan->banknum, chan->prognum); - - chan->interp_method = FLUID_INTERP_DEFAULT; - chan->tuning = NULL; - chan->nrpn_select = 0; - chan->nrpn_active = 0; -} - -/* - @param is_all_ctrl_off if nonzero, only resets some controllers, according to - http://www.midi.org/techspecs/rp15.php -*/ -void -fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off) -{ - int i; - - chan->channel_pressure = 0; - chan->pitch_bend = 0x2000; /* Range is 0x4000, pitch bend wheel starts in centered position */ - - for (i = 0; i < GEN_LAST; i++) { - chan->gen[i] = 0.0f; - chan->gen_abs[i] = 0; - } - - if (is_all_ctrl_off) { - for (i = 0; i < ALL_SOUND_OFF; i++) { - if (i >= EFFECTS_DEPTH1 && i <= EFFECTS_DEPTH5) { - continue; - } - if (i >= SOUND_CTRL1 && i <= SOUND_CTRL10) { - continue; - } - if (i == BANK_SELECT_MSB || i == BANK_SELECT_LSB || i == VOLUME_MSB || - i == VOLUME_LSB || i == PAN_MSB || i == PAN_LSB) { - continue; - } - - SETCC(chan, i, 0); - } - } - else { - for (i = 0; i < 128; i++) { - SETCC(chan, i, 0); - } - } - - /* Reset polyphonic key pressure on all voices */ - for (i = 0; i < 128; i++) { - fluid_channel_set_key_pressure(chan, i, 0); - } - - /* Set RPN controllers to NULL state */ - SETCC(chan, RPN_LSB, 127); - SETCC(chan, RPN_MSB, 127); - - /* Set NRPN controllers to NULL state */ - SETCC(chan, NRPN_LSB, 127); - SETCC(chan, NRPN_MSB, 127); - - /* Expression (MSB & LSB) */ - SETCC(chan, EXPRESSION_MSB, 127); - SETCC(chan, EXPRESSION_LSB, 127); - - if (!is_all_ctrl_off) { - - chan->pitch_wheel_sensitivity = 2; /* two semi-tones */ - - /* Just like panning, a value of 64 indicates no change for sound ctrls */ - for (i = SOUND_CTRL1; i <= SOUND_CTRL10; i++) { - SETCC(chan, i, 64); - } - - /* Volume / initial attenuation (MSB & LSB) */ - SETCC(chan, VOLUME_MSB, 100); - SETCC(chan, VOLUME_LSB, 0); - - /* Pan (MSB & LSB) */ - SETCC(chan, PAN_MSB, 64); - SETCC(chan, PAN_LSB, 0); - - /* Reverb */ - /* SETCC(chan, EFFECTS_DEPTH1, 40); */ - /* Note: although XG standard specifies the default amount of reverb to - be 40, most people preferred having it at zero. - See http://lists.gnu.org/archive/html/fluid-dev/2009-07/msg00016.html */ - } -} - -void -fluid_channel_reset(fluid_channel_t* chan) -{ - fluid_channel_init(chan); - fluid_channel_init_ctrl(chan,0); -} - -/* - * delete_fluid_channel - */ -int -delete_fluid_channel(fluid_channel_t* chan) -{ - if (chan->preset) delete_fluid_preset (chan->preset); - FLUID_FREE(chan); - return FLUID_OK; -} - -/* - * fluid_channel_set_preset - */ -int -fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset) -{ - fluid_preset_notify(chan->preset, FLUID_PRESET_UNSELECTED, chan->channum); - fluid_preset_notify(preset, FLUID_PRESET_SELECTED, chan->channum); - - if (chan->preset) delete_fluid_preset (chan->preset); - chan->preset = preset; - return FLUID_OK; -} - -/* - * fluid_channel_get_preset - */ -fluid_preset_t* -fluid_channel_get_preset(fluid_channel_t* chan) -{ - return chan->preset; -} - -/* - * fluid_channel_get_banknum - */ -unsigned int -fluid_channel_get_banknum(fluid_channel_t* chan) -{ - return chan->banknum; -} - -/* - * fluid_channel_set_prognum - */ -int -fluid_channel_set_prognum(fluid_channel_t* chan, int prognum) -{ - chan->prognum = prognum; - return FLUID_OK; -} - -/* - * fluid_channel_get_prognum - */ -int -fluid_channel_get_prognum(fluid_channel_t* chan) -{ - return chan->prognum; -} - -/* - * fluid_channel_set_banknum - */ -int -fluid_channel_set_banknum(fluid_channel_t* chan, unsigned int banknum) -{ - chan->banknum = banknum; - return FLUID_OK; -} - -/* - * fluid_channel_cc - */ -int -fluid_channel_cc(fluid_channel_t* chan, int num, int value) -{ - chan->cc[num] = value; - - switch (num) { - - case SUSTAIN_SWITCH: - { - if (value < 64) { -/* printf("** sustain off\n"); */ - fluid_synth_damp_voices(chan->synth, chan->channum); - } else { -/* printf("** sustain on\n"); */ - } - } - break; - - case BANK_SELECT_MSB: - { - if (chan->channum == 9 && fluid_settings_str_equal(chan->synth->settings, "synth.drums-channel.active", "yes")) { - return FLUID_OK; /* ignored */ - } - - chan->bank_msb = (unsigned char) (value & 0x7f); -/* printf("** bank select msb recieved: %d\n", value); */ - - /* I fixed the handling of a MIDI bank select controller 0, - e.g., bank select MSB (or "coarse" bank select according to - my spec). Prior to this fix a channel's bank number was only - changed upon reception of MIDI bank select controller 32, - e.g, bank select LSB (or "fine" bank-select according to my - spec). [KLE] - - FIXME: is this correct? [PH] */ - fluid_channel_set_banknum(chan, (unsigned int)(value & 0x7f)); /* KLE */ - } - break; - - case BANK_SELECT_LSB: - { - if (chan->channum == 9 && fluid_settings_str_equal(chan->synth->settings, "synth.drums-channel.active", "yes")) { - return FLUID_OK; /* ignored */ - } - /* FIXME: according to the Downloadable Sounds II specification, - bit 31 should be set when we receive the message on channel - 10 (drum channel) */ - fluid_channel_set_banknum(chan, (((unsigned int) value & 0x7f) - + ((unsigned int) chan->bank_msb << 7))); - } - break; - - case ALL_NOTES_OFF: - fluid_synth_all_notes_off(chan->synth, chan->channum); - break; - - case ALL_SOUND_OFF: - fluid_synth_all_sounds_off(chan->synth, chan->channum); - break; - - case ALL_CTRL_OFF: - fluid_channel_init_ctrl(chan,1); - fluid_synth_modulate_voices_all(chan->synth, chan->channum); - break; - - case DATA_ENTRY_MSB: - { - int data = (value << 7) + chan->cc[DATA_ENTRY_LSB]; - - if (chan->nrpn_active) /* NRPN is active? */ - { - /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ - if ((chan->cc[NRPN_MSB] == 120) && (chan->cc[NRPN_LSB] < 100)) - { - if (chan->nrpn_select < GEN_LAST) - { - float val = fluid_gen_scale_nrpn(chan->nrpn_select, data); - fluid_synth_set_gen(chan->synth, chan->channum, chan->nrpn_select, val); - } - - chan->nrpn_select = 0; /* Reset to 0 */ - } - } - else if (chan->cc[RPN_MSB] == 0) /* RPN is active: MSB = 0? */ - { - switch (chan->cc[RPN_LSB]) - { - case RPN_PITCH_BEND_RANGE: - fluid_channel_pitch_wheel_sens (chan, value); /* Set bend range in semitones */ - /* FIXME - Handle LSB? (Fine bend range in cents) */ - break; - case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over +/-1 semitone (+/- 100 cents, 8192 = center) */ - fluid_synth_set_gen(chan->synth, chan->channum, GEN_FINETUNE, - (data - 8192) / 8192.0 * 100.0); - break; - case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */ - fluid_synth_set_gen(chan->synth, chan->channum, GEN_COARSETUNE, - value - 64); - break; - case RPN_TUNING_PROGRAM_CHANGE: - break; - case RPN_TUNING_BANK_SELECT: - break; - case RPN_MODULATION_DEPTH_RANGE: - break; - } - } - - break; - } - - case NRPN_MSB: - chan->cc[NRPN_LSB] = 0; - chan->nrpn_select = 0; - chan->nrpn_active = 1; - break; - - case NRPN_LSB: - /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ - if (chan->cc[NRPN_MSB] == 120) { - if (value == 100) { - chan->nrpn_select += 100; - } else if (value == 101) { - chan->nrpn_select += 1000; - } else if (value == 102) { - chan->nrpn_select += 10000; - } else if (value < 100) { - chan->nrpn_select += value; - } - } - - chan->nrpn_active = 1; - break; - - case RPN_MSB: - case RPN_LSB: - chan->nrpn_active = 0; - break; - - default: - fluid_synth_modulate_voices(chan->synth, chan->channum, 1, num); - } - - return FLUID_OK; -} - -/* - * fluid_channel_get_cc - */ -int -fluid_channel_get_cc(fluid_channel_t* chan, int num) -{ - return ((num >= 0) && (num < 128))? chan->cc[num] : 0; -} - -/* - * fluid_channel_pressure - */ -int -fluid_channel_pressure(fluid_channel_t* chan, int val) -{ - chan->channel_pressure = val; - fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_CHANNELPRESSURE); - return FLUID_OK; -} - -/* - * fluid_channel_pitch_bend - */ -int -fluid_channel_pitch_bend(fluid_channel_t* chan, int val) -{ - chan->pitch_bend = val; - fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_PITCHWHEEL); - return FLUID_OK; -} - -/* - * fluid_channel_pitch_wheel_sens - */ -int -fluid_channel_pitch_wheel_sens(fluid_channel_t* chan, int val) -{ - chan->pitch_wheel_sensitivity = val; - fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_PITCHWHEELSENS); - return FLUID_OK; -} - -/* - * fluid_channel_get_num - */ -int -fluid_channel_get_num(fluid_channel_t* chan) -{ - return chan->channum; -} - -/* Purpose: - * Sets the index of the interpolation method used on this channel, - * as in fluid_interp in fluidlite.h - */ -void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method) -{ - chan->interp_method = new_method; -} - -/* Purpose: - * Returns the index of the interpolation method used on this channel, - * as in fluid_interp in fluidlite.h - */ -int fluid_channel_get_interp_method(fluid_channel_t* chan) -{ - return chan->interp_method; -} - -unsigned int fluid_channel_get_sfontnum(fluid_channel_t* chan) -{ - return chan->sfontnum; -} - -int fluid_channel_set_sfontnum(fluid_channel_t* chan, unsigned int sfontnum) -{ - chan->sfontnum = sfontnum; - return FLUID_OK; -} diff --git a/libraries/fluidlite/src/fluid_chan.h b/libraries/fluidlite/src/fluid_chan.h deleted file mode 100644 index 3c33c9395..000000000 --- a/libraries/fluidlite/src/fluid_chan.h +++ /dev/null @@ -1,114 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUID_CHAN_H -#define _FLUID_CHAN_H - -#include "fluidsynth_priv.h" -#include "fluid_midi.h" -#include "fluid_tuning.h" - -/* - * fluid_channel_t - */ -struct _fluid_channel_t -{ - int channum; - unsigned int sfontnum; - unsigned int banknum; - unsigned int prognum; - fluid_preset_t* preset; - fluid_synth_t* synth; - char key_pressure[128]; - short channel_pressure; - short pitch_bend; - short pitch_wheel_sensitivity; - - /* controller values */ - short cc[128]; - - /* cached values of last MSB values of MSB/LSB controllers */ - unsigned char bank_msb; - int interp_method; - - /* the micro-tuning */ - fluid_tuning_t* tuning; - - /* NRPN system */ - short nrpn_select; - short nrpn_active; /* 1 if data entry CCs are for NRPN, 0 if RPN */ - - /* The values of the generators, set by NRPN messages, or by - * fluid_synth_set_gen(), are cached in the channel so they can be - * applied to future notes. They are copied to a voice's generators - * in fluid_voice_init(), wihich calls fluid_gen_init(). */ - fluid_real_t gen[GEN_LAST]; - - /* By default, the NRPN values are relative to the values of the - * generators set in the SoundFont. For example, if the NRPN - * specifies an attack of 100 msec then 100 msec will be added to the - * combined attack time of the sound font and the modulators. - * - * However, it is useful to be able to specify the generator value - * absolutely, completely ignoring the generators of the sound font - * and the values of modulators. The gen_abs field, is a boolean - * flag indicating whether the NRPN value is absolute or not. - */ - char gen_abs[GEN_LAST]; -}; - -fluid_channel_t* new_fluid_channel(fluid_synth_t* synth, int num); -int delete_fluid_channel(fluid_channel_t* chan); -void fluid_channel_init(fluid_channel_t* chan); -void fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off); -void fluid_channel_reset(fluid_channel_t* chan); -int fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset); -fluid_preset_t* fluid_channel_get_preset(fluid_channel_t* chan); -unsigned int fluid_channel_get_sfontnum(fluid_channel_t* chan); -int fluid_channel_set_sfontnum(fluid_channel_t* chan, unsigned int sfont); -unsigned int fluid_channel_get_banknum(fluid_channel_t* chan); -int fluid_channel_set_banknum(fluid_channel_t* chan, unsigned int bank); -int fluid_channel_set_prognum(fluid_channel_t* chan, int prognum); -int fluid_channel_get_prognum(fluid_channel_t* chan); -int fluid_channel_cc(fluid_channel_t* chan, int ctrl, int val); -int fluid_channel_pressure(fluid_channel_t* chan, int val); -int fluid_channel_pitch_bend(fluid_channel_t* chan, int val); -int fluid_channel_pitch_wheel_sens(fluid_channel_t* chan, int val); -int fluid_channel_get_cc(fluid_channel_t* chan, int num); -int fluid_channel_get_num(fluid_channel_t* chan); -void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method); -int fluid_channel_get_interp_method(fluid_channel_t* chan); - -#define fluid_channel_get_key_pressure(chan, key) \ - ((chan)->key_pressure[key]) -#define fluid_channel_set_key_pressure(chan, key, val) \ - ((chan)->key_pressure[key] = (val)) -#define fluid_channel_set_tuning(_c, _t) { (_c)->tuning = _t; } -#define fluid_channel_has_tuning(_c) ((_c)->tuning != NULL) -#define fluid_channel_get_tuning(_c) ((_c)->tuning) -#define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64) -#define fluid_channel_set_gen(_c, _n, _v, _a) { (_c)->gen[_n] = _v; (_c)->gen_abs[_n] = _a; } -#define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n]) -#define fluid_channel_get_gen_abs(_c, _n) ((_c)->gen_abs[_n]) - -#define fluid_channel_get_min_note_length_ticks(chan) \ - ((chan)->synth->min_note_length_ticks) - -#endif /* _FLUID_CHAN_H */ diff --git a/libraries/fluidlite/src/fluid_chorus.c b/libraries/fluidlite/src/fluid_chorus.c deleted file mode 100644 index c5b6eed07..000000000 --- a/libraries/fluidlite/src/fluid_chorus.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * August 24, 1998 - * Copyright (C) 1998 Juergen Mueller And Sundry Contributors - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Juergen Mueller And Sundry Contributors are not responsible for - * the consequences of using this software. - */ - -/* - - CHANGES - - - Adapted for fluidsynth, Peter Hanappe, March 2002 - - - Variable delay line implementation using bandlimited - interpolation, code reorganization: Markus Nentwig May 2002 - - */ - - -/* - * Chorus effect. - * - * Flow diagram scheme for n delays ( 1 <= n <= MAX_CHORUS ): - * - * * gain-in ___ - * ibuff -----+--------------------------------------------->| | - * | _________ | | - * | | | * level 1 | | - * +---->| delay 1 |----------------------------->| | - * | |_________| | | - * | /|\ | | - * : | | | - * : +-----------------+ +--------------+ | + | - * : | Delay control 1 |<--| mod. speed 1 | | | - * : +-----------------+ +--------------+ | | - * | _________ | | - * | | | * level n | | - * +---->| delay n |----------------------------->| | - * |_________| | | - * /|\ |___| - * | | - * +-----------------+ +--------------+ | * gain-out - * | Delay control n |<--| mod. speed n | | - * +-----------------+ +--------------+ +----->obuff - * - * - * The delay i is controlled by a sine or triangle modulation i ( 1 <= i <= n). - * - * The delay of each block is modulated between 0..depth ms - * - */ - - -/* Variable delay line implementation - * ================================== - * - * The modulated delay needs the value of the delayed signal between - * samples. A lowpass filter is used to obtain intermediate values - * between samples (bandlimited interpolation). The sample pulse - * train is convoluted with the impulse response of the low pass - * filter (sinc function). To make it work with a small number of - * samples, the sinc function is windowed (Hamming window). - * - */ - -#include "fluid_chorus.h" -#include "fluid_sys.h" - -#define MAX_CHORUS 99 -#define MAX_DELAY 100 -#define MAX_DEPTH 10 -#define MIN_SPEED_HZ 0.29 -#define MAX_SPEED_HZ 5 - -/* Length of one delay line in samples: - * Set through MAX_SAMPLES_LN2. - * For example: - * MAX_SAMPLES_LN2=12 - * => MAX_SAMPLES=pow(2,12)=4096 - * => MAX_SAMPLES_ANDMASK=4095 - */ -#define MAX_SAMPLES_LN2 12 - -#define MAX_SAMPLES (1 << (MAX_SAMPLES_LN2-1)) -#define MAX_SAMPLES_ANDMASK (MAX_SAMPLES-1) - - -/* Interpolate how many steps between samples? Must be power of two - For example: 8 => use a resolution of 256 steps between any two - samples -*/ -#define INTERPOLATION_SUBSAMPLES_LN2 8 -#define INTERPOLATION_SUBSAMPLES (1 << (INTERPOLATION_SUBSAMPLES_LN2-1)) -#define INTERPOLATION_SUBSAMPLES_ANDMASK (INTERPOLATION_SUBSAMPLES-1) - -/* Use how many samples for interpolation? Must be odd. '7' sounds - relatively clean, when listening to the modulated delay signal - alone. For a demo on aliasing try '1' With '3', the aliasing is - still quite pronounced for some input frequencies -*/ -#define INTERPOLATION_SAMPLES 5 - -/* Private data for SKEL file */ -struct _fluid_chorus_t { - /* Store the values between fluid_chorus_set_xxx and fluid_chorus_update - * Logic behind this: - * - both 'parameter' and 'new_parameter' hold the same value. - * - To change the chorus settings, 'new_parameter' is modified and - * fluid_chorus_update is called. - * - If the new value is valid, it is copied to 'parameter'. - * - If it is invalid, 'new_parameter' is restored to 'parameter'. - */ - int type; /* current value */ - int new_type; /* next value, if parameter check is OK */ - fluid_real_t depth_ms; /* current value */ - fluid_real_t new_depth_ms; /* next value, if parameter check is OK */ - fluid_real_t level; /* current value */ - fluid_real_t new_level; /* next value, if parameter check is OK */ - fluid_real_t speed_Hz; /* current value */ - fluid_real_t new_speed_Hz; /* next value, if parameter check is OK */ - int number_blocks; /* current value */ - int new_number_blocks; /* next value, if parameter check is OK */ - - fluid_real_t *chorusbuf; - int counter; - long phase[MAX_CHORUS]; - long modulation_period_samples; - int *lookup_tab; - fluid_real_t sample_rate; - - /* sinc lookup table */ - fluid_real_t sinc_table[INTERPOLATION_SAMPLES][INTERPOLATION_SUBSAMPLES]; -}; - -void fluid_chorus_triangle(int *buf, int len, int depth); -void fluid_chorus_sine(int *buf, int len, int depth); - -fluid_chorus_t* -new_fluid_chorus(fluid_real_t sample_rate) -{ - int i; int ii; - fluid_chorus_t* chorus; - - chorus = FLUID_NEW(fluid_chorus_t); - if (chorus == NULL) { - fluid_log(FLUID_PANIC, "chorus: Out of memory"); - return NULL; - } - - FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t)); - - chorus->sample_rate = sample_rate; - - /* Lookup table for the SI function (impulse response of an ideal low pass) */ - - /* i: Offset in terms of whole samples */ - for (i = 0; i < INTERPOLATION_SAMPLES; i++){ - - /* ii: Offset in terms of fractional samples ('subsamples') */ - for (ii = 0; ii < INTERPOLATION_SUBSAMPLES; ii++){ - /* Move the origin into the center of the table */ - double i_shifted = ((double) i- ((double) INTERPOLATION_SAMPLES) / 2. - + (double) ii / (double) INTERPOLATION_SUBSAMPLES); - if (fabs(i_shifted) < 0.000001) { - /* sinc(0) cannot be calculated straightforward (limit needed - for 0/0) */ - chorus->sinc_table[i][ii] = (fluid_real_t)1.; - - } else { - chorus->sinc_table[i][ii] = (fluid_real_t)sin(i_shifted * M_PI) / (M_PI * i_shifted); - /* Hamming window */ - chorus->sinc_table[i][ii] *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * M_PI * i_shifted / (fluid_real_t)INTERPOLATION_SAMPLES)); - }; - }; - }; - - /* allocate lookup tables */ - chorus->lookup_tab = FLUID_ARRAY(int, (int) (chorus->sample_rate / MIN_SPEED_HZ)); - if (chorus->lookup_tab == NULL) { - fluid_log(FLUID_PANIC, "chorus: Out of memory"); - goto error_recovery; - } - - /* allocate sample buffer */ - - chorus->chorusbuf = FLUID_ARRAY(fluid_real_t, MAX_SAMPLES); - if (chorus->chorusbuf == NULL) { - fluid_log(FLUID_PANIC, "chorus: Out of memory"); - goto error_recovery; - } - - if (fluid_chorus_init(chorus) != FLUID_OK){ - goto error_recovery; - }; - - return chorus; - - error_recovery: - delete_fluid_chorus(chorus); - return NULL; -} - - -int -fluid_chorus_init(fluid_chorus_t* chorus) -{ - int i; - - for (i = 0; i < MAX_SAMPLES; i++) { - chorus->chorusbuf[i] = 0.0; - } - - /* initialize the chorus with the default settings */ - fluid_chorus_set_nr(chorus, FLUID_CHORUS_DEFAULT_N); - fluid_chorus_set_level(chorus, FLUID_CHORUS_DEFAULT_LEVEL); - fluid_chorus_set_speed_Hz(chorus, FLUID_CHORUS_DEFAULT_SPEED); - fluid_chorus_set_depth_ms(chorus, FLUID_CHORUS_DEFAULT_DEPTH); - fluid_chorus_set_type(chorus, FLUID_CHORUS_MOD_SINE); - - return fluid_chorus_update(chorus); -} - -/* Purpose: - * Sets the number of stages. - * Requires call to fluid_chorus_update afterwards. - * Range checking is performed there.*/ -void fluid_chorus_set_nr(fluid_chorus_t* chorus, int nr) -{ - chorus->new_number_blocks = nr; -} - -/* Purpose: - * API function, read the current state of the chorus - */ -int fluid_chorus_get_nr(fluid_chorus_t* chorus) -{ - return chorus->number_blocks; -} - -/* Purpose: - * Sets the mixing level of the signal from each delay line (linear). - * Requires calling fluid_chorus_update afterwards.*/ -void fluid_chorus_set_level(fluid_chorus_t* chorus, fluid_real_t level) -{ - chorus->new_level = level; -} - -/* Purpose: - * API function, read the current state of the chorus - */ -fluid_real_t fluid_chorus_get_level(fluid_chorus_t* chorus) -{ - return chorus->level; -} - -/* Purpose: - * Sets the modulation frequency. - * Requires call to fluid_chorus_update afterwards. - * Range checking is performed there.*/ -void fluid_chorus_set_speed_Hz(fluid_chorus_t* chorus, fluid_real_t speed_Hz) -{ - chorus->new_speed_Hz = speed_Hz; -} - -/* Purpose: - * API function, read the current state of the chorus - */ -fluid_real_t fluid_chorus_get_speed_Hz(fluid_chorus_t* chorus) -{ - return chorus->speed_Hz; -} - -/* Purpose: - * Sets the modulation depth in ms. - * Requires call to fluid_chorus_update afterwards. - * Range checking is performed there.*/ -void fluid_chorus_set_depth_ms(fluid_chorus_t* chorus, fluid_real_t depth_ms) -{ - chorus->new_depth_ms=depth_ms; -} - -/* Purpose: - * API function, read the current state of the chorus - */ -fluid_real_t fluid_chorus_get_depth_ms(fluid_chorus_t* chorus) -{ - return chorus->depth_ms; -} - -/* Purpose: - * Sets the type of the modulation waveform. - * Requires call to fluid_chorus_update afterwards. - * Check for meaningful values is performed there.*/ -void fluid_chorus_set_type(fluid_chorus_t* chorus, int type) -{ - chorus->new_type=type; -} - -/* Purpose: - * API function, read the current state of the chorus - */ -int fluid_chorus_get_type(fluid_chorus_t* chorus) -{ - return chorus->type; -} - -void -delete_fluid_chorus(fluid_chorus_t* chorus) -{ - if (chorus == NULL) { - return; - } - - if (chorus->chorusbuf != NULL) { - FLUID_FREE(chorus->chorusbuf); - } - - if (chorus->lookup_tab != NULL) { - FLUID_FREE(chorus->lookup_tab); - } - - FLUID_FREE(chorus); -} - - -/* Purpose: - * Calculates the internal chorus parameters using the settings from - * fluid_chorus_set_xxx. */ -int -fluid_chorus_update(fluid_chorus_t* chorus) -{ - int i; - int modulation_depth_samples; - - if (chorus->new_number_blocks < 0) { - fluid_log(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0."); - chorus->new_number_blocks = 0; - } else if (chorus->new_number_blocks > MAX_CHORUS) { - fluid_log(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.", - MAX_CHORUS); - chorus->new_number_blocks = MAX_CHORUS; - }; - - if (chorus->new_speed_Hz < MIN_SPEED_HZ) { - fluid_log(FLUID_WARN, "chorus: speed is too low (min %f)! Setting value to min.", - (double) MIN_SPEED_HZ); - chorus->new_speed_Hz = MIN_SPEED_HZ; - } else if (chorus->new_speed_Hz > MAX_SPEED_HZ) { - fluid_log(FLUID_WARN, "chorus: speed must be below %f Hz! Setting value to max.", - (double) MAX_SPEED_HZ); - chorus->new_speed_Hz = MAX_SPEED_HZ; - } - if (chorus->new_depth_ms < 0.0) { - fluid_log(FLUID_WARN, "chorus: depth must be positive! Setting value to 0."); - chorus->new_depth_ms = 0.0; - } - /* Depth: Check for too high value through modulation_depth_samples. */ - - if (chorus->new_level < 0.0) { - fluid_log(FLUID_WARN, "chorus: level must be positive! Setting value to 0."); - chorus->new_level = 0.0; - } else if (chorus->new_level > 10) { - fluid_log(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! " - "Setting it to 0.1."); - chorus->new_level = 0.1; - } - - /* The modulating LFO goes through a full period every x samples: */ - chorus->modulation_period_samples = chorus->sample_rate / chorus->new_speed_Hz; - - /* The variation in delay time is x: */ - modulation_depth_samples = (int) - (chorus->new_depth_ms / 1000.0 /* convert modulation depth in ms to s*/ - * chorus->sample_rate); - - if (modulation_depth_samples > MAX_SAMPLES) { - fluid_log(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES); - modulation_depth_samples = MAX_SAMPLES; - } - - /* initialize LFO table */ - if (chorus->type == FLUID_CHORUS_MOD_SINE) { - fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples, - modulation_depth_samples); - } else if (chorus->type == FLUID_CHORUS_MOD_TRIANGLE) { - fluid_chorus_triangle(chorus->lookup_tab, chorus->modulation_period_samples, - modulation_depth_samples); - } else { - fluid_log(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave."); - chorus->type = FLUID_CHORUS_MOD_SINE; - fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples, - modulation_depth_samples); - }; - - for (i = 0; i < chorus->number_blocks; i++) { - /* Set the phase of the chorus blocks equally spaced */ - chorus->phase[i] = (int) ((double) chorus->modulation_period_samples - * (double) i / (double) chorus->number_blocks); - } - - /* Start of the circular buffer */ - chorus->counter = 0; - - chorus->type = chorus->new_type; - chorus->depth_ms = chorus->new_depth_ms; - chorus->level = chorus->new_level; - chorus->speed_Hz = chorus->new_speed_Hz; - chorus->number_blocks = chorus->new_number_blocks; - return FLUID_OK; - -/* failure: */ - /* Note: This lives on the assumption, that the last chorus values were correct. - * If not, this will loop forever and a day. */ -/* fluid_log(FLUID_WARN, "chorus: Restoring last good settings"); */ -/* chorus->new_type = chorus->type; */ -/* chorus->new_depth_ms = chorus->depth_ms; */ -/* chorus->new_level = chorus->level; */ -/* chorus->new_speed_Hz = chorus->speed_Hz; */ -/* chorus->new_number_blocks = chorus->number_blocks; */ -/* return FLUID_FAILED; */ -} - - -void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in, - fluid_real_t *left_out, fluid_real_t *right_out) -{ - int sample_index; - int i; - fluid_real_t d_in, d_out; - - for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) { - - d_in = in[sample_index]; - d_out = 0.0f; - -# if 0 - /* Debug: Listen to the chorus signal only */ - left_out[sample_index]=0; - right_out[sample_index]=0; -#endif - - /* Write the current sample into the circular buffer */ - chorus->chorusbuf[chorus->counter] = d_in; - - for (i = 0; i < chorus->number_blocks; i++) { - int ii; - /* Calculate the delay in subsamples for the delay line of chorus block nr. */ - - /* The value in the lookup table is so, that this expression - * will always be positive. It will always include a number of - * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to - * remain positive at all times. */ - int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter - - chorus->lookup_tab[chorus->phase[i]]); - - int pos_samples = pos_subsamples/INTERPOLATION_SUBSAMPLES; - - /* modulo divide by INTERPOLATION_SUBSAMPLES */ - pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK; - - for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){ - /* Add the delayed signal to the chorus sum d_out Note: The - * delay in the delay line moves backwards for increasing - * delay!*/ - - /* The & in chorusbuf[...] is equivalent to a division modulo - MAX_SAMPLES, only faster. */ - d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK] - * chorus->sinc_table[ii][pos_subsamples]; - - pos_samples--; - }; - /* Cycle the phase of the modulating LFO */ - chorus->phase[i]++; - chorus->phase[i] %= (chorus->modulation_period_samples); - } /* foreach chorus block */ - - d_out *= chorus->level; - - /* Add the chorus sum d_out to output */ - left_out[sample_index] += d_out; - right_out[sample_index] += d_out; - - /* Move forward in circular buffer */ - chorus->counter++; - chorus->counter %= MAX_SAMPLES; - - } /* foreach sample */ -} - -/* Duplication of code ... (replaces sample data instead of mixing) */ -void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in, - fluid_real_t *left_out, fluid_real_t *right_out) -{ - int sample_index; - int i; - fluid_real_t d_in, d_out; - - for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) { - - d_in = in[sample_index]; - d_out = 0.0f; - -# if 0 - /* Debug: Listen to the chorus signal only */ - left_out[sample_index]=0; - right_out[sample_index]=0; -#endif - - /* Write the current sample into the circular buffer */ - chorus->chorusbuf[chorus->counter] = d_in; - - for (i = 0; i < chorus->number_blocks; i++) { - int ii; - /* Calculate the delay in subsamples for the delay line of chorus block nr. */ - - /* The value in the lookup table is so, that this expression - * will always be positive. It will always include a number of - * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to - * remain positive at all times. */ - int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter - - chorus->lookup_tab[chorus->phase[i]]); - - int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES; - - /* modulo divide by INTERPOLATION_SUBSAMPLES */ - pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK; - - for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){ - /* Add the delayed signal to the chorus sum d_out Note: The - * delay in the delay line moves backwards for increasing - * delay!*/ - - /* The & in chorusbuf[...] is equivalent to a division modulo - MAX_SAMPLES, only faster. */ - d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK] - * chorus->sinc_table[ii][pos_subsamples]; - - pos_samples--; - }; - /* Cycle the phase of the modulating LFO */ - chorus->phase[i]++; - chorus->phase[i] %= (chorus->modulation_period_samples); - } /* foreach chorus block */ - - d_out *= chorus->level; - - /* Store the chorus sum d_out to output */ - left_out[sample_index] = d_out; - right_out[sample_index] = d_out; - - /* Move forward in circular buffer */ - chorus->counter++; - chorus->counter %= MAX_SAMPLES; - - } /* foreach sample */ -} - -/* Purpose: - * - * Calculates a modulation waveform (sine) Its value ( modulo - * MAXSAMPLES) varies between 0 and depth*INTERPOLATION_SUBSAMPLES. - * Its period length is len. The waveform data will be used modulo - * MAXSAMPLES only. Since MAXSAMPLES is substracted from the waveform - * a couple of times here, the resulting (current position in - * buffer)-(waveform sample) will always be positive. - */ -void fluid_chorus_sine(int *buf, int len, int depth) -{ - int i; - double val; - - for (i = 0; i < len; i++) { - val = sin((double) i / (double)len * 2.0 * M_PI); - buf[i] = (int) ((1.0 + val) * (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES); - buf[i] -= 3* MAX_SAMPLES * INTERPOLATION_SUBSAMPLES; - // printf("%i %i\n",i,buf[i]); - } -} - -/* Purpose: - * Calculates a modulation waveform (triangle) - * See fluid_chorus_sine for comments. - */ -void fluid_chorus_triangle(int *buf, int len, int depth) -{ - int i=0; - int ii=len-1; - double val; - double val2; - - while (i <= ii){ - val = i * 2.0 / len * (double)depth * (double) INTERPOLATION_SUBSAMPLES; - val2= (int) (val + 0.5) - 3 * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES; - buf[i++] = (int) val2; - buf[ii--] = (int) val2; - } -} - -void -fluid_chorus_reset(fluid_chorus_t* chorus) -{ - fluid_chorus_init(chorus); -} diff --git a/libraries/fluidlite/src/fluid_chorus.h b/libraries/fluidlite/src/fluid_chorus.h deleted file mode 100644 index 9cf858d0b..000000000 --- a/libraries/fluidlite/src/fluid_chorus.h +++ /dev/null @@ -1,56 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#ifndef _FLUID_CHORUS_H -#define _FLUID_CHORUS_H - -#include "fluidsynth_priv.h" - - -typedef struct _fluid_chorus_t fluid_chorus_t; - -/* - * chorus - */ -fluid_chorus_t* new_fluid_chorus(fluid_real_t sample_rate); -void delete_fluid_chorus(fluid_chorus_t* chorus); -void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in, - fluid_real_t *left_out, fluid_real_t *right_out); -void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in, - fluid_real_t *left_out, fluid_real_t *right_out); - -int fluid_chorus_init(fluid_chorus_t* chorus); -void fluid_chorus_reset(fluid_chorus_t* chorus); - -void fluid_chorus_set_nr(fluid_chorus_t* chorus, int nr); -void fluid_chorus_set_level(fluid_chorus_t* chorus, fluid_real_t level); -void fluid_chorus_set_speed_Hz(fluid_chorus_t* chorus, fluid_real_t speed_Hz); -void fluid_chorus_set_depth_ms(fluid_chorus_t* chorus, fluid_real_t depth_ms); -void fluid_chorus_set_type(fluid_chorus_t* chorus, int type); -int fluid_chorus_update(fluid_chorus_t* chorus); -int fluid_chorus_get_nr(fluid_chorus_t* chorus); -fluid_real_t fluid_chorus_get_level(fluid_chorus_t* chorus); -fluid_real_t fluid_chorus_get_speed_Hz(fluid_chorus_t* chorus); -fluid_real_t fluid_chorus_get_depth_ms(fluid_chorus_t* chorus); -int fluid_chorus_get_type(fluid_chorus_t* chorus); - - -#endif /* _FLUID_CHORUS_H */ diff --git a/libraries/fluidlite/src/fluid_config.h b/libraries/fluidlite/src/fluid_config.h deleted file mode 100644 index 5482bbeda..000000000 --- a/libraries/fluidlite/src/fluid_config.h +++ /dev/null @@ -1,35 +0,0 @@ -/* Define to activate debugging message */ -#undef DEBUG - -/* Version number of package */ -#define VERSION "1.1.0" - -/* Define to 1 if your processor stores words with the most significant byte - first (like Motorola and SPARC, unlike Intel and VAX). */ -#undef WORDS_BIGENDIAN - -#define SF3_DISABLED 0 -#define SF3_XIPH_VORBIS 1 -#define SF3_STB_VORBIS 2 - -#ifndef SF3_SUPPORT -#define SF3_SUPPORT SF3_DISABLED -#endif - -#ifndef WITH_FLOAT -#define WITH_FLOAT 1 -#endif - -#define HAVE_STRING_H 1 -#define HAVE_STDLIB_H 1 -#define HAVE_STDIO_H 1 -#define HAVE_MATH_H 1 -#define HAVE_STDARG_H 1 -#define HAVE_FCNTL_H 1 -#define HAVE_LIMITS_H 1 - - -//#pragma warning(disable : 4244) -//#pragma warning(disable : 4101) -//#pragma warning(disable : 4305) -//#pragma warning(disable : 4996) diff --git a/libraries/fluidlite/src/fluid_conv.c b/libraries/fluidlite/src/fluid_conv.c deleted file mode 100644 index 7a48fbe2b..000000000 --- a/libraries/fluidlite/src/fluid_conv.c +++ /dev/null @@ -1,320 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#include "fluid_conv.h" - - -/* conversion tables */ -fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE]; -fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE]; -fluid_real_t fluid_atten2amp_tab[FLUID_ATTEN_AMP_SIZE]; -fluid_real_t fluid_posbp_tab[128]; -fluid_real_t fluid_concave_tab[128]; -fluid_real_t fluid_convex_tab[128]; -fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE]; - -/* - * void fluid_synth_init - * - * Does all the initialization for this module. - */ -void -fluid_conversion_config(void) -{ - int i; - double x; - - for (i = 0; i < FLUID_CENTS_HZ_SIZE; i++) { - fluid_ct2hz_tab[i] = (fluid_real_t) pow(2.0, (double) i / 1200.0); - } - - /* centibels to amplitude conversion - * Note: SF2.01 section 8.1.3: Initial attenuation range is - * between 0 and 144 dB. Therefore a negative attenuation is - * not allowed. - */ - for (i = 0; i < FLUID_CB_AMP_SIZE; i++) { - fluid_cb2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / -200.0); - } - - /* NOTE: EMU8k and EMU10k devices don't conform to the SoundFont - * specification in regards to volume attenuation. The below calculation - * is an approx. equation for generating a table equivelant to the - * cb_to_amp_table[] in tables.c of the TiMidity++ source, which I'm told - * was generated from device testing. By the spec this should be centibels. - */ - for (i = 0; i < FLUID_ATTEN_AMP_SIZE; i++) { - fluid_atten2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / FLUID_ATTEN_POWER_FACTOR); - } - - /* initialize the conversion tables (see fluid_mod.c - fluid_mod_get_value cases 4 and 8) */ - - /* concave unipolar positive transform curve */ - fluid_concave_tab[0] = 0.0; - fluid_concave_tab[127] = 1.0; - - /* convex unipolar positive transform curve */ - fluid_convex_tab[0] = 0; - fluid_convex_tab[127] = 1.0; - x = log10(128.0 / 127.0); - - /* There seems to be an error in the specs. The equations are - implemented according to the pictures on SF2.01 page 73. */ - - for (i = 1; i < 127; i++) { - x = -20.0 / 96.0 * log((i * i) / (127.0 * 127.0)) / log(10.0); - fluid_convex_tab[i] = (fluid_real_t) (1.0 - x); - fluid_concave_tab[127 - i] = (fluid_real_t) x; - } - - /* initialize the pan conversion table */ - x = PI / 2.0 / (FLUID_PAN_SIZE - 1.0); - for (i = 0; i < FLUID_PAN_SIZE; i++) { - fluid_pan_tab[i] = (fluid_real_t) sin(i * x); - } -} - -/* - * fluid_ct2hz - */ -fluid_real_t -fluid_ct2hz_real(fluid_real_t cents) -{ - if (cents < 0) - return (fluid_real_t) 1.0; - else if (cents < 900) { - return (fluid_real_t) 6.875 * fluid_ct2hz_tab[(int) (cents + 300)]; - } else if (cents < 2100) { - return (fluid_real_t) 13.75 * fluid_ct2hz_tab[(int) (cents - 900)]; - } else if (cents < 3300) { - return (fluid_real_t) 27.5 * fluid_ct2hz_tab[(int) (cents - 2100)]; - } else if (cents < 4500) { - return (fluid_real_t) 55.0 * fluid_ct2hz_tab[(int) (cents - 3300)]; - } else if (cents < 5700) { - return (fluid_real_t) 110.0 * fluid_ct2hz_tab[(int) (cents - 4500)]; - } else if (cents < 6900) { - return (fluid_real_t) 220.0 * fluid_ct2hz_tab[(int) (cents - 5700)]; - } else if (cents < 8100) { - return (fluid_real_t) 440.0 * fluid_ct2hz_tab[(int) (cents - 6900)]; - } else if (cents < 9300) { - return (fluid_real_t) 880.0 * fluid_ct2hz_tab[(int) (cents - 8100)]; - } else if (cents < 10500) { - return (fluid_real_t) 1760.0 * fluid_ct2hz_tab[(int) (cents - 9300)]; - } else if (cents < 11700) { - return (fluid_real_t) 3520.0 * fluid_ct2hz_tab[(int) (cents - 10500)]; - } else if (cents < 12900) { - return (fluid_real_t) 7040.0 * fluid_ct2hz_tab[(int) (cents - 11700)]; - } else if (cents < 14100) { - return (fluid_real_t) 14080.0 * fluid_ct2hz_tab[(int) (cents - 12900)]; - } else { - return (fluid_real_t) 1.0; /* some loony trying to make you deaf */ - } -} - -/* - * fluid_ct2hz - */ -fluid_real_t -fluid_ct2hz(fluid_real_t cents) -{ - /* Filter fc limit: SF2.01 page 48 # 8 */ - if (cents >= 13500){ - cents = 13500; /* 20 kHz */ - } else if (cents < 1500){ - cents = 1500; /* 20 Hz */ - } - return fluid_ct2hz_real(cents); -} - -/* - * fluid_cb2amp - * - * in: a value between 0 and 960, 0 is no attenuation - * out: a value between 1 and 0 - */ -fluid_real_t -fluid_cb2amp(fluid_real_t cb) -{ - /* - * cb: an attenuation in 'centibels' (1/10 dB) - * SF2.01 page 49 # 48 limits it to 144 dB. - * 96 dB is reasonable for 16 bit systems, 144 would make sense for 24 bit. - */ - - /* minimum attenuation: 0 dB */ - if (cb < 0) { - return 1.0; - } - if (cb >= FLUID_CB_AMP_SIZE) { - return 0.0; - } - return fluid_cb2amp_tab[(int) cb]; -} - -/* - * fluid_atten2amp - * - * in: a value between 0 and 1440, 0 is no attenuation - * out: a value between 1 and 0 - * - * Note: Volume attenuation is supposed to be centibels but EMU8k/10k don't - * follow this. Thats the reason for separate fluid_cb2amp and fluid_atten2amp. - */ -fluid_real_t -fluid_atten2amp(fluid_real_t atten) -{ - if (atten < 0) return 1.0; - else if (atten >= FLUID_ATTEN_AMP_SIZE) return 0.0; - else return fluid_atten2amp_tab[(int) atten]; -} - -/* - * fluid_tc2sec_delay - */ -fluid_real_t -fluid_tc2sec_delay(fluid_real_t tc) -{ - /* SF2.01 section 8.1.2 items 21, 23, 25, 33 - * SF2.01 section 8.1.3 items 21, 23, 25, 33 - * - * The most negative number indicates a delay of 0. Range is limited - * from -12000 to 5000 */ - if (tc <= -32768.0f) { - return (fluid_real_t) 0.0f; - }; - if (tc < -12000.) { - tc = (fluid_real_t) -12000.0f; - } - if (tc > 5000.0f) { - tc = (fluid_real_t) 5000.0f; - } - return (fluid_real_t) pow(2.0, (double) tc / 1200.0); -} - -/* - * fluid_tc2sec_attack - */ -fluid_real_t -fluid_tc2sec_attack(fluid_real_t tc) -{ - /* SF2.01 section 8.1.2 items 26, 34 - * SF2.01 section 8.1.3 items 26, 34 - * The most negative number indicates a delay of 0 - * Range is limited from -12000 to 8000 */ - if (tc<=-32768.){return (fluid_real_t) 0.0;}; - if (tc<-12000.){tc=(fluid_real_t) -12000.0;}; - if (tc>8000.){tc=(fluid_real_t) 8000.0;}; - return (fluid_real_t) pow(2.0, (double) tc / 1200.0); -} - -/* - * fluid_tc2sec - */ -fluid_real_t -fluid_tc2sec(fluid_real_t tc) -{ - /* No range checking here! */ - return (fluid_real_t) pow(2.0, (double) tc / 1200.0); -} - -/* - * fluid_tc2sec_release - */ -fluid_real_t -fluid_tc2sec_release(fluid_real_t tc) -{ - /* SF2.01 section 8.1.2 items 30, 38 - * SF2.01 section 8.1.3 items 30, 38 - * No 'most negative number' rule here! - * Range is limited from -12000 to 8000 */ - if (tc<=-32768.){return (fluid_real_t) 0.0;}; - if (tc<-12000.){tc=(fluid_real_t) -12000.0;}; - if (tc>8000.){tc=(fluid_real_t) 8000.0;}; - return (fluid_real_t) pow(2.0, (double) tc / 1200.0); -} - -/* - * fluid_act2hz - * - * Convert from absolute cents to Hertz - */ -fluid_real_t -fluid_act2hz(fluid_real_t c) -{ - return (fluid_real_t) (8.176 * pow(2.0, (double) c / 1200.0)); -} - -/* - * fluid_hz2ct - * - * Convert from Hertz to cents - */ -fluid_real_t -fluid_hz2ct(fluid_real_t f) -{ - return (fluid_real_t) (6900 + 1200 * log(f / 440.0) / log(2.0)); -} - -/* - * fluid_pan - */ -fluid_real_t -fluid_pan(fluid_real_t c, int left) -{ - if (left) { - c = -c; - } - if (c < -500) { - return (fluid_real_t) 0.0; - } else if (c > 500) { - return (fluid_real_t) 1.0; - } else { - return fluid_pan_tab[(int) (c + 500)]; - } -} - -/* - * fluid_concave - */ -fluid_real_t -fluid_concave(fluid_real_t val) -{ - if (val < 0) { - return 0; - } else if (val > 127) { - return 1; - } - return fluid_concave_tab[(int) val]; -} - -/* - * fluid_convex - */ -fluid_real_t -fluid_convex(fluid_real_t val) -{ - if (val < 0) { - return 0; - } else if (val > 127) { - return 1; - } - return fluid_convex_tab[(int) val]; -} diff --git a/libraries/fluidlite/src/fluid_conv.h b/libraries/fluidlite/src/fluid_conv.h deleted file mode 100644 index 33b150811..000000000 --- a/libraries/fluidlite/src/fluid_conv.h +++ /dev/null @@ -1,63 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUID_CONV_H -#define _FLUID_CONV_H - -#include "fluidsynth_priv.h" - -#define FLUID_CENTS_HZ_SIZE 1200 -#define FLUID_VEL_CB_SIZE 128 -#define FLUID_CB_AMP_SIZE 961 -#define FLUID_ATTEN_AMP_SIZE 1441 -#define FLUID_PAN_SIZE 1002 - -/* EMU 8k/10k don't follow spec in regards to volume attenuation. - * This factor is used in the equation pow (10.0, cb / FLUID_ATTEN_POWER_FACTOR). - * By the standard this should be -200.0. */ -/* 07/11/2008 modified by S. Christian Collins for increased velocity sensitivity. Now it equals the response of EMU10K1 programming.*/ -#define FLUID_ATTEN_POWER_FACTOR (-200.0) /* was (-531.509)*/ - -void fluid_conversion_config(void); - -fluid_real_t fluid_ct2hz_real(fluid_real_t cents); -fluid_real_t fluid_ct2hz(fluid_real_t cents); -fluid_real_t fluid_cb2amp(fluid_real_t cb); -fluid_real_t fluid_atten2amp(fluid_real_t atten); -fluid_real_t fluid_tc2sec(fluid_real_t tc); -fluid_real_t fluid_tc2sec_delay(fluid_real_t tc); -fluid_real_t fluid_tc2sec_attack(fluid_real_t tc); -fluid_real_t fluid_tc2sec_release(fluid_real_t tc); -fluid_real_t fluid_act2hz(fluid_real_t c); -fluid_real_t fluid_hz2ct(fluid_real_t c); -fluid_real_t fluid_pan(fluid_real_t c, int left); -fluid_real_t fluid_concave(fluid_real_t val); -fluid_real_t fluid_convex(fluid_real_t val); - -extern fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE]; -extern fluid_real_t fluid_vel2cb_tab[FLUID_VEL_CB_SIZE]; -extern fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE]; -extern fluid_real_t fluid_posbp_tab[128]; -extern fluid_real_t fluid_concave_tab[128]; -extern fluid_real_t fluid_convex_tab[128]; -extern fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE]; - - -#endif /* _FLUID_CONV_H */ diff --git a/libraries/fluidlite/src/fluid_defsfont.c b/libraries/fluidlite/src/fluid_defsfont.c deleted file mode 100644 index 86bb04a69..000000000 --- a/libraries/fluidlite/src/fluid_defsfont.c +++ /dev/null @@ -1,3432 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * SoundFont file loading code borrowed from Smurf SoundFont Editor - * Copyright (C) 1999-2001 Josh Green - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#include "fluid_defsfont.h" -#include "fluid_sfont.h" -/* Todo: Get rid of that 'include' */ -#include "fluid_sys.h" - -#if SF3_SUPPORT == SF3_XIPH_VORBIS -#include "vorbis/codec.h" -#include "vorbis/vorbisenc.h" -#include "vorbis/vorbisfile.h" - -struct VorbisData { - int pos; // current position in audio->data() - char* data; - int datasize; -}; - -static struct VorbisData vorbisData; - -static size_t ovRead(void* ptr, size_t size, size_t nmemb, void* datasource); -static int ovSeek(void* datasource, ogg_int64_t offset, int whence); -static long ovTell(void* datasource); - -static ov_callbacks ovCallbacks = { ovRead, ovSeek, 0, ovTell }; - -//--------------------------------------------------------- -// ovRead -//--------------------------------------------------------- - -static size_t ovRead(void* ptr, size_t size, size_t nmemb, void* datasource) -{ - struct VorbisData* vd = (struct VorbisData*)datasource; - size_t n = size * nmemb; - if (vd->datasize < (int)vd->pos + (int)n) - n = vd->datasize - vd->pos; - if (n) { - const char* src = vd->data + vd->pos; - memcpy(ptr, src, n); - vd->pos += n; - } - - return n; -} - -//--------------------------------------------------------- -// ovSeek -//--------------------------------------------------------- - -static int ovSeek(void* datasource, ogg_int64_t offset, int whence) -{ - struct VorbisData* vd = (struct VorbisData*)datasource; - switch(whence) { - case SEEK_SET: - vd->pos = offset; - break; - case SEEK_CUR: - vd->pos += offset; - break; - case SEEK_END: - vd->pos = vd->datasize - offset; - break; - } - return 0; -} - -//--------------------------------------------------------- -// ovTell -//--------------------------------------------------------- - -static long ovTell(void* datasource) -{ - struct VorbisData* vd = (struct VorbisData*)datasource; - return vd->pos; -} -#endif - -#if SF3_SUPPORT == SF3_STB_VORBIS -#define STB_VORBIS_HEADER_ONLY -#include "stb_vorbis.c" -#endif - -/*************************************************************** - * - * SFONT LOADER - */ - -static void* default_fopen(fluid_fileapi_t *fileapi, const char * path) -{ - return FLUID_FOPEN(path, "rb"); -} - -static int default_fclose(void* handle) -{ - return FLUID_FCLOSE((FILE *)handle); -} - -static long default_ftell(void* handle) -{ - return FLUID_FTELL((FILE *)handle); -} - -static int safe_fread(void *buf, int count, void* handle) -{ - if (FLUID_FREAD(buf, count, 1, (FILE *)handle) != 1) - { - if (feof ((FILE *)handle)) - gerr (ErrEof, _("EOF while attemping to read %d bytes"), count); - else - FLUID_LOG (FLUID_ERR, _("File read failed")); - - return FLUID_FAILED; - } - return FLUID_OK; -} - -static int safe_fseek(void* handle, long ofs, int whence) -{ - if (FLUID_FSEEK((FILE *)handle, ofs, whence) != 0) { - FLUID_LOG (FLUID_ERR, _("File seek failed with offset = %ld and whence = %d"), ofs, whence); - return FLUID_FAILED; - } - return FLUID_OK; -} - -static const fluid_fileapi_t default_fileapi = -{ - NULL, - NULL, - default_fopen, - safe_fread, - safe_fseek, - default_fclose, - default_ftell -}; - -static fluid_fileapi_t* fluid_default_fileapi = (fluid_fileapi_t*)&default_fileapi; - -void fluid_init_default_fileapi(fluid_fileapi_t* fileapi) { - fileapi->data = NULL; - fileapi->free = NULL; - fileapi->fopen = default_fopen; - fileapi->fread = safe_fread; - fileapi->fseek = safe_fseek; - fileapi->fclose = default_fclose; - fileapi->ftell = default_ftell; -} - -void fluid_set_default_fileapi(fluid_fileapi_t* fileapi) { - fluid_fileapi_delete(fluid_default_fileapi); - fluid_default_fileapi = fileapi == NULL ? (fluid_fileapi_t*)&default_fileapi : fileapi; -} - -fluid_sfloader_t* new_fluid_defsfloader() -{ - fluid_sfloader_t* loader; - - loader = FLUID_NEW(fluid_sfloader_t); - if (loader == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - loader->data = NULL; - loader->fileapi = fluid_default_fileapi; - loader->free = delete_fluid_defsfloader; - loader->load = fluid_defsfloader_load; - - return loader; -} - -int delete_fluid_defsfloader(fluid_sfloader_t* loader) -{ - if (loader) { - FLUID_FREE(loader); - } - return FLUID_OK; -} - -fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename) -{ - fluid_defsfont_t* defsfont; - fluid_sfont_t* sfont; - - defsfont = new_fluid_defsfont(); - - if (defsfont == NULL) { - return NULL; - } - - sfont = loader->data ? (fluid_sfont_t*)loader->data : FLUID_NEW(fluid_sfont_t); - if (sfont == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - sfont->data = defsfont; - sfont->free = fluid_defsfont_sfont_delete; - sfont->get_name = fluid_defsfont_sfont_get_name; - sfont->get_preset = fluid_defsfont_sfont_get_preset; - sfont->iteration_start = fluid_defsfont_sfont_iteration_start; - sfont->iteration_next = fluid_defsfont_sfont_iteration_next; - - if (fluid_defsfont_load(defsfont, filename, loader->fileapi) == FLUID_FAILED) { - delete_fluid_defsfont(defsfont); - return NULL; - } - - return sfont; -} - - - -/*************************************************************** - * - * PUBLIC INTERFACE - */ - -int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont) -{ - if (delete_fluid_defsfont(sfont->data) != 0) { - return -1; - } - FLUID_FREE(sfont); - return 0; -} - -char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont) -{ - return fluid_defsfont_get_name((fluid_defsfont_t*) sfont->data); -} - -fluid_preset_t* -fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum) -{ - fluid_preset_t* preset; - fluid_defpreset_t* defpreset; - - defpreset = fluid_defsfont_get_preset((fluid_defsfont_t*) sfont->data, bank, prenum); - - if (defpreset == NULL) { - return NULL; - } - - preset = FLUID_NEW(fluid_preset_t); - if (preset == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - preset->sfont = sfont; - preset->data = defpreset; - preset->free = fluid_defpreset_preset_delete; - preset->get_name = fluid_defpreset_preset_get_name; - preset->get_banknum = fluid_defpreset_preset_get_banknum; - preset->get_num = fluid_defpreset_preset_get_num; - preset->noteon = fluid_defpreset_preset_noteon; - preset->notify = NULL; - - return preset; -} - -void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont) -{ - fluid_defsfont_iteration_start((fluid_defsfont_t*) sfont->data); -} - -int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset) -{ - preset->free = fluid_defpreset_preset_delete; - preset->get_name = fluid_defpreset_preset_get_name; - preset->get_banknum = fluid_defpreset_preset_get_banknum; - preset->get_num = fluid_defpreset_preset_get_num; - preset->noteon = fluid_defpreset_preset_noteon; - preset->notify = NULL; - - return fluid_defsfont_iteration_next((fluid_defsfont_t*) sfont->data, preset); -} - -int fluid_defpreset_preset_delete(fluid_preset_t* preset) -{ - FLUID_FREE(preset); - - /* TODO: free modulators */ - - return 0; -} - -char* fluid_defpreset_preset_get_name(fluid_preset_t* preset) -{ - return fluid_defpreset_get_name((fluid_defpreset_t*) preset->data); -} - -int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset) -{ - return fluid_defpreset_get_banknum((fluid_defpreset_t*) preset->data); -} - -int fluid_defpreset_preset_get_num(fluid_preset_t* preset) -{ - return fluid_defpreset_get_num((fluid_defpreset_t*) preset->data); -} - -int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, - int chan, int key, int vel) -{ - return fluid_defpreset_noteon((fluid_defpreset_t*) preset->data, synth, chan, key, vel); -} - - - - -/*************************************************************** - * - * SFONT - */ - -/* - * new_fluid_defsfont - */ -fluid_defsfont_t* new_fluid_defsfont() -{ - fluid_defsfont_t* sfont; - - sfont = FLUID_NEW(fluid_defsfont_t); - if (sfont == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - sfont->filename = NULL; - sfont->samplepos = 0; - sfont->samplesize = 0; - sfont->sample = NULL; - sfont->sampledata = NULL; - sfont->preset = NULL; - - return sfont; -} - -/* - * delete_fluid_defsfont - */ -int delete_fluid_defsfont(fluid_defsfont_t* sfont) -{ - fluid_list_t *list; - fluid_defpreset_t* preset; - fluid_sample_t* sample; - - /* Check that no samples are currently used */ - for (list = sfont->sample; list; list = fluid_list_next(list)) { - sample = (fluid_sample_t*) fluid_list_get(list); - if (fluid_sample_refcount(sample) != 0) { - return -1; - } - } - - if (sfont->filename != NULL) { - FLUID_FREE(sfont->filename); - } - - for (list = sfont->sample; list; list = fluid_list_next(list)) { - delete_fluid_sample((fluid_sample_t*) fluid_list_get(list)); - } - - if (sfont->sample) { - delete_fluid_list(sfont->sample); - } - - if (sfont->sampledata != NULL) { - FLUID_FREE(sfont->sampledata); - } - - preset = sfont->preset; - while (preset != NULL) { - sfont->preset = preset->next; - delete_fluid_defpreset(preset); - preset = sfont->preset; - } - - FLUID_FREE(sfont); - return FLUID_OK; -} - -/* - * fluid_defsfont_get_name - */ -char* fluid_defsfont_get_name(fluid_defsfont_t* sfont) -{ - return sfont->filename; -} - -void (*preset_callback) (unsigned int bank, unsigned int num, char* name)=NULL; -void fluid_synth_set_preset_callback(void* callback) -{ - preset_callback=callback; -} - -/* - * fluid_defsfont_load - */ -int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file, fluid_fileapi_t* fapi) -{ - SFData* sfdata; - fluid_list_t *p; - SFPreset* sfpreset; - SFSample* sfsample; - fluid_sample_t* sample; - fluid_defpreset_t* preset; - - sfont->filename = FLUID_MALLOC(1 + FLUID_STRLEN(file)); - if (sfont->filename == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return FLUID_FAILED; - } - FLUID_STRCPY(sfont->filename, file); - - /* The actual loading is done in the sfont and sffile files */ - sfdata = sfload_file(file, fapi); - if (sfdata == NULL) { - FLUID_LOG(FLUID_ERR, "Couldn't load soundfont file"); - return FLUID_FAILED; - } - - /* Keep track of the position and size of the sample data because - it's loaded separately (and might be unoaded/reloaded in future) */ - sfont->samplepos = sfdata->samplepos; - sfont->samplesize = sfdata->samplesize; - - /* load sample data in one block */ - if (fluid_defsfont_load_sampledata(sfont, fapi) != FLUID_OK) - goto err_exit; - - /* Create all the sample headers */ - p = sfdata->sample; - while (p != NULL) { - sfsample = (SFSample *) p->data; - - sample = new_fluid_sample(); - if (sample == NULL) goto err_exit; - - if (fluid_sample_import_sfont(sample, sfsample, sfont) != FLUID_OK) - goto err_exit; - - fluid_defsfont_add_sample(sfont, sample); - fluid_voice_optimize_sample(sample); - p = fluid_list_next(p); - } - - /* Load all the presets */ - p = sfdata->preset; - while (p != NULL) { - sfpreset = (SFPreset *) p->data; - preset = new_fluid_defpreset(sfont); - if (preset == NULL) goto err_exit; - - if (fluid_defpreset_import_sfont(preset, sfpreset, sfont) != FLUID_OK) - goto err_exit; - - fluid_defsfont_add_preset(sfont, preset); - if(preset_callback) preset_callback(preset->bank,preset->num,preset->name); - p = fluid_list_next(p); - } - sfont_close (sfdata, fapi); - - return FLUID_OK; - -err_exit: - sfont_close (sfdata, fapi); - return FLUID_FAILED; -} - -/* fluid_defsfont_add_sample - * - * Add a sample to the SoundFont - */ -int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample) -{ - sfont->sample = fluid_list_append(sfont->sample, sample); - return FLUID_OK; -} - -/* fluid_defsfont_add_preset - * - * Add a preset to the SoundFont - */ -int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset) -{ - fluid_defpreset_t *cur, *prev; - if (sfont->preset == NULL) { - preset->next = NULL; - sfont->preset = preset; - } else { - /* sort them as we go along. very basic sorting trick. */ - cur = sfont->preset; - prev = NULL; - while (cur != NULL) { - if ((preset->bank < cur->bank) - || ((preset->bank == cur->bank) && (preset->num < cur->num))) { - if (prev == NULL) { - preset->next = cur; - sfont->preset = preset; - } else { - preset->next = cur; - prev->next = preset; - } - return FLUID_OK; - } - prev = cur; - cur = cur->next; - } - preset->next = NULL; - prev->next = preset; - } - return FLUID_OK; -} - -/* - * fluid_defsfont_load_sampledata - */ -int -fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont, fluid_fileapi_t* fapi) -{ - fluid_file fd; - unsigned short endian; - fd = fapi->fopen(fapi, sfont->filename); - if (fd == NULL) { - FLUID_LOG(FLUID_ERR, "Can't open soundfont file"); - return FLUID_FAILED; - } - if (fapi->fseek(fd, sfont->samplepos, SEEK_SET) == FLUID_FAILED) { - perror("error"); - FLUID_LOG(FLUID_ERR, "Failed to seek position in data file"); - return FLUID_FAILED; - } - sfont->sampledata = (short*) FLUID_MALLOC(sfont->samplesize); - if (sfont->sampledata == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return FLUID_FAILED; - } - if (fapi->fread(sfont->sampledata, sfont->samplesize, fd) == FLUID_FAILED) { - FLUID_LOG(FLUID_ERR, "Failed to read sample data"); - return FLUID_FAILED; - } - fapi->fclose(fd); - - /* I'm not sure this endian test is waterproof... */ - endian = 0x0100; - - /* If this machine is big endian, the sample have to byte swapped */ - if (((char *) &endian)[0]) { - unsigned char* cbuf; - unsigned char hi, lo; - unsigned int i, j; - short s; - cbuf = (unsigned char*) sfont->sampledata; - for (i = 0, j = 0; j < sfont->samplesize; i++) { - lo = cbuf[j++]; - hi = cbuf[j++]; - s = (hi << 8) | lo; - sfont->sampledata[i] = s; - } - } - return FLUID_OK; -} - -/* - * fluid_defsfont_get_sample - */ -fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s) -{ - fluid_list_t* list; - fluid_sample_t* sample; - - for (list = sfont->sample; list; list = fluid_list_next(list)) { - - sample = (fluid_sample_t*) fluid_list_get(list); - - if (FLUID_STRCMP(sample->name, s) == 0) { - -#if SF3_SUPPORT - if (sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS) { - short *sampledata = NULL; - int sampleframes = 0; - -#if SF3_SUPPORT == SF3_XIPH_VORBIS - int sampledata_size = 0; - OggVorbis_File vf; - - vorbisData.pos = 0; - vorbisData.data = (char*)sample->data + sample->start; - vorbisData.datasize = sample->end + 1 - sample->start; - - if (ov_open_callbacks(&vorbisData, &vf, 0, 0, ovCallbacks) == 0) { -#define BUFFER_SIZE 4096 - int bytes_read = 0; - int section = 0; - for (;;) { - // allocate additional memory for samples - sampledata = realloc(sampledata, sampledata_size + BUFFER_SIZE); - bytes_read = ov_read(&vf, (char*)sampledata + sampledata_size, BUFFER_SIZE, 0, sizeof(short), 1, §ion); - if (bytes_read > 0) { - sampledata_size += bytes_read; - } else { - // shrink sampledata to actual size - sampledata = realloc(sampledata, sampledata_size); - break; - } - } - - ov_clear(&vf); - } - - // because we actually need num of frames so we should divide num of bytes to frame size - sampleframes = sampledata_size / sizeof(short); -#endif - -#if SF3_SUPPORT == SF3_STB_VORBIS - const uint8 *data = (uint8*)sample->data + sample->start; - const int datasize = sample->end + 1 - sample->start; - - int channels; - sampleframes = stb_vorbis_decode_memory(data, datasize, &channels, NULL, &sampledata); -#endif - // point sample data to uncompressed data stream - sample->data = sampledata; - sample->start = 0; - sample->end = sampleframes - 1; - - /* loop is fowled?? (cluck cluck :) */ - if (sample->loopend > sample->end || - sample->loopstart >= sample->loopend || - sample->loopstart <= sample->start) { - /* can pad loop by 8 samples and ensure at least 4 for loop (2*8+4) */ - if ((sample->end - sample->start) >= 20) { - sample->loopstart = sample->start + 8; - sample->loopend = sample->end - 8; - } else { /* loop is fowled, sample is tiny (can't pad 8 samples) */ - sample->loopstart = sample->start + 1; - sample->loopend = sample->end - 1; - } - } - - sample->sampletype &= ~FLUID_SAMPLETYPE_OGG_VORBIS; - sample->sampletype |= FLUID_SAMPLETYPE_OGG_VORBIS_UNPACKED; - - fluid_voice_optimize_sample(sample); - } -#endif - - return sample; - } - } - - return NULL; -} - -/* - * fluid_defsfont_get_preset - */ -fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int num) -{ - fluid_defpreset_t* preset = sfont->preset; - while (preset != NULL) { - if ((preset->bank == bank) && ((preset->num == num))) { - return preset; - } - preset = preset->next; - } - return NULL; -} - -/* - * fluid_defsfont_iteration_start - */ -void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont) -{ - sfont->iter_cur = sfont->preset; -} - -/* - * fluid_defsfont_iteration_next - */ -int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset) -{ - if (sfont->iter_cur == NULL) { - return 0; - } - - preset->data = (void*) sfont->iter_cur; - sfont->iter_cur = fluid_defpreset_next(sfont->iter_cur); - return 1; -} - -/*************************************************************** - * - * PRESET - */ - -/* - * new_fluid_defpreset - */ -fluid_defpreset_t* -new_fluid_defpreset(fluid_defsfont_t* sfont) -{ - fluid_defpreset_t* preset = FLUID_NEW(fluid_defpreset_t); - if (preset == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - preset->next = NULL; - preset->sfont = sfont; - preset->name[0] = 0; - preset->bank = 0; - preset->num = 0; - preset->global_zone = NULL; - preset->zone = NULL; - return preset; -} - -/* - * delete_fluid_defpreset - */ -int -delete_fluid_defpreset(fluid_defpreset_t* preset) -{ - int err = FLUID_OK; - fluid_preset_zone_t* zone; - if (preset->global_zone != NULL) { - if (delete_fluid_preset_zone(preset->global_zone) != FLUID_OK) { - err = FLUID_FAILED; - } - preset->global_zone = NULL; - } - zone = preset->zone; - while (zone != NULL) { - preset->zone = zone->next; - if (delete_fluid_preset_zone(zone) != FLUID_OK) { - err = FLUID_FAILED; - } - zone = preset->zone; - } - FLUID_FREE(preset); - return err; -} - -int -fluid_defpreset_get_banknum(fluid_defpreset_t* preset) -{ - return preset->bank; -} - -int -fluid_defpreset_get_num(fluid_defpreset_t* preset) -{ - return preset->num; -} - -char* -fluid_defpreset_get_name(fluid_defpreset_t* preset) -{ - return preset->name; -} - -/* - * fluid_defpreset_next - */ -fluid_defpreset_t* -fluid_defpreset_next(fluid_defpreset_t* preset) -{ - return preset->next; -} - - -/* - * fluid_defpreset_noteon - */ -int -fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) -{ - fluid_preset_zone_t *preset_zone, *global_preset_zone; - fluid_inst_t* inst; - fluid_inst_zone_t *inst_zone, *global_inst_zone; - fluid_sample_t* sample; - fluid_voice_t* voice; - fluid_mod_t * mod; - fluid_mod_t * mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */ - int mod_list_count; - int i; - - global_preset_zone = fluid_defpreset_get_global_zone(preset); - - /* run thru all the zones of this preset */ - preset_zone = fluid_defpreset_get_zone(preset); - while (preset_zone != NULL) { - - /* check if the note falls into the key and velocity range of this - preset */ - if (fluid_preset_zone_inside_range(preset_zone, key, vel)) { - - inst = fluid_preset_zone_get_inst(preset_zone); - global_inst_zone = fluid_inst_get_global_zone(inst); - - /* run thru all the zones of this instrument */ - inst_zone = fluid_inst_get_zone(inst); - while (inst_zone != NULL) { - - /* make sure this instrument zone has a valid sample */ - sample = fluid_inst_zone_get_sample(inst_zone); - if (fluid_sample_in_rom(sample) || (sample == NULL)) { - inst_zone = fluid_inst_zone_next(inst_zone); - continue; - } - - /* check if the note falls into the key and velocity range of this - instrument */ - - if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) { - - /* this is a good zone. allocate a new synthesis process and - initialize it */ - - voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel); - if (voice == NULL) { - return FLUID_FAILED; - } - - - /* Instrument level, generators */ - - for (i = 0; i < GEN_LAST; i++) { - - /* SF 2.01 section 9.4 'bullet' 4: - * - * A generator in a local instrument zone supersedes a - * global instrument zone generator. Both cases supersede - * the default generator -> voice_gen_set */ - - if (inst_zone->gen[i].flags){ - fluid_voice_gen_set(voice, i, inst_zone->gen[i].val); - - } else if ((global_inst_zone != NULL) && (global_inst_zone->gen[i].flags)) { - fluid_voice_gen_set(voice, i, global_inst_zone->gen[i].val); - - } else { - /* The generator has not been defined in this instrument. - * Do nothing, leave it at the default. - */ - } - - } /* for all generators */ - - /* global instrument zone, modulators: Put them all into a - * list. */ - - mod_list_count = 0; - - if (global_inst_zone){ - mod = global_inst_zone->mod; - while (mod){ - mod_list[mod_list_count++] = mod; - mod = mod->next; - } - } - - /* local instrument zone, modulators. - * Replace modulators with the same definition in the list: - * SF 2.01 page 69, 'bullet' 8 - */ - mod = inst_zone->mod; - - while (mod){ - - /* 'Identical' modulators will be deleted by setting their - * list entry to NULL. The list length is known, NULL - * entries will be ignored later. SF2.01 section 9.5.1 - * page 69, 'bullet' 3 defines 'identical'. */ - - for (i = 0; i < mod_list_count; i++){ - if (mod_list[i] && fluid_mod_test_identity(mod,mod_list[i])){ - mod_list[i] = NULL; - } - } - - /* Finally add the new modulator to to the list. */ - mod_list[mod_list_count++] = mod; - mod = mod->next; - } - - /* Add instrument modulators (global / local) to the voice. */ - for (i = 0; i < mod_list_count; i++){ - - mod = mod_list[i]; - - if (mod != NULL){ /* disabled modulators CANNOT be skipped. */ - - /* Instrument modulators -supersede- existing (default) - * modulators. SF 2.01 page 69, 'bullet' 6 */ - fluid_voice_add_mod(voice, mod, FLUID_VOICE_OVERWRITE); - } - } - - /* Preset level, generators */ - - for (i = 0; i < GEN_LAST; i++) { - - /* SF 2.01 section 8.5 page 58: If some generators are - * encountered at preset level, they should be ignored */ - if ((i != GEN_STARTADDROFS) - && (i != GEN_ENDADDROFS) - && (i != GEN_STARTLOOPADDROFS) - && (i != GEN_ENDLOOPADDROFS) - && (i != GEN_STARTADDRCOARSEOFS) - && (i != GEN_ENDADDRCOARSEOFS) - && (i != GEN_STARTLOOPADDRCOARSEOFS) - && (i != GEN_KEYNUM) - && (i != GEN_VELOCITY) - && (i != GEN_ENDLOOPADDRCOARSEOFS) - && (i != GEN_SAMPLEMODE) - && (i != GEN_EXCLUSIVECLASS) - && (i != GEN_OVERRIDEROOTKEY)) { - - /* SF 2.01 section 9.4 'bullet' 9: A generator in a - * local preset zone supersedes a global preset zone - * generator. The effect is -added- to the destination - * summing node -> voice_gen_incr */ - - if (preset_zone->gen[i].flags) { - fluid_voice_gen_incr(voice, i, preset_zone->gen[i].val); - } else if ((global_preset_zone != NULL) && global_preset_zone->gen[i].flags) { - fluid_voice_gen_incr(voice, i, global_preset_zone->gen[i].val); - } else { - /* The generator has not been defined in this preset - * Do nothing, leave it unchanged. - */ - } - } /* if available at preset level */ - } /* for all generators */ - - - /* Global preset zone, modulators: put them all into a - * list. */ - mod_list_count = 0; - if (global_preset_zone){ - mod = global_preset_zone->mod; - while (mod){ - mod_list[mod_list_count++] = mod; - mod = mod->next; - } - } - - /* Process the modulators of the local preset zone. Kick - * out all identical modulators from the global preset zone - * (SF 2.01 page 69, second-last bullet) */ - - mod = preset_zone->mod; - while (mod){ - for (i = 0; i < mod_list_count; i++){ - if (mod_list[i] && fluid_mod_test_identity(mod,mod_list[i])){ - mod_list[i] = NULL; - } - } - - /* Finally add the new modulator to the list. */ - mod_list[mod_list_count++] = mod; - mod = mod->next; - } - - /* Add preset modulators (global / local) to the voice. */ - for (i = 0; i < mod_list_count; i++){ - mod = mod_list[i]; - if ((mod != NULL) && (mod->amount != 0)) { /* disabled modulators can be skipped. */ - - /* Preset modulators -add- to existing instrument / - * default modulators. SF2.01 page 70 first bullet on - * page */ - fluid_voice_add_mod(voice, mod, FLUID_VOICE_ADD); - } - } - - /* add the synthesis process to the synthesis loop. */ - fluid_synth_start_voice(synth, voice); - - /* Store the ID of the first voice that was created by this noteon event. - * Exclusive class may only terminate older voices. - * That avoids killing voices, which have just been created. - * (a noteon event can create several voice processes with the same exclusive - * class - for example when using stereo samples) - */ - } - - inst_zone = fluid_inst_zone_next(inst_zone); - } - } - preset_zone = fluid_preset_zone_next(preset_zone); - } - - return FLUID_OK; -} - -/* - * fluid_defpreset_set_global_zone - */ -int -fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone) -{ - preset->global_zone = zone; - return FLUID_OK; -} - -/* - * fluid_defpreset_import_sfont - */ -int -fluid_defpreset_import_sfont(fluid_defpreset_t* preset, - SFPreset* sfpreset, - fluid_defsfont_t* sfont) -{ - fluid_list_t *p; - SFZone* sfzone; - fluid_preset_zone_t* zone; - int count; - char zone_name[256]; - if (FLUID_STRLEN(sfpreset->name) > 0) { - FLUID_STRCPY(preset->name, sfpreset->name); - } else { - FLUID_SPRINTF(preset->name, "Bank%d,Preset%d", sfpreset->bank, sfpreset->prenum); - } - preset->bank = sfpreset->bank; - preset->num = sfpreset->prenum; - p = sfpreset->zone; - count = 0; - while (p != NULL) { - sfzone = (SFZone *) p->data; - FLUID_SPRINTF(zone_name, "%s/%d", preset->name, count); - zone = new_fluid_preset_zone(zone_name); - if (zone == NULL) { - return FLUID_FAILED; - } - if (fluid_preset_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) { - return FLUID_FAILED; - } - if ((count == 0) && (fluid_preset_zone_get_inst(zone) == NULL)) { - fluid_defpreset_set_global_zone(preset, zone); - } else if (fluid_defpreset_add_zone(preset, zone) != FLUID_OK) { - return FLUID_FAILED; - } - p = fluid_list_next(p); - count++; - } - return FLUID_OK; -} - -/* - * fluid_defpreset_add_zone - */ -int -fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone) -{ - if (preset->zone == NULL) { - zone->next = NULL; - preset->zone = zone; - } else { - zone->next = preset->zone; - preset->zone = zone; - } - return FLUID_OK; -} - -/* - * fluid_defpreset_get_zone - */ -fluid_preset_zone_t* -fluid_defpreset_get_zone(fluid_defpreset_t* preset) -{ - return preset->zone; -} - -/* - * fluid_defpreset_get_global_zone - */ -fluid_preset_zone_t* -fluid_defpreset_get_global_zone(fluid_defpreset_t* preset) -{ - return preset->global_zone; -} - -/* - * fluid_preset_zone_next - */ -fluid_preset_zone_t* -fluid_preset_zone_next(fluid_preset_zone_t* preset) -{ - return preset->next; -} - -/* - * new_fluid_preset_zone - */ -fluid_preset_zone_t* -new_fluid_preset_zone(char *name) -{ - int size; - fluid_preset_zone_t* zone = NULL; - zone = FLUID_NEW(fluid_preset_zone_t); - if (zone == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - zone->next = NULL; - size = 1 + FLUID_STRLEN(name); - zone->name = FLUID_MALLOC(size); - if (zone->name == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - FLUID_FREE(zone); - return NULL; - } - FLUID_STRCPY(zone->name, name); - zone->inst = NULL; - zone->keylo = 0; - zone->keyhi = 128; - zone->vello = 0; - zone->velhi = 128; - - /* Flag all generators as unused (default, they will be set when they are found - * in the sound font). - * This also sets the generator values to default, but that is of no concern here.*/ - fluid_gen_set_default_values(&zone->gen[0]); - zone->mod = NULL; /* list of modulators */ - return zone; -} - -/*************************************************************** - * - * PRESET_ZONE - */ - -/* - * delete_fluid_preset_zone - */ -int -delete_fluid_preset_zone(fluid_preset_zone_t* zone) -{ - fluid_mod_t *mod, *tmp; - - mod = zone->mod; - while (mod) /* delete the modulators */ - { - tmp = mod; - mod = mod->next; - fluid_mod_delete (tmp); - } - - if (zone->name) FLUID_FREE (zone->name); - if (zone->inst) delete_fluid_inst (zone->inst); - FLUID_FREE(zone); - return FLUID_OK; -} - -/* - * fluid_preset_zone_import_sfont - */ -int -fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont) -{ - fluid_list_t *r; - SFGen* sfgen; - int count; - for (count = 0, r = sfzone->gen; r != NULL; count++) { - sfgen = (SFGen *) r->data; - switch (sfgen->id) { - case GEN_KEYRANGE: - zone->keylo = (int) sfgen->amount.range.lo; - zone->keyhi = (int) sfgen->amount.range.hi; - break; - case GEN_VELRANGE: - zone->vello = (int) sfgen->amount.range.lo; - zone->velhi = (int) sfgen->amount.range.hi; - break; - default: - /* FIXME: some generators have an unsigne word amount value but i don't know which ones */ - zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; - zone->gen[sfgen->id].flags = GEN_SET; - break; - } - r = fluid_list_next(r); - } - if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) { - zone->inst = (fluid_inst_t*) new_fluid_inst(); - if (zone->inst == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return FLUID_FAILED; - } - if (fluid_inst_import_sfont(zone->inst, (SFInst *) sfzone->instsamp->data, sfont) != FLUID_OK) { - return FLUID_FAILED; - } - } - - /* Import the modulators (only SF2.1 and higher) */ - for (count = 0, r = sfzone->mod; r != NULL; count++) { - - SFMod* mod_src = (SFMod *)r->data; - fluid_mod_t * mod_dest = fluid_mod_new(); - int type; - - if (mod_dest == NULL){ - return FLUID_FAILED; - } - mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ - - /* *** Amount *** */ - mod_dest->amount = mod_src->amount; - - /* *** Source *** */ - mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */ - mod_dest->flags1 = 0; - - /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ - if (mod_src->src & (1<<7)){ - mod_dest->flags1 |= FLUID_MOD_CC; - } else { - mod_dest->flags1 |= FLUID_MOD_GC; - } - - /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ - if (mod_src->src & (1<<8)){ - mod_dest->flags1 |= FLUID_MOD_NEGATIVE; - } else { - mod_dest->flags1 |= FLUID_MOD_POSITIVE; - } - - /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ - if (mod_src->src & (1<<9)){ - mod_dest->flags1 |= FLUID_MOD_BIPOLAR; - } else { - mod_dest->flags1 |= FLUID_MOD_UNIPOLAR; - } - - /* modulator source types: SF2.01 section 8.2.1 page 52 */ - type=(mod_src->src) >> 10; - type &= 63; /* type is a 6-bit value */ - if (type == 0){ - mod_dest->flags1 |= FLUID_MOD_LINEAR; - } else if (type == 1){ - mod_dest->flags1 |= FLUID_MOD_CONCAVE; - } else if (type == 2){ - mod_dest->flags1 |= FLUID_MOD_CONVEX; - } else if (type == 3){ - mod_dest->flags1 |= FLUID_MOD_SWITCH; - } else { - /* This shouldn't happen - unknown type! - * Deactivate the modulator by setting the amount to 0. */ - mod_dest->amount=0; - } - - /* *** Dest *** */ - mod_dest->dest = mod_src->dest; /* index of controlled generator */ - - /* *** Amount source *** */ - mod_dest->src2 = mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, p.50 */ - mod_dest->flags2 = 0; - - /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ - if (mod_src->amtsrc & (1<<7)){ - mod_dest->flags2 |= FLUID_MOD_CC; - } else { - mod_dest->flags2 |= FLUID_MOD_GC; - } - - /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ - if (mod_src->amtsrc & (1<<8)){ - mod_dest->flags2 |= FLUID_MOD_NEGATIVE; - } else { - mod_dest->flags2 |= FLUID_MOD_POSITIVE; - } - - /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ - if (mod_src->amtsrc & (1<<9)){ - mod_dest->flags2 |= FLUID_MOD_BIPOLAR; - } else { - mod_dest->flags2 |= FLUID_MOD_UNIPOLAR; - } - - /* modulator source types: SF2.01 section 8.2.1 page 52 */ - type = (mod_src->amtsrc) >> 10; - type &= 63; /* type is a 6-bit value */ - if (type == 0){ - mod_dest->flags2 |= FLUID_MOD_LINEAR; - } else if (type == 1){ - mod_dest->flags2 |= FLUID_MOD_CONCAVE; - } else if (type == 2){ - mod_dest->flags2 |= FLUID_MOD_CONVEX; - } else if (type == 3){ - mod_dest->flags2 |= FLUID_MOD_SWITCH; - } else { - /* This shouldn't happen - unknown type! - * Deactivate the modulator by setting the amount to 0. */ - mod_dest->amount=0; - } - - /* *** Transform *** */ - /* SF2.01 only uses the 'linear' transform (0). - * Deactivate the modulator by setting the amount to 0 in any other case. - */ - if (mod_src->trans !=0){ - mod_dest->amount = 0; - } - - /* Store the new modulator in the zone The order of modulators - * will make a difference, at least in an instrument context: The - * second modulator overwrites the first one, if they only differ - * in amount. */ - if (count == 0){ - zone->mod = mod_dest; - } else { - fluid_mod_t * last_mod = zone->mod; - - /* Find the end of the list */ - while (last_mod->next != NULL){ - last_mod=last_mod->next; - } - - last_mod->next = mod_dest; - } - - r = fluid_list_next(r); - } /* foreach modulator */ - - return FLUID_OK; -} - -/* - * fluid_preset_zone_get_inst - */ -fluid_inst_t* -fluid_preset_zone_get_inst(fluid_preset_zone_t* zone) -{ - return zone->inst; -} - -/* - * fluid_preset_zone_inside_range - */ -int -fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel) -{ - return ((zone->keylo <= key) && - (zone->keyhi >= key) && - (zone->vello <= vel) && - (zone->velhi >= vel)); -} - -/*************************************************************** - * - * INST - */ - -/* - * new_fluid_inst - */ -fluid_inst_t* -new_fluid_inst() -{ - fluid_inst_t* inst = FLUID_NEW(fluid_inst_t); - if (inst == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - inst->name[0] = 0; - inst->global_zone = NULL; - inst->zone = NULL; - return inst; -} - -/* - * delete_fluid_inst - */ -int -delete_fluid_inst(fluid_inst_t* inst) -{ - fluid_inst_zone_t* zone; - int err = FLUID_OK; - if (inst->global_zone != NULL) { - if (delete_fluid_inst_zone(inst->global_zone) != FLUID_OK) { - err = FLUID_FAILED; - } - inst->global_zone = NULL; - } - zone = inst->zone; - while (zone != NULL) { - inst->zone = zone->next; - if (delete_fluid_inst_zone(zone) != FLUID_OK) { - err = FLUID_FAILED; - } - zone = inst->zone; - } - FLUID_FREE(inst); - return err; -} - -/* - * fluid_inst_set_global_zone - */ -int -fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone) -{ - inst->global_zone = zone; - return FLUID_OK; -} - -/* - * fluid_inst_import_sfont - */ -int -fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont) -{ - fluid_list_t *p; - SFZone* sfzone; - fluid_inst_zone_t* zone; - char zone_name[256]; - int count; - - p = sfinst->zone; - if (FLUID_STRLEN(sfinst->name) > 0) { - FLUID_STRCPY(inst->name, sfinst->name); - } else { - FLUID_STRCPY(inst->name, ""); - } - - count = 0; - while (p != NULL) { - - sfzone = (SFZone *) p->data; - FLUID_SPRINTF(zone_name, "%s/%d", inst->name, count); - - zone = new_fluid_inst_zone(zone_name); - if (zone == NULL) { - return FLUID_FAILED; - } - - if (fluid_inst_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) { - return FLUID_FAILED; - } - - if ((count == 0) && (fluid_inst_zone_get_sample(zone) == NULL)) { - fluid_inst_set_global_zone(inst, zone); - - } else if (fluid_inst_add_zone(inst, zone) != FLUID_OK) { - return FLUID_FAILED; - } - - p = fluid_list_next(p); - count++; - } - return FLUID_OK; -} - -/* - * fluid_inst_add_zone - */ -int -fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone) -{ - if (inst->zone == NULL) { - zone->next = NULL; - inst->zone = zone; - } else { - zone->next = inst->zone; - inst->zone = zone; - } - return FLUID_OK; -} - -/* - * fluid_inst_get_zone - */ -fluid_inst_zone_t* -fluid_inst_get_zone(fluid_inst_t* inst) -{ - return inst->zone; -} - -/* - * fluid_inst_get_global_zone - */ -fluid_inst_zone_t* -fluid_inst_get_global_zone(fluid_inst_t* inst) -{ - return inst->global_zone; -} - -/*************************************************************** - * - * INST_ZONE - */ - -/* - * new_fluid_inst_zone - */ -fluid_inst_zone_t* -new_fluid_inst_zone(char* name) -{ - int size; - fluid_inst_zone_t* zone = NULL; - zone = FLUID_NEW(fluid_inst_zone_t); - if (zone == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - zone->next = NULL; - size = 1 + FLUID_STRLEN(name); - zone->name = FLUID_MALLOC(size); - if (zone->name == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - FLUID_FREE(zone); - return NULL; - } - FLUID_STRCPY(zone->name, name); - zone->sample = NULL; - zone->keylo = 0; - zone->keyhi = 128; - zone->vello = 0; - zone->velhi = 128; - - /* Flag the generators as unused. - * This also sets the generator values to default, but they will be overwritten anyway, if used.*/ - fluid_gen_set_default_values(&zone->gen[0]); - zone->mod=NULL; /* list of modulators */ - return zone; -} - -/* - * delete_fluid_inst_zone - */ -int -delete_fluid_inst_zone(fluid_inst_zone_t* zone) -{ - fluid_mod_t *mod, *tmp; - - mod = zone->mod; - while (mod) /* delete the modulators */ - { - tmp = mod; - mod = mod->next; - fluid_mod_delete (tmp); - } - - if (zone->name) FLUID_FREE (zone->name); - FLUID_FREE(zone); - return FLUID_OK; -} - -/* - * fluid_inst_zone_next - */ -fluid_inst_zone_t* -fluid_inst_zone_next(fluid_inst_zone_t* zone) -{ - return zone->next; -} - -/* - * fluid_inst_zone_import_sfont - */ -int -fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont) -{ - fluid_list_t *r; - SFGen* sfgen; - int count; - - for (count = 0, r = sfzone->gen; r != NULL; count++) { - sfgen = (SFGen *) r->data; - switch (sfgen->id) { - case GEN_KEYRANGE: - zone->keylo = (int) sfgen->amount.range.lo; - zone->keyhi = (int) sfgen->amount.range.hi; - break; - case GEN_VELRANGE: - zone->vello = (int) sfgen->amount.range.lo; - zone->velhi = (int) sfgen->amount.range.hi; - break; - default: - /* FIXME: some generators have an unsigned word amount value but - i don't know which ones */ - zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; - zone->gen[sfgen->id].flags = GEN_SET; - break; - } - r = fluid_list_next(r); - } - - /* FIXME */ -/* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */ -/* FLUID_LOG(FLUID_DBG, "ExclusiveClass=%d\n", (int) zone->gen[GEN_EXCLUSIVECLASS].val); */ -/* } */ - - if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) { - zone->sample = fluid_defsfont_get_sample(sfont, ((SFSample *) sfzone->instsamp->data)->name); - if (zone->sample == NULL) { - FLUID_LOG(FLUID_ERR, "Couldn't find sample name"); - return FLUID_FAILED; - } - } - - /* Import the modulators (only SF2.1 and higher) */ - for (count = 0, r = sfzone->mod; r != NULL; count++) { - SFMod* mod_src = (SFMod *) r->data; - int type; - fluid_mod_t* mod_dest; - - mod_dest = fluid_mod_new(); - if (mod_dest == NULL){ - return FLUID_FAILED; - } - - mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ - - /* *** Amount *** */ - mod_dest->amount = mod_src->amount; - - /* *** Source *** */ - mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */ - mod_dest->flags1 = 0; - - /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ - if (mod_src->src & (1<<7)){ - mod_dest->flags1 |= FLUID_MOD_CC; - } else { - mod_dest->flags1 |= FLUID_MOD_GC; - } - - /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ - if (mod_src->src & (1<<8)){ - mod_dest->flags1 |= FLUID_MOD_NEGATIVE; - } else { - mod_dest->flags1 |= FLUID_MOD_POSITIVE; - } - - /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ - if (mod_src->src & (1<<9)){ - mod_dest->flags1 |= FLUID_MOD_BIPOLAR; - } else { - mod_dest->flags1 |= FLUID_MOD_UNIPOLAR; - } - - /* modulator source types: SF2.01 section 8.2.1 page 52 */ - type = (mod_src->src) >> 10; - type &= 63; /* type is a 6-bit value */ - if (type == 0){ - mod_dest->flags1 |= FLUID_MOD_LINEAR; - } else if (type == 1){ - mod_dest->flags1 |= FLUID_MOD_CONCAVE; - } else if (type == 2){ - mod_dest->flags1 |= FLUID_MOD_CONVEX; - } else if (type == 3){ - mod_dest->flags1 |= FLUID_MOD_SWITCH; - } else { - /* This shouldn't happen - unknown type! - * Deactivate the modulator by setting the amount to 0. */ - mod_dest->amount = 0; - } - - /* *** Dest *** */ - mod_dest->dest=mod_src->dest; /* index of controlled generator */ - - /* *** Amount source *** */ - mod_dest->src2=mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, page 50 */ - mod_dest->flags2 = 0; - - /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ - if (mod_src->amtsrc & (1<<7)){ - mod_dest->flags2 |= FLUID_MOD_CC; - } else { - mod_dest->flags2 |= FLUID_MOD_GC; - } - - /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ - if (mod_src->amtsrc & (1<<8)){ - mod_dest->flags2 |= FLUID_MOD_NEGATIVE; - } else { - mod_dest->flags2 |= FLUID_MOD_POSITIVE; - } - - /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ - if (mod_src->amtsrc & (1<<9)){ - mod_dest->flags2 |= FLUID_MOD_BIPOLAR; - } else { - mod_dest->flags2 |= FLUID_MOD_UNIPOLAR; - } - - /* modulator source types: SF2.01 section 8.2.1 page 52 */ - type=(mod_src->amtsrc) >> 10; - type &= 63; /* type is a 6-bit value */ - if (type == 0){ - mod_dest->flags2 |= FLUID_MOD_LINEAR; - } else if (type == 1){ - mod_dest->flags2 |= FLUID_MOD_CONCAVE; - } else if (type == 2){ - mod_dest->flags2 |= FLUID_MOD_CONVEX; - } else if (type == 3){ - mod_dest->flags2 |= FLUID_MOD_SWITCH; - } else { - /* This shouldn't happen - unknown type! - * Deactivate the modulator by setting the amount to 0. */ - mod_dest->amount = 0; - } - - /* *** Transform *** */ - /* SF2.01 only uses the 'linear' transform (0). - * Deactivate the modulator by setting the amount to 0 in any other case. - */ - if (mod_src->trans !=0){ - mod_dest->amount = 0; - } - - /* Store the new modulator in the zone - * The order of modulators will make a difference, at least in an instrument context: - * The second modulator overwrites the first one, if they only differ in amount. */ - if (count == 0){ - zone->mod=mod_dest; - } else { - fluid_mod_t * last_mod=zone->mod; - /* Find the end of the list */ - while (last_mod->next != NULL){ - last_mod=last_mod->next; - } - last_mod->next=mod_dest; - } - - r = fluid_list_next(r); - } /* foreach modulator */ - return FLUID_OK; -} - -/* - * fluid_inst_zone_get_sample - */ -fluid_sample_t* -fluid_inst_zone_get_sample(fluid_inst_zone_t* zone) -{ - return zone->sample; -} - -/* - * fluid_inst_zone_inside_range - */ -int -fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel) -{ - return ((zone->keylo <= key) && - (zone->keyhi >= key) && - (zone->vello <= vel) && - (zone->velhi >= vel)); -} - -/*************************************************************** - * - * SAMPLE - */ - -/* - * new_fluid_sample - */ -fluid_sample_t* -new_fluid_sample() -{ - fluid_sample_t* sample = NULL; - - sample = FLUID_NEW(fluid_sample_t); - if (sample == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - memset(sample, 0, sizeof(fluid_sample_t)); - sample->valid = 1; - - return sample; -} - -/* - * delete_fluid_sample - */ -int -delete_fluid_sample(fluid_sample_t* sample) -{ -#if SF3_SUPPORT - if (sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS_UNPACKED) { - if (sample->data != NULL) FLUID_FREE(sample->data); - } -#endif - - FLUID_FREE(sample); - return FLUID_OK; -} - -/* - * fluid_sample_in_rom - */ -int -fluid_sample_in_rom(fluid_sample_t* sample) -{ - return (sample->sampletype & FLUID_SAMPLETYPE_ROM); -} - -/* - * fluid_sample_import_sfont - */ -int -fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont) -{ - FLUID_STRCPY(sample->name, sfsample->name); - sample->data = sfont->sampledata; - sample->start = sfsample->start; - sample->end = sfsample->start + sfsample->end; - sample->loopstart = sfsample->start + sfsample->loopstart; - sample->loopend = sfsample->start + sfsample->loopend; - sample->samplerate = sfsample->samplerate; - sample->origpitch = sfsample->origpitch; - sample->pitchadj = sfsample->pitchadj; - sample->sampletype = sfsample->sampletype; - - if (sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS) - { - - } - - if (sample->sampletype & FLUID_SAMPLETYPE_ROM) { - sample->valid = 0; - FLUID_LOG(FLUID_WARN, "Ignoring sample %s: can't use ROM samples", sample->name); - } - if (sample->end - sample->start < 8) { - sample->valid = 0; - FLUID_LOG(FLUID_WARN, "Ignoring sample %s: too few sample data points", sample->name); - } else { -/* if (sample->loopstart < sample->start + 8) { */ -/* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required before loop start", sample->name); */ -/* sample->loopstart = sample->start + 8; */ -/* } */ -/* if (sample->loopend > sample->end - 8) { */ -/* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required after loop end", sample->name); */ -/* sample->loopend = sample->end - 8; */ -/* } */ - } - return FLUID_OK; -} - - - -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ - - - -/*=================================sfload.c======================== - Borrowed from Smurf SoundFont Editor by Josh Green - =================================================================*/ - -/* - functions for loading data from sfont files, with appropriate byte swapping - on big endian machines. Sfont IDs are not swapped because the ID read is - equivalent to the matching ID list in memory regardless of LE/BE machine -*/ - -#ifdef WORDS_BIGENDIAN -#define READCHUNK(var,fd,fapi) G_STMT_START { \ - if (fapi->fread(var, 8, fd) == FLUID_FAILED) \ - return(FAIL); \ - ((SFChunk *)(var))->size = GUINT32_FROM_BE(((SFChunk *)(var))->size); \ -} G_STMT_END -#else -#define READCHUNK(var,fd,fapi) G_STMT_START { \ - if (fapi->fread(var, 8, fd) == FLUID_FAILED) \ - return(FAIL); \ - ((SFChunk *)(var))->size = GUINT32_FROM_LE(((SFChunk *)(var))->size); \ -} G_STMT_END -#endif -#define READID(var,fd,fapi) G_STMT_START { \ - if (fapi->fread(var, 4, fd) == FLUID_FAILED) \ - return(FAIL); \ -} G_STMT_END -#define READSTR(var,fd,fapi) G_STMT_START { \ - if (fapi->fread(var, 20, fd) == FLUID_FAILED) \ - return(FAIL); \ - (var)[20] = '\0'; \ -} G_STMT_END -#ifdef WORDS_BIGENDIAN -#define READD(var,fd,fapi) G_STMT_START { \ - unsigned int _temp; \ - if (fapi->fread(&_temp, 4, fd) == FLUID_FAILED) \ - return(FAIL); \ - var = GINT32_FROM_BE(_temp); \ -} G_STMT_END -#else -#define READD(var,fd,fapi) G_STMT_START { \ - unsigned int _temp; \ - if (fapi->fread(&_temp, 4, fd) == FLUID_FAILED) \ - return(FAIL); \ - var = GINT32_FROM_LE(_temp); \ -} G_STMT_END -#endif -#ifdef WORDS_BIGENDIAN -#define READW(var,fd,fapi) G_STMT_START { \ - unsigned short _temp; \ - if (fapi->fread(&_temp, 2, fd) == FLUID_FAILED) \ - return(FAIL); \ -var = GINT16_FROM_BE(_temp); \ -} G_STMT_END -#else -#define READW(var,fd,fapi) G_STMT_START { \ - unsigned short _temp; \ - if (fapi->fread(&_temp, 2, fd) == FLUID_FAILED) \ - return(FAIL); \ - var = GINT16_FROM_LE(_temp); \ -} G_STMT_END -#endif -#define READB(var,fd,fapi) G_STMT_START { \ - if (fapi->fread(&var, 1, fd) == FLUID_FAILED) \ - return(FAIL); \ -} G_STMT_END -#define FSKIP(size,fd,fapi) G_STMT_START { \ - if (fapi->fseek(fd, size, SEEK_CUR) == FLUID_FAILED) \ - return(FAIL); \ -} G_STMT_END -#define FSKIPW(fd,fapi) G_STMT_START { \ - if (fapi->fseek(fd, 2, SEEK_CUR) == FLUID_FAILED) \ - return(FAIL); \ -} G_STMT_END - -/* removes and advances a fluid_list_t pointer */ -#define SLADVREM(list, item) G_STMT_START { \ - fluid_list_t *_temp = item; \ - item = fluid_list_next(item); \ - list = fluid_list_remove_link(list, _temp); \ - delete1_fluid_list(_temp); \ -} G_STMT_END - -static int chunkid (unsigned int id); -static int load_body (unsigned int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int read_listchunk (SFChunk * chunk, void * fd, fluid_fileapi_t* fapi); -static int process_info (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int process_sdta (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk, - int * size, void * fd, fluid_fileapi_t* fapi); -static int process_pdta (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int load_phdr (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int load_pbag (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int load_pmod (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int load_pgen (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int load_ihdr (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int load_ibag (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int load_imod (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int load_igen (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int load_shdr (unsigned int size, SFData * sf, void * fd, fluid_fileapi_t* fapi); -static int fixup_pgen (SFData * sf); -static int fixup_igen (SFData * sf); -static int fixup_sample (SFData * sf); - -char idlist[] = { - "RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD" - "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdr" -}; - -static unsigned int sdtachunk_size; - -/* sound font file load functions */ -static int -chunkid (unsigned int id) -{ - unsigned int i; - unsigned int *p; - - p = (unsigned int *) idlist; - for (i = 0; i < sizeof (idlist) / sizeof (int); i++, p += 1) - if (*p == id) - return (i + 1); - - return (UNKN_ID); -} - -SFData * -sfload_file (const char * fname, fluid_fileapi_t* fapi) -{ - SFData *sf = NULL; - void *fd; - int fsize = 0; - int err = FALSE; - - if ((fd = fapi->fopen (fapi, fname)) == NULL) - { - FLUID_LOG (FLUID_ERR, _("Unable to open file \"%s\""), fname); - return (NULL); - } - - if (!(sf = FLUID_NEW (SFData))) - { - FLUID_LOG(FLUID_ERR, "Out of memory"); - err = TRUE; - } - - if (!err) - { - memset (sf, 0, sizeof (SFData)); /* zero sfdata */ - sf->fname = FLUID_STRDUP (fname); /* copy file name */ - sf->sffd = fd; - } - - /* get size of file */ - if (!err && fapi->fseek (fd, 0L, SEEK_END) == FLUID_FAILED) - { /* seek to end of file */ - err = TRUE; - FLUID_LOG (FLUID_ERR, _("Seek to end of file failed")); - } - if (!err && (fsize = fapi->ftell (fd)) == FLUID_FAILED) - { /* position = size */ - err = TRUE; - FLUID_LOG (FLUID_ERR, _("Get end of file position failed")); - } - if (!err) - fapi->fseek (fd, 0, SEEK_SET); - - if (!err && !load_body (fsize, sf, fd, fapi)) - err = TRUE; /* load the sfont */ - - if (err) - { - if (sf) - sfont_close (sf, fapi); - return (NULL); - } - - return (sf); -} - -static int -load_body (unsigned int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - SFChunk chunk; - - READCHUNK (&chunk, fd, fapi); /* load RIFF chunk */ - if (chunkid (chunk.id) != RIFF_ID) { /* error if not RIFF */ - FLUID_LOG (FLUID_ERR, _("Not a RIFF file")); - return (FAIL); - } - - READID (&chunk.id, fd, fapi); /* load file ID */ - if (chunkid (chunk.id) != SFBK_ID) { /* error if not SFBK_ID */ - FLUID_LOG (FLUID_ERR, _("Not a sound font file")); - return (FAIL); - } - - if (chunk.size != size - 8) { - gerr (ErrCorr, _("Sound font file size mismatch")); - return (FAIL); - } - - /* Process INFO block */ - if (!read_listchunk (&chunk, fd, fapi)) - return (FAIL); - if (chunkid (chunk.id) != INFO_ID) - return (gerr (ErrCorr, _("Invalid ID found when expecting INFO chunk"))); - if (!process_info (chunk.size, sf, fd, fapi)) - return (FAIL); - - /* Process sample chunk */ - if (!read_listchunk (&chunk, fd, fapi)) - return (FAIL); - if (chunkid (chunk.id) != SDTA_ID) - return (gerr (ErrCorr, - _("Invalid ID found when expecting SAMPLE chunk"))); - if (!process_sdta (chunk.size, sf, fd, fapi)) - return (FAIL); - - /* process HYDRA chunk */ - if (!read_listchunk (&chunk, fd, fapi)) - return (FAIL); - if (chunkid (chunk.id) != PDTA_ID) - return (gerr (ErrCorr, _("Invalid ID found when expecting HYDRA chunk"))); - if (!process_pdta (chunk.size, sf, fd, fapi)) - return (FAIL); - - if (!fixup_pgen (sf)) - return (FAIL); - if (!fixup_igen (sf)) - return (FAIL); - if (!fixup_sample (sf)) - return (FAIL); - - /* sort preset list by bank, preset # */ - sf->preset = fluid_list_sort (sf->preset, - (fluid_compare_func_t) sfont_preset_compare_func); - - return (OK); -} - -static int -read_listchunk (SFChunk * chunk, void * fd, fluid_fileapi_t* fapi) -{ - READCHUNK (chunk, fd, fapi); /* read list chunk */ - if (chunkid (chunk->id) != LIST_ID) /* error if ! list chunk */ - return (gerr (ErrCorr, _("Invalid chunk id in level 0 parse"))); - READID (&chunk->id, fd, fapi); /* read id string */ - chunk->size -= 4; - return (OK); -} - -static int -process_info (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - SFChunk chunk; - unsigned char id; - char *item; - unsigned short ver; - - while (size > 0) - { - READCHUNK (&chunk, fd, fapi); - size -= 8; - - id = chunkid (chunk.id); - - if (id == IFIL_ID) - { /* sound font version chunk? */ - if (chunk.size != 4) - return (gerr (ErrCorr, - _("Sound font version info chunk has invalid size"))); - - READW (ver, fd, fapi); - sf->version.major = ver; - READW (ver, fd, fapi); - sf->version.minor = ver; - - if (sf->version.major < 2) { - FLUID_LOG (FLUID_ERR, - _("Sound font version is %d.%d which is not" - " supported, convert to version 2.0x"), - sf->version.major, - sf->version.minor); - return (FAIL); - } - -#if SF3_SUPPORT - if (sf->version.major == 3) {} - else -#endif - if (sf->version.major > 2) { - FLUID_LOG (FLUID_WARN, - _("Sound font version is %d.%d which is newer than" - " what this version of FLUID Synth was designed for (v2.0x)"), - sf->version.major, - sf->version.minor); - return (FAIL); - } - } - else if (id == IVER_ID) - { /* ROM version chunk? */ - if (chunk.size != 4) - return (gerr (ErrCorr, - _("ROM version info chunk has invalid size"))); - - READW (ver, fd, fapi); - sf->romver.major = ver; - READW (ver, fd, fapi); - sf->romver.minor = ver; - } - else if (id != UNKN_ID) - { - if ((id != ICMT_ID && chunk.size > 256) || (chunk.size > 65536) - || (chunk.size % 2)) - return (gerr (ErrCorr, - _("INFO sub chunk %.4s has invalid chunk size" - " of %d bytes"), &chunk.id, chunk.size)); - - /* alloc for chunk id and da chunk */ - if (!(item = FLUID_MALLOC (chunk.size + 1))) - { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return (FAIL); - } - - /* attach to INFO list, sfont_close will cleanup if FAIL occurs */ - sf->info = fluid_list_append (sf->info, item); - - *(unsigned char *) item = id; - if (fapi->fread (&item[1], chunk.size, fd) == FLUID_FAILED) - return (FAIL); - - /* force terminate info item (don't forget uint8 info ID) */ - *(item + chunk.size) = '\0'; - } - else - return (gerr (ErrCorr, _("Invalid chunk id in INFO chunk"))); - size -= chunk.size; - } - - if (size < 0) - return (gerr (ErrCorr, _("INFO chunk size mismatch"))); - - return (OK); -} - -static int -process_sdta (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - SFChunk chunk; - - if (size == 0) - return (OK); /* no sample data? */ - - /* read sub chunk */ - READCHUNK (&chunk, fd, fapi); - size -= 8; - - if (chunkid (chunk.id) != SMPL_ID) - return (gerr (ErrCorr, - _("Expected SMPL chunk found invalid id instead"))); - - if ((size - chunk.size) != 0) - return (gerr (ErrCorr, _("SDTA chunk size mismatch"))); - - /* sample data follows */ - sf->samplepos = fapi->ftell (fd); - - /* used in fixup_sample() to check validity of sample headers */ - sdtachunk_size = chunk.size; - sf->samplesize = chunk.size; - - FSKIP (chunk.size, fd, fapi); - - return (OK); -} - -static int -pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk, - int * size, void * fd, fluid_fileapi_t* fapi) -{ - unsigned int id; - char *expstr; - - expstr = CHNKIDSTR (expid); /* in case we need it */ - - READCHUNK (chunk, fd, fapi); - *size -= 8; - - if ((id = chunkid (chunk->id)) != expid) - return (gerr (ErrCorr, _("Expected" - " PDTA sub-chunk \"%.4s\" found invalid id instead"), expstr)); - - if (chunk->size % reclen) /* valid chunk size? */ - return (gerr (ErrCorr, - _("\"%.4s\" chunk size is not a multiple of %d bytes"), expstr, - reclen)); - if ((*size -= chunk->size) < 0) - return (gerr (ErrCorr, - _("\"%.4s\" chunk size exceeds remaining PDTA chunk size"), expstr)); - return (OK); -} - -static int -process_pdta (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - SFChunk chunk; - - if (!pdtahelper (PHDR_ID, SFPHDRSIZE, &chunk, &size, fd, fapi)) - return (FAIL); - if (!load_phdr (chunk.size, sf, fd, fapi)) - return (FAIL); - - if (!pdtahelper (PBAG_ID, SFBAGSIZE, &chunk, &size, fd, fapi)) - return (FAIL); - if (!load_pbag (chunk.size, sf, fd, fapi)) - return (FAIL); - - if (!pdtahelper (PMOD_ID, SFMODSIZE, &chunk, &size, fd, fapi)) - return (FAIL); - if (!load_pmod (chunk.size, sf, fd, fapi)) - return (FAIL); - - if (!pdtahelper (PGEN_ID, SFGENSIZE, &chunk, &size, fd, fapi)) - return (FAIL); - if (!load_pgen (chunk.size, sf, fd, fapi)) - return (FAIL); - - if (!pdtahelper (IHDR_ID, SFIHDRSIZE, &chunk, &size, fd, fapi)) - return (FAIL); - if (!load_ihdr (chunk.size, sf, fd, fapi)) - return (FAIL); - - if (!pdtahelper (IBAG_ID, SFBAGSIZE, &chunk, &size, fd, fapi)) - return (FAIL); - if (!load_ibag (chunk.size, sf, fd, fapi)) - return (FAIL); - - if (!pdtahelper (IMOD_ID, SFMODSIZE, &chunk, &size, fd, fapi)) - return (FAIL); - if (!load_imod (chunk.size, sf, fd, fapi)) - return (FAIL); - - if (!pdtahelper (IGEN_ID, SFGENSIZE, &chunk, &size, fd, fapi)) - return (FAIL); - if (!load_igen (chunk.size, sf, fd, fapi)) - return (FAIL); - - if (!pdtahelper (SHDR_ID, SFSHDRSIZE, &chunk, &size, fd, fapi)) - return (FAIL); - if (!load_shdr (chunk.size, sf, fd, fapi)) - return (FAIL); - - return (OK); -} - -/* preset header loader */ -static int -load_phdr (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - int i, i2; - SFPreset *p, *pr = NULL; /* ptr to current & previous preset */ - unsigned short zndx, pzndx = 0; - - if (size % SFPHDRSIZE || size == 0) - return (gerr (ErrCorr, _("Preset header chunk size is invalid"))); - - i = size / SFPHDRSIZE - 1; - if (i == 0) - { /* at least one preset + term record */ - FLUID_LOG (FLUID_WARN, _("File contains no presets")); - FSKIP (SFPHDRSIZE, fd, fapi); - return (OK); - } - - for (; i > 0; i--) - { /* load all preset headers */ - p = FLUID_NEW (SFPreset); - sf->preset = fluid_list_append (sf->preset, p); - p->zone = NULL; /* In case of failure, sfont_close can cleanup */ - READSTR (p->name, fd, fapi); /* possible read failure ^ */ - READW (p->prenum, fd, fapi); - READW (p->bank, fd, fapi); - READW (zndx, fd, fapi); - READD (p->libr, fd, fapi); - READD (p->genre, fd, fapi); - READD (p->morph, fd, fapi); - - if (pr) - { /* not first preset? */ - if (zndx < pzndx) - return (gerr (ErrCorr, _("Preset header indices not monotonic"))); - i2 = zndx - pzndx; - while (i2--) - { - pr->zone = fluid_list_prepend (pr->zone, NULL); - } - } - else if (zndx > 0) /* 1st preset, warn if ofs >0 */ - FLUID_LOG (FLUID_WARN, _("%d preset zones not referenced, discarding"), zndx); - pr = p; /* update preset ptr */ - pzndx = zndx; - } - - FSKIP (24, fd, fapi); - READW (zndx, fd, fapi); /* Read terminal generator index */ - FSKIP (12, fd, fapi); - - if (zndx < pzndx) - return (gerr (ErrCorr, _("Preset header indices not monotonic"))); - i2 = zndx - pzndx; - while (i2--) - { - pr->zone = fluid_list_prepend (pr->zone, NULL); - } - - return (OK); -} - -/* preset bag loader */ -static int -load_pbag (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - fluid_list_t *p, *p2; - SFZone *z, *pz = NULL; - unsigned short genndx, modndx; - unsigned short pgenndx = 0, pmodndx = 0; - unsigned short i; - - if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */ - return (gerr (ErrCorr, _("Preset bag chunk size is invalid"))); - - p = sf->preset; - while (p) - { /* traverse through presets */ - p2 = ((SFPreset *) (p->data))->zone; - while (p2) - { /* traverse preset's zones */ - if ((size -= SFBAGSIZE) < 0) - return (gerr (ErrCorr, _("Preset bag chunk size mismatch"))); - z = FLUID_NEW (SFZone); - p2->data = z; - z->gen = NULL; /* Init gen and mod before possible failure, */ - z->mod = NULL; /* to ensure proper cleanup (sfont_close) */ - READW (genndx, fd, fapi); /* possible read failure ^ */ - READW (modndx, fd, fapi); - z->instsamp = NULL; - - if (pz) - { /* if not first zone */ - if (genndx < pgenndx) - return (gerr (ErrCorr, - _("Preset bag generator indices not monotonic"))); - if (modndx < pmodndx) - return (gerr (ErrCorr, - _("Preset bag modulator indices not monotonic"))); - i = genndx - pgenndx; - while (i--) - pz->gen = fluid_list_prepend (pz->gen, NULL); - i = modndx - pmodndx; - while (i--) - pz->mod = fluid_list_prepend (pz->mod, NULL); - } - pz = z; /* update previous zone ptr */ - pgenndx = genndx; /* update previous zone gen index */ - pmodndx = modndx; /* update previous zone mod index */ - p2 = fluid_list_next (p2); - } - p = fluid_list_next (p); - } - - size -= SFBAGSIZE; - if (size != 0) - return (gerr (ErrCorr, _("Preset bag chunk size mismatch"))); - - READW (genndx, fd, fapi); - READW (modndx, fd, fapi); - - if (!pz) - { - if (genndx > 0) - FLUID_LOG (FLUID_WARN, _("No preset generators and terminal index not 0")); - if (modndx > 0) - FLUID_LOG (FLUID_WARN, _("No preset modulators and terminal index not 0")); - return (OK); - } - - if (genndx < pgenndx) - return (gerr (ErrCorr, _("Preset bag generator indices not monotonic"))); - if (modndx < pmodndx) - return (gerr (ErrCorr, _("Preset bag modulator indices not monotonic"))); - i = genndx - pgenndx; - while (i--) - pz->gen = fluid_list_prepend (pz->gen, NULL); - i = modndx - pmodndx; - while (i--) - pz->mod = fluid_list_prepend (pz->mod, NULL); - - return (OK); -} - -/* preset modulator loader */ -static int -load_pmod (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - fluid_list_t *p, *p2, *p3; - SFMod *m; - - p = sf->preset; - while (p) - { /* traverse through all presets */ - p2 = ((SFPreset *) (p->data))->zone; - while (p2) - { /* traverse this preset's zones */ - p3 = ((SFZone *) (p2->data))->mod; - while (p3) - { /* load zone's modulators */ - if ((size -= SFMODSIZE) < 0) - return (gerr (ErrCorr, - _("Preset modulator chunk size mismatch"))); - m = FLUID_NEW (SFMod); - p3->data = m; - READW (m->src, fd, fapi); - READW (m->dest, fd, fapi); - READW (m->amount, fd, fapi); - READW (m->amtsrc, fd, fapi); - READW (m->trans, fd, fapi); - p3 = fluid_list_next (p3); - } - p2 = fluid_list_next (p2); - } - p = fluid_list_next (p); - } - - /* - If there isn't even a terminal record - Hmmm, the specs say there should be one, but.. - */ - if (size == 0) - return (OK); - - size -= SFMODSIZE; - if (size != 0) - return (gerr (ErrCorr, _("Preset modulator chunk size mismatch"))); - FSKIP (SFMODSIZE, fd, fapi); /* terminal mod */ - - return (OK); -} - -/* ------------------------------------------------------------------- - * preset generator loader - * generator (per preset) loading rules: - * Zones with no generators or modulators shall be annihilated - * Global zone must be 1st zone, discard additional ones (instrumentless zones) - * - * generator (per zone) loading rules (in order of decreasing precedence): - * KeyRange is 1st in list (if exists), else discard - * if a VelRange exists only preceded by a KeyRange, else discard - * if a generator follows an instrument discard it - * if a duplicate generator exists replace previous one - * ------------------------------------------------------------------- */ -static int -load_pgen (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - fluid_list_t *p, *p2, *p3, *dup, **hz = NULL; - SFZone *z; - SFGen *g; - SFGenAmount genval; - unsigned short genid; - int level, skip, drop, gzone, discarded; - - p = sf->preset; - while (p) - { /* traverse through all presets */ - gzone = FALSE; - discarded = FALSE; - p2 = ((SFPreset *) (p->data))->zone; - if (p2) - hz = &p2; - while (p2) - { /* traverse preset's zones */ - level = 0; - z = (SFZone *) (p2->data); - p3 = z->gen; - while (p3) - { /* load zone's generators */ - dup = NULL; - skip = FALSE; - drop = FALSE; - if ((size -= SFGENSIZE) < 0) - return (gerr (ErrCorr, - _("Preset generator chunk size mismatch"))); - - READW (genid, fd, fapi); - - if (genid == Gen_KeyRange) - { /* nothing precedes */ - if (level == 0) - { - level = 1; - READB (genval.range.lo, fd, fapi); - READB (genval.range.hi, fd, fapi); - } - else - skip = TRUE; - } - else if (genid == Gen_VelRange) - { /* only KeyRange precedes */ - if (level <= 1) - { - level = 2; - READB (genval.range.lo, fd, fapi); - READB (genval.range.hi, fd, fapi); - } - else - skip = TRUE; - } - else if (genid == Gen_Instrument) - { /* inst is last gen */ - level = 3; - READW (genval.uword, fd, fapi); - ((SFZone *) (p2->data))->instsamp = GINT_TO_POINTER (genval.uword + 1); - break; /* break out of generator loop */ - } - else - { - level = 2; - if (gen_validp (genid)) - { /* generator valid? */ - READW (genval.sword, fd, fapi); - dup = gen_inlist (genid, z->gen); - } - else - skip = TRUE; - } - - if (!skip) - { - if (!dup) - { /* if gen ! dup alloc new */ - g = FLUID_NEW (SFGen); - p3->data = g; - g->id = genid; - } - else - { - g = (SFGen *) (dup->data); /* ptr to orig gen */ - drop = TRUE; - } - g->amount = genval; - } - else - { /* Skip this generator */ - discarded = TRUE; - drop = TRUE; - FSKIPW (fd, fapi); - } - - if (!drop) - p3 = fluid_list_next (p3); /* next gen */ - else - SLADVREM (z->gen, p3); /* drop place holder */ - - } /* generator loop */ - - if (level == 3) - SLADVREM (z->gen, p3); /* zone has inst? */ - else - { /* congratulations its a global zone */ - if (!gzone) - { /* Prior global zones? */ - gzone = TRUE; - - /* if global zone is not 1st zone, relocate */ - if (*hz != p2) - { - void* save = p2->data; - FLUID_LOG (FLUID_WARN, - _("Preset \"%s\": Global zone is not first zone"), - ((SFPreset *) (p->data))->name); - SLADVREM (*hz, p2); - *hz = fluid_list_prepend (*hz, save); - continue; - } - } - else - { /* previous global zone exists, discard */ - FLUID_LOG (FLUID_WARN, - _("Preset \"%s\": Discarding invalid global zone"), - ((SFPreset *) (p->data))->name); - sfont_zone_delete (sf, hz, (SFZone *) (p2->data)); - } - } - - while (p3) - { /* Kill any zones following an instrument */ - discarded = TRUE; - if ((size -= SFGENSIZE) < 0) - return (gerr (ErrCorr, - _("Preset generator chunk size mismatch"))); - FSKIP (SFGENSIZE, fd, fapi); - SLADVREM (z->gen, p3); - } - - p2 = fluid_list_next (p2); /* next zone */ - } - if (discarded) - FLUID_LOG(FLUID_WARN, - _("Preset \"%s\": Some invalid generators were discarded"), - ((SFPreset *) (p->data))->name); - p = fluid_list_next (p); - } - - /* in case there isn't a terminal record */ - if (size == 0) - return (OK); - - size -= SFGENSIZE; - if (size != 0) - return (gerr (ErrCorr, _("Preset generator chunk size mismatch"))); - FSKIP (SFGENSIZE, fd, fapi); /* terminal gen */ - - return (OK); -} - -/* instrument header loader */ -static int -load_ihdr (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - int i, i2; - SFInst *p, *pr = NULL; /* ptr to current & previous instrument */ - unsigned short zndx, pzndx = 0; - - if (size % SFIHDRSIZE || size == 0) /* chunk size is valid? */ - return (gerr (ErrCorr, _("Instrument header has invalid size"))); - - size = size / SFIHDRSIZE - 1; - if (size == 0) - { /* at least one preset + term record */ - FLUID_LOG (FLUID_WARN, _("File contains no instruments")); - FSKIP (SFIHDRSIZE, fd, fapi); - return (OK); - } - - for (i = 0; i < size; i++) - { /* load all instrument headers */ - p = FLUID_NEW (SFInst); - sf->inst = fluid_list_append (sf->inst, p); - p->zone = NULL; /* For proper cleanup if fail (sfont_close) */ - READSTR (p->name, fd, fapi); /* Possible read failure ^ */ - READW (zndx, fd, fapi); - - if (pr) - { /* not first instrument? */ - if (zndx < pzndx) - return (gerr (ErrCorr, - _("Instrument header indices not monotonic"))); - i2 = zndx - pzndx; - while (i2--) - pr->zone = fluid_list_prepend (pr->zone, NULL); - } - else if (zndx > 0) /* 1st inst, warn if ofs >0 */ - FLUID_LOG (FLUID_WARN, _("%d instrument zones not referenced, discarding"), - zndx); - pzndx = zndx; - pr = p; /* update instrument ptr */ - } - - FSKIP (20, fd, fapi); - READW (zndx, fd, fapi); - - if (zndx < pzndx) - return (gerr (ErrCorr, _("Instrument header indices not monotonic"))); - i2 = zndx - pzndx; - while (i2--) - pr->zone = fluid_list_prepend (pr->zone, NULL); - - return (OK); -} - -/* instrument bag loader */ -static int -load_ibag (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - fluid_list_t *p, *p2; - SFZone *z, *pz = NULL; - unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0; - int i; - - if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */ - return (gerr (ErrCorr, _("Instrument bag chunk size is invalid"))); - - p = sf->inst; - while (p) - { /* traverse through inst */ - p2 = ((SFInst *) (p->data))->zone; - while (p2) - { /* load this inst's zones */ - if ((size -= SFBAGSIZE) < 0) - return (gerr (ErrCorr, _("Instrument bag chunk size mismatch"))); - z = FLUID_NEW (SFZone); - p2->data = z; - z->gen = NULL; /* In case of failure, */ - z->mod = NULL; /* sfont_close can clean up */ - READW (genndx, fd, fapi); /* READW = possible read failure */ - READW (modndx, fd, fapi); - z->instsamp = NULL; - - if (pz) - { /* if not first zone */ - if (genndx < pgenndx) - return (gerr (ErrCorr, - _("Instrument generator indices not monotonic"))); - if (modndx < pmodndx) - return (gerr (ErrCorr, - _("Instrument modulator indices not monotonic"))); - i = genndx - pgenndx; - while (i--) - pz->gen = fluid_list_prepend (pz->gen, NULL); - i = modndx - pmodndx; - while (i--) - pz->mod = fluid_list_prepend (pz->mod, NULL); - } - pz = z; /* update previous zone ptr */ - pgenndx = genndx; - pmodndx = modndx; - p2 = fluid_list_next (p2); - } - p = fluid_list_next (p); - } - - size -= SFBAGSIZE; - if (size != 0) - return (gerr (ErrCorr, _("Instrument chunk size mismatch"))); - - READW (genndx, fd, fapi); - READW (modndx, fd, fapi); - - if (!pz) - { /* in case that all are no zoners */ - if (genndx > 0) - FLUID_LOG (FLUID_WARN, - _("No instrument generators and terminal index not 0")); - if (modndx > 0) - FLUID_LOG (FLUID_WARN, - _("No instrument modulators and terminal index not 0")); - return (OK); - } - - if (genndx < pgenndx) - return (gerr (ErrCorr, _("Instrument generator indices not monotonic"))); - if (modndx < pmodndx) - return (gerr (ErrCorr, _("Instrument modulator indices not monotonic"))); - i = genndx - pgenndx; - while (i--) - pz->gen = fluid_list_prepend (pz->gen, NULL); - i = modndx - pmodndx; - while (i--) - pz->mod = fluid_list_prepend (pz->mod, NULL); - - return (OK); -} - -/* instrument modulator loader */ -static int -load_imod (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - fluid_list_t *p, *p2, *p3; - SFMod *m; - - p = sf->inst; - while (p) - { /* traverse through all inst */ - p2 = ((SFInst *) (p->data))->zone; - while (p2) - { /* traverse this inst's zones */ - p3 = ((SFZone *) (p2->data))->mod; - while (p3) - { /* load zone's modulators */ - if ((size -= SFMODSIZE) < 0) - return (gerr (ErrCorr, - _("Instrument modulator chunk size mismatch"))); - m = FLUID_NEW (SFMod); - p3->data = m; - READW (m->src, fd, fapi); - READW (m->dest, fd, fapi); - READW (m->amount, fd, fapi); - READW (m->amtsrc, fd, fapi); - READW (m->trans, fd, fapi); - p3 = fluid_list_next (p3); - } - p2 = fluid_list_next (p2); - } - p = fluid_list_next (p); - } - - /* - If there isn't even a terminal record - Hmmm, the specs say there should be one, but.. - */ - if (size == 0) - return (OK); - - size -= SFMODSIZE; - if (size != 0) - return (gerr (ErrCorr, _("Instrument modulator chunk size mismatch"))); - FSKIP (SFMODSIZE, fd, fapi); /* terminal mod */ - - return (OK); -} - -/* load instrument generators (see load_pgen for loading rules) */ -static int -load_igen (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - fluid_list_t *p, *p2, *p3, *dup, **hz = NULL; - SFZone *z; - SFGen *g; - SFGenAmount genval; - unsigned short genid; - int level, skip, drop, gzone, discarded; - - p = sf->inst; - while (p) - { /* traverse through all instruments */ - gzone = FALSE; - discarded = FALSE; - p2 = ((SFInst *) (p->data))->zone; - if (p2) - hz = &p2; - while (p2) - { /* traverse this instrument's zones */ - level = 0; - z = (SFZone *) (p2->data); - p3 = z->gen; - while (p3) - { /* load zone's generators */ - dup = NULL; - skip = FALSE; - drop = FALSE; - if ((size -= SFGENSIZE) < 0) - return (gerr (ErrCorr, _("IGEN chunk size mismatch"))); - - READW (genid, fd, fapi); - - if (genid == Gen_KeyRange) - { /* nothing precedes */ - if (level == 0) - { - level = 1; - READB (genval.range.lo, fd, fapi); - READB (genval.range.hi, fd, fapi); - } - else - skip = TRUE; - } - else if (genid == Gen_VelRange) - { /* only KeyRange precedes */ - if (level <= 1) - { - level = 2; - READB (genval.range.lo, fd, fapi); - READB (genval.range.hi, fd, fapi); - } - else - skip = TRUE; - } - else if (genid == Gen_SampleId) - { /* sample is last gen */ - level = 3; - READW (genval.uword, fd, fapi); - ((SFZone *) (p2->data))->instsamp = GINT_TO_POINTER (genval.uword + 1); - break; /* break out of generator loop */ - } - else - { - level = 2; - if (gen_valid (genid)) - { /* gen valid? */ - READW (genval.sword, fd, fapi); - dup = gen_inlist (genid, z->gen); - } - else - skip = TRUE; - } - - if (!skip) - { - if (!dup) - { /* if gen ! dup alloc new */ - g = FLUID_NEW (SFGen); - p3->data = g; - g->id = genid; - } - else - { - g = (SFGen *) (dup->data); - drop = TRUE; - } - g->amount = genval; - } - else - { /* skip this generator */ - discarded = TRUE; - drop = TRUE; - FSKIPW (fd, fapi); - } - - if (!drop) - p3 = fluid_list_next (p3); /* next gen */ - else - SLADVREM (z->gen, p3); - - } /* generator loop */ - - if (level == 3) - SLADVREM (z->gen, p3); /* zone has sample? */ - else - { /* its a global zone */ - if (!gzone) - { - gzone = TRUE; - - /* if global zone is not 1st zone, relocate */ - if (*hz != p2) - { - void* save = p2->data; - FLUID_LOG (FLUID_WARN, - _("Instrument \"%s\": Global zone is not first zone"), - ((SFPreset *) (p->data))->name); - SLADVREM (*hz, p2); - *hz = fluid_list_prepend (*hz, save); - continue; - } - } - else - { /* previous global zone exists, discard */ - FLUID_LOG (FLUID_WARN, - _("Instrument \"%s\": Discarding invalid global zone"), - ((SFInst *) (p->data))->name); - sfont_zone_delete (sf, hz, (SFZone *) (p2->data)); - } - } - - while (p3) - { /* Kill any zones following a sample */ - discarded = TRUE; - if ((size -= SFGENSIZE) < 0) - return (gerr (ErrCorr, - _("Instrument generator chunk size mismatch"))); - FSKIP (SFGENSIZE, fd, fapi); - SLADVREM (z->gen, p3); - } - - p2 = fluid_list_next (p2); /* next zone */ - } - if (discarded) - FLUID_LOG(FLUID_WARN, - _("Instrument \"%s\": Some invalid generators were discarded"), - ((SFInst *) (p->data))->name); - p = fluid_list_next (p); - } - - /* for those non-terminal record cases, grr! */ - if (size == 0) - return (OK); - - size -= SFGENSIZE; - if (size != 0) - return (gerr (ErrCorr, _("IGEN chunk size mismatch"))); - FSKIP (SFGENSIZE, fd, fapi); /* terminal gen */ - - return (OK); -} - -/* sample header loader */ -static int -load_shdr (unsigned int size, SFData * sf, void * fd, fluid_fileapi_t* fapi) -{ - unsigned int i; - SFSample *p; - - if (size % SFSHDRSIZE || size == 0) /* size is multiple of SHDR size? */ - return (gerr (ErrCorr, _("Sample header has invalid size"))); - - size = size / SFSHDRSIZE - 1; - if (size == 0) - { /* at least one sample + term record? */ - FLUID_LOG (FLUID_WARN, _("File contains no samples")); - FSKIP (SFSHDRSIZE, fd, fapi); - return (OK); - } - - /* load all sample headers */ - for (i = 0; i < size; i++) - { - p = FLUID_NEW (SFSample); - sf->sample = fluid_list_append (sf->sample, p); - READSTR (p->name, fd, fapi); - READD (p->start, fd, fapi); - READD (p->end, fd, fapi); /* - end, loopstart and loopend */ - READD (p->loopstart, fd, fapi); /* - will be checked and turned into */ - READD (p->loopend, fd, fapi); /* - offsets in fixup_sample() */ - READD (p->samplerate, fd, fapi); - READB (p->origpitch, fd, fapi); - READB (p->pitchadj, fd, fapi); - FSKIPW (fd, fapi); /* skip sample link */ - READW (p->sampletype, fd, fapi); - p->samfile = 0; - } - - FSKIP (SFSHDRSIZE, fd, fapi); /* skip terminal shdr */ - - return (OK); -} - -/* "fixup" (inst # -> inst ptr) instrument references in preset list */ -static int -fixup_pgen (SFData * sf) -{ - fluid_list_t *p, *p2, *p3; - SFZone *z; - uintptr i; - - p = sf->preset; - while (p) - { - p2 = ((SFPreset *) (p->data))->zone; - while (p2) - { /* traverse this preset's zones */ - z = (SFZone *) (p2->data); - if ((i = GPOINTER_TO_INT (z->instsamp))) - { /* load instrument # */ - p3 = fluid_list_nth (sf->inst, i - 1); - if (!p3) - return (gerr (ErrCorr, - _("Preset %03d %03d: Invalid instrument reference"), - ((SFPreset *) (p->data))->bank, - ((SFPreset *) (p->data))->prenum)); - z->instsamp = p3; - } - else - z->instsamp = NULL; - p2 = fluid_list_next (p2); - } - p = fluid_list_next (p); - } - - return (OK); -} - -/* "fixup" (sample # -> sample ptr) sample references in instrument list */ -static int -fixup_igen (SFData * sf) -{ - fluid_list_t *p, *p2, *p3; - SFZone *z; - uintptr i; - - p = sf->inst; - while (p) - { - p2 = ((SFInst *) (p->data))->zone; - while (p2) - { /* traverse instrument's zones */ - z = (SFZone *) (p2->data); - if ((i = GPOINTER_TO_INT (z->instsamp))) - { /* load sample # */ - p3 = fluid_list_nth (sf->sample, i - 1); - if (!p3) - return (gerr (ErrCorr, - _("Instrument \"%s\": Invalid sample reference"), - ((SFInst *) (p->data))->name)); - z->instsamp = p3; - } - p2 = fluid_list_next (p2); - } - p = fluid_list_next (p); - } - - return (OK); -} - -/* convert sample end, loopstart and loopend to offsets and check if valid */ -static int -fixup_sample (SFData * sf) -{ - fluid_list_t *p; - SFSample *sam; - int invalid_loops=FALSE; - int invalid_loopstart; - int invalid_loopend, loopend_end_mismatch; - - p = sf->sample; - while (p) - { - sam = (SFSample *) (p->data); - - /* The SoundFont 2.4 spec defines the loop start index as the first sample point of the loop */ - invalid_loopstart = (sam->loopstart < sam->start) || (sam->loopstart >= sam->loopend); - /* while loop end is the first point AFTER the last sample of the loop. - * this is as it should be. however we cannot be sure whether any of sam.loopend or sam.end - * is correct. hours of thinking through this have concluded, that it would be best practice - * to mangle with loops as little as necessary by only making sure loopend is within - * sdtachunk_size. incorrect soundfont shall preferably fail loudly. */ - invalid_loopend = (sam->loopend > sdtachunk_size) || (sam->loopstart >= sam->loopend); - - loopend_end_mismatch = (sam->loopend > sam->end); - - /* if sample is not a ROM sample and end is over the sample data chunk - or sam start is greater than 4 less than the end (at least 4 samples) */ - if ((!(sam->sampletype & FLUID_SAMPLETYPE_ROM) - && sam->end > sdtachunk_size) || sam->start > (sam->end - 4)) { - FLUID_LOG (FLUID_WARN, _("Sample '%s' start/end file positions are invalid," - " disabling and will not be saved"), sam->name); - - /* disable sample by setting all sample markers to 0 */ - sam->start = sam->end = sam->loopstart = sam->loopend = 0; - - return (OK); - } - /* compressed samples get fixed up after decompression */ - else if (sam->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS) - {} - else if (invalid_loopstart || invalid_loopend || loopend_end_mismatch) - { - /* loop is fowled?? (cluck cluck :) */ - invalid_loops |= TRUE; - - /* force incorrect loop points into the sample range, ignore padding */ - if(invalid_loopstart) - { - sam->loopstart = sam->start + 1; - } - - if(invalid_loopend) - { - sam->loopend = sam->end - 1; - } - } - - /* convert sample end, loopstart, loopend to offsets from sam->start */ - sam->end -= sam->start + 1; /* marks last sample, contrary to SF spec. */ - sam->loopstart -= sam->start; - sam->loopend -= sam->start; - - p = fluid_list_next (p); - } - - return (OK); -} - -/*=================================sfont.c======================== - Smurf SoundFont Editor - ================================================================*/ - - -/* optimum chunk area sizes (could be more optimum) */ -#define PRESET_CHUNK_OPTIMUM_AREA 256 -#define INST_CHUNK_OPTIMUM_AREA 256 -#define SAMPLE_CHUNK_OPTIMUM_AREA 256 -#define ZONE_CHUNK_OPTIMUM_AREA 256 -#define MOD_CHUNK_OPTIMUM_AREA 256 -#define GEN_CHUNK_OPTIMUM_AREA 256 - -unsigned short badgen[] = { Gen_Unused1, Gen_Unused2, Gen_Unused3, Gen_Unused4, - Gen_Reserved1, Gen_Reserved2, Gen_Reserved3, 0 -}; - -unsigned short badpgen[] = { Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs, - Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_EndAddrCoarseOfs, - Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity, - Gen_EndLoopAddrCoarseOfs, Gen_SampleModes, Gen_ExclusiveClass, - Gen_OverrideRootKey, 0 -}; - -/* close SoundFont file and delete a SoundFont structure */ -void -sfont_close (SFData * sf, fluid_fileapi_t* fapi) -{ - fluid_list_t *p, *p2; - - if (sf->sffd) - fapi->fclose (sf->sffd); - - if (sf->fname) - free (sf->fname); - - p = sf->info; - while (p) - { - free (p->data); - p = fluid_list_next (p); - } - delete_fluid_list(sf->info); - sf->info = NULL; - - p = sf->preset; - while (p) - { /* loop over presets */ - p2 = ((SFPreset *) (p->data))->zone; - while (p2) - { /* loop over preset's zones */ - sfont_free_zone (p2->data); - p2 = fluid_list_next (p2); - } /* free preset's zone list */ - delete_fluid_list (((SFPreset *) (p->data))->zone); - FLUID_FREE (p->data); /* free preset chunk */ - p = fluid_list_next (p); - } - delete_fluid_list (sf->preset); - sf->preset = NULL; - - p = sf->inst; - while (p) - { /* loop over instruments */ - p2 = ((SFInst *) (p->data))->zone; - while (p2) - { /* loop over inst's zones */ - sfont_free_zone (p2->data); - p2 = fluid_list_next (p2); - } /* free inst's zone list */ - delete_fluid_list (((SFInst *) (p->data))->zone); - FLUID_FREE (p->data); - p = fluid_list_next (p); - } - delete_fluid_list (sf->inst); - sf->inst = NULL; - - p = sf->sample; - while (p) - { - FLUID_FREE (p->data); - p = fluid_list_next (p); - } - delete_fluid_list (sf->sample); - sf->sample = NULL; - - FLUID_FREE (sf); -} - -/* free all elements of a zone (Preset or Instrument) */ -void -sfont_free_zone (SFZone * zone) -{ - fluid_list_t *p; - - if (!zone) - return; - - p = zone->gen; - while (p) - { /* Free gen chunks for this zone */ - if (p->data) - FLUID_FREE (p->data); - p = fluid_list_next (p); - } - delete_fluid_list (zone->gen); /* free genlist */ - - p = zone->mod; - while (p) - { /* Free mod chunks for this zone */ - if (p->data) - FLUID_FREE (p->data); - p = fluid_list_next (p); - } - delete_fluid_list (zone->mod); /* free modlist */ - - FLUID_FREE (zone); /* free zone chunk */ -} - -/* preset sort function, first by bank, then by preset # */ -int -sfont_preset_compare_func (void* a, void* b) -{ - int aval, bval; - - aval = (int) (((SFPreset *) a)->bank) << 16 | ((SFPreset *) a)->prenum; - bval = (int) (((SFPreset *) b)->bank) << 16 | ((SFPreset *) b)->prenum; - - return (aval - bval); -} - -/* delete zone from zone list */ -void -sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone) -{ - *zlist = fluid_list_remove (*zlist, (void*) zone); - sfont_free_zone (zone); -} - -/* Find generator in gen list */ -fluid_list_t * -gen_inlist (int gen, fluid_list_t * genlist) -{ /* is generator in gen list? */ - fluid_list_t *p; - - p = genlist; - while (p) - { - if (p->data == NULL) - return (NULL); - if (gen == ((SFGen *) p->data)->id) - break; - p = fluid_list_next (p); - } - return (p); -} - -/* check validity of instrument generator */ -int -gen_valid (int gen) -{ /* is generator id valid? */ - int i = 0; - - if (gen > Gen_MaxValid) - return (FALSE); - while (badgen[i] && badgen[i] != gen) - i++; - return (badgen[i] == 0); -} - -/* check validity of preset generator */ -int -gen_validp (int gen) -{ /* is preset generator valid? */ - int i = 0; - - if (!gen_valid (gen)) - return (FALSE); - while (badpgen[i] && badpgen[i] != (unsigned short) gen) - i++; - return (badpgen[i] == 0); -} - -/*================================util.c===========================*/ - -/* Logging function, returns FAIL to use as a return value in calling funcs */ -int -gerr (int ev, char * fmt, ...) -{ - va_list args; - - va_start (args, fmt); - vprintf(fmt, args); - va_end (args); - - printf("\n"); - - return (FAIL); -} diff --git a/libraries/fluidlite/src/fluid_defsfont.h b/libraries/fluidlite/src/fluid_defsfont.h deleted file mode 100644 index e610b2599..000000000 --- a/libraries/fluidlite/src/fluid_defsfont.h +++ /dev/null @@ -1,603 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * SoundFont loading code borrowed from Smurf SoundFont Editor by Josh Green - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#ifndef _FLUID_DEFSFONT_H -#define _FLUID_DEFSFONT_H - - -#include "fluidlite.h" -#include "fluidsynth_priv.h" -#include "fluid_list.h" - - - -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ - -/*-----------------------------------sfont.h----------------------------*/ - -#define SF_SAMPMODES_LOOP 1 -#define SF_SAMPMODES_UNROLL 2 - -#define SF_MIN_SAMPLERATE 400 -#define SF_MAX_SAMPLERATE 50000 - -#define SF_MIN_SAMPLE_LENGTH 32 - -/* Sound Font structure defines */ - -typedef struct _SFVersion -{ /* version structure */ - unsigned short major; - unsigned short minor; -} -SFVersion; - -typedef struct _SFMod -{ /* Modulator structure */ - unsigned short src; /* source modulator */ - unsigned short dest; /* destination generator */ - signed short amount; /* signed, degree of modulation */ - unsigned short amtsrc; /* second source controls amnt of first */ - unsigned short trans; /* transform applied to source */ -} -SFMod; - -typedef union _SFGenAmount -{ /* Generator amount structure */ - signed short sword; /* signed 16 bit value */ - unsigned short uword; /* unsigned 16 bit value */ - struct - { - unsigned char lo; /* low value for ranges */ - unsigned char hi; /* high value for ranges */ - } - range; -} -SFGenAmount; - -typedef struct _SFGen -{ /* Generator structure */ - unsigned short id; /* generator ID */ - SFGenAmount amount; /* generator value */ -} -SFGen; - -typedef struct _SFZone -{ /* Sample/instrument zone structure */ - fluid_list_t *instsamp; /* instrument/sample pointer for zone */ - fluid_list_t *gen; /* list of generators */ - fluid_list_t *mod; /* list of modulators */ -} -SFZone; - -typedef struct _SFSample -{ /* Sample structure */ - char name[21]; /* Name of sample */ - unsigned char samfile; /* Loaded sfont/sample buffer = 0/1 */ - unsigned int start; /* Offset in sample area to start of sample */ - unsigned int end; /* Offset from start to end of sample, - this is the last point of the - sample, the SF spec has this as the - 1st point after, corrected on - load/save */ - unsigned int loopstart; /* Offset from start to start of loop */ - unsigned int loopend; /* Offset from start to end of loop, - marks the first point after loop, - whose sample value is ideally - equivalent to loopstart */ - unsigned int samplerate; /* Sample rate recorded at */ - unsigned char origpitch; /* root midi key number */ - signed char pitchadj; /* pitch correction in cents */ - unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */ -} -SFSample; - -typedef struct _SFInst -{ /* Instrument structure */ - char name[21]; /* Name of instrument */ - fluid_list_t *zone; /* list of instrument zones */ -} -SFInst; - -typedef struct _SFPreset -{ /* Preset structure */ - char name[21]; /* preset name */ - unsigned short prenum; /* preset number */ - unsigned short bank; /* bank number */ - unsigned int libr; /* Not used (preserved) */ - unsigned int genre; /* Not used (preserved) */ - unsigned int morph; /* Not used (preserved) */ - fluid_list_t *zone; /* list of preset zones */ -} -SFPreset; - -/* NOTE: sffd is also used to determine if sound font is new (NULL) */ -typedef struct _SFData -{ /* Sound font data structure */ - SFVersion version; /* sound font version */ - SFVersion romver; /* ROM version */ - unsigned int samplepos; /* position within sffd of the sample chunk */ - unsigned int samplesize; /* length within sffd of the sample chunk */ - char *fname; /* file name */ - FILE *sffd; /* loaded sfont file descriptor */ - fluid_list_t *info; /* linked list of info strings (1st byte is ID) */ - fluid_list_t *preset; /* linked list of preset info */ - fluid_list_t *inst; /* linked list of instrument info */ - fluid_list_t *sample; /* linked list of sample info */ -} -SFData; - -/* sf file chunk IDs */ -enum -{ UNKN_ID, RIFF_ID, LIST_ID, SFBK_ID, - INFO_ID, SDTA_ID, PDTA_ID, /* info/sample/preset */ - - IFIL_ID, ISNG_ID, INAM_ID, IROM_ID, /* info ids (1st byte of info strings) */ - IVER_ID, ICRD_ID, IENG_ID, IPRD_ID, /* more info ids */ - ICOP_ID, ICMT_ID, ISFT_ID, /* and yet more info ids */ - - SNAM_ID, SMPL_ID, /* sample ids */ - PHDR_ID, PBAG_ID, PMOD_ID, PGEN_ID, /* preset ids */ - IHDR_ID, IBAG_ID, IMOD_ID, IGEN_ID, /* instrument ids */ - SHDR_ID /* sample info */ -}; - -/* generator types */ -typedef enum -{ Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs, - Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_ModLFO2Pitch, - Gen_VibLFO2Pitch, Gen_ModEnv2Pitch, Gen_FilterFc, Gen_FilterQ, - Gen_ModLFO2FilterFc, Gen_ModEnv2FilterFc, Gen_EndAddrCoarseOfs, - Gen_ModLFO2Vol, Gen_Unused1, Gen_ChorusSend, Gen_ReverbSend, Gen_Pan, - Gen_Unused2, Gen_Unused3, Gen_Unused4, - Gen_ModLFODelay, Gen_ModLFOFreq, Gen_VibLFODelay, Gen_VibLFOFreq, - Gen_ModEnvDelay, Gen_ModEnvAttack, Gen_ModEnvHold, Gen_ModEnvDecay, - Gen_ModEnvSustain, Gen_ModEnvRelease, Gen_Key2ModEnvHold, - Gen_Key2ModEnvDecay, Gen_VolEnvDelay, Gen_VolEnvAttack, - Gen_VolEnvHold, Gen_VolEnvDecay, Gen_VolEnvSustain, Gen_VolEnvRelease, - Gen_Key2VolEnvHold, Gen_Key2VolEnvDecay, Gen_Instrument, - Gen_Reserved1, Gen_KeyRange, Gen_VelRange, - Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity, - Gen_Attenuation, Gen_Reserved2, Gen_EndLoopAddrCoarseOfs, - Gen_CoarseTune, Gen_FineTune, Gen_SampleId, Gen_SampleModes, - Gen_Reserved3, Gen_ScaleTune, Gen_ExclusiveClass, Gen_OverrideRootKey, - Gen_Dummy -} -Gen_Type; - -#define Gen_MaxValid Gen_Dummy - 1 /* maximum valid generator */ -#define Gen_Count Gen_Dummy /* count of generators */ -#define GenArrSize sizeof(SFGenAmount)*Gen_Count /* gen array size */ - -/* generator unit type */ -typedef enum -{ - None, /* No unit type */ - Unit_Smpls, /* in samples */ - Unit_32kSmpls, /* in 32k samples */ - Unit_Cent, /* in cents (1/100th of a semitone) */ - Unit_HzCent, /* in Hz Cents */ - Unit_TCent, /* in Time Cents */ - Unit_cB, /* in centibels (1/100th of a decibel) */ - Unit_Percent, /* in percentage */ - Unit_Semitone, /* in semitones */ - Unit_Range /* a range of values */ -} -Gen_Unit; - -/* global data */ - -extern unsigned short badgen[]; /* list of bad generators */ -extern unsigned short badpgen[]; /* list of bad preset generators */ - -/* functions */ -void sfont_init_chunks (void); - -void sfont_close (SFData * sf, fluid_fileapi_t * fileapi); -void sfont_free_zone (SFZone * zone); -int sfont_preset_compare_func (void* a, void* b); - -void sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone); - -fluid_list_t *gen_inlist (int gen, fluid_list_t * genlist); -int gen_valid (int gen); -int gen_validp (int gen); - - -/*-----------------------------------sffile.h----------------------------*/ -/* - File structures and routines (used to be in sffile.h) -*/ - -#define CHNKIDSTR(id) &idlist[(id - 1) * 4] - -/* sfont file chunk sizes */ -#define SFPHDRSIZE 38 -#define SFBAGSIZE 4 -#define SFMODSIZE 10 -#define SFGENSIZE 4 -#define SFIHDRSIZE 22 -#define SFSHDRSIZE 46 - -/* sfont file data structures */ -typedef struct _SFChunk -{ /* RIFF file chunk structure */ - unsigned int id; /* chunk id */ - unsigned int size; /* size of the following chunk */ -} -SFChunk; - -typedef struct _SFPhdr -{ - unsigned char name[20]; /* preset name */ - unsigned short preset; /* preset number */ - unsigned short bank; /* bank number */ - unsigned short pbagndx; /* index into preset bag */ - unsigned int library; /* just for preserving them */ - unsigned int genre; /* Not used */ - unsigned int morphology; /* Not used */ -} -SFPhdr; - -typedef struct _SFBag -{ - unsigned short genndx; /* index into generator list */ - unsigned short modndx; /* index into modulator list */ -} -SFBag; - -typedef struct _SFIhdr -{ - char name[20]; /* Name of instrument */ - unsigned short ibagndx; /* Instrument bag index */ -} -SFIhdr; - -typedef struct _SFShdr -{ /* Sample header loading struct */ - char name[20]; /* Sample name */ - unsigned int start; /* Offset to start of sample */ - unsigned int end; /* Offset to end of sample */ - unsigned int loopstart; /* Offset to start of loop */ - unsigned int loopend; /* Offset to end of loop */ - unsigned int samplerate; /* Sample rate recorded at */ - unsigned char origpitch; /* root midi key number */ - signed char pitchadj; /* pitch correction in cents */ - unsigned short samplelink; /* Not used */ - unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */ -} -SFShdr; - -/* data */ -extern char idlist[]; - -/* functions */ -SFData *sfload_file (const char * fname, fluid_fileapi_t * fileapi); - - - -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ - -/* GLIB - Library of useful routines for C programming - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - - -/* Provide definitions for some commonly used macros. - * Some of them are only provided if they haven't already - * been defined. It is assumed that if they are already - * defined then the current definition is correct. - */ -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -#define GPOINTER_TO_INT(p) ((uintptr) (p)) -#define GINT_TO_POINTER(i) ((void *) (uintptr)(i)) - -char* g_strdup (const char *str); - - - - - -/* Provide simple macro statement wrappers (adapted from Perl): - * G_STMT_START { statements; } G_STMT_END; - * can be used as a single statement, as in - * if (x) G_STMT_START { ... } G_STMT_END; else ... - * - * For gcc we will wrap the statements within `({' and `})' braces. - * For SunOS they will be wrapped within `if (1)' and `else (void) 0', - * and otherwise within `do' and `while (0)'. - */ -#if !(defined (G_STMT_START) && defined (G_STMT_END)) -# if defined (__GNUC__) && !defined (__STRICT_ANSI__) && !defined (__cplusplus) -# define G_STMT_START (void)( -# define G_STMT_END ) -# else -# if (defined (sun) || defined (__sun__)) -# define G_STMT_START if (1) -# define G_STMT_END else (void)0 -# else -# define G_STMT_START do -# define G_STMT_END while (0) -# endif -# endif -#endif - - -/* Basic bit swapping functions - */ -#define GUINT16_SWAP_LE_BE_CONSTANT(val) ((unsigned short) ( \ - (((unsigned short) (val) & (unsigned short) 0x00ffU) << 8) | \ - (((unsigned short) (val) & (unsigned short) 0xff00U) >> 8))) -#define GUINT32_SWAP_LE_BE_CONSTANT(val) ((unsigned int) ( \ - (((unsigned int) (val) & (unsigned int) 0x000000ffU) << 24) | \ - (((unsigned int) (val) & (unsigned int) 0x0000ff00U) << 8) | \ - (((unsigned int) (val) & (unsigned int) 0x00ff0000U) >> 8) | \ - (((unsigned int) (val) & (unsigned int) 0xff000000U) >> 24))) - -#define GUINT16_SWAP_LE_BE(val) (GUINT16_SWAP_LE_BE_CONSTANT (val)) -#define GUINT32_SWAP_LE_BE(val) (GUINT32_SWAP_LE_BE_CONSTANT (val)) - -#define GINT16_TO_LE(val) ((signed short) (val)) -#define GUINT16_TO_LE(val) ((unsigned short) (val)) -#define GINT16_TO_BE(val) ((signed short) GUINT16_SWAP_LE_BE (val)) -#define GUINT16_TO_BE(val) (GUINT16_SWAP_LE_BE (val)) -#define GINT32_TO_LE(val) ((signed int) (val)) -#define GUINT32_TO_LE(val) ((unsigned int) (val)) -#define GINT32_TO_BE(val) ((signed int) GUINT32_SWAP_LE_BE (val)) -#define GUINT32_TO_BE(val) (GUINT32_SWAP_LE_BE (val)) - -/* The G*_TO_?E() macros are defined in glibconfig.h. - * The transformation is symmetric, so the FROM just maps to the TO. - */ -#define GINT16_FROM_LE(val) (GINT16_TO_LE (val)) -#define GUINT16_FROM_LE(val) (GUINT16_TO_LE (val)) -#define GINT16_FROM_BE(val) (GINT16_TO_BE (val)) -#define GUINT16_FROM_BE(val) (GUINT16_TO_BE (val)) -#define GINT32_FROM_LE(val) (GINT32_TO_LE (val)) -#define GUINT32_FROM_LE(val) (GUINT32_TO_LE (val)) -#define GINT32_FROM_BE(val) (GINT32_TO_BE (val)) -#define GUINT32_FROM_BE(val) (GUINT32_TO_BE (val)) - - -/*-----------------------------------util.h----------------------------*/ -/* - Utility functions (formerly in util.h) - */ -#define FAIL 0 -#define OK 1 - -enum -{ ErrWarn, ErrFatal, ErrStatus, ErrCorr, ErrEof, ErrMem, Errno, - ErrRead, ErrWrite -}; - -#define ErrMax ErrWrite -#define ErrnoStart Errno -#define ErrnoEnd ErrWrite - -int gerr (int ev, char * fmt, ...); - - -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ - - - -/*************************************************************** - * - * FORWARD DECLARATIONS - */ -typedef struct _fluid_defsfont_t fluid_defsfont_t; -typedef struct _fluid_defpreset_t fluid_defpreset_t; -typedef struct _fluid_preset_zone_t fluid_preset_zone_t; -typedef struct _fluid_inst_t fluid_inst_t; -typedef struct _fluid_inst_zone_t fluid_inst_zone_t; - -/* - - Public interface - - */ - -fluid_sfloader_t* new_fluid_defsfloader(void); -int delete_fluid_defsfloader(fluid_sfloader_t* loader); -fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename); - - -int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont); -char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont); -fluid_preset_t* fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); -void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont); -int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset); - - -int fluid_defpreset_preset_delete(fluid_preset_t* preset); -char* fluid_defpreset_preset_get_name(fluid_preset_t* preset); -int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset); -int fluid_defpreset_preset_get_num(fluid_preset_t* preset); -int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); - - -/* - * fluid_defsfont_t - */ -struct _fluid_defsfont_t -{ - char* filename; /* the filename of this soundfont */ - unsigned int samplepos; /* the position in the file at which the sample data starts */ - unsigned int samplesize; /* the size of the sample data */ - short* sampledata; /* the sample data, loaded in ram */ - fluid_list_t* sample; /* the samples in this soundfont */ - fluid_defpreset_t* preset; /* the presets of this soundfont */ - - fluid_preset_t iter_preset; /* preset interface used in the iteration */ - fluid_defpreset_t* iter_cur; /* the current preset in the iteration */ -}; - - -fluid_defsfont_t* new_fluid_defsfont(void); -int delete_fluid_defsfont(fluid_defsfont_t* sfont); -int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file, fluid_fileapi_t * fileapi); -char* fluid_defsfont_get_name(fluid_defsfont_t* sfont); -fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int prenum); -void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont); -int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset); -int fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont, fluid_fileapi_t * fileapi); -int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample); -int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset); -fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s); - - -/* - * fluid_preset_t - */ -struct _fluid_defpreset_t -{ - fluid_defpreset_t* next; - fluid_defsfont_t* sfont; /* the soundfont this preset belongs to */ - char name[21]; /* the name of the preset */ - unsigned int bank; /* the bank number */ - unsigned int num; /* the preset number */ - fluid_preset_zone_t* global_zone; /* the global zone of the preset */ - fluid_preset_zone_t* zone; /* the chained list of preset zones */ -}; - -fluid_defpreset_t* new_fluid_defpreset(fluid_defsfont_t* sfont); -int delete_fluid_defpreset(fluid_defpreset_t* preset); -fluid_defpreset_t* fluid_defpreset_next(fluid_defpreset_t* preset); -int fluid_defpreset_import_sfont(fluid_defpreset_t* preset, SFPreset* sfpreset, fluid_defsfont_t* sfont); -int fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone); -int fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone); -fluid_preset_zone_t* fluid_defpreset_get_zone(fluid_defpreset_t* preset); -fluid_preset_zone_t* fluid_defpreset_get_global_zone(fluid_defpreset_t* preset); -int fluid_defpreset_get_banknum(fluid_defpreset_t* preset); -int fluid_defpreset_get_num(fluid_defpreset_t* preset); -char* fluid_defpreset_get_name(fluid_defpreset_t* preset); -int fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); - -/* - * fluid_preset_zone - */ -struct _fluid_preset_zone_t -{ - fluid_preset_zone_t* next; - char* name; - fluid_inst_t* inst; - int keylo; - int keyhi; - int vello; - int velhi; - fluid_gen_t gen[GEN_LAST]; - fluid_mod_t * mod; /* List of modulators */ -}; - -fluid_preset_zone_t* new_fluid_preset_zone(char* name); -int delete_fluid_preset_zone(fluid_preset_zone_t* zone); -fluid_preset_zone_t* fluid_preset_zone_next(fluid_preset_zone_t* preset); -int fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone* sfzone, fluid_defsfont_t* sfont); -int fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel); -fluid_inst_t* fluid_preset_zone_get_inst(fluid_preset_zone_t* zone); - -/* - * fluid_inst_t - */ -struct _fluid_inst_t -{ - char name[21]; - fluid_inst_zone_t* global_zone; - fluid_inst_zone_t* zone; -}; - -fluid_inst_t* new_fluid_inst(void); -int delete_fluid_inst(fluid_inst_t* inst); -int fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont); -int fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone); -int fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone); -fluid_inst_zone_t* fluid_inst_get_zone(fluid_inst_t* inst); -fluid_inst_zone_t* fluid_inst_get_global_zone(fluid_inst_t* inst); - -/* - * fluid_inst_zone_t - */ -struct _fluid_inst_zone_t -{ - fluid_inst_zone_t* next; - char* name; - fluid_sample_t* sample; - int keylo; - int keyhi; - int vello; - int velhi; - fluid_gen_t gen[GEN_LAST]; - fluid_mod_t * mod; /* List of modulators */ -}; - -fluid_inst_zone_t* new_fluid_inst_zone(char* name); -int delete_fluid_inst_zone(fluid_inst_zone_t* zone); -fluid_inst_zone_t* fluid_inst_zone_next(fluid_inst_zone_t* zone); -int fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont); -int fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel); -fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone); - - - -fluid_sample_t* new_fluid_sample(void); -int delete_fluid_sample(fluid_sample_t* sample); -int fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont); -int fluid_sample_in_rom(fluid_sample_t* sample); - - -#endif /* _FLUID_SFONT_H */ diff --git a/libraries/fluidlite/src/fluid_dsp_float.c b/libraries/fluidlite/src/fluid_dsp_float.c deleted file mode 100644 index ab005a071..000000000 --- a/libraries/fluidlite/src/fluid_dsp_float.c +++ /dev/null @@ -1,685 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#include "fluidsynth_priv.h" -#include "fluid_phase.h" - -/* Purpose: - * - * Interpolates audio data (obtains values between the samples of the original - * waveform data). - * - * Variables loaded from the voice structure (assigned in fluid_voice_write()): - * - dsp_data: Pointer to the original waveform data - * - dsp_phase: The position in the original waveform data. - * This has an integer and a fractional part (between samples). - * - dsp_phase_incr: For each output sample, the position in the original - * waveform advances by dsp_phase_incr. This also has an integer - * part and a fractional part. - * If a sample is played at root pitch (no pitch change), - * dsp_phase_incr is integer=1 and fractional=0. - * - dsp_amp: The current amplitude envelope value. - * - dsp_amp_incr: The changing rate of the amplitude envelope. - * - * A couple of variables are used internally, their results are discarded: - * - dsp_i: Index through the output buffer - * - dsp_buf: Output buffer of floating point values (FLUID_BUFSIZE in length) - */ - -#include "fluidsynth_priv.h" -#include "fluid_synth.h" -#include "fluid_voice.h" - - -/* Interpolation (find a value between two samples of the original waveform) */ - -/* Linear interpolation table (2 coefficients centered on 1st) */ -static fluid_real_t interp_coeff_linear[FLUID_INTERP_MAX][2]; - -/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */ -static fluid_real_t interp_coeff[FLUID_INTERP_MAX][4]; - -/* 7th order interpolation (7 coefficients centered on 3rd) */ -static fluid_real_t sinc_table7[FLUID_INTERP_MAX][7]; - - -#define SINC_INTERP_ORDER 7 /* 7th order constant */ - - -/* Initializes interpolation tables */ -void fluid_dsp_float_config (void) -{ - int i, i2; - double x, v; - double i_shifted; - - /* Initialize the coefficients for the interpolation. The math comes - * from a mail, posted by Olli Niemitalo to the music-dsp mailing - * list (I found it in the music-dsp archives - * http://www.smartelectronix.com/musicdsp/). */ - - for (i = 0; i < FLUID_INTERP_MAX; i++) - { - x = (double) i / (double) FLUID_INTERP_MAX; - - interp_coeff[i][0] = (fluid_real_t)(x * (-0.5 + x * (1 - 0.5 * x))); - interp_coeff[i][1] = (fluid_real_t)(1.0 + x * x * (1.5 * x - 2.5)); - interp_coeff[i][2] = (fluid_real_t)(x * (0.5 + x * (2.0 - 1.5 * x))); - interp_coeff[i][3] = (fluid_real_t)(0.5 * x * x * (x - 1.0)); - - interp_coeff_linear[i][0] = (fluid_real_t)(1.0 - x); - interp_coeff_linear[i][1] = (fluid_real_t)x; - } - - /* i: Offset in terms of whole samples */ - for (i = 0; i < SINC_INTERP_ORDER; i++) - { /* i2: Offset in terms of fractional samples ('subsamples') */ - for (i2 = 0; i2 < FLUID_INTERP_MAX; i2++) - { - /* center on middle of table */ - i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0) - + (double)i2 / (double)FLUID_INTERP_MAX; - - /* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */ - if (fabs (i_shifted) > 0.000001) - { - v = (fluid_real_t)sin (i_shifted * M_PI) / (M_PI * i_shifted); - /* Hamming window */ - v *= (fluid_real_t)0.5 * (1.0 + cos (2.0 * M_PI * i_shifted / (fluid_real_t)SINC_INTERP_ORDER)); - } - else v = 1.0; - - sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v; - } - } - -#if 0 - for (i = 0; i < FLUID_INTERP_MAX; i++) - { - printf ("%d %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f\n", - i, sinc_table7[0][i], sinc_table7[1][i], sinc_table7[2][i], - sinc_table7[3][i], sinc_table7[4][i], sinc_table7[5][i], sinc_table7[6][i]); - } -#endif -} - - -/* No interpolation. Just take the sample, which is closest to - * the playback pointer. Questionable quality, but very - * efficient. */ -int -fluid_dsp_float_interpolate_none (fluid_voice_t *voice) -{ - fluid_phase_t dsp_phase = voice->phase; - fluid_phase_t dsp_phase_incr; - short int *dsp_data = voice->sample->data; - fluid_real_t *dsp_buf = voice->dsp_buf; - fluid_real_t dsp_amp = voice->amp; - fluid_real_t dsp_amp_incr = voice->amp_incr; - unsigned int dsp_i = 0; - unsigned int dsp_phase_index; - unsigned int end_index; - int looping; - - /* Convert playback "speed" floating point value to phase index/fract */ - fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); - - /* voice is currently looping? */ - looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE - || (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE - && voice->volenv_section < FLUID_VOICE_ENVRELEASE); - - end_index = looping ? voice->loopend - 1 : voice->end; - - while (1) - { - dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */ - - /* interpolate sequence of sample points */ - for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) - { - dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index]; - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */ - dsp_amp += dsp_amp_incr; - } - - /* break out if not looping (buffer may not be full) */ - if (!looping) break; - - /* go back to loop start */ - if (dsp_phase_index > end_index) - { - fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); - voice->has_looped = 1; - } - - /* break out if filled buffer */ - if (dsp_i >= FLUID_BUFSIZE) break; - } - - voice->phase = dsp_phase; - voice->amp = dsp_amp; - - return (dsp_i); -} - -/* Straight line interpolation. - * Returns number of samples processed (usually FLUID_BUFSIZE but could be - * smaller if end of sample occurs). - */ -int -fluid_dsp_float_interpolate_linear (fluid_voice_t *voice) -{ - fluid_phase_t dsp_phase = voice->phase; - fluid_phase_t dsp_phase_incr; - short int *dsp_data = voice->sample->data; - fluid_real_t *dsp_buf = voice->dsp_buf; - fluid_real_t dsp_amp = voice->amp; - fluid_real_t dsp_amp_incr = voice->amp_incr; - unsigned int dsp_i = 0; - unsigned int dsp_phase_index; - unsigned int end_index; - short int point; - fluid_real_t *coeffs; - int looping; - - /* Convert playback "speed" floating point value to phase index/fract */ - fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); - - /* voice is currently looping? */ - looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE - || (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE - && voice->volenv_section < FLUID_VOICE_ENVRELEASE); - - /* last index before 2nd interpolation point must be specially handled */ - end_index = (looping ? voice->loopend - 1 : voice->end) - 1; - - /* 2nd interpolation point to use at end of loop or sample */ - if (looping) point = dsp_data[voice->loopstart]; /* loop start */ - else point = dsp_data[voice->end]; /* duplicate end for samples no longer looping */ - - while (1) - { - dsp_phase_index = fluid_phase_index (dsp_phase); - - /* interpolate the sequence of sample points */ - for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) - { - coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index] - + coeffs[1] * dsp_data[dsp_phase_index+1]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - /* break out if buffer filled */ - if (dsp_i >= FLUID_BUFSIZE) break; - - end_index++; /* we're now interpolating the last point */ - - /* interpolate within last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index] - + coeffs[1] * point); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; /* increment amplitude */ - } - - if (!looping) break; /* break out if not looping (end of sample) */ - - /* go back to loop start (if past */ - if (dsp_phase_index > end_index) - { - fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); - voice->has_looped = 1; - } - - /* break out if filled buffer */ - if (dsp_i >= FLUID_BUFSIZE) break; - - end_index--; /* set end back to second to last sample point */ - } - - voice->phase = dsp_phase; - voice->amp = dsp_amp; - - return (dsp_i); -} - -/* 4th order (cubic) interpolation. - * Returns number of samples processed (usually FLUID_BUFSIZE but could be - * smaller if end of sample occurs). - */ -int -fluid_dsp_float_interpolate_4th_order (fluid_voice_t *voice) -{ - fluid_phase_t dsp_phase = voice->phase; - fluid_phase_t dsp_phase_incr; - short int *dsp_data = voice->sample->data; - fluid_real_t *dsp_buf = voice->dsp_buf; - fluid_real_t dsp_amp = voice->amp; - fluid_real_t dsp_amp_incr = voice->amp_incr; - unsigned int dsp_i = 0; - unsigned int dsp_phase_index; - unsigned int start_index, end_index; - short int start_point, end_point1, end_point2; - fluid_real_t *coeffs; - int looping; - - /* Convert playback "speed" floating point value to phase index/fract */ - fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); - - /* voice is currently looping? */ - looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE - || (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE - && voice->volenv_section < FLUID_VOICE_ENVRELEASE); - - /* last index before 4th interpolation point must be specially handled */ - end_index = (looping ? voice->loopend - 1 : voice->end) - 2; - - if (voice->has_looped) /* set start_index and start point if looped or not */ - { - start_index = voice->loopstart; - start_point = dsp_data[voice->loopend - 1]; /* last point in loop (wrap around) */ - } - else - { - start_index = voice->start; - start_point = dsp_data[voice->start]; /* just duplicate the point */ - } - - /* get points off the end (loop start if looping, duplicate point if end) */ - if (looping) - { - end_point1 = dsp_data[voice->loopstart]; - end_point2 = dsp_data[voice->loopstart + 1]; - } - else - { - end_point1 = dsp_data[voice->end]; - end_point2 = end_point1; - } - - while (1) - { - dsp_phase_index = fluid_phase_index (dsp_phase); - - /* interpolate first sample point (start or loop start) if needed */ - for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * start_point - + coeffs[1] * dsp_data[dsp_phase_index] - + coeffs[2] * dsp_data[dsp_phase_index+1] - + coeffs[3] * dsp_data[dsp_phase_index+2]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - /* interpolate the sequence of sample points */ - for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) - { - coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] - + coeffs[1] * dsp_data[dsp_phase_index] - + coeffs[2] * dsp_data[dsp_phase_index+1] - + coeffs[3] * dsp_data[dsp_phase_index+2]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - /* break out if buffer filled */ - if (dsp_i >= FLUID_BUFSIZE) break; - - end_index++; /* we're now interpolating the 2nd to last point */ - - /* interpolate within 2nd to last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] - + coeffs[1] * dsp_data[dsp_phase_index] - + coeffs[2] * dsp_data[dsp_phase_index+1] - + coeffs[3] * end_point1); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - end_index++; /* we're now interpolating the last point */ - - /* interpolate within the last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] - + coeffs[1] * dsp_data[dsp_phase_index] - + coeffs[2] * end_point1 - + coeffs[3] * end_point2); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - if (!looping) break; /* break out if not looping (end of sample) */ - - /* go back to loop start */ - if (dsp_phase_index > end_index) - { - fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); - - if (!voice->has_looped) - { - voice->has_looped = 1; - start_index = voice->loopstart; - start_point = dsp_data[voice->loopend - 1]; - } - } - - /* break out if filled buffer */ - if (dsp_i >= FLUID_BUFSIZE) break; - - end_index -= 2; /* set end back to third to last sample point */ - } - - voice->phase = dsp_phase; - voice->amp = dsp_amp; - - return (dsp_i); -} - -/* 7th order interpolation. - * Returns number of samples processed (usually FLUID_BUFSIZE but could be - * smaller if end of sample occurs). - */ -int -fluid_dsp_float_interpolate_7th_order (fluid_voice_t *voice) -{ - fluid_phase_t dsp_phase = voice->phase; - fluid_phase_t dsp_phase_incr; - short int *dsp_data = voice->sample->data; - fluid_real_t *dsp_buf = voice->dsp_buf; - fluid_real_t dsp_amp = voice->amp; - fluid_real_t dsp_amp_incr = voice->amp_incr; - unsigned int dsp_i = 0; - unsigned int dsp_phase_index; - unsigned int start_index, end_index; - short int start_points[3]; - short int end_points[3]; - fluid_real_t *coeffs; - int looping; - - /* Convert playback "speed" floating point value to phase index/fract */ - fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); - - /* add 1/2 sample to dsp_phase since 7th order interpolation is centered on - * the 4th sample point */ - fluid_phase_incr (dsp_phase, (fluid_phase_t)0x80000000); - - /* voice is currently looping? */ - looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE - || (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE - && voice->volenv_section < FLUID_VOICE_ENVRELEASE); - - /* last index before 7th interpolation point must be specially handled */ - end_index = (looping ? voice->loopend - 1 : voice->end) - 3; - - if (voice->has_looped) /* set start_index and start point if looped or not */ - { - start_index = voice->loopstart; - start_points[0] = dsp_data[voice->loopend - 1]; - start_points[1] = dsp_data[voice->loopend - 2]; - start_points[2] = dsp_data[voice->loopend - 3]; - } - else - { - start_index = voice->start; - start_points[0] = dsp_data[voice->start]; /* just duplicate the start point */ - start_points[1] = start_points[0]; - start_points[2] = start_points[0]; - } - - /* get the 3 points off the end (loop start if looping, duplicate point if end) */ - if (looping) - { - end_points[0] = dsp_data[voice->loopstart]; - end_points[1] = dsp_data[voice->loopstart + 1]; - end_points[2] = dsp_data[voice->loopstart + 2]; - } - else - { - end_points[0] = dsp_data[voice->end]; - end_points[1] = end_points[0]; - end_points[2] = end_points[0]; - } - - while (1) - { - dsp_phase_index = fluid_phase_index (dsp_phase); - - /* interpolate first sample point (start or loop start) if needed */ - for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)start_points[2] - + coeffs[1] * (fluid_real_t)start_points[1] - + coeffs[2] * (fluid_real_t)start_points[0] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] - + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - start_index++; - - /* interpolate 2nd to first sample point (start or loop start) if needed */ - for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)start_points[1] - + coeffs[1] * (fluid_real_t)start_points[0] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] - + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - start_index++; - - /* interpolate 3rd to first sample point (start or loop start) if needed */ - for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)start_points[0] - + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] - + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - start_index -= 2; /* set back to original start index */ - - - /* interpolate the sequence of sample points */ - for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) - { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] - + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] - + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - /* break out if buffer filled */ - if (dsp_i >= FLUID_BUFSIZE) break; - - end_index++; /* we're now interpolating the 3rd to last point */ - - /* interpolate within 3rd to last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] - + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] - + coeffs[6] * (fluid_real_t)end_points[0]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - end_index++; /* we're now interpolating the 2nd to last point */ - - /* interpolate within 2nd to last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] - + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)end_points[0] - + coeffs[6] * (fluid_real_t)end_points[1]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - end_index++; /* we're now interpolating the last point */ - - /* interpolate within last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] - + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)end_points[0] - + coeffs[5] * (fluid_real_t)end_points[1] - + coeffs[6] * (fluid_real_t)end_points[2]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - if (!looping) break; /* break out if not looping (end of sample) */ - - /* go back to loop start */ - if (dsp_phase_index > end_index) - { - fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); - - if (!voice->has_looped) - { - voice->has_looped = 1; - start_index = voice->loopstart; - start_points[0] = dsp_data[voice->loopend - 1]; - start_points[1] = dsp_data[voice->loopend - 2]; - start_points[2] = dsp_data[voice->loopend - 3]; - } - } - - /* break out if filled buffer */ - if (dsp_i >= FLUID_BUFSIZE) break; - - end_index -= 3; /* set end back to 4th to last sample point */ - } - - /* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on - * the 4th sample point (correct back to real value) */ - fluid_phase_decr (dsp_phase, (fluid_phase_t)0x80000000); - - voice->phase = dsp_phase; - voice->amp = dsp_amp; - - return (dsp_i); -} diff --git a/libraries/fluidlite/src/fluid_gen.c b/libraries/fluidlite/src/fluid_gen.c deleted file mode 100644 index 37b8679d9..000000000 --- a/libraries/fluidlite/src/fluid_gen.c +++ /dev/null @@ -1,149 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#include "fluid_gen.h" -#include "fluid_chan.h" - - -/* See SFSpec21 $8.1.3 */ -fluid_gen_info_t fluid_gen_info[] = { - /* number/name init scale min max def */ - { GEN_STARTADDROFS, 1, 1, 0.0f, 1e10f, 0.0f }, - { GEN_ENDADDROFS, 1, 1, -1e10f, 0.0f, 0.0f }, - { GEN_STARTLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f }, - { GEN_ENDLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f }, - { GEN_STARTADDRCOARSEOFS, 0, 1, 0.0f, 1e10f, 0.0f }, - { GEN_MODLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, - { GEN_VIBLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, - { GEN_MODENVTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, - { GEN_FILTERFC, 1, 2, 1500.0f, 13500.0f, 13500.0f }, - { GEN_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f }, - { GEN_MODLFOTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f }, - { GEN_MODENVTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f }, - { GEN_ENDADDRCOARSEOFS, 0, 1, -1e10f, 0.0f, 0.0f }, - { GEN_MODLFOTOVOL, 1, 1, -960.0f, 960.0f, 0.0f }, - { GEN_UNUSED1, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_CHORUSSEND, 1, 1, 0.0f, 1000.0f, 0.0f }, - { GEN_REVERBSEND, 1, 1, 0.0f, 1000.0f, 0.0f }, - { GEN_PAN, 1, 1, -500.0f, 500.0f, 0.0f }, - { GEN_UNUSED2, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_UNUSED3, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_UNUSED4, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_MODLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_MODLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f }, - { GEN_VIBLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_VIBLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f }, - { GEN_MODENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_MODENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_MODENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_MODENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_MODENVSUSTAIN, 0, 1, 0.0f, 1000.0f, 0.0f }, - { GEN_MODENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_KEYTOMODENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f }, - { GEN_KEYTOMODENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f }, - { GEN_VOLENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_VOLENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_VOLENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_VOLENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_VOLENVSUSTAIN, 0, 1, 0.0f, 1440.0f, 0.0f }, - { GEN_VOLENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_KEYTOVOLENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f }, - { GEN_KEYTOVOLENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f }, - { GEN_INSTRUMENT, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_RESERVED1, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_KEYRANGE, 0, 0, 0.0f, 127.0f, 0.0f }, - { GEN_VELRANGE, 0, 0, 0.0f, 127.0f, 0.0f }, - { GEN_STARTLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f }, - { GEN_KEYNUM, 1, 0, 0.0f, 127.0f, -1.0f }, - { GEN_VELOCITY, 1, 1, 0.0f, 127.0f, -1.0f }, - { GEN_ATTENUATION, 1, 1, 0.0f, 1440.0f, 0.0f }, - { GEN_RESERVED2, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_ENDLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f }, - { GEN_COARSETUNE, 0, 1, -120.0f, 120.0f, 0.0f }, - { GEN_FINETUNE, 0, 1, -99.0f, 99.0f, 0.0f }, - { GEN_SAMPLEID, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_SAMPLEMODE, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_RESERVED3, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_SCALETUNE, 0, 1, 0.0f, 1200.0f, 100.0f }, - { GEN_EXCLUSIVECLASS, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_OVERRIDEROOTKEY, 1, 0, 0.0f, 127.0f, -1.0f }, - { GEN_PITCH, 1, 0, 0.0f, 127.0f, 0.0f } -}; - - -/** - * Set an array of generators to their default values. - * @param gen Array of generators (should be #GEN_LAST in size). - * @return Always returns 0 - */ -int -fluid_gen_set_default_values(fluid_gen_t* gen) -{ - int i; - - for (i = 0; i < GEN_LAST; i++) { - gen[i].flags = GEN_UNUSED; - gen[i].mod = 0.0; - gen[i].nrpn = 0.0; - gen[i].val = fluid_gen_info[i].def; - } - - return FLUID_OK; -} - - -/* fluid_gen_init - * - * Set an array of generators to their initial value - */ -int -fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel) -{ - int i; - - fluid_gen_set_default_values(gen); - - for (i = 0; i < GEN_LAST; i++) { - gen[i].nrpn = fluid_channel_get_gen(channel, i); - - /* This is an extension to the SoundFont standard. More - * documentation is available at the fluid_synth_set_gen2() - * function. */ - if (fluid_channel_get_gen_abs(channel, i)) { - gen[i].flags = GEN_ABS_NRPN; - } - } - - return FLUID_OK; -} - -fluid_real_t fluid_gen_scale(int gen, float value) -{ - return (fluid_gen_info[gen].min - + value * (fluid_gen_info[gen].max - fluid_gen_info[gen].min)); -} - -fluid_real_t fluid_gen_scale_nrpn(int gen, int data) -{ - fluid_real_t value = (float) data - 8192.0f; - fluid_clip(value, -8192, 8192); - return value * (float) fluid_gen_info[gen].nrpn_scale; -} diff --git a/libraries/fluidlite/src/fluid_gen.h b/libraries/fluidlite/src/fluid_gen.h deleted file mode 100644 index 0b8790ba0..000000000 --- a/libraries/fluidlite/src/fluid_gen.h +++ /dev/null @@ -1,44 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#ifndef _FLUID_GEN_H -#define _FLUID_GEN_H - -#include "fluidsynth_priv.h" - -typedef struct _fluid_gen_info_t { - char num; /* Generator number */ - char init; /* Does the generator need to be initialized (cfr. fluid_voice_init()) */ - char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */ - float min; /* The minimum value */ - float max; /* The maximum value */ - float def; /* The default value (cfr. fluid_gen_set_default_values()) */ -} fluid_gen_info_t; - -#define fluid_gen_set_mod(_gen, _val) { (_gen)->mod = (double) (_val); } -#define fluid_gen_set_nrpn(_gen, _val) { (_gen)->nrpn = (double) (_val); } - -fluid_real_t fluid_gen_scale(int gen, float value); -fluid_real_t fluid_gen_scale_nrpn(int gen, int nrpn); -int fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel); - - -#endif /* _FLUID_GEN_H */ diff --git a/libraries/fluidlite/src/fluid_hash.c b/libraries/fluidlite/src/fluid_hash.c deleted file mode 100644 index 9094908af..000000000 --- a/libraries/fluidlite/src/fluid_hash.c +++ /dev/null @@ -1,388 +0,0 @@ -/* GLIB - Library of useful routines for C programming - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* - * Modified by the GLib Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GLib Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GLib at ftp://ftp.gtk.org/pub/gtk/. - */ - -/* - * MT safe - */ - -#include "fluidsynth_priv.h" -#include "fluid_hash.h" - - -#define HASH_TABLE_MIN_SIZE 7 -#define HASH_TABLE_MAX_SIZE 13845163 - - -typedef struct _fluid_hashnode_t fluid_hashnode_t; - -struct _fluid_hashnode_t { - char* key; - void* value; - int type; - fluid_hashnode_t *next; -}; - -static fluid_hashnode_t* new_fluid_hashnode(char* key, void* value, int type); -static void delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del); -static void delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del); - -struct _fluid_hashtable_t { - unsigned int size; - unsigned int nnodes; - fluid_hashnode_t **nodes; - fluid_hash_delete_t del; -}; - -#define FLUID_HASHTABLE_RESIZE(hash_table) \ - if ((3 * hash_table->size <= hash_table->nnodes) \ - && (hash_table->size < HASH_TABLE_MAX_SIZE)) { \ - fluid_hashtable_resize(hash_table); \ - } - -static void fluid_hashtable_resize(fluid_hashtable_t *hash_table); -static fluid_hashnode_t** fluid_hashtable_lookup_node(fluid_hashtable_t *hash_table, char* key); - -/** - * new_fluid_hashtable: - * - * Creates a new #fluid_hashtable_t. - * - * Return value: a new #fluid_hashtable_t. - **/ -fluid_hashtable_t* -new_fluid_hashtable(fluid_hash_delete_t del) -{ - fluid_hashtable_t *hash_table; - unsigned int i; - - hash_table = FLUID_NEW(fluid_hashtable_t); - hash_table->size = HASH_TABLE_MIN_SIZE; - hash_table->nnodes = 0; - hash_table->nodes = FLUID_ARRAY(fluid_hashnode_t*, hash_table->size); - hash_table->del = del; - - for (i = 0; i < hash_table->size; i++) { - hash_table->nodes[i] = NULL; - } - - return hash_table; -} - -/** - * delete_fluid_hashtable: - * @hash_table: a #fluid_hashtable_t. - * - * Destroys the #fluid_hashtable_t. If keys and/or values are dynamically - * allocated, you should either free them first or create the #fluid_hashtable_t - * using fluid_hashtable_new_full(). In the latter case the destroy functions - * you supplied will be called on all keys and values before destroying - * the #fluid_hashtable_t. - **/ -void -delete_fluid_hashtable(fluid_hashtable_t *hash_table) -{ - unsigned int i; - - if (hash_table == NULL) { - return; - } - - for (i = 0; i < hash_table->size; i++) { - delete_fluid_hashnodes(hash_table->nodes[i], hash_table->del); - } - - FLUID_FREE(hash_table->nodes); - FLUID_FREE(hash_table); -} - - -static /*inline*/ fluid_hashnode_t** -fluid_hashtable_lookup_node (fluid_hashtable_t* hash_table, char* key) -{ - fluid_hashnode_t **node; - - node = &hash_table->nodes[fluid_str_hash(key) % hash_table->size]; - - while (*node && (FLUID_STRCMP((*node)->key, key) != 0)) { - node = &(*node)->next; - } - - return node; -} - -/** - * fluid_hashtable_lookup: - * @hash_table: a #fluid_hashtable_t. - * @key: the key to look up. - * - * Looks up a key in a #fluid_hashtable_t. - * - * Return value: the associated value, or %NULL if the key is not found. - **/ -int -fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, int* type) -{ - fluid_hashnode_t *node; - - node = *fluid_hashtable_lookup_node(hash_table, key); - - if (node) { - if (value) { - *value = node->value; - } - if (type) { - *type = node->type; - } - return 1; - } else { - return 0; - } -} - -/** - * fluid_hashtable_insert: - * @hash_table: a #fluid_hashtable_t. - * @key: a key to insert. - * @value: the value to associate with the key. - * - * Inserts a new key and value into a #fluid_hashtable_t. - * - * If the key already exists in the #fluid_hashtable_t its current value is replaced - * with the new value. If you supplied a @value_destroy_func when creating the - * #fluid_hashtable_t, the old value is freed using that function. If you supplied - * a @key_destroy_func when creating the #fluid_hashtable_t, the passed key is freed - * using that function. - **/ -void -fluid_hashtable_insert(fluid_hashtable_t *hash_table, char* key, void* value, int type) -{ - fluid_hashnode_t **node; - - node = fluid_hashtable_lookup_node(hash_table, key); - - if (*node) { - (*node)->value = value; - (*node)->type = type; - } else { - *node = new_fluid_hashnode(key, value, type); - hash_table->nnodes++; - FLUID_HASHTABLE_RESIZE(hash_table); - } -} - - -/** - * fluid_hashtable_replace: - * @hash_table: a #GHashTable. - * @key: a key to insert. - * @value: the value to associate with the key. - * - * Inserts a new key and value into a #GHashTable similar to - * fluid_hashtable_insert(). The difference is that if the key already exists - * in the #GHashTable, it gets replaced by the new key. If you supplied a - * @value_destroy_func when creating the #GHashTable, the old value is freed - * using that function. If you supplied a @key_destroy_func when creating the - * #GHashTable, the old key is freed using that function. - **/ -void -fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, int type) -{ - fluid_hashnode_t **node; - - node = fluid_hashtable_lookup_node(hash_table, key); - - if (*node) { - if (hash_table->del) { - hash_table->del((*node)->value, (*node)->type); - } - (*node)->value = value; - - } else { - *node = new_fluid_hashnode(key, value, type); - hash_table->nnodes++; - FLUID_HASHTABLE_RESIZE(hash_table); - } -} - -/** - * fluid_hashtable_remove: - * @hash_table: a #fluid_hashtable_t. - * @key: the key to remove. - * - * Removes a key and its associated value from a #fluid_hashtable_t. - * - * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the - * key and value are freed using the supplied destroy functions, otherwise - * you have to make sure that any dynamically allocated values are freed - * yourself. - * - * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. - **/ -int -fluid_hashtable_remove (fluid_hashtable_t *hash_table, char* key) -{ - fluid_hashnode_t **node, *dest; - - node = fluid_hashtable_lookup_node(hash_table, key); - if (*node) { - dest = *node; - (*node) = dest->next; - delete_fluid_hashnode(dest, hash_table->del); - hash_table->nnodes--; - - FLUID_HASHTABLE_RESIZE (hash_table); - - return 1; - } - - return 0; -} - -/** - * fluid_hashtable_foreach: - * @hash_table: a #fluid_hashtable_t. - * @func: the function to call for each key/value pair. - * @user_data: user data to pass to the function. - * - * Calls the given function for each of the key/value pairs in the - * #fluid_hashtable_t. The function is passed the key and value of each - * pair, and the given @user_data parameter. The hash table may not - * be modified while iterating over it (you can't add/remove - * items). To remove all items matching a predicate, use - * fluid_hashtable_remove(). - **/ -void -fluid_hashtable_foreach(fluid_hashtable_t *hash_table, fluid_hash_iter_t func, void* data) -{ - fluid_hashnode_t *node = NULL; - unsigned int i; - - for (i = 0; i < hash_table->size; i++) { - for (node = hash_table->nodes[i]; node != NULL; node = node->next) { - (*func)(node->key, node->value, node->type, data); - } - } -} - -/** - * fluid_hashtable_size: - * @hash_table: a #fluid_hashtable_t. - * - * Returns the number of elements contained in the #fluid_hashtable_t. - * - * Return value: the number of key/value pairs in the #fluid_hashtable_t. - **/ -unsigned int -fluid_hashtable_size(fluid_hashtable_t *hash_table) -{ - return hash_table->nnodes; -} - -static void -fluid_hashtable_resize(fluid_hashtable_t *hash_table) -{ - fluid_hashnode_t **new_nodes; - fluid_hashnode_t *node; - fluid_hashnode_t *next; - unsigned int hash_val; - int new_size; - unsigned int i; - - new_size = 3 * hash_table->size + 1; - new_size = (new_size > HASH_TABLE_MAX_SIZE)? HASH_TABLE_MAX_SIZE : new_size; - -/* printf("%s: %d: resizing, new size = %d\n", __FILE__, __LINE__, new_size); */ - - new_nodes = FLUID_ARRAY(fluid_hashnode_t*, new_size); - FLUID_MEMSET(new_nodes, 0, new_size * sizeof(fluid_hashnode_t*)); - - for (i = 0; i < hash_table->size; i++) { - for (node = hash_table->nodes[i]; node; node = next) { - next = node->next; - hash_val = fluid_str_hash(node->key) % new_size; - node->next = new_nodes[hash_val]; - new_nodes[hash_val] = node; - } - } - - FLUID_FREE(hash_table->nodes); - hash_table->nodes = new_nodes; - hash_table->size = new_size; -} - -static fluid_hashnode_t* -new_fluid_hashnode(char* key, void* value, int type) -{ - fluid_hashnode_t *hash_node; - - hash_node = FLUID_NEW(fluid_hashnode_t); - - hash_node->key = FLUID_STRDUP(key); - hash_node->value = value; - hash_node->type = type; - hash_node->next = NULL; - - return hash_node; -} - -static void -delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del) -{ - if (del) { - (*del)(hash_node->value, hash_node->type); - } - if (hash_node->key) { - FLUID_FREE(hash_node->key); - } - FLUID_FREE(hash_node); -} - -static void -delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del) -{ - while (hash_node) { - fluid_hashnode_t *next = hash_node->next; - delete_fluid_hashnode(hash_node, del); - hash_node = next; - } -} - - -/* 31 bit hash function */ -unsigned int -fluid_str_hash(char* key) -{ - char *p = key; - unsigned int h = *p; - - if (h) { - for (p += 1; *p != '\0'; p++) { - h = (h << 5) - h + *p; - } - } - - return h; -} diff --git a/libraries/fluidlite/src/fluid_hash.h b/libraries/fluidlite/src/fluid_hash.h deleted file mode 100644 index af0b5dbd1..000000000 --- a/libraries/fluidlite/src/fluid_hash.h +++ /dev/null @@ -1,64 +0,0 @@ -/* GLIB - Library of useful routines for C programming - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* - * Modified by the GLib Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GLib Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GLib at ftp://ftp.gtk.org/pub/gtk/. - */ - -/* - * Demolished by Peter Hanappe [December 2002] - * - * - only string as key - * - stores additional type info - * - removed use of GLib types (gpointer, gint, ...) - * - reduced the number of API functions - * - changed names to fluid_hashtable_... - */ - -#ifndef _FLUID_HASH_H -#define _FLUID_HASH_H - - -typedef int (*fluid_hash_iter_t)(char* key, void* value, int type, void* data); -typedef void (*fluid_hash_delete_t)(void* value, int type); - -fluid_hashtable_t* new_fluid_hashtable(fluid_hash_delete_t delete_func); -void delete_fluid_hashtable(fluid_hashtable_t *hash_table); - -void fluid_hashtable_insert(fluid_hashtable_t *hash_table, char* key, void* value, int type); - -void fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, int type); - -/* Returns non-zero if found, 0 if not found */ -int fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, int* type); - -/* Returns non-zero if removed, 0 if not removed */ -int fluid_hashtable_remove(fluid_hashtable_t *hash_table, char* key); - -void fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hash_iter_t fun, void* data); - -unsigned int fluid_hashtable_size(fluid_hashtable_t *hash_table); - -unsigned int fluid_str_hash(char* v); - -#endif /* _FLUID_HASH_H */ - diff --git a/libraries/fluidlite/src/fluid_list.c b/libraries/fluidlite/src/fluid_list.c deleted file mode 100644 index 2f9d04ff8..000000000 --- a/libraries/fluidlite/src/fluid_list.c +++ /dev/null @@ -1,257 +0,0 @@ -/* GLIB - Library of useful routines for C programming - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* - * Modified by the GLib Team and others 1997-1999. See the AUTHORS - * file for a list of people on the GLib Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GLib at ftp://ftp.gtk.org/pub/gtk/. - */ - - - -#include "fluid_list.h" - - -fluid_list_t* -new_fluid_list(void) -{ - fluid_list_t* list; - list = (fluid_list_t*) FLUID_MALLOC(sizeof(fluid_list_t)); - list->data = NULL; - list->next = NULL; - return list; -} - -void -delete_fluid_list(fluid_list_t *list) -{ - fluid_list_t *next; - while (list) { - next = list->next; - FLUID_FREE(list); - list = next; - } -} - -void -delete1_fluid_list(fluid_list_t *list) -{ - if (list) { - FLUID_FREE(list); - } -} - -fluid_list_t* -fluid_list_append(fluid_list_t *list, void* data) -{ - fluid_list_t *new_list; - fluid_list_t *last; - - new_list = new_fluid_list(); - new_list->data = data; - - if (list) - { - last = fluid_list_last(list); - /* g_assert (last != NULL); */ - last->next = new_list; - - return list; - } - else - return new_list; -} - -fluid_list_t* -fluid_list_prepend(fluid_list_t *list, void* data) -{ - fluid_list_t *new_list; - - new_list = new_fluid_list(); - new_list->data = data; - new_list->next = list; - - return new_list; -} - -fluid_list_t* -fluid_list_nth(fluid_list_t *list, int n) -{ - while ((n-- > 0) && list) { - list = list->next; - } - - return list; -} - -fluid_list_t* -fluid_list_remove(fluid_list_t *list, void* data) -{ - fluid_list_t *tmp; - fluid_list_t *prev; - - prev = NULL; - tmp = list; - - while (tmp) { - if (tmp->data == data) { - if (prev) { - prev->next = tmp->next; - } - if (list == tmp) { - list = list->next; - } - tmp->next = NULL; - delete_fluid_list(tmp); - - break; - } - - prev = tmp; - tmp = tmp->next; - } - - return list; -} - -fluid_list_t* -fluid_list_remove_link(fluid_list_t *list, fluid_list_t *link) -{ - fluid_list_t *tmp; - fluid_list_t *prev; - - prev = NULL; - tmp = list; - - while (tmp) { - if (tmp == link) { - if (prev) { - prev->next = tmp->next; - } - if (list == tmp) { - list = list->next; - } - tmp->next = NULL; - break; - } - - prev = tmp; - tmp = tmp->next; - } - - return list; -} - -static fluid_list_t* -fluid_list_sort_merge(fluid_list_t *l1, fluid_list_t *l2, fluid_compare_func_t compare_func) -{ - fluid_list_t list, *l; - - l = &list; - - while (l1 && l2) { - if (compare_func(l1->data,l2->data) < 0) { - l = l->next = l1; - l1 = l1->next; - } else { - l = l->next = l2; - l2 = l2->next; - } - } - l->next= l1 ? l1 : l2; - - return list.next; -} - -fluid_list_t* -fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func) -{ - fluid_list_t *l1, *l2; - - if (!list) { - return NULL; - } - if (!list->next) { - return list; - } - - l1 = list; - l2 = list->next; - - while ((l2 = l2->next) != NULL) { - if ((l2 = l2->next) == NULL) - break; - l1=l1->next; - } - l2 = l1->next; - l1->next = NULL; - - return fluid_list_sort_merge(fluid_list_sort(list, compare_func), - fluid_list_sort(l2, compare_func), - compare_func); -} - - -fluid_list_t* -fluid_list_last(fluid_list_t *list) -{ - if (list) { - while (list->next) - list = list->next; - } - - return list; -} - -int -fluid_list_size(fluid_list_t *list) -{ - int n = 0; - while (list) { - n++; - list = list->next; - } - return n; -} - -fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data) -{ - fluid_list_t *new_list; - fluid_list_t *cur; - fluid_list_t *prev = NULL; - - new_list = new_fluid_list(); - new_list->data = data; - - cur = list; - while ((n-- > 0) && cur) { - prev = cur; - cur = cur->next; - } - - new_list->next = cur; - - if (prev) { - prev->next = new_list; - return list; - } else { - return new_list; - } -} diff --git a/libraries/fluidlite/src/fluid_list.h b/libraries/fluidlite/src/fluid_list.h deleted file mode 100644 index df40475ee..000000000 --- a/libraries/fluidlite/src/fluid_list.h +++ /dev/null @@ -1,61 +0,0 @@ -/* GLIB - Library of useful routines for C programming - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef _FLUID_LIST_H -#define _FLUID_LIST_H - -#include "fluidsynth_priv.h" - -/* - * - * Lists - * - * A sound font loader has to pack the data from the .SF2 file into - * list structures of this type. - * - */ - -typedef struct _fluid_list_t fluid_list_t; - -typedef int (*fluid_compare_func_t)(void* a, void* b); - -struct _fluid_list_t -{ - void* data; - fluid_list_t *next; -}; - -fluid_list_t* new_fluid_list(void); -void delete_fluid_list(fluid_list_t *list); -void delete1_fluid_list(fluid_list_t *list); -fluid_list_t* fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func); -fluid_list_t* fluid_list_append(fluid_list_t *list, void* data); -fluid_list_t* fluid_list_prepend(fluid_list_t *list, void* data); -fluid_list_t* fluid_list_remove(fluid_list_t *list, void* data); -fluid_list_t* fluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink); -fluid_list_t* fluid_list_nth(fluid_list_t *list, int n); -fluid_list_t* fluid_list_last(fluid_list_t *list); -fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data); -int fluid_list_size(fluid_list_t *list); - -#define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL) -#define fluid_list_get(slist) ((slist) ? ((slist)->data) : NULL) - - -#endif /* _FLUID_LIST_H */ diff --git a/libraries/fluidlite/src/fluid_midi.h b/libraries/fluidlite/src/fluid_midi.h deleted file mode 100644 index b9b85db1a..000000000 --- a/libraries/fluidlite/src/fluid_midi.h +++ /dev/null @@ -1,247 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUID_MIDI_H -#define _FLUID_MIDI_H - -#include "fluidsynth_priv.h" -#include "fluid_sys.h" -#include "fluid_list.h" - -typedef struct _fluid_midi_parser_t fluid_midi_parser_t; - -fluid_midi_parser_t* new_fluid_midi_parser(void); -int delete_fluid_midi_parser(fluid_midi_parser_t* parser); -fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigned char c); - -int fluid_midi_send_event(fluid_synth_t* synth, fluid_player_t* player, fluid_midi_event_t* evt); - - -/*************************************************************** - * - * CONSTANTS & ENUM - */ - - -#define MAX_NUMBER_OF_TRACKS 128 - -enum fluid_midi_event_type { - /* channel messages */ - NOTE_OFF = 0x80, - NOTE_ON = 0x90, - KEY_PRESSURE = 0xa0, - CONTROL_CHANGE = 0xb0, - PROGRAM_CHANGE = 0xc0, - CHANNEL_PRESSURE = 0xd0, - PITCH_BEND = 0xe0, - /* system exclusive */ - MIDI_SYSEX = 0xf0, - /* system common - never in midi files */ - MIDI_TIME_CODE = 0xf1, - MIDI_SONG_POSITION = 0xf2, - MIDI_SONG_SELECT = 0xf3, - MIDI_TUNE_REQUEST = 0xf6, - MIDI_EOX = 0xf7, - /* system real-time - never in midi files */ - MIDI_SYNC = 0xf8, - MIDI_TICK = 0xf9, - MIDI_START = 0xfa, - MIDI_CONTINUE = 0xfb, - MIDI_STOP = 0xfc, - MIDI_ACTIVE_SENSING = 0xfe, - MIDI_SYSTEM_RESET = 0xff, - /* meta event - for midi files only */ - MIDI_META_EVENT = 0xff -}; - -enum fluid_midi_control_change { - BANK_SELECT_MSB = 0x00, - MODULATION_MSB = 0x01, - BREATH_MSB = 0x02, - FOOT_MSB = 0x04, - PORTAMENTO_TIME_MSB = 0x05, - DATA_ENTRY_MSB = 0x06, - VOLUME_MSB = 0x07, - BALANCE_MSB = 0x08, - PAN_MSB = 0x0A, - EXPRESSION_MSB = 0x0B, - EFFECTS1_MSB = 0x0C, - EFFECTS2_MSB = 0x0D, - GPC1_MSB = 0x10, /* general purpose controller */ - GPC2_MSB = 0x11, - GPC3_MSB = 0x12, - GPC4_MSB = 0x13, - BANK_SELECT_LSB = 0x20, - MODULATION_WHEEL_LSB = 0x21, - BREATH_LSB = 0x22, - FOOT_LSB = 0x24, - PORTAMENTO_TIME_LSB = 0x25, - DATA_ENTRY_LSB = 0x26, - VOLUME_LSB = 0x27, - BALANCE_LSB = 0x28, - PAN_LSB = 0x2A, - EXPRESSION_LSB = 0x2B, - EFFECTS1_LSB = 0x2C, - EFFECTS2_LSB = 0x2D, - GPC1_LSB = 0x30, - GPC2_LSB = 0x31, - GPC3_LSB = 0x32, - GPC4_LSB = 0x33, - SUSTAIN_SWITCH = 0x40, - PORTAMENTO_SWITCH = 0x41, - SOSTENUTO_SWITCH = 0x42, - SOFT_PEDAL_SWITCH = 0x43, - LEGATO_SWITCH = 0x45, - HOLD2_SWITCH = 0x45, - SOUND_CTRL1 = 0x46, - SOUND_CTRL2 = 0x47, - SOUND_CTRL3 = 0x48, - SOUND_CTRL4 = 0x49, - SOUND_CTRL5 = 0x4A, - SOUND_CTRL6 = 0x4B, - SOUND_CTRL7 = 0x4C, - SOUND_CTRL8 = 0x4D, - SOUND_CTRL9 = 0x4E, - SOUND_CTRL10 = 0x4F, - GPC5 = 0x50, - GPC6 = 0x51, - GPC7 = 0x52, - GPC8 = 0x53, - PORTAMENTO_CTRL = 0x54, - EFFECTS_DEPTH1 = 0x5B, - EFFECTS_DEPTH2 = 0x5C, - EFFECTS_DEPTH3 = 0x5D, - EFFECTS_DEPTH4 = 0x5E, - EFFECTS_DEPTH5 = 0x5F, - DATA_ENTRY_INCR = 0x60, - DATA_ENTRY_DECR = 0x61, - NRPN_LSB = 0x62, - NRPN_MSB = 0x63, - RPN_LSB = 0x64, - RPN_MSB = 0x65, - ALL_SOUND_OFF = 0x78, - ALL_CTRL_OFF = 0x79, - LOCAL_CONTROL = 0x7A, - ALL_NOTES_OFF = 0x7B, - OMNI_OFF = 0x7C, - OMNI_ON = 0x7D, - POLY_OFF = 0x7E, - POLY_ON = 0x7F -}; - -/* General MIDI RPN event numbers (LSB, MSB = 0) */ -enum midi_rpn_event { - RPN_PITCH_BEND_RANGE = 0x00, - RPN_CHANNEL_FINE_TUNE = 0x01, - RPN_CHANNEL_COARSE_TUNE = 0x02, - RPN_TUNING_PROGRAM_CHANGE = 0x03, - RPN_TUNING_BANK_SELECT = 0x04, - RPN_MODULATION_DEPTH_RANGE = 0x05 -}; - -enum midi_meta_event { - MIDI_COPYRIGHT = 0x02, - MIDI_TRACK_NAME = 0x03, - MIDI_INST_NAME = 0x04, - MIDI_LYRIC = 0x05, - MIDI_MARKER = 0x06, - MIDI_CUE_POINT = 0x07, - MIDI_EOT = 0x2f, - MIDI_SET_TEMPO = 0x51, - MIDI_SMPTE_OFFSET = 0x54, - MIDI_TIME_SIGNATURE = 0x58, - MIDI_KEY_SIGNATURE = 0x59, - MIDI_SEQUENCER_EVENT = 0x7f -}; - -/* MIDI SYSEX useful manufacturer values */ -enum midi_sysex_manuf { - MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */ - MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */ - MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */ -}; - -#define MIDI_SYSEX_DEVICE_ID_ALL 0x7F /**< Device ID used in SYSEX messages to indicate all devices */ - -/* SYSEX sub-ID #1 which follows device ID */ -#define MIDI_SYSEX_MIDI_TUNING_ID 0x08 /**< Sysex sub-ID #1 for MIDI tuning messages */ -#define MIDI_SYSEX_GM_ID 0x09 /**< Sysex sub-ID #1 for General MIDI messages */ - -/** - * SYSEX tuning message IDs. - */ -enum midi_sysex_tuning_msg_id { - MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */ - MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */ - MIDI_SYSEX_TUNING_NOTE_TUNE = 0x02, /**< Tuning note change message (realtime) */ - MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK = 0x03, /**< Bulk tuning dump request (with bank, non-realtime) */ - MIDI_SYSEX_TUNING_BULK_DUMP_BANK = 0x04, /**< Bulk tuning dump resonse (with bank, non-realtime) */ - MIDI_SYSEX_TUNING_OCTAVE_DUMP_1BYTE = 0x05, /**< Octave tuning dump using 1 byte values (non-realtime) */ - MIDI_SYSEX_TUNING_OCTAVE_DUMP_2BYTE = 0x06, /**< Octave tuning dump using 2 byte values (non-realtime) */ - MIDI_SYSEX_TUNING_NOTE_TUNE_BANK = 0x07, /**< Tuning note change message (with bank, realtime/non-realtime) */ - MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE = 0x08, /**< Octave tuning message using 1 byte values (realtime/non-realtime) */ - MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */ -}; - -/* General MIDI sub-ID #2 */ -#define MIDI_SYSEX_GM_ON 0x01 /**< Enable GM mode */ -#define MIDI_SYSEX_GM_OFF 0x02 /**< Disable GM mode */ - - -enum fluid_player_status -{ - FLUID_PLAYER_READY, - FLUID_PLAYER_PLAYING, - FLUID_PLAYER_DONE -}; - -enum fluid_driver_status -{ - FLUID_MIDI_READY, - FLUID_MIDI_LISTENING, - FLUID_MIDI_DONE -}; - -/*************************************************************** - * - * TYPE DEFINITIONS & FUNCTION DECLARATIONS - */ - -/* From ctype.h */ -#define fluid_isascii(c) (((c) & ~0x7f) == 0) - - - -/* - * fluid_midi_event_t - */ -struct _fluid_midi_event_t { - fluid_midi_event_t* next; /* Don't use it, it will dissappear. Used in midi tracks. */ - unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */ - unsigned char type; /* MIDI event type */ - unsigned char channel; /* MIDI channel */ - unsigned int param1; /* First parameter */ - unsigned int param2; /* Second parameter */ -}; - - - - -#endif /* _FLUID_MIDI_H */ diff --git a/libraries/fluidlite/src/fluid_mod.c b/libraries/fluidlite/src/fluid_mod.c deleted file mode 100644 index 61e59a268..000000000 --- a/libraries/fluidlite/src/fluid_mod.c +++ /dev/null @@ -1,434 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#include "fluid_mod.h" -#include "fluid_chan.h" -#include "fluid_voice.h" - -/* - * fluid_mod_clone - */ -void -fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src) -{ - mod->dest = src->dest; - mod->src1 = src->src1; - mod->flags1 = src->flags1; - mod->src2 = src->src2; - mod->flags2 = src->flags2; - mod->amount = src->amount; -} - -/* - * fluid_mod_set_source1 - */ -void -fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags) -{ - mod->src1 = src; - mod->flags1 = flags; -} - -/* - * fluid_mod_set_source2 - */ -void -fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags) -{ - mod->src2 = src; - mod->flags2 = flags; -} - -/* - * fluid_mod_set_dest - */ -void -fluid_mod_set_dest(fluid_mod_t* mod, int dest) -{ - mod->dest = dest; -} - -/* - * fluid_mod_set_amount - */ -void -fluid_mod_set_amount(fluid_mod_t* mod, double amount) -{ - mod->amount = (double) amount; -} - -int fluid_mod_get_source1(fluid_mod_t* mod) -{ - return mod->src1; -} - -int fluid_mod_get_flags1(fluid_mod_t* mod) -{ - return mod->flags1; -} - -int fluid_mod_get_source2(fluid_mod_t* mod) -{ - return mod->src2; -} - -int fluid_mod_get_flags2(fluid_mod_t* mod) -{ - return mod->flags2; -} - -int fluid_mod_get_dest(fluid_mod_t* mod) -{ - return mod->dest; -} - -double fluid_mod_get_amount(fluid_mod_t* mod) -{ - return (fluid_real_t) mod->amount; -} - - -/* - * fluid_mod_get_value - */ -fluid_real_t -fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice) -{ - fluid_real_t v1 = 0.0, v2 = 1.0; - fluid_real_t range1 = 127.0, range2 = 127.0; - - if (chan == NULL) { - return 0.0f; - } - - /* 'special treatment' for default controller - * - * Reference: SF2.01 section 8.4.2 - * - * The GM default controller 'vel-to-filter cut off' is not clearly - * defined: If implemented according to the specs, the filter - * frequency jumps between vel=63 and vel=64. To maintain - * compatibility with existing sound fonts, the implementation is - * 'hardcoded', it is impossible to implement using only one - * modulator otherwise. - * - * I assume here, that the 'intention' of the paragraph is one - * octave (1200 cents) filter frequency shift between vel=127 and - * vel=64. 'amount' is (-2400), at least as long as the controller - * is set to default. - * - * Further, the 'appearance' of the modulator (source enumerator, - * destination enumerator, flags etc) is different from that - * described in section 8.4.2, but it matches the definition used in - * several SF2.1 sound fonts (where it is used only to turn it off). - * */ - if ((mod->src2 == FLUID_MOD_VELOCITY) && - (mod->src1 == FLUID_MOD_VELOCITY) && - (mod->flags1 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR - | FLUID_MOD_NEGATIVE | FLUID_MOD_LINEAR)) && - (mod->flags2 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR - | FLUID_MOD_POSITIVE | FLUID_MOD_SWITCH)) && - (mod->dest == GEN_FILTERFC)) { -// S. Christian Collins' mod, to stop forcing velocity based filtering -/* - if (voice->vel < 64){ - return (fluid_real_t) mod->amount / 2.0; - } else { - return (fluid_real_t) mod->amount * (127 - voice->vel) / 127; - } -*/ - return 0; // (fluid_real_t) mod->amount / 2.0; - } -// end S. Christian Collins' mod - - /* get the initial value of the first source */ - if (mod->src1 > 0) { - if (mod->flags1 & FLUID_MOD_CC) { - v1 = fluid_channel_get_cc(chan, mod->src1); - } else { /* source 1 is one of the direct controllers */ - switch (mod->src1) { - case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */ - v1 = range1; - break; - case FLUID_MOD_VELOCITY: - v1 = voice->vel; - break; - case FLUID_MOD_KEY: - v1 = voice->key; - break; - case FLUID_MOD_KEYPRESSURE: - v1 = fluid_channel_get_key_pressure(chan, voice->key); - break; - case FLUID_MOD_CHANNELPRESSURE: - v1 = chan->channel_pressure; - break; - case FLUID_MOD_PITCHWHEEL: - v1 = chan->pitch_bend; - range1 = 0x4000; - break; - case FLUID_MOD_PITCHWHEELSENS: - v1 = chan->pitch_wheel_sensitivity; - break; - default: - v1 = 0.0; - } - } - - /* transform the input value */ - switch (mod->flags1 & 0x0f) { - case 0: /* linear, unipolar, positive */ - v1 /= range1; - break; - case 1: /* linear, unipolar, negative */ - v1 = 1.0f - v1 / range1; - break; - case 2: /* linear, bipolar, positive */ - v1 = -1.0f + 2.0f * v1 / range1; - break; - case 3: /* linear, bipolar, negative */ - v1 = 1.0f - 2.0f * v1 / range1; - break; - case 4: /* concave, unipolar, positive */ - v1 = fluid_concave(v1); - break; - case 5: /* concave, unipolar, negative */ - v1 = fluid_concave(127 - v1); - break; - case 6: /* concave, bipolar, positive */ - v1 = (v1 > 64)? fluid_concave(2 * (v1 - 64)) : -fluid_concave(2 * (64 - v1)); - break; - case 7: /* concave, bipolar, negative */ - v1 = (v1 > 64)? -fluid_concave(2 * (v1 - 64)) : fluid_concave(2 * (64 - v1)); - break; - case 8: /* convex, unipolar, positive */ - v1 = fluid_convex(v1); - break; - case 9: /* convex, unipolar, negative */ - v1 = fluid_convex(127 - v1); - break; - case 10: /* convex, bipolar, positive */ - v1 = (v1 > 64)? fluid_convex(2 * (v1 - 64)) : -fluid_convex(2 * (64 - v1)); - break; - case 11: /* convex, bipolar, negative */ - v1 = (v1 > 64)? -fluid_convex(2 * (v1 - 64)) : fluid_convex(2 * (64 - v1)); - break; - case 12: /* switch, unipolar, positive */ - v1 = (v1 >= 64)? 1.0f : 0.0f; - break; - case 13: /* switch, unipolar, negative */ - v1 = (v1 >= 64)? 0.0f : 1.0f; - break; - case 14: /* switch, bipolar, positive */ - v1 = (v1 >= 64)? 1.0f : -1.0f; - break; - case 15: /* switch, bipolar, negative */ - v1 = (v1 >= 64)? -1.0f : 1.0f; - break; - } - } else { - return 0.0; - } - - /* no need to go further */ - if (v1 == 0.0f) { - return 0.0f; - } - - /* get the second input source */ - if (mod->src2 > 0) { - if (mod->flags2 & FLUID_MOD_CC) { - v2 = fluid_channel_get_cc(chan, mod->src2); - } else { - switch (mod->src2) { - case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */ - v2 = range2; - break; - case FLUID_MOD_VELOCITY: - v2 = voice->vel; - break; - case FLUID_MOD_KEY: - v2 = voice->key; - break; - case FLUID_MOD_KEYPRESSURE: - v2 = fluid_channel_get_key_pressure(chan, voice->key); - break; - case FLUID_MOD_CHANNELPRESSURE: - v2 = chan->channel_pressure; - break; - case FLUID_MOD_PITCHWHEEL: - v2 = chan->pitch_bend; - break; - case FLUID_MOD_PITCHWHEELSENS: - v2 = chan->pitch_wheel_sensitivity; - break; - default: - v1 = 0.0f; - } - } - - /* transform the second input value */ - switch (mod->flags2 & 0x0f) { - case 0: /* linear, unipolar, positive */ - v2 /= range2; - break; - case 1: /* linear, unipolar, negative */ - v2 = 1.0f - v2 / range2; - break; - case 2: /* linear, bipolar, positive */ - v2 = -1.0f + 2.0f * v2 / range2; - break; - case 3: /* linear, bipolar, negative */ - v2 = -1.0f + 2.0f * v2 / range2; - break; - case 4: /* concave, unipolar, positive */ - v2 = fluid_concave(v2); - break; - case 5: /* concave, unipolar, negative */ - v2 = fluid_concave(127 - v2); - break; - case 6: /* concave, bipolar, positive */ - v2 = (v2 > 64)? fluid_concave(2 * (v2 - 64)) : -fluid_concave(2 * (64 - v2)); - break; - case 7: /* concave, bipolar, negative */ - v2 = (v2 > 64)? -fluid_concave(2 * (v2 - 64)) : fluid_concave(2 * (64 - v2)); - break; - case 8: /* convex, unipolar, positive */ - v2 = fluid_convex(v2); - break; - case 9: /* convex, unipolar, negative */ - v2 = 1.0f - fluid_convex(v2); - break; - case 10: /* convex, bipolar, positive */ - v2 = (v2 > 64)? -fluid_convex(2 * (v2 - 64)) : fluid_convex(2 * (64 - v2)); - break; - case 11: /* convex, bipolar, negative */ - v2 = (v2 > 64)? -fluid_convex(2 * (v2 - 64)) : fluid_convex(2 * (64 - v2)); - break; - case 12: /* switch, unipolar, positive */ - v2 = (v2 >= 64)? 1.0f : 0.0f; - break; - case 13: /* switch, unipolar, negative */ - v2 = (v2 >= 64)? 0.0f : 1.0f; - break; - case 14: /* switch, bipolar, positive */ - v2 = (v2 >= 64)? 1.0f : -1.0f; - break; - case 15: /* switch, bipolar, negative */ - v2 = (v2 >= 64)? -1.0f : 1.0f; - break; - } - } else { - v2 = 1.0f; - } - - /* it's as simple as that: */ - return (fluid_real_t) mod->amount * v1 * v2; -} - -/* - * fluid_mod_new - */ -fluid_mod_t* -fluid_mod_new() -{ - fluid_mod_t* mod = FLUID_NEW(fluid_mod_t); - if (mod == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - return mod; -} - -/* - * fluid_mod_delete - */ -void -fluid_mod_delete(fluid_mod_t * mod) -{ - FLUID_FREE(mod); -} - -/* - * fluid_mod_test_identity - */ -/* Purpose: - * Checks, if two modulators are identical. - * SF2.01 section 9.5.1 page 69, 'bullet' 3 defines 'identical'. - */ -int fluid_mod_test_identity(fluid_mod_t * mod1, fluid_mod_t * mod2){ - if (mod1->dest != mod2->dest){return 0;}; - if (mod1->src1 != mod2->src1){return 0;}; - if (mod1->src2 != mod2->src2){return 0;}; - if (mod1->flags1 != mod2->flags1){return 0;} - if (mod1->flags2 != mod2->flags2){return 0;} - return 1; -} - -/* debug function: Prints the contents of a modulator */ -void fluid_dump_modulator(fluid_mod_t * mod){ - int src1=mod->src1; - int dest=mod->dest; - int src2=mod->src2; - int flags1=mod->flags1; - int flags2=mod->flags2; - fluid_real_t amount=(fluid_real_t)mod->amount; - - printf("Src: "); - if (flags1 & FLUID_MOD_CC){ - printf("MIDI CC=%i",src1); - } else { - switch(src1){ - case FLUID_MOD_NONE: - printf("None"); break; - case FLUID_MOD_VELOCITY: - printf("note-on velocity"); break; - case FLUID_MOD_KEY: - printf("Key nr"); break; - case FLUID_MOD_KEYPRESSURE: - printf("Poly pressure"); break; - case FLUID_MOD_CHANNELPRESSURE: - printf("Chan pressure"); break; - case FLUID_MOD_PITCHWHEEL: - printf("Pitch Wheel"); break; - case FLUID_MOD_PITCHWHEELSENS: - printf("Pitch Wheel sens"); break; - default: - printf("(unknown: %i)", src1); - }; /* switch src1 */ - }; /* if not CC */ - if (flags1 & FLUID_MOD_NEGATIVE){printf("- ");} else {printf("+ ");}; - if (flags1 & FLUID_MOD_BIPOLAR){printf("bip ");} else {printf("unip ");}; - printf("-> "); - switch(dest){ - case GEN_FILTERQ: printf("Q"); break; - case GEN_FILTERFC: printf("fc"); break; - case GEN_VIBLFOTOPITCH: printf("VibLFO-to-pitch"); break; - case GEN_MODENVTOPITCH: printf("ModEnv-to-pitch"); break; - case GEN_MODLFOTOPITCH: printf("ModLFO-to-pitch"); break; - case GEN_CHORUSSEND: printf("Chorus send"); break; - case GEN_REVERBSEND: printf("Reverb send"); break; - case GEN_PAN: printf("pan"); break; - case GEN_ATTENUATION: printf("att"); break; - default: printf("dest %i",dest); - }; /* switch dest */ - printf(", amount %f flags %i src2 %i flags2 %i\n",amount, flags1, src2, flags2); -} - - diff --git a/libraries/fluidlite/src/fluid_mod.h b/libraries/fluidlite/src/fluid_mod.h deleted file mode 100644 index 5be950972..000000000 --- a/libraries/fluidlite/src/fluid_mod.h +++ /dev/null @@ -1,40 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#ifndef _FLUID_MOD_H -#define _FLUID_MOD_H - -#include "fluidsynth_priv.h" -#include "fluid_conv.h" - -void fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src); -fluid_real_t fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice); -void fluid_dump_modulator(fluid_mod_t * mod); - -#define fluid_mod_has_source(mod,cc,ctrl) \ -( ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) != 0) && (cc != 0)) \ - || ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) == 0) && (cc == 0)))) \ -|| ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) != 0) && (cc != 0)) \ - || ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) == 0) && (cc == 0))))) - -#define fluid_mod_has_dest(mod,gen) ((mod)->dest == gen) - - -#endif /* _FLUID_MOD_H */ diff --git a/libraries/fluidlite/src/fluid_phase.h b/libraries/fluidlite/src/fluid_phase.h deleted file mode 100644 index 019f6a3f1..000000000 --- a/libraries/fluidlite/src/fluid_phase.h +++ /dev/null @@ -1,115 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#ifndef _FLUID_PHASE_H -#define _FLUID_PHASE_H - -#include "fluid_config.h" - -/* - * phase - */ - -#define FLUID_INTERP_BITS 8 -#define FLUID_INTERP_BITS_MASK 0xff000000 -#define FLUID_INTERP_BITS_SHIFT 24 -#define FLUID_INTERP_MAX 256 - -#define FLUID_FRACT_MAX ((double)4294967296.0) - -/* fluid_phase_t -* Purpose: -* Playing pointer for voice playback -* -* When a sample is played back at a different pitch, the playing pointer in the -* source sample will not advance exactly one sample per output sample. -* This playing pointer is implemented using fluid_phase_t. -* It is a 64 bit number. The higher 32 bits contain the 'index' (number of -* the current sample), the lower 32 bits the fractional part. -*/ -typedef unsigned long long fluid_phase_t; - -/* Purpose: - * Set a to b. - * a: fluid_phase_t - * b: fluid_phase_t - */ -#define fluid_phase_set(a,b) a=b; - -#define fluid_phase_set_int(a, b) ((a) = ((unsigned long long)(b)) << 32) - -/* Purpose: - * Sets the phase a to a phase increment given in b. - * For example, assume b is 0.9. After setting a to it, adding a to - * the playing pointer will advance it by 0.9 samples. */ -#define fluid_phase_set_float(a, b) \ - (a) = (((unsigned long long)(b)) << 32) \ - | (uint32) (((double)(b) - (int)(b)) * (double)FLUID_FRACT_MAX) - -/* create a fluid_phase_t from an index and a fraction value */ -#define fluid_phase_from_index_fract(index, fract) \ - ((((unsigned long long)(index)) << 32) + (fract)) - -/* Purpose: - * Return the index and the fractional part, respectively. */ -#define fluid_phase_index(_x) \ - ((unsigned int)((_x) >> 32)) -#define fluid_phase_fract(_x) \ - ((uint32)((_x) & 0xFFFFFFFF)) - -/* Get the phase index with fractional rounding */ -#define fluid_phase_index_round(_x) \ - ((unsigned int)(((_x) + 0x80000000) >> 32)) - - -/* Purpose: - * Takes the fractional part of the argument phase and - * calculates the corresponding position in the interpolation table. - * The fractional position of the playing pointer is calculated with a quite high - * resolution (32 bits). It would be unpractical to keep a set of interpolation - * coefficients for each possible fractional part... - */ -#define fluid_phase_fract_to_tablerow(_x) \ - ((unsigned int)(fluid_phase_fract(_x) & FLUID_INTERP_BITS_MASK) >> FLUID_INTERP_BITS_SHIFT) - -#define fluid_phase_double(_x) \ - ((double)(fluid_phase_index(_x)) + ((double)fluid_phase_fract(_x) / FLUID_FRACT_MAX)) - -/* Purpose: - * Advance a by a step of b (both are fluid_phase_t). - */ -#define fluid_phase_incr(a, b) a += b - -/* Purpose: - * Subtract b from a (both are fluid_phase_t). - */ -#define fluid_phase_decr(a, b) a -= b - -/* Purpose: - * Subtract b samples from a. - */ -#define fluid_phase_sub_int(a, b) ((a) -= (unsigned long long)(b) << 32) - -/* Purpose: - * Creates the expression a.index++. */ -#define fluid_phase_index_plusplus(a) (((a) += 0x100000000LL) - -#endif /* _FLUID_PHASE_H */ diff --git a/libraries/fluidlite/src/fluid_rev.c b/libraries/fluidlite/src/fluid_rev.c deleted file mode 100644 index 3f09b6d63..000000000 --- a/libraries/fluidlite/src/fluid_rev.c +++ /dev/null @@ -1,561 +0,0 @@ -/* - - Freeverb - - Written by Jezar at Dreampoint, June 2000 - http://www.dreampoint.co.uk - This code is public domain - - Translated to C by Peter Hanappe, Mai 2001 -*/ - -#include "fluid_rev.h" - -/*************************************************************** - * - * REVERB - */ - -/* Denormalising: - * - * According to music-dsp thread 'Denormalise', Pentium processors - * have a hardware 'feature', that is of interest here, related to - * numeric underflow. We have a recursive filter. The output decays - * exponentially, if the input stops. So the numbers get smaller and - * smaller... At some point, they reach 'denormal' level. This will - * lead to drastic spikes in the CPU load. The effect was reproduced - * with the reverb - sometimes the average load over 10 s doubles!!. - * - * The 'undenormalise' macro fixes the problem: As soon as the number - * is close enough to denormal level, the macro forces the number to - * 0.0f. The original macro is: - * - * #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f - * - * This will zero out a number when it reaches the denormal level. - * Advantage: Maximum dynamic range Disadvantage: We'll have to check - * every sample, expensive. The alternative macro comes from a later - * mail from Jon Watte. It will zap a number before it reaches - * denormal level. Jon suggests to run it once per block instead of - * every sample. - */ - -# if defined(WITH_FLOATX) -# define zap_almost_zero(sample) (((*(unsigned int*)&(sample))&0x7f800000) < 0x08000000)?0.0f:(sample) -# else -/* 1e-20 was chosen as an arbitrary (small) threshold. */ -#define zap_almost_zero(sample) fabs(sample)<1e-10 ? 0 : sample; -#endif - -/* Denormalising part II: - * - * Another method fixes the problem cheaper: Use a small DC-offset in - * the filter calculations. Now the signals converge not against 0, - * but against the offset. The constant offset is invisible from the - * outside world (i.e. it does not appear at the output. There is a - * very small turn-on transient response, which should not cause - * problems. - */ - - -//#define DC_OFFSET 0 -#define DC_OFFSET 1e-8 -//#define DC_OFFSET 0.001f -typedef struct _fluid_allpass fluid_allpass; -typedef struct _fluid_comb fluid_comb; - -struct _fluid_allpass { - fluid_real_t feedback; - fluid_real_t *buffer; - int bufsize; - int bufidx; -}; - -void fluid_allpass_setbuffer(fluid_allpass* allpass, fluid_real_t *buf, int size); -void fluid_allpass_init(fluid_allpass* allpass); -void fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val); -fluid_real_t fluid_allpass_getfeedback(fluid_allpass* allpass); - -void -fluid_allpass_setbuffer(fluid_allpass* allpass, fluid_real_t *buf, int size) -{ - allpass->bufidx = 0; - allpass->buffer = buf; - allpass->bufsize = size; -} - -void -fluid_allpass_init(fluid_allpass* allpass) -{ - int i; - int len = allpass->bufsize; - fluid_real_t* buf = allpass->buffer; - for (i = 0; i < len; i++) { - buf[i] = DC_OFFSET; /* this is not 100 % correct. */ - } -} - -void -fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val) -{ - allpass->feedback = val; -} - -fluid_real_t -fluid_allpass_getfeedback(fluid_allpass* allpass) -{ - return allpass->feedback; -} - -#define fluid_allpass_process(_allpass, _input) \ -{ \ - fluid_real_t output; \ - fluid_real_t bufout; \ - bufout = _allpass.buffer[_allpass.bufidx]; \ - output = bufout-_input; \ - _allpass.buffer[_allpass.bufidx] = _input + (bufout * _allpass.feedback); \ - if (++_allpass.bufidx >= _allpass.bufsize) { \ - _allpass.bufidx = 0; \ - } \ - _input = output; \ -} - -/* fluid_real_t fluid_allpass_process(fluid_allpass* allpass, fluid_real_t input) */ -/* { */ -/* fluid_real_t output; */ -/* fluid_real_t bufout; */ -/* bufout = allpass->buffer[allpass->bufidx]; */ -/* undenormalise(bufout); */ -/* output = -input + bufout; */ -/* allpass->buffer[allpass->bufidx] = input + (bufout * allpass->feedback); */ -/* if (++allpass->bufidx >= allpass->bufsize) { */ -/* allpass->bufidx = 0; */ -/* } */ -/* return output; */ -/* } */ - -struct _fluid_comb { - fluid_real_t feedback; - fluid_real_t filterstore; - fluid_real_t damp1; - fluid_real_t damp2; - fluid_real_t *buffer; - int bufsize; - int bufidx; -}; - -void fluid_comb_setbuffer(fluid_comb* comb, fluid_real_t *buf, int size); -void fluid_comb_init(fluid_comb* comb); -void fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val); -fluid_real_t fluid_comb_getdamp(fluid_comb* comb); -void fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val); -fluid_real_t fluid_comb_getfeedback(fluid_comb* comb); - -void -fluid_comb_setbuffer(fluid_comb* comb, fluid_real_t *buf, int size) -{ - comb->filterstore = 0; - comb->bufidx = 0; - comb->buffer = buf; - comb->bufsize = size; -} - -void -fluid_comb_init(fluid_comb* comb) -{ - int i; - fluid_real_t* buf = comb->buffer; - int len = comb->bufsize; - for (i = 0; i < len; i++) { - buf[i] = DC_OFFSET; /* This is not 100 % correct. */ - } -} - -void -fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val) -{ - comb->damp1 = val; - comb->damp2 = 1 - val; -} - -fluid_real_t -fluid_comb_getdamp(fluid_comb* comb) -{ - return comb->damp1; -} - -void -fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val) -{ - comb->feedback = val; -} - -fluid_real_t -fluid_comb_getfeedback(fluid_comb* comb) -{ - return comb->feedback; -} - -#define fluid_comb_process(_comb, _input, _output) \ -{ \ - fluid_real_t _tmp = _comb.buffer[_comb.bufidx]; \ - _comb.filterstore = (_tmp * _comb.damp2) + (_comb.filterstore * _comb.damp1); \ - _comb.buffer[_comb.bufidx] = _input + (_comb.filterstore * _comb.feedback); \ - if (++_comb.bufidx >= _comb.bufsize) { \ - _comb.bufidx = 0; \ - } \ - _output += _tmp; \ -} - -/* fluid_real_t fluid_comb_process(fluid_comb* comb, fluid_real_t input) */ -/* { */ -/* fluid_real_t output; */ - -/* output = comb->buffer[comb->bufidx]; */ -/* undenormalise(output); */ -/* comb->filterstore = (output * comb->damp2) + (comb->filterstore * comb->damp1); */ -/* undenormalise(comb->filterstore); */ -/* comb->buffer[comb->bufidx] = input + (comb->filterstore * comb->feedback); */ -/* if (++comb->bufidx >= comb->bufsize) { */ -/* comb->bufidx = 0; */ -/* } */ - -/* return output; */ -/* } */ - -#define numcombs 8 -#define numallpasses 4 -#define fixedgain 0.015f -#define scalewet 3.0f -#define scaledamp 1.0f -#define scaleroom 0.28f -#define offsetroom 0.7f -#define initialroom 0.5f -#define initialdamp 0.2f -#define initialwet 1 -#define initialdry 0 -#define initialwidth 1 -#define stereospread 23 - -/* - These values assume 44.1KHz sample rate - they will probably be OK for 48KHz sample rate - but would need scaling for 96KHz (or other) sample rates. - The values were obtained by listening tests. -*/ -#define combtuningL1 1116 -#define combtuningR1 1116 + stereospread -#define combtuningL2 1188 -#define combtuningR2 1188 + stereospread -#define combtuningL3 1277 -#define combtuningR3 1277 + stereospread -#define combtuningL4 1356 -#define combtuningR4 1356 + stereospread -#define combtuningL5 1422 -#define combtuningR5 1422 + stereospread -#define combtuningL6 1491 -#define combtuningR6 1491 + stereospread -#define combtuningL7 1557 -#define combtuningR7 1557 + stereospread -#define combtuningL8 1617 -#define combtuningR8 1617 + stereospread -#define allpasstuningL1 556 -#define allpasstuningR1 556 + stereospread -#define allpasstuningL2 441 -#define allpasstuningR2 441 + stereospread -#define allpasstuningL3 341 -#define allpasstuningR3 341 + stereospread -#define allpasstuningL4 225 -#define allpasstuningR4 225 + stereospread - -struct _fluid_revmodel_t { - fluid_real_t roomsize; - fluid_real_t damp; - fluid_real_t wet, wet1, wet2; - fluid_real_t width; - fluid_real_t gain; - /* - The following are all declared inline - to remove the need for dynamic allocation - with its subsequent error-checking messiness - */ - /* Comb filters */ - fluid_comb combL[numcombs]; - fluid_comb combR[numcombs]; - /* Allpass filters */ - fluid_allpass allpassL[numallpasses]; - fluid_allpass allpassR[numallpasses]; - /* Buffers for the combs */ - fluid_real_t bufcombL1[combtuningL1]; - fluid_real_t bufcombR1[combtuningR1]; - fluid_real_t bufcombL2[combtuningL2]; - fluid_real_t bufcombR2[combtuningR2]; - fluid_real_t bufcombL3[combtuningL3]; - fluid_real_t bufcombR3[combtuningR3]; - fluid_real_t bufcombL4[combtuningL4]; - fluid_real_t bufcombR4[combtuningR4]; - fluid_real_t bufcombL5[combtuningL5]; - fluid_real_t bufcombR5[combtuningR5]; - fluid_real_t bufcombL6[combtuningL6]; - fluid_real_t bufcombR6[combtuningR6]; - fluid_real_t bufcombL7[combtuningL7]; - fluid_real_t bufcombR7[combtuningR7]; - fluid_real_t bufcombL8[combtuningL8]; - fluid_real_t bufcombR8[combtuningR8]; - /* Buffers for the allpasses */ - fluid_real_t bufallpassL1[allpasstuningL1]; - fluid_real_t bufallpassR1[allpasstuningR1]; - fluid_real_t bufallpassL2[allpasstuningL2]; - fluid_real_t bufallpassR2[allpasstuningR2]; - fluid_real_t bufallpassL3[allpasstuningL3]; - fluid_real_t bufallpassR3[allpasstuningR3]; - fluid_real_t bufallpassL4[allpasstuningL4]; - fluid_real_t bufallpassR4[allpasstuningR4]; -}; - -void fluid_revmodel_update(fluid_revmodel_t* rev); -void fluid_revmodel_init(fluid_revmodel_t* rev); - -fluid_revmodel_t* -new_fluid_revmodel() -{ - fluid_revmodel_t* rev; - rev = FLUID_NEW(fluid_revmodel_t); - if (rev == NULL) { - return NULL; - } - - /* Tie the components to their buffers */ - fluid_comb_setbuffer(&rev->combL[0], rev->bufcombL1, combtuningL1); - fluid_comb_setbuffer(&rev->combR[0], rev->bufcombR1, combtuningR1); - fluid_comb_setbuffer(&rev->combL[1], rev->bufcombL2, combtuningL2); - fluid_comb_setbuffer(&rev->combR[1], rev->bufcombR2, combtuningR2); - fluid_comb_setbuffer(&rev->combL[2], rev->bufcombL3, combtuningL3); - fluid_comb_setbuffer(&rev->combR[2], rev->bufcombR3, combtuningR3); - fluid_comb_setbuffer(&rev->combL[3], rev->bufcombL4, combtuningL4); - fluid_comb_setbuffer(&rev->combR[3], rev->bufcombR4, combtuningR4); - fluid_comb_setbuffer(&rev->combL[4], rev->bufcombL5, combtuningL5); - fluid_comb_setbuffer(&rev->combR[4], rev->bufcombR5, combtuningR5); - fluid_comb_setbuffer(&rev->combL[5], rev->bufcombL6, combtuningL6); - fluid_comb_setbuffer(&rev->combR[5], rev->bufcombR6, combtuningR6); - fluid_comb_setbuffer(&rev->combL[6], rev->bufcombL7, combtuningL7); - fluid_comb_setbuffer(&rev->combR[6], rev->bufcombR7, combtuningR7); - fluid_comb_setbuffer(&rev->combL[7], rev->bufcombL8, combtuningL8); - fluid_comb_setbuffer(&rev->combR[7], rev->bufcombR8, combtuningR8); - fluid_allpass_setbuffer(&rev->allpassL[0], rev->bufallpassL1, allpasstuningL1); - fluid_allpass_setbuffer(&rev->allpassR[0], rev->bufallpassR1, allpasstuningR1); - fluid_allpass_setbuffer(&rev->allpassL[1], rev->bufallpassL2, allpasstuningL2); - fluid_allpass_setbuffer(&rev->allpassR[1], rev->bufallpassR2, allpasstuningR2); - fluid_allpass_setbuffer(&rev->allpassL[2], rev->bufallpassL3, allpasstuningL3); - fluid_allpass_setbuffer(&rev->allpassR[2], rev->bufallpassR3, allpasstuningR3); - fluid_allpass_setbuffer(&rev->allpassL[3], rev->bufallpassL4, allpasstuningL4); - fluid_allpass_setbuffer(&rev->allpassR[3], rev->bufallpassR4, allpasstuningR4); - /* Set default values */ - fluid_allpass_setfeedback(&rev->allpassL[0], 0.5f); - fluid_allpass_setfeedback(&rev->allpassR[0], 0.5f); - fluid_allpass_setfeedback(&rev->allpassL[1], 0.5f); - fluid_allpass_setfeedback(&rev->allpassR[1], 0.5f); - fluid_allpass_setfeedback(&rev->allpassL[2], 0.5f); - fluid_allpass_setfeedback(&rev->allpassR[2], 0.5f); - fluid_allpass_setfeedback(&rev->allpassL[3], 0.5f); - fluid_allpass_setfeedback(&rev->allpassR[3], 0.5f); - - /* set values manually, since calling set functions causes update - and all values should be initialized for an update */ - rev->roomsize = initialroom * scaleroom + offsetroom; - rev->damp = initialdamp * scaledamp; - rev->wet = initialwet * scalewet; - rev->width = initialwidth; - rev->gain = fixedgain; - - /* now its okay to update reverb */ - fluid_revmodel_update(rev); - - /* Clear all buffers */ - fluid_revmodel_init(rev); - return rev; -} - -void -delete_fluid_revmodel(fluid_revmodel_t* rev) -{ - FLUID_FREE(rev); -} - -void -fluid_revmodel_init(fluid_revmodel_t* rev) -{ - int i; - for (i = 0; i < numcombs;i++) { - fluid_comb_init(&rev->combL[i]); - fluid_comb_init(&rev->combR[i]); - } - for (i = 0; i < numallpasses; i++) { - fluid_allpass_init(&rev->allpassL[i]); - fluid_allpass_init(&rev->allpassR[i]); - } -} - -void -fluid_revmodel_reset(fluid_revmodel_t* rev) -{ - fluid_revmodel_init(rev); -} - -void -fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in, - fluid_real_t *left_out, fluid_real_t *right_out) -{ - int i, k = 0; - fluid_real_t outL, outR, input; - - for (k = 0; k < FLUID_BUFSIZE; k++) { - - outL = outR = 0; - - /* The original Freeverb code expects a stereo signal and 'input' - * is set to the sum of the left and right input sample. Since - * this code works on a mono signal, 'input' is set to twice the - * input sample. */ - input = (2 * in[k] + DC_OFFSET) * rev->gain; - - /* Accumulate comb filters in parallel */ - for (i = 0; i < numcombs; i++) { - fluid_comb_process(rev->combL[i], input, outL); - fluid_comb_process(rev->combR[i], input, outR); - } - /* Feed through allpasses in series */ - for (i = 0; i < numallpasses; i++) { - fluid_allpass_process(rev->allpassL[i], outL); - fluid_allpass_process(rev->allpassR[i], outR); - } - - /* Remove the DC offset */ - outL -= DC_OFFSET; - outR -= DC_OFFSET; - - /* Calculate output REPLACING anything already there */ - left_out[k] = outL * rev->wet1 + outR * rev->wet2; - right_out[k] = outR * rev->wet1 + outL * rev->wet2; - } -} - -void -fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in, - fluid_real_t *left_out, fluid_real_t *right_out) -{ - int i, k = 0; - fluid_real_t outL, outR, input; - - for (k = 0; k < FLUID_BUFSIZE; k++) { - - outL = outR = 0; - - /* The original Freeverb code expects a stereo signal and 'input' - * is set to the sum of the left and right input sample. Since - * this code works on a mono signal, 'input' is set to twice the - * input sample. */ - input = (2 * in[k] + DC_OFFSET) * rev->gain; - - /* Accumulate comb filters in parallel */ - for (i = 0; i < numcombs; i++) { - fluid_comb_process(rev->combL[i], input, outL); - fluid_comb_process(rev->combR[i], input, outR); - } - /* Feed through allpasses in series */ - for (i = 0; i < numallpasses; i++) { - fluid_allpass_process(rev->allpassL[i], outL); - fluid_allpass_process(rev->allpassR[i], outR); - } - - /* Remove the DC offset */ - outL -= DC_OFFSET; - outR -= DC_OFFSET; - - /* Calculate output MIXING with anything already there */ - left_out[k] += outL * rev->wet1 + outR * rev->wet2; - right_out[k] += outR * rev->wet1 + outL * rev->wet2; - } -} - -void -fluid_revmodel_update(fluid_revmodel_t* rev) -{ - /* Recalculate internal values after parameter change */ - int i; - - rev->wet1 = rev->wet * (rev->width / 2 + 0.5f); - rev->wet2 = rev->wet * ((1 - rev->width) / 2); - - for (i = 0; i < numcombs; i++) { - fluid_comb_setfeedback(&rev->combL[i], rev->roomsize); - fluid_comb_setfeedback(&rev->combR[i], rev->roomsize); - } - - for (i = 0; i < numcombs; i++) { - fluid_comb_setdamp(&rev->combL[i], rev->damp); - fluid_comb_setdamp(&rev->combR[i], rev->damp); - } -} - -/* - The following get/set functions are not inlined, because - speed is never an issue when calling them, and also - because as you develop the reverb model, you may - wish to take dynamic action when they are called. -*/ -void -fluid_revmodel_setroomsize(fluid_revmodel_t* rev, fluid_real_t value) -{ -/* fluid_clip(value, 0.0f, 1.0f); */ - rev->roomsize = (value * scaleroom) + offsetroom; - fluid_revmodel_update(rev); -} - -fluid_real_t -fluid_revmodel_getroomsize(fluid_revmodel_t* rev) -{ - return (rev->roomsize - offsetroom) / scaleroom; -} - -void -fluid_revmodel_setdamp(fluid_revmodel_t* rev, fluid_real_t value) -{ -/* fluid_clip(value, 0.0f, 1.0f); */ - rev->damp = value * scaledamp; - fluid_revmodel_update(rev); -} - -fluid_real_t -fluid_revmodel_getdamp(fluid_revmodel_t* rev) -{ - return rev->damp / scaledamp; -} - -void -fluid_revmodel_setlevel(fluid_revmodel_t* rev, fluid_real_t value) -{ - fluid_clip(value, 0.0f, 1.0f); - rev->wet = value * scalewet; - fluid_revmodel_update(rev); -} - -fluid_real_t -fluid_revmodel_getlevel(fluid_revmodel_t* rev) -{ - return rev->wet / scalewet; -} - -void -fluid_revmodel_setwidth(fluid_revmodel_t* rev, fluid_real_t value) -{ -/* fluid_clip(value, 0.0f, 1.0f); */ - rev->width = value; - fluid_revmodel_update(rev); -} - -fluid_real_t -fluid_revmodel_getwidth(fluid_revmodel_t* rev) -{ - return rev->width; -} diff --git a/libraries/fluidlite/src/fluid_rev.h b/libraries/fluidlite/src/fluid_rev.h deleted file mode 100644 index 9dc3c213c..000000000 --- a/libraries/fluidlite/src/fluid_rev.h +++ /dev/null @@ -1,67 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#ifndef _FLUID_REV_H -#define _FLUID_REV_H - -#include "fluidsynth_priv.h" - -typedef struct _fluid_revmodel_t fluid_revmodel_t; - - -/* - * reverb - */ -fluid_revmodel_t* new_fluid_revmodel(void); -void delete_fluid_revmodel(fluid_revmodel_t* rev); - -void fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in, - fluid_real_t *left_out, fluid_real_t *right_out); - -void fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in, - fluid_real_t *left_out, fluid_real_t *right_out); - -void fluid_revmodel_reset(fluid_revmodel_t* rev); - -void fluid_revmodel_setroomsize(fluid_revmodel_t* rev, fluid_real_t value); -void fluid_revmodel_setdamp(fluid_revmodel_t* rev, fluid_real_t value); -void fluid_revmodel_setlevel(fluid_revmodel_t* rev, fluid_real_t value); -void fluid_revmodel_setwidth(fluid_revmodel_t* rev, fluid_real_t value); -void fluid_revmodel_setmode(fluid_revmodel_t* rev, fluid_real_t value); - -fluid_real_t fluid_revmodel_getroomsize(fluid_revmodel_t* rev); -fluid_real_t fluid_revmodel_getdamp(fluid_revmodel_t* rev); -fluid_real_t fluid_revmodel_getlevel(fluid_revmodel_t* rev); -fluid_real_t fluid_revmodel_getwidth(fluid_revmodel_t* rev); - -/* - * reverb preset - */ -typedef struct _fluid_revmodel_presets_t { - char* name; - fluid_real_t roomsize; - fluid_real_t damp; - fluid_real_t width; - fluid_real_t level; -} fluid_revmodel_presets_t; - - -#endif /* _FLUID_REV_H */ diff --git a/libraries/fluidlite/src/fluid_settings.c b/libraries/fluidlite/src/fluid_settings.c deleted file mode 100644 index 26a108624..000000000 --- a/libraries/fluidlite/src/fluid_settings.c +++ /dev/null @@ -1,822 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#include "fluidsynth_priv.h" -#include "fluid_sys.h" -#include "fluid_hash.h" -#include "fluid_synth.h" -#include "fluid_settings.h" - -/* maximum allowed components of a settings variable (separated by '.') */ -#define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */ -#define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */ - -static void fluid_settings_init(fluid_settings_t* settings); -static void fluid_settings_hash_delete(void* value, int type); -static int fluid_settings_tokenize(const char* s, char *buf, char** ptr); - - -typedef struct { - char* value; - char* def; - int hints; - fluid_list_t* options; - fluid_str_update_t update; - void* data; -} fluid_str_setting_t; - -static fluid_str_setting_t* -new_fluid_str_setting(const char* value, char* def, int hints, fluid_str_update_t fun, void* data) -{ - fluid_str_setting_t* str; - str = FLUID_NEW(fluid_str_setting_t); - str->value = value? FLUID_STRDUP(value) : NULL; - str->def = def? FLUID_STRDUP(def) : NULL; - str->hints = hints; - str->options = NULL; - str->update = fun; - str->data = data; - return str; -} - -static void delete_fluid_str_setting(fluid_str_setting_t* str) -{ - if (str) { - if (str->value) { - FLUID_FREE(str->value); - } - if (str->def) { - FLUID_FREE(str->def); - } - if (str->options) { - fluid_list_t* list = str->options; - - while (list) { - FLUID_FREE (list->data); - list = fluid_list_next(list); - } - - delete_fluid_list(str->options); - } - FLUID_FREE(str); - } -} - - - - -typedef struct { - double value; - double def; - double min; - double max; - int hints; - fluid_num_update_t update; - void* data; -} fluid_num_setting_t; - - -static fluid_num_setting_t* -new_fluid_num_setting(double min, double max, double def, - int hints, fluid_num_update_t fun, void* data) -{ - fluid_num_setting_t* setting; - setting = FLUID_NEW(fluid_num_setting_t); - setting->value = def; - setting->def = def; - setting->min = min; - setting->max = max; - setting->hints = hints; - setting->update = fun; - setting->data = data; - return setting; -} - -static void delete_fluid_num_setting(fluid_num_setting_t* setting) -{ - if (setting) { - FLUID_FREE(setting); - } -} - - - - -typedef struct { - int value; - int def; - int min; - int max; - int hints; - fluid_int_update_t update; - void* data; -} fluid_int_setting_t; - - -static fluid_int_setting_t* -new_fluid_int_setting(int min, int max, int def, - int hints, fluid_int_update_t fun, void* data) -{ - fluid_int_setting_t* setting; - setting = FLUID_NEW(fluid_int_setting_t); - setting->value = def; - setting->def = def; - setting->min = min; - setting->max = max; - setting->hints = hints; - setting->update = fun; - setting->data = data; - return setting; -} - -static void delete_fluid_int_setting(fluid_int_setting_t* setting) -{ - if (setting) { - FLUID_FREE(setting); - } -} - - - -fluid_settings_t* new_fluid_settings() -{ - fluid_settings_t* settings = new_fluid_hashtable(fluid_settings_hash_delete); - if (settings == NULL) { - return NULL; - } - fluid_settings_init(settings); - return settings; -} - -void delete_fluid_settings(fluid_settings_t* settings) -{ - delete_fluid_hashtable(settings); -} - -void fluid_settings_hash_delete(void* value, int type) -{ - switch (type) { - case FLUID_NUM_TYPE: - delete_fluid_num_setting((fluid_num_setting_t*) value); - break; - case FLUID_INT_TYPE: - delete_fluid_int_setting((fluid_int_setting_t*) value); - break; - case FLUID_STR_TYPE: - delete_fluid_str_setting((fluid_str_setting_t*) value); - break; - case FLUID_SET_TYPE: - delete_fluid_hashtable((fluid_hashtable_t*) value); - break; - } -} - -void fluid_settings_init(fluid_settings_t* settings) -{ - fluid_synth_settings(settings); -} - -static int fluid_settings_tokenize(const char* s, char *buf, char** ptr) -{ - char *tokstr, *tok; - int n = 0; - - if (strlen (s) > MAX_SETTINGS_LABEL) - { - FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars", - MAX_SETTINGS_LABEL); - return 0; - } - - FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */ - tokstr = buf; - - while ((tok = fluid_strtok (&tokstr, "."))) - { - if (n > MAX_SETTINGS_TOKENS) - { - FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d", - MAX_SETTINGS_TOKENS); - return 0; - } - - ptr[n++] = tok; - } - - return n; -} - -/** returns 1 if the value exists, 0 otherwise */ -static int fluid_settings_get(fluid_settings_t* settings, - char** name, int len, - void** value, int* type) -{ - fluid_hashtable_t* table = settings; - int t; - void* v = NULL; - int n; - - for (n = 0; n < len; n++) { - - if (table == NULL) { - return 0; - } - - if (!fluid_hashtable_lookup(table, name[n], &v, &t)) { - return 0; - } - - table = (t == FLUID_SET_TYPE)? (fluid_hashtable_t*) v : NULL; - } - - if (value) { - *value = v; - } - - if (type) { - *type = t; - } - - return 1; -} - -/** returns 1 if the value has been set, zero otherwise */ -static int fluid_settings_set(fluid_settings_t* settings, - char** name, int len, - void* value, int type) -{ - fluid_hashtable_t* table = settings; - int t; - void* v; - int n, num = len - 1; - - for (n = 0; n < num; n++) { - - if (fluid_hashtable_lookup(table, name[n], &v, &t)) { - - if (t == FLUID_SET_TYPE) { - table = (fluid_hashtable_t*) v; - } else { - /* path ends prematurely */ - FLUID_LOG(FLUID_WARN, "'%s' is not a node", name[n]); - return 0; - } - - } else { - /* create a new node */ - fluid_hashtable_t* tmp; - tmp = new_fluid_hashtable(fluid_settings_hash_delete); - fluid_hashtable_insert(table, name[n], tmp, FLUID_SET_TYPE); - table = tmp; - } - } - - fluid_hashtable_replace(table, name[num], value, type); - - return 1; -} - -/** returns 1 if the value has been registered correctly, 0 - otherwise */ -int fluid_settings_register_str(fluid_settings_t* settings, const char* name, char* def, int hints, - fluid_str_update_t fun, void* data) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - fluid_str_setting_t* setting; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { - setting = new_fluid_str_setting(def, def, hints, fun, data); - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE); - - } else { - /* if variable already exists, don't change its value. */ - if (type == FLUID_STR_TYPE) { - setting = (fluid_str_setting_t*) value; - setting->update = fun; - setting->data = data; - setting->def = def? FLUID_STRDUP(def) : NULL; - setting->hints = hints; - return 1; - } else { - FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); - return 1; - } - } -} - -/** returns 1 if the value has been register correctly, zero - otherwise */ -int fluid_settings_register_num(fluid_settings_t* settings, const char* name, double def, - double min, double max, int hints, - fluid_num_update_t fun, void* data) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { - /* insert a new setting */ - fluid_num_setting_t* setting; - setting = new_fluid_num_setting(min, max, def, hints, fun, data); - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE); - - } else { - if (type == FLUID_NUM_TYPE) { - /* update the existing setting but don't change its value */ - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; - setting->update = fun; - setting->data = data; - setting->min = min; - setting->max = max; - setting->def = def; - setting->hints = hints; - return 1; - - } else { - /* type mismatch */ - FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); - return 0; - } - } -} - -/** returns 1 if the value has been register correctly, zero - otherwise */ -int fluid_settings_register_int(fluid_settings_t* settings, const char* name, int def, - int min, int max, int hints, - fluid_int_update_t fun, void* data) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { - /* insert a new setting */ - fluid_int_setting_t* setting; - setting = new_fluid_int_setting(min, max, def, hints, fun, data); - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE); - - } else { - if (type == FLUID_INT_TYPE) { - /* update the existing setting but don't change its value */ - fluid_int_setting_t* setting = (fluid_int_setting_t*) value; - setting->update = fun; - setting->data = data; - setting->min = min; - setting->max = max; - setting->def = def; - setting->hints = hints; - return 1; - - } else { - /* type mismatch */ - FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); - return 0; - } - } -} - -int fluid_settings_get_type(fluid_settings_t* settings, const char* name) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - return (fluid_settings_get(settings, tokens, ntokens, &value, &type))? type : FLUID_NO_TYPE; -} - -int fluid_settings_get_hints(fluid_settings_t* settings, const char* name) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { - if (type == FLUID_NUM_TYPE) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; - return setting->hints; - } else if (type == FLUID_STR_TYPE) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; - return setting->hints; - } else { - return 0; - } - } else { - return 0; - } -} - -int fluid_settings_is_realtime(fluid_settings_t* settings, const char* name) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { - if (type == FLUID_NUM_TYPE) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; - return setting->update != NULL; - - } else if (type == FLUID_STR_TYPE) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; - return setting->update != NULL; - } else { - return 0; - } - } else { - return 0; - } -} - -int fluid_settings_setstr(fluid_settings_t* settings, const char* name, const char* str) -{ - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - int type; - void* value; - fluid_str_setting_t* setting; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { - - if (type != FLUID_STR_TYPE) { - return 0; - } - - setting = (fluid_str_setting_t*) value; - - if (setting->value) { - FLUID_FREE(setting->value); - } - setting->value = str? FLUID_STRDUP(str) : NULL; - - if (setting->update) { - (*setting->update)(setting->data, name, setting->value); - } - - return 1; - - } else { - /* insert a new setting */ - fluid_str_setting_t* setting; - setting = new_fluid_str_setting(str, NULL, 0, NULL, NULL); - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE); - } -} - -int fluid_settings_getstr(fluid_settings_t* settings, const char* name, char** str) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_STR_TYPE)) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; - *str = setting->value; - return 1; - } - *str = NULL; - return 0; -} - -int fluid_settings_str_equal(fluid_settings_t* settings, const char* name, char* s) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_STR_TYPE)) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; - return FLUID_STRCMP(setting->value, s) == 0; - } - return 0; -} - -char* -fluid_settings_getstr_default(fluid_settings_t* settings, const char* name) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_STR_TYPE)) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; - return setting->def; - } else { - return NULL; - } -} - -int fluid_settings_add_option(fluid_settings_t* settings, const char* name, char* s) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_STR_TYPE)) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; - char* copy = FLUID_STRDUP(s); - setting->options = fluid_list_append(setting->options, copy); - return 1; - } else { - return 0; - } -} - -int fluid_settings_remove_option(fluid_settings_t* settings, const char* name, char* s) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_STR_TYPE)) { - - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; - fluid_list_t* list = setting->options; - - while (list) { - char* option = (char*) fluid_list_get(list); - if (FLUID_STRCMP(s, option) == 0) { - FLUID_FREE (option); - setting->options = fluid_list_remove_link(setting->options, list); - return 1; - } - list = fluid_list_next(list); - } - - return 0; - } else { - return 0; - } -} - -int fluid_settings_setnum(fluid_settings_t* settings, const char* name, double val) -{ - int type; - void* value; - fluid_num_setting_t* setting; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { - - if (type != FLUID_NUM_TYPE) { - return 0; - } - - setting = (fluid_num_setting_t*) value; - - if (val < setting->min) { - val = setting->min; - } else if (val > setting->max) { - val = setting->max; - } - - setting->value = val; - - if (setting->update) { - (*setting->update)(setting->data, name, val); - } - - return 1; - - } else { - /* insert a new setting */ - fluid_num_setting_t* setting; - setting = new_fluid_num_setting(-1e10, 1e10, 0.0f, 0, NULL, NULL); - setting->value = val; - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE); - } -} - -int fluid_settings_getnum(fluid_settings_t* settings, const char* name, double* val) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_NUM_TYPE)) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; - *val = setting->value; - return 1; - } - return 0; -} - - -void fluid_settings_getnum_range(fluid_settings_t* settings, const char* name, double* min, double* max) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_NUM_TYPE)) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; - *min = setting->min; - *max = setting->max; - } -} - -double -fluid_settings_getnum_default(fluid_settings_t* settings, const char* name) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_NUM_TYPE)) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; - return setting->def; - } else { - return 0.0f; - } -} - - -int fluid_settings_setint(fluid_settings_t* settings, const char* name, int val) -{ - int type; - void* value; - fluid_int_setting_t* setting; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { - - if (type != FLUID_INT_TYPE) { - return 0; - } - - setting = (fluid_int_setting_t*) value; - - if (val < setting->min) { - val = setting->min; - } else if (val > setting->max) { - val = setting->max; - } - - setting->value = val; - - if (setting->update) { - (*setting->update)(setting->data, name, val); - } - - return 1; - - } else { - /* insert a new setting */ - fluid_int_setting_t* setting; - setting = new_fluid_int_setting(INT_MIN, INT_MAX, 0, 0, NULL, NULL); - setting->value = val; - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE); - } -} - -int fluid_settings_getint(fluid_settings_t* settings, const char* name, int* val) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_INT_TYPE)) { - fluid_int_setting_t* setting = (fluid_int_setting_t*) value; - *val = setting->value; - return 1; - } - return 0; -} - - -void fluid_settings_getint_range(fluid_settings_t* settings, const char* name, int* min, int* max) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_INT_TYPE)) { - fluid_int_setting_t* setting = (fluid_int_setting_t*) value; - *min = setting->min; - *max = setting->max; - } -} - -int -fluid_settings_getint_default(fluid_settings_t* settings, const char* name) -{ - int type; - void* value; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - - ntokens = fluid_settings_tokenize(name, buf, tokens); - - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_INT_TYPE)) { - fluid_int_setting_t* setting = (fluid_int_setting_t*) value; - return setting->def; - } else { - return 0.0f; - } -} diff --git a/libraries/fluidlite/src/fluid_settings.h b/libraries/fluidlite/src/fluid_settings.h deleted file mode 100644 index 761ae7a5a..000000000 --- a/libraries/fluidlite/src/fluid_settings.h +++ /dev/null @@ -1,55 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#ifndef _FLUID_SETTINGS_H -#define _FLUID_SETTINGS_H - - - -/** returns 1 if the option was added, 0 otherwise */ -int fluid_settings_add_option(fluid_settings_t* settings, const char* name, char* s); - -/** returns 1 if the option was added, 0 otherwise */ -int fluid_settings_remove_option(fluid_settings_t* settings, const char* name, char* s); - - -typedef int (*fluid_num_update_t)(void* data, const char* name, double value); -typedef int (*fluid_str_update_t)(void* data, const char* name, char* value); -typedef int (*fluid_int_update_t)(void* data, const char* name, int value); - -/** returns 0 if the value has been resgister correctly, non-zero - otherwise */ -int fluid_settings_register_str(fluid_settings_t* settings, const char* name, char* def, int hints, - fluid_str_update_t fun, void* data); - -/** returns 0 if the value has been resgister correctly, non-zero - otherwise */ -int fluid_settings_register_num(fluid_settings_t* settings, const char* name, double min, double max, - double def, int hints, fluid_num_update_t fun, void* data); - - -/** returns 0 if the value has been resgister correctly, non-zero - otherwise */ -int fluid_settings_register_int(fluid_settings_t* settings, const char* name, int min, int max, - int def, int hints, fluid_int_update_t fun, void* data); - - -#endif /* _FLUID_SETTINGS_H */ diff --git a/libraries/fluidlite/src/fluid_sfont.h b/libraries/fluidlite/src/fluid_sfont.h deleted file mode 100644 index 793675097..000000000 --- a/libraries/fluidlite/src/fluid_sfont.h +++ /dev/null @@ -1,77 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#ifndef _PRIV_FLUID_SFONT_H -#define _PRIV_FLUID_SFONT_H - - -/* - * Utility macros to access soundfonts, presets, and samples - */ - -#define fluid_fileapi_delete(_fileapi) { \ - if ((_fileapi) && (_fileapi)->free) \ - (*(_fileapi)->free)(_fileapi); \ -} -#define fluid_sfloader_delete(_loader) { \ - if (_loader) { \ - fluid_fileapi_delete((_loader)->fileapi); \ - if ((_loader)->free) (*(_loader)->free)(_loader); \ - } \ - } -#define fluid_sfloader_load(_loader, _filename) (*(_loader)->load)(_loader, _filename) - - -#define delete_fluid_sfont(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0) -#define fluid_sfont_get_name(_sf) (*(_sf)->get_name)(_sf) -#define fluid_sfont_get_preset(_sf,_bank,_prenum) (*(_sf)->get_preset)(_sf,_bank,_prenum) -#define fluid_sfont_iteration_start(_sf) (*(_sf)->iteration_start)(_sf) -#define fluid_sfont_iteration_next(_sf,_pr) (*(_sf)->iteration_next)(_sf,_pr) -#define fluid_sfont_get_data(_sf) (_sf)->data -#define fluid_sfont_set_data(_sf,_p) { (_sf)->data = (void*) (_p); } - - -#define delete_fluid_preset(_preset) \ - { if ((_preset) && (_preset)->free) { (*(_preset)->free)(_preset); }} - -#define fluid_preset_get_data(_preset) (_preset)->data -#define fluid_preset_set_data(_preset,_p) { (_preset)->data = (void*) (_p); } -#define fluid_preset_get_name(_preset) (*(_preset)->get_name)(_preset) -#define fluid_preset_get_banknum(_preset) (*(_preset)->get_banknum)(_preset) -#define fluid_preset_get_num(_preset) (*(_preset)->get_num)(_preset) - -#define fluid_preset_noteon(_preset,_synth,_ch,_key,_vel) \ - (*(_preset)->noteon)(_preset,_synth,_ch,_key,_vel) - -#define fluid_preset_notify(_preset,_reason,_chan) \ - { if ((_preset) && (_preset)->notify) { (*(_preset)->notify)(_preset,_reason,_chan); }} - - -#define fluid_sample_incr_ref(_sample) { (_sample)->refcount++; } - -#define fluid_sample_decr_ref(_sample) \ - (_sample)->refcount--; \ - if (((_sample)->refcount == 0) && ((_sample)->notify)) \ - (*(_sample)->notify)(_sample, FLUID_SAMPLE_DONE); - - - -#endif /* _PRIV_FLUID_SFONT_H */ diff --git a/libraries/fluidlite/src/fluid_synth.c b/libraries/fluidlite/src/fluid_synth.c deleted file mode 100644 index 5b3d4191b..000000000 --- a/libraries/fluidlite/src/fluid_synth.c +++ /dev/null @@ -1,3575 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#include - -#include "fluid_synth.h" -#include "fluid_sys.h" -#include "fluid_chan.h" -#include "fluid_tuning.h" -#include "fluid_settings.h" -#include "fluid_sfont.h" - -fluid_sfloader_t* new_fluid_defsfloader(void); - -/************************************************************************ - * - * These functions were added after the v1.0 API freeze. They are not - * in synth.h. They should be added as soon as a new development - * version is started. - * - ************************************************************************/ - -int fluid_synth_program_select2(fluid_synth_t* synth, - int chan, - char* sfont_name, - unsigned int bank_num, - unsigned int preset_num); - -fluid_sfont_t* fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name); - -int fluid_synth_set_gen2(fluid_synth_t* synth, int chan, - int param, float value, - int absolute, int normalized); - - -/*************************************************************** - * - * GLOBAL - */ - -/* has the synth module been initialized? */ -static int fluid_synth_initialized = 0; -static void fluid_synth_init(void); -static void init_dither(void); - -static int fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data, - int len, char *response, - int *response_len, int avail_response, - int *handled, int dryrun); - -/* default modulators - * SF2.01 page 52 ff: - * - * There is a set of predefined default modulators. They have to be - * explicitly overridden by the sound font in order to turn them off. - */ - -fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */ -fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */ -fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */ -fluid_mod_t default_mod2viblfo_mod; /* SF2.01 section 8.4.4 */ -fluid_mod_t default_att_mod; /* SF2.01 section 8.4.5 */ -fluid_mod_t default_pan_mod; /* SF2.01 section 8.4.6 */ -fluid_mod_t default_expr_mod; /* SF2.01 section 8.4.7 */ -fluid_mod_t default_reverb_mod; /* SF2.01 section 8.4.8 */ -fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */ -fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */ - -/* reverb presets */ -static fluid_revmodel_presets_t revmodel_preset[] = { - /* name */ /* roomsize */ /* damp */ /* width */ /* level */ - { "Test 1", 0.2f, 0.0f, 0.5f, 0.9f }, - { "Test 2", 0.4f, 0.2f, 0.5f, 0.8f }, - { "Test 3", 0.6f, 0.4f, 0.5f, 0.7f }, - { "Test 4", 0.8f, 0.7f, 0.5f, 0.6f }, - { "Test 5", 0.8f, 1.0f, 0.5f, 0.5f }, - { NULL, 0.0f, 0.0f, 0.0f, 0.0f } -}; - - -/*************************************************************** - * - * INITIALIZATION & UTILITIES - */ - - -void fluid_synth_settings(fluid_settings_t* settings) -{ - fluid_settings_register_str(settings, "synth.verbose", "no", 0, NULL, NULL); - fluid_settings_register_str(settings, "synth.dump", "no", 0, NULL, NULL); - fluid_settings_register_str(settings, "synth.reverb.active", "yes", 0, NULL, NULL); - fluid_settings_register_str(settings, "synth.chorus.active", "yes", 0, NULL, NULL); - fluid_settings_register_str(settings, "synth.ladspa.active", "no", 0, NULL, NULL); - fluid_settings_register_str(settings, "midi.portname", "", 0, NULL, NULL); - fluid_settings_register_str(settings, "synth.drums-channel.active", "yes", 0, NULL, NULL); - - fluid_settings_register_int(settings, "synth.polyphony", - 256, 16, 4096, 0, NULL, NULL); - fluid_settings_register_int(settings, "synth.midi-channels", - 16, 16, 256, 0, NULL, NULL); - fluid_settings_register_num(settings, "synth.gain", - 0.2f, 0.0f, 10.0f, - 0, NULL, NULL); - fluid_settings_register_int(settings, "synth.audio-channels", - 1, 1, 256, 0, NULL, NULL); - fluid_settings_register_int(settings, "synth.audio-groups", - 1, 1, 256, 0, NULL, NULL); - fluid_settings_register_int(settings, "synth.effects-channels", - 2, 2, 2, 0, NULL, NULL); - fluid_settings_register_num(settings, "synth.sample-rate", - 44100.0f, 22050.0f, 96000.0f, - 0, NULL, NULL); - fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0, NULL, NULL); -} - -/* - * void fluid_synth_init - * - * Does all the initialization for this module. - */ -static void -fluid_synth_init() -{ - fluid_synth_initialized++; - - fluid_conversion_config(); - - fluid_dsp_float_config(); - - fluid_sys_config(); - - init_dither(); - - - /* SF2.01 page 53 section 8.4.1: MIDI Note-On Velocity to Initial Attenuation */ - fluid_mod_set_source1(&default_vel2att_mod, /* The modulator we are programming here */ - FLUID_MOD_VELOCITY, /* Source. VELOCITY corresponds to 'index=2'. */ - FLUID_MOD_GC /* Not a MIDI continuous controller */ - | FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */ - | FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */ - | FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */ - ); - fluid_mod_set_source2(&default_vel2att_mod, 0, 0); /* No 2nd source */ - fluid_mod_set_dest(&default_vel2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ - fluid_mod_set_amount(&default_vel2att_mod, 960.0); /* Modulation amount: 960 */ - - - - /* SF2.01 page 53 section 8.4.2: MIDI Note-On Velocity to Filter Cutoff - * Have to make a design decision here. The specs don't make any sense this way or another. - * One sound font, 'Kingston Piano', which has been praised for its quality, tries to - * override this modulator with an amount of 0 and positive polarity (instead of what - * the specs say, D=1) for the secondary source. - * So if we change the polarity to 'positive', one of the best free sound fonts works... - */ - fluid_mod_set_source1(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */ - FLUID_MOD_GC /* CC=0 */ - | FLUID_MOD_LINEAR /* type=0 */ - | FLUID_MOD_UNIPOLAR /* P=0 */ - | FLUID_MOD_NEGATIVE /* D=1 */ - ); - fluid_mod_set_source2(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */ - FLUID_MOD_GC /* CC=0 */ - | FLUID_MOD_SWITCH /* type=3 */ - | FLUID_MOD_UNIPOLAR /* P=0 */ - // do not remove | FLUID_MOD_NEGATIVE /* D=1 */ - | FLUID_MOD_POSITIVE /* D=0 */ - ); - fluid_mod_set_dest(&default_vel2filter_mod, GEN_FILTERFC); /* Target: Initial filter cutoff */ - fluid_mod_set_amount(&default_vel2filter_mod, -2400); - - - - /* SF2.01 page 53 section 8.4.3: MIDI Channel pressure to Vibrato LFO pitch depth */ - fluid_mod_set_source1(&default_at2viblfo_mod, FLUID_MOD_CHANNELPRESSURE, /* Index=13 */ - FLUID_MOD_GC /* CC=0 */ - | FLUID_MOD_LINEAR /* type=0 */ - | FLUID_MOD_UNIPOLAR /* P=0 */ - | FLUID_MOD_POSITIVE /* D=0 */ - ); - fluid_mod_set_source2(&default_at2viblfo_mod, 0,0); /* no second source */ - fluid_mod_set_dest(&default_at2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */ - fluid_mod_set_amount(&default_at2viblfo_mod, 50); - - - - /* SF2.01 page 53 section 8.4.4: Mod wheel (Controller 1) to Vibrato LFO pitch depth */ - fluid_mod_set_source1(&default_mod2viblfo_mod, 1, /* Index=1 */ - FLUID_MOD_CC /* CC=1 */ - | FLUID_MOD_LINEAR /* type=0 */ - | FLUID_MOD_UNIPOLAR /* P=0 */ - | FLUID_MOD_POSITIVE /* D=0 */ - ); - fluid_mod_set_source2(&default_mod2viblfo_mod, 0,0); /* no second source */ - fluid_mod_set_dest(&default_mod2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */ - fluid_mod_set_amount(&default_mod2viblfo_mod, 50); - - - - /* SF2.01 page 55 section 8.4.5: MIDI continuous controller 7 to initial attenuation*/ - fluid_mod_set_source1(&default_att_mod, 7, /* index=7 */ - FLUID_MOD_CC /* CC=1 */ - | FLUID_MOD_CONCAVE /* type=1 */ - | FLUID_MOD_UNIPOLAR /* P=0 */ - | FLUID_MOD_NEGATIVE /* D=1 */ - ); - fluid_mod_set_source2(&default_att_mod, 0, 0); /* No second source */ - fluid_mod_set_dest(&default_att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ - fluid_mod_set_amount(&default_att_mod, 960.0); /* Amount: 960 */ - - - - /* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */ - fluid_mod_set_source1(&default_pan_mod, 10, /* index=10 */ - FLUID_MOD_CC /* CC=1 */ - | FLUID_MOD_LINEAR /* type=0 */ - | FLUID_MOD_BIPOLAR /* P=1 */ - | FLUID_MOD_POSITIVE /* D=0 */ - ); - fluid_mod_set_source2(&default_pan_mod, 0, 0); /* No second source */ - fluid_mod_set_dest(&default_pan_mod, GEN_PAN); /* Target: pan */ - /* Amount: 500. The SF specs $8.4.6, p. 55 syas: "Amount = 1000 - tenths of a percent". The center value (64) corresponds to 50%, - so it follows that amount = 50% x 1000/% = 500. */ - fluid_mod_set_amount(&default_pan_mod, 500.0); - - - /* SF2.01 page 55 section 8.4.7: MIDI continuous controller 11 to initial attenuation*/ - fluid_mod_set_source1(&default_expr_mod, 11, /* index=11 */ - FLUID_MOD_CC /* CC=1 */ - | FLUID_MOD_CONCAVE /* type=1 */ - | FLUID_MOD_UNIPOLAR /* P=0 */ - | FLUID_MOD_NEGATIVE /* D=1 */ - ); - fluid_mod_set_source2(&default_expr_mod, 0, 0); /* No second source */ - fluid_mod_set_dest(&default_expr_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ - fluid_mod_set_amount(&default_expr_mod, 960.0); /* Amount: 960 */ - - - - /* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */ - fluid_mod_set_source1(&default_reverb_mod, 91, /* index=91 */ - FLUID_MOD_CC /* CC=1 */ - | FLUID_MOD_LINEAR /* type=0 */ - | FLUID_MOD_UNIPOLAR /* P=0 */ - | FLUID_MOD_POSITIVE /* D=0 */ - ); - fluid_mod_set_source2(&default_reverb_mod, 0, 0); /* No second source */ - fluid_mod_set_dest(&default_reverb_mod, GEN_REVERBSEND); /* Target: Reverb send */ - fluid_mod_set_amount(&default_reverb_mod, 200); /* Amount: 200 ('tenths of a percent') */ - - - - /* SF2.01 page 55 section 8.4.9: MIDI continuous controller 93 to Reverb send */ - fluid_mod_set_source1(&default_chorus_mod, 93, /* index=93 */ - FLUID_MOD_CC /* CC=1 */ - | FLUID_MOD_LINEAR /* type=0 */ - | FLUID_MOD_UNIPOLAR /* P=0 */ - | FLUID_MOD_POSITIVE /* D=0 */ - ); - fluid_mod_set_source2(&default_chorus_mod, 0, 0); /* No second source */ - fluid_mod_set_dest(&default_chorus_mod, GEN_CHORUSSEND); /* Target: Chorus */ - fluid_mod_set_amount(&default_chorus_mod, 200); /* Amount: 200 ('tenths of a percent') */ - - - - /* SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch ... */ - fluid_mod_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */ - FLUID_MOD_GC /* CC =0 */ - | FLUID_MOD_LINEAR /* type=0 */ - | FLUID_MOD_BIPOLAR /* P=1 */ - | FLUID_MOD_POSITIVE /* D=0 */ - ); - fluid_mod_set_source2(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEELSENS, /* Index = 16 */ - FLUID_MOD_GC /* CC=0 */ - | FLUID_MOD_LINEAR /* type=0 */ - | FLUID_MOD_UNIPOLAR /* P=0 */ - | FLUID_MOD_POSITIVE /* D=0 */ - ); - fluid_mod_set_dest(&default_pitch_bend_mod, GEN_PITCH); /* Destination: Initial pitch */ - fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */ -} - - -int fluid_synth_verify_settings(fluid_settings_t *settings) -{ - return 0; -} - -/*************************************************************** - * - * FLUID SYNTH - */ - -/* - * new_fluid_synth - */ -fluid_synth_t* -new_fluid_synth(fluid_settings_t *settings) -{ - int i; - fluid_synth_t* synth; - fluid_sfloader_t* loader; - - /* initialize all the conversion tables and other stuff */ - if (fluid_synth_initialized == 0) { - fluid_synth_init(); - } - - fluid_synth_verify_settings(settings); - - /* allocate a new synthesizer object */ - synth = FLUID_NEW(fluid_synth_t); - if (synth == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t)); - - //fluid_mutex_init(synth->busy); - - synth->settings = settings; - - synth->with_reverb = fluid_settings_str_equal(settings, "synth.reverb.active", "yes"); - synth->with_chorus = fluid_settings_str_equal(settings, "synth.chorus.active", "yes"); - synth->verbose = fluid_settings_str_equal(settings, "synth.verbose", "yes"); - synth->dump = fluid_settings_str_equal(settings, "synth.dump", "yes"); - - fluid_settings_getint(settings, "synth.polyphony", &synth->polyphony); - fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate); - fluid_settings_getint(settings, "synth.midi-channels", &synth->midi_channels); - fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels); - fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups); - fluid_settings_getint(settings, "synth.effects-channels", &synth->effects_channels); - fluid_settings_getnum(settings, "synth.gain", &synth->gain); - fluid_settings_getint(settings, "synth.min-note-length", &i); - synth->min_note_length_ticks = (unsigned int) (i*synth->sample_rate/1000.0f); - - - /* register the callbacks */ - fluid_settings_register_num(settings, "synth.gain", - 0.2f, 0.0f, 10.0f, 0, - (fluid_num_update_t) fluid_synth_update_gain, synth); - fluid_settings_register_int(settings, "synth.polyphony", - synth->polyphony, 16, 4096, 0, - (fluid_int_update_t) fluid_synth_update_polyphony, - synth); - - /* do some basic sanity checking on the settings */ - - if (synth->midi_channels % 16 != 0) { - int n = synth->midi_channels / 16; - synth->midi_channels = (n + 1) * 16; - fluid_settings_setint(settings, "synth.midi-channels", synth->midi_channels); - FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is not a multiple of 16. " - "I'll increase the number of channels to the next multiple."); - } - - if (synth->audio_channels < 1) { - FLUID_LOG(FLUID_WARN, "Requested number of audio channels is smaller than 1. " - "Changing this setting to 1."); - synth->audio_channels = 1; - } else if (synth->audio_channels > 128) { - FLUID_LOG(FLUID_WARN, "Requested number of audio channels is too big (%d). " - "Limiting this setting to 128.", synth->audio_channels); - synth->audio_channels = 128; - } - - if (synth->audio_groups < 1) { - FLUID_LOG(FLUID_WARN, "Requested number of audio groups is smaller than 1. " - "Changing this setting to 1."); - synth->audio_groups = 1; - } else if (synth->audio_groups > 128) { - FLUID_LOG(FLUID_WARN, "Requested number of audio groups is too big (%d). " - "Limiting this setting to 128.", synth->audio_groups); - synth->audio_groups = 128; - } - - if (synth->effects_channels != 2) { - FLUID_LOG(FLUID_WARN, "Invalid number of effects channels (%d)." - "Setting effects channels to 2.", synth->effects_channels); - synth->effects_channels = 2; - } - - - /* The number of buffers is determined by the higher number of nr - * groups / nr audio channels. If LADSPA is unused, they should be - * the same. */ - synth->nbuf = synth->audio_channels; - if (synth->audio_groups > synth->nbuf) { - synth->nbuf = synth->audio_groups; - } - -#ifdef LADSPA - /* Create and initialize the Fx unit.*/ - synth->LADSPA_FxUnit = new_fluid_LADSPA_FxUnit(synth); -#endif - - /* as soon as the synth is created it starts playing. */ - synth->state = FLUID_SYNTH_PLAYING; - synth->sfont = NULL; - synth->noteid = 0; - synth->ticks = 0; - synth->tuning = NULL; - - /* allocate and add the default sfont loader */ - loader = new_fluid_defsfloader(); - - if (loader == NULL) { - FLUID_LOG(FLUID_WARN, "Failed to create the default SoundFont loader"); - } else { - fluid_synth_add_sfloader(synth, loader); - } - - /* allocate all channel objects */ - synth->channel = FLUID_ARRAY(fluid_channel_t*, synth->midi_channels); - if (synth->channel == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - goto error_recovery; - } - for (i = 0; i < synth->midi_channels; i++) { - synth->channel[i] = new_fluid_channel(synth, i); - if (synth->channel[i] == NULL) { - goto error_recovery; - } - } - - /* allocate all synthesis processes */ - synth->nvoice = synth->polyphony; - synth->voice = FLUID_ARRAY(fluid_voice_t*, synth->nvoice); - if (synth->voice == NULL) { - goto error_recovery; - } - for (i = 0; i < synth->nvoice; i++) { - synth->voice[i] = new_fluid_voice(synth->sample_rate); - if (synth->voice[i] == NULL) { - goto error_recovery; - } - } - - /* Allocate the sample buffers */ - synth->left_buf = NULL; - synth->right_buf = NULL; - synth->fx_left_buf = NULL; - synth->fx_right_buf = NULL; - - /* Left and right audio buffers */ - - synth->left_buf = FLUID_ARRAY(fluid_real_t*, synth->nbuf); - synth->right_buf = FLUID_ARRAY(fluid_real_t*, synth->nbuf); - - if ((synth->left_buf == NULL) || (synth->right_buf == NULL)) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - goto error_recovery; - } - - FLUID_MEMSET(synth->left_buf, 0, synth->nbuf * sizeof(fluid_real_t*)); - FLUID_MEMSET(synth->right_buf, 0, synth->nbuf * sizeof(fluid_real_t*)); - - for (i = 0; i < synth->nbuf; i++) { - - synth->left_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE); - synth->right_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE); - - if ((synth->left_buf[i] == NULL) || (synth->right_buf[i] == NULL)) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - goto error_recovery; - } - } - - /* Effects audio buffers */ - - synth->fx_left_buf = FLUID_ARRAY(fluid_real_t*, synth->effects_channels); - synth->fx_right_buf = FLUID_ARRAY(fluid_real_t*, synth->effects_channels); - - if ((synth->fx_left_buf == NULL) || (synth->fx_right_buf == NULL)) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - goto error_recovery; - } - - FLUID_MEMSET(synth->fx_left_buf, 0, 2 * sizeof(fluid_real_t*)); - FLUID_MEMSET(synth->fx_right_buf, 0, 2 * sizeof(fluid_real_t*)); - - for (i = 0; i < synth->effects_channels; i++) { - synth->fx_left_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE); - synth->fx_right_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE); - - if ((synth->fx_left_buf[i] == NULL) || (synth->fx_right_buf[i] == NULL)) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - goto error_recovery; - } - } - - - synth->cur = FLUID_BUFSIZE; - synth->dither_index = 0; - - /* allocate the reverb module */ - synth->reverb = new_fluid_revmodel(); - if (synth->reverb == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - goto error_recovery; - } - - fluid_synth_set_reverb(synth, - FLUID_REVERB_DEFAULT_ROOMSIZE, - FLUID_REVERB_DEFAULT_DAMP, - FLUID_REVERB_DEFAULT_WIDTH, - FLUID_REVERB_DEFAULT_LEVEL); - - /* allocate the chorus module */ - synth->chorus = new_fluid_chorus(synth->sample_rate); - if (synth->chorus == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - goto error_recovery; - } - - if(fluid_settings_str_equal(settings, "synth.drums-channel.active", "yes")) - fluid_synth_bank_select(synth,9,DRUM_INST_BANK); - - return synth; - - error_recovery: - delete_fluid_synth(synth); - return NULL; -} - -/** - * Set sample rate of the synth. - * NOTE: This function is currently experimental and should only be - * used when no voices or notes are active, and before any rendering calls. - * @param synth FluidSynth instance - * @param sample_rate New sample rate (Hz) - * @since 1.1.2 - */ -void -fluid_synth_set_sample_rate(fluid_synth_t* synth, float sample_rate) -{ - int i; - for (i = 0; i < synth->nvoice; i++) { - delete_fluid_voice(synth->voice[i]); - synth->voice[i] = new_fluid_voice(synth->sample_rate); - } - - delete_fluid_chorus(synth->chorus); - synth->chorus = new_fluid_chorus(synth->sample_rate); -} - -/* - * delete_fluid_synth - */ -int -delete_fluid_synth(fluid_synth_t* synth) -{ - int i, k; - fluid_list_t *list; - fluid_sfont_t* sfont; - fluid_bank_offset_t* bank_offset; - fluid_sfloader_t* loader; - - if (synth == NULL) { - return FLUID_OK; - } - - synth->state = FLUID_SYNTH_STOPPED; - - /* turn off all voices, needed to unload SoundFont data */ - if (synth->voice != NULL) { - for (i = 0; i < synth->nvoice; i++) { - if (synth->voice[i] && fluid_voice_is_playing (synth->voice[i])) - fluid_voice_off (synth->voice[i]); - } - } - - /* delete all the SoundFonts */ - for (list = synth->sfont; list; list = fluid_list_next(list)) { - sfont = (fluid_sfont_t*) fluid_list_get(list); - delete_fluid_sfont(sfont); - } - - delete_fluid_list(synth->sfont); - - /* and the SoundFont offsets */ - for (list = synth->bank_offsets; list; list = fluid_list_next(list)) { - bank_offset = (fluid_bank_offset_t*) fluid_list_get(list); - FLUID_FREE(bank_offset); - } - - delete_fluid_list(synth->bank_offsets); - - - /* delete all the SoundFont loaders */ - - for (list = synth->loaders; list; list = fluid_list_next(list)) { - loader = (fluid_sfloader_t*) fluid_list_get(list); - fluid_sfloader_delete(loader); - } - - delete_fluid_list(synth->loaders); - - - if (synth->channel != NULL) { - for (i = 0; i < synth->midi_channels; i++) { - if (synth->channel[i] != NULL) { - delete_fluid_channel(synth->channel[i]); - } - } - FLUID_FREE(synth->channel); - } - - if (synth->voice != NULL) { - for (i = 0; i < synth->nvoice; i++) { - if (synth->voice[i] != NULL) { - delete_fluid_voice(synth->voice[i]); - } - } - FLUID_FREE(synth->voice); - } - - /* free all the sample buffers */ - if (synth->left_buf != NULL) { - for (i = 0; i < synth->nbuf; i++) { - if (synth->left_buf[i] != NULL) { - FLUID_FREE(synth->left_buf[i]); - } - } - FLUID_FREE(synth->left_buf); - } - - if (synth->right_buf != NULL) { - for (i = 0; i < synth->nbuf; i++) { - if (synth->right_buf[i] != NULL) { - FLUID_FREE(synth->right_buf[i]); - } - } - FLUID_FREE(synth->right_buf); - } - - if (synth->fx_left_buf != NULL) { - for (i = 0; i < 2; i++) { - if (synth->fx_left_buf[i] != NULL) { - FLUID_FREE(synth->fx_left_buf[i]); - } - } - FLUID_FREE(synth->fx_left_buf); - } - - if (synth->fx_right_buf != NULL) { - for (i = 0; i < 2; i++) { - if (synth->fx_right_buf[i] != NULL) { - FLUID_FREE(synth->fx_right_buf[i]); - } - } - FLUID_FREE(synth->fx_right_buf); - } - - /* release the reverb module */ - if (synth->reverb != NULL) { - delete_fluid_revmodel(synth->reverb); - } - - /* release the chorus module */ - if (synth->chorus != NULL) { - delete_fluid_chorus(synth->chorus); - } - - /* free the tunings, if any */ - if (synth->tuning != NULL) { - for (i = 0; i < 128; i++) { - if (synth->tuning[i] != NULL) { - for (k = 0; k < 128; k++) { - if (synth->tuning[i][k] != NULL) { - FLUID_FREE(synth->tuning[i][k]); - } - } - FLUID_FREE(synth->tuning[i]); - } - } - FLUID_FREE(synth->tuning); - } - -#ifdef LADSPA - /* Release the LADSPA Fx unit */ - fluid_LADSPA_shutdown(synth->LADSPA_FxUnit); - FLUID_FREE(synth->LADSPA_FxUnit); -#endif - - //fluid_mutex_destroy(synth->busy); - - FLUID_FREE(synth); - - return FLUID_OK; -} - -/* - * fluid_synth_error - * - * The error messages are not thread-safe, yet. They are still stored - * in a global message buffer (see fluid_sys.c). - * */ -char* -fluid_synth_error(fluid_synth_t* synth) -{ - return fluid_error(); -} - -/* - * fluid_synth_noteon - */ -int -fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel) -{ - fluid_channel_t* channel; - - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - /* notes with velocity zero go to noteoff */ - if (vel == 0) { - return fluid_synth_noteoff(synth, chan, key); - } - - channel = synth->channel[chan]; - - /* make sure this channel has a preset */ - if (channel->preset == NULL) { - if (synth->verbose) { - FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t\t%.3f\t%d\t%s", - chan, key, vel, 0, - (float) synth->ticks / 44100.0f, - 0.0f, 0, "channel has no preset"); - } - return FLUID_FAILED; - } - - /* If there is another voice process on the same channel and key, - advance it to the release phase. */ - fluid_synth_release_voice_on_same_note(synth, chan, key); - - return fluid_synth_start(synth, synth->noteid++, channel->preset, 0, chan, key, vel); -} - -/* - * fluid_synth_noteoff - */ -int -fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key) -{ - int i; - fluid_voice_t* voice; - int status = FLUID_FAILED; -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (_ON(voice) && (voice->chan == chan) && (voice->key == key)) { - if (synth->verbose) { - int used_voices = 0; - int k; - for (k = 0; k < synth->polyphony; k++) { - if (!_AVAILABLE(synth->voice[k])) { - used_voices++; - } - } - FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t\t%.3f\t%d", - voice->chan, voice->key, 0, voice->id, - (float) (voice->start_time + voice->ticks) / 44100.0f, - (float) voice->ticks / 44100.0f, - used_voices); - } /* if verbose */ - fluid_voice_noteoff(voice); - status = FLUID_OK; - } /* if voice on */ - } /* for all voices */ - return status; -} - -/* - * fluid_synth_damp_voices - */ -int -fluid_synth_damp_voices(fluid_synth_t* synth, int chan) -{ - int i; - fluid_voice_t* voice; - -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if ((voice->chan == chan) && _SUSTAINED(voice)) { -/* printf("turned off sustained note: chan=%d, key=%d, vel=%d\n", voice->chan, voice->key, voice->vel); */ - fluid_voice_noteoff(voice); - } - } - - return FLUID_OK; -} - -/* - * fluid_synth_cc - */ -int -fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val) -{ -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - if ((num < 0) || (num >= 128)) { - FLUID_LOG(FLUID_WARN, "Ctrl out of range"); - return FLUID_FAILED; - } - if ((val < 0) || (val >= 128)) { - FLUID_LOG(FLUID_WARN, "Value out of range"); - return FLUID_FAILED; - } - - if (synth->verbose) { - FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); - } - - /* set the controller value in the channel */ - fluid_channel_cc(synth->channel[chan], num, val); - - return FLUID_OK; -} - -/* - * fluid_synth_cc - */ -int -fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval) -{ - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - if ((num < 0) || (num >= 128)) { - FLUID_LOG(FLUID_WARN, "Ctrl out of range"); - return FLUID_FAILED; - } - - *pval = synth->channel[chan]->cc[num]; - return FLUID_OK; -} - -/** - * Process a MIDI SYSEX (system exclusive) message. - * @param synth FluidSynth instance - * @param data Buffer containing SYSEX data (not including 0xF0 and 0xF7) - * @param len Length of data in buffer - * @param response Buffer to store response to or NULL to ignore - * @param response_len IN/OUT parameter, in: size of response buffer, out: - * amount of data written to response buffer (if FLUID_FAILED is returned and - * this value is non-zero, it indicates the response buffer is too small) - * @param handled Optional location to store boolean value if message was - * recognized and handled or not (set to TRUE if it was handled) - * @param dryrun TRUE to just do a dry run but not actually execute the SYSEX - * command (useful for checking if a SYSEX message would be handled) - * @return FLUID_OK on success, FLUID_FAILED otherwise - * @since 1.1.0 - */ -/* SYSEX format (0xF0 and 0xF7 not passed to this function): - * Non-realtime: 0xF0 0x7E [BODY] 0xF7 - * Realtime: 0xF0 0x7F [BODY] 0xF7 - * Tuning messages: 0xF0 0x7E/0x7F 0x08 [BODY] 0xF7 - */ -int -fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, - char *response, int *response_len, int *handled, int dryrun) -{ - int avail_response = 0; - - if (handled) *handled = 0; //FALSE - - if (response_len) - { - avail_response = *response_len; - *response_len = 0; - } - - if(!(synth != NULL)) return FLUID_FAILED; - if(!(data != NULL)) return FLUID_FAILED; - if(!(len > 0)) return FLUID_FAILED; - if(!(!response || response_len)) return FLUID_FAILED; - - if (len < 4) return FLUID_OK; - - /* MIDI tuning SYSEX message? */ - if ((data[0] == MIDI_SYSEX_UNIV_NON_REALTIME || data[0] == MIDI_SYSEX_UNIV_REALTIME) - && data[2] == MIDI_SYSEX_MIDI_TUNING_ID) - //&& (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) -> we don't handle device id - { - int result; - //fluid_synth_api_enter(synth); -> we don't handle mutex - result = fluid_synth_sysex_midi_tuning (synth, data, len, response, - response_len, avail_response, - handled, dryrun); - - return result; - } - return FLUID_OK; -} - -/* Handler for MIDI tuning SYSEX messages */ -static int -fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data, int len, - char *response, int *response_len, int avail_response, - int *handled, int dryrun) -{ - int realtime, msgid; - int bank = 0, prog, channels; - double tunedata[128]; - int keys[128]; - char name[17]; - int note, frac, frac2; - uint8 chksum; - int i, count, index; - const char *dataptr; - char *resptr; - - realtime = data[0] == MIDI_SYSEX_UNIV_REALTIME; - msgid = data[3]; - - switch (msgid) - { - case MIDI_SYSEX_TUNING_BULK_DUMP_REQ: - case MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK: - if (data[3] == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) - { - if (len != 5 || data[4] & 0x80 || !response) - return FLUID_OK; - - *response_len = 406; - prog = data[4]; - } - else - { - if (len != 6 || data[4] & 0x80 || data[5] & 0x80 || !response) - return FLUID_OK; - - *response_len = 407; - bank = data[4]; - prog = data[5]; - } - - if (dryrun) - { - if (handled) *handled = 1; //TRUE - return FLUID_OK; - } - - if (avail_response < *response_len) return FLUID_FAILED; - - /* Get tuning data, return if tuning not found */ - if (fluid_synth_tuning_dump (synth, bank, prog, name, 17, tunedata) == FLUID_FAILED) - { - *response_len = 0; - return FLUID_OK; - } - - resptr = response; - - *resptr++ = MIDI_SYSEX_UNIV_NON_REALTIME; - *resptr++ = 0; //no synth->device_id - *resptr++ = MIDI_SYSEX_MIDI_TUNING_ID; - *resptr++ = MIDI_SYSEX_TUNING_BULK_DUMP; - - if (msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK) - *resptr++ = bank; - - *resptr++ = prog; - strncpy(resptr, name, 16); //FLUID_STRNCPY - resptr += 16; - - for (i = 0; i < 128; i++) - { - note = tunedata[i] / 100.0; - fluid_clip (note, 0, 127); - - frac = ((tunedata[i] - note * 100.0) * 16384.0 + 50.0) / 100.0; - fluid_clip (frac, 0, 16383); - - *resptr++ = note; - *resptr++ = frac >> 7; - *resptr++ = frac & 0x7F; - } - - if (msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) - { /* NOTE: Checksum is not as straight forward as the bank based messages */ - chksum = MIDI_SYSEX_UNIV_NON_REALTIME ^ MIDI_SYSEX_MIDI_TUNING_ID - ^ MIDI_SYSEX_TUNING_BULK_DUMP ^ prog; - - for (i = 21; i < 128 * 3 + 21; i++) - chksum ^= response[i]; - } - else - { - for (i = 1, chksum = 0; i < 406; i++) - chksum ^= response[i]; - } - - *resptr++ = chksum & 0x7F; - - if (handled) *handled = 1; //TRUE - break; - case MIDI_SYSEX_TUNING_NOTE_TUNE: - case MIDI_SYSEX_TUNING_NOTE_TUNE_BANK: - dataptr = data + 4; - - if (msgid == MIDI_SYSEX_TUNING_NOTE_TUNE) - { - if (len < 10 || data[4] & 0x80 || data[5] & 0x80 || len != data[5] * 4 + 6) - return FLUID_OK; - } - else - { - if (len < 11 || data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80 - || len != data[5] * 4 + 7) - return FLUID_OK; - - bank = *dataptr++; - } - - if (dryrun) - { - if (handled) *handled = 1; //TRUE - return FLUID_OK; - } - - prog = *dataptr++; - count = *dataptr++; - - for (i = 0, index = 0; i < count; i++) - { - note = *dataptr++; - if (note & 0x80) return FLUID_OK; - keys[index] = note; - - note = *dataptr++; - frac = *dataptr++; - frac2 = *dataptr++; - - if (note & 0x80 || frac & 0x80 || frac2 & 0x80) - return FLUID_OK; - - frac = frac << 7 | frac2; - - /* No change pitch value? Doesn't really make sense to send that, but.. */ - if (note == 0x7F && frac == 16383) continue; - - tunedata[index] = note * 100.0 + (frac * 100.0 / 16384.0); - index++; - } - - if (index > 0) - { - if (fluid_synth_tune_notes (synth, bank, prog, index, keys, tunedata, - realtime) == FLUID_FAILED) - return FLUID_FAILED; - } - - if (handled) *handled = 1; //TRUE - break; - case MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE: - case MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE: - if ((msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE && len != 19) - || (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE && len != 31)) - return FLUID_OK; - - if (data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80) - return FLUID_OK; - - if (dryrun) - { - if (handled) *handled = 1; //TRUE - return FLUID_OK; - } - - channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6]; - - if (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE) - { - for (i = 0; i < 12; i++) - { - frac = data[i + 7]; - if (frac & 0x80) return FLUID_OK; - tunedata[i] = (int)frac - 64; - } - } - else - { - for (i = 0; i < 12; i++) - { - frac = data[i * 2 + 7]; - frac2 = data[i * 2 + 8]; - if (frac & 0x80 || frac2 & 0x80) return FLUID_OK; - tunedata[i] = (((int)frac << 7 | (int)frac2) - 8192) * (200.0 / 16384.0); - } - } - - if (fluid_synth_activate_octave_tuning (synth, 0, 0, "SYSEX", - tunedata, realtime) == FLUID_FAILED) - return FLUID_FAILED; - - if (channels) - { - for (i = 0; i < 16; i++) - { - if (channels & (1 << i)) - fluid_synth_activate_tuning (synth, i, 0, 0, realtime); - } - } - - if (handled) *handled = 1; //TRUE - break; - } - - return FLUID_OK; -} - -/* - * fluid_synth_all_notes_off - * - * put all notes on this channel into released state. - */ -int -fluid_synth_all_notes_off(fluid_synth_t* synth, int chan) -{ - int i; - fluid_voice_t* voice; - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (_PLAYING(voice) && (voice->chan == chan)) { - fluid_voice_noteoff(voice); - } - } - return FLUID_OK; -} - -/* - * fluid_synth_all_sounds_off - * - * immediately stop all notes on this channel. - */ -int -fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan) -{ - int i; - fluid_voice_t* voice; - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (_PLAYING(voice) && (voice->chan == chan)) { - fluid_voice_off(voice); - } - } - return FLUID_OK; -} - -/* - * fluid_synth_all_voices_stop - * - * Purpose: - * Immediately stop all voices without any other funny business - */ -int -fluid_synth_all_voices_stop(fluid_synth_t* synth) -{ - int i; - fluid_voice_t* voice; - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (_PLAYING(voice)) { - fluid_voice_off(voice); - } - } - - return FLUID_OK; -} - -/* - * fluid_synth_all_voices_pause - * - * Purpose: - * Immediately stop all notes without any other funny business - */ -int -fluid_synth_all_voices_pause(fluid_synth_t* synth) -{ - int i; - fluid_voice_t* voice; - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (_PLAYING(voice)) { - fluid_voice_noteoff(voice); - } - } - - return FLUID_OK; -} - -/* - * fluid_synth_system_reset - * - * Purpose: - * Respond to the MIDI command 'system reset' (0xFF, big red 'panic' button) - */ -int -fluid_synth_system_reset(fluid_synth_t* synth) -{ - int i; - fluid_voice_t* voice; - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (_PLAYING(voice)) { - fluid_voice_off(voice); - } - } - - for (i = 0; i < synth->midi_channels; i++) { - fluid_channel_reset(synth->channel[i]); - } - - fluid_chorus_reset(synth->chorus); - fluid_revmodel_reset(synth->reverb); - - return FLUID_OK; -} - -/* - * fluid_synth_modulate_voices - * - * tell all synthesis processes on this channel to update their - * synthesis parameters after a control change. - */ -int -fluid_synth_modulate_voices(fluid_synth_t* synth, int chan, int is_cc, int ctrl) -{ - int i; - fluid_voice_t* voice; - -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (voice->chan == chan) { - fluid_voice_modulate(voice, is_cc, ctrl); - } - } - return FLUID_OK; -} - -/* - * fluid_synth_modulate_voices_all - * - * Tell all synthesis processes on this channel to update their - * synthesis parameters after an all control off message (i.e. all - * controller have been reset to their default value). - */ -int -fluid_synth_modulate_voices_all(fluid_synth_t* synth, int chan) -{ - int i; - fluid_voice_t* voice; - -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (voice->chan == chan) { - fluid_voice_modulate_all(voice); - } - } - return FLUID_OK; -} - -/** - * Set the MIDI channel pressure controller value. - * @param synth FluidSynth instance - * @param chan MIDI channel number - * @param val MIDI channel pressure value (7 bit, 0-127) - * @return FLUID_OK on success - * - * Assign to the MIDI channel pressure controller value on a specific MIDI channel - * in real time. - */ -int -fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val) -{ - -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - if (synth->verbose) { - FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val); - } - - /* set the channel pressure value in the channel */ - fluid_channel_pressure(synth->channel[chan], val); - - return FLUID_OK; -} - -/** - * Set the MIDI polyphonic key pressure controller value. - * @param synth FluidSynth instance - * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @param key MIDI key number (0-127) - * @param val MIDI key pressure value (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise - */ -int -fluid_synth_key_pressure(fluid_synth_t* synth, int chan, int key, int val) -{ - int result = FLUID_OK; - if (key < 0 || key > 127) { - return FLUID_FAILED; - } - if (val < 0 || val > 127) { - return FLUID_FAILED; - } - - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "keypressure\t%d\t%d\t%d", chan, key, val); - - fluid_channel_set_key_pressure (synth->channel[chan], key, val); - - // fluid_synth_update_key_pressure_LOCAL - { - fluid_voice_t* voice; - int i; - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - - if (voice->chan == chan && voice->key == key) { - result = fluid_voice_modulate(voice, 0, FLUID_MOD_KEYPRESSURE); - if (result != FLUID_OK) - break; - } - } - } - - return result; -} - -/** - * Set the MIDI pitch bend controller value. - * @param synth FluidSynth instance - * @param chan MIDI channel number - * @param val MIDI pitch bend value (14 bit, 0-16383 with 8192 being center) - * @return FLUID_OK on success - * - * Assign to the MIDI pitch bend controller value on a specific MIDI channel - * in real time. - */ -int -fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val) -{ - -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - if (synth->verbose) { - FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); - } - - /* set the pitch-bend value in the channel */ - fluid_channel_pitch_bend(synth->channel[chan], val); - - return FLUID_OK; -} - -/* - * fluid_synth_pitch_bend - */ -int -fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend) -{ - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - *ppitch_bend = synth->channel[chan]->pitch_bend; - return FLUID_OK; -} - -/* - * Fluid_synth_pitch_wheel_sens - */ -int -fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val) -{ - - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - if (synth->verbose) { - FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); - } - - /* set the pitch-bend value in the channel */ - fluid_channel_pitch_wheel_sens(synth->channel[chan], val); - - return FLUID_OK; -} - -/* - * fluid_synth_get_pitch_wheel_sens - * - * Note : this function was added after version 1.0 API freeze. - * So its API is not in the synth.h file. It should be added in some later - * version of fluidsynth. Maybe v2.0 ? -- Antoine Schmitt May 2003 - */ - -int -fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval) -{ - - // check the ranges of the arguments - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - // get the pitch-bend value in the channel - *pval = synth->channel[chan]->pitch_wheel_sensitivity; - - return FLUID_OK; -} - -/* - * fluid_synth_get_preset - */ -fluid_preset_t* -fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum, - unsigned int banknum, unsigned int prognum) -{ - fluid_preset_t* preset = NULL; - fluid_sfont_t* sfont = NULL; - int offset; - - sfont = fluid_synth_get_sfont_by_id(synth, sfontnum); - - if (sfont != NULL) { - offset = fluid_synth_get_bank_offset(synth, sfontnum); - preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum); - if (preset != NULL) { - return preset; - } - } - return NULL; -} - -/* - * fluid_synth_get_preset2 - */ -fluid_preset_t* -fluid_synth_get_preset2(fluid_synth_t* synth, char* sfont_name, - unsigned int banknum, unsigned int prognum) -{ - fluid_preset_t* preset = NULL; - fluid_sfont_t* sfont = NULL; - int offset; - - sfont = fluid_synth_get_sfont_by_name(synth, sfont_name); - - if (sfont != NULL) { - offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont)); - preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum); - if (preset != NULL) { - return preset; - } - } - return NULL; -} - -fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth, - unsigned int banknum, - unsigned int prognum) -{ - fluid_preset_t* preset = NULL; - fluid_sfont_t* sfont = NULL; - fluid_list_t* list = synth->sfont; - int offset; - - while (list) { - - sfont = (fluid_sfont_t*) fluid_list_get(list); - offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont)); - preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum); - - if (preset != NULL) { - preset->sfont = sfont; /* FIXME */ - return preset; - } - - list = fluid_list_next(list); - - } - return NULL; -} - - -/* - * fluid_synth_program_change - */ -int -fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) -{ - fluid_preset_t* preset = NULL; - fluid_channel_t* channel; - unsigned int banknum; - unsigned int sfont_id; - int subst_bank, subst_prog; - - if ((prognum < 0) || (prognum >= FLUID_NUM_PROGRAMS) || - (chan < 0) || (chan >= synth->midi_channels)) - { - FLUID_LOG(FLUID_ERR, "Index out of range (chan=%d, prog=%d)", chan, prognum); - return FLUID_FAILED; - } - - channel = synth->channel[chan]; - banknum = fluid_channel_get_banknum(channel); - - /* inform the channel of the new program number */ - fluid_channel_set_prognum(channel, prognum); - - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); - - if (channel->channum == 9 && fluid_settings_str_equal(synth->settings, "synth.drums-channel.active", "yes")) { - preset = fluid_synth_find_preset(synth, DRUM_INST_BANK, prognum); - } - else - { - preset = fluid_synth_find_preset(synth, banknum, prognum); - } - - /* Fallback to another preset if not found */ - if (!preset) - { - subst_bank = banknum; - subst_prog = prognum; - - /* Melodic instrument? */ - if (banknum != DRUM_INST_BANK) - { - subst_bank = 0; - - /* Fallback first to bank 0:prognum */ - preset = fluid_synth_find_preset(synth, 0, prognum); - - /* Fallback to first preset in bank 0 */ - if (!preset && prognum != 0) - { - preset = fluid_synth_find_preset(synth, 0, 0); - subst_prog = 0; - } - } - else /* Percussion: Fallback to preset 0 in percussion bank */ - { - preset = fluid_synth_find_preset(synth, DRUM_INST_BANK, 0); - subst_prog = 0; - } - - if (preset) - FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]", - chan, banknum, prognum, subst_bank, subst_prog); - } - - sfont_id = preset? fluid_sfont_get_id(preset->sfont) : 0; - fluid_channel_set_sfontnum(channel, sfont_id); - fluid_channel_set_preset(channel, preset); - - return FLUID_OK; -} - -/* - * fluid_synth_bank_select - */ -int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank) -{ - if ((chan >= 0) && (chan < synth->midi_channels)) { - fluid_channel_set_banknum(synth->channel[chan], bank); - return FLUID_OK; - } - return FLUID_FAILED; -} - - -/* - * fluid_synth_sfont_select - */ -int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id) -{ - if ((chan >= 0) && (chan < synth->midi_channels)) { - fluid_channel_set_sfontnum(synth->channel[chan], sfont_id); - return FLUID_OK; - } - return FLUID_FAILED; -} - -/* - * fluid_synth_get_program - */ -int -fluid_synth_get_program(fluid_synth_t* synth, int chan, - unsigned int* sfont_id, unsigned int* bank_num, unsigned int* preset_num) -{ - fluid_channel_t* channel; - if ((chan >= 0) && (chan < synth->midi_channels)) { - channel = synth->channel[chan]; - *sfont_id = fluid_channel_get_sfontnum(channel); - *bank_num = fluid_channel_get_banknum(channel); - *preset_num = fluid_channel_get_prognum(channel); - return FLUID_OK; - } - return FLUID_FAILED; -} - -/* - * fluid_synth_program_select - */ -int fluid_synth_program_select(fluid_synth_t* synth, - int chan, - unsigned int sfont_id, - unsigned int bank_num, - unsigned int preset_num) -{ - fluid_preset_t* preset = NULL; - fluid_channel_t* channel; - - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_ERR, "Channel number out of range (chan=%d)", chan); - return FLUID_FAILED; - } - channel = synth->channel[chan]; - - preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num); - if (preset == NULL) { - FLUID_LOG(FLUID_ERR, - "There is no preset with bank number %d and preset number %d in SoundFont %d", - bank_num, preset_num, sfont_id); - return FLUID_FAILED; - } - - /* inform the channel of the new bank and program number */ - fluid_channel_set_sfontnum(channel, sfont_id); - fluid_channel_set_banknum(channel, bank_num); - fluid_channel_set_prognum(channel, preset_num); - - fluid_channel_set_preset(channel, preset); - - return FLUID_OK; -} - -/* - * fluid_synth_program_select2 - */ -int fluid_synth_program_select2(fluid_synth_t* synth, - int chan, - char* sfont_name, - unsigned int bank_num, - unsigned int preset_num) -{ - fluid_preset_t* preset = NULL; - fluid_channel_t* channel; - fluid_sfont_t* sfont = NULL; - int offset; - - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_ERR, "Channel number out of range (chan=%d)", chan); - return FLUID_FAILED; - } - channel = synth->channel[chan]; - - sfont = fluid_synth_get_sfont_by_name(synth, sfont_name); - if (sfont == NULL) { - FLUID_LOG(FLUID_ERR, "Could not find SoundFont %s", sfont_name); - return FLUID_FAILED; - } - - offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont)); - preset = fluid_sfont_get_preset(sfont, bank_num - offset, preset_num); - if (preset == NULL) { - FLUID_LOG(FLUID_ERR, - "There is no preset with bank number %d and preset number %d in SoundFont %s", - bank_num, preset_num, sfont_name); - return FLUID_FAILED; - } - - /* inform the channel of the new bank and program number */ - fluid_channel_set_sfontnum(channel, fluid_sfont_get_id(sfont)); - fluid_channel_set_banknum(channel, bank_num); - fluid_channel_set_prognum(channel, preset_num); - - fluid_channel_set_preset(channel, preset); - - return FLUID_OK; -} - -/* - * fluid_synth_update_presets - */ -void fluid_synth_update_presets(fluid_synth_t* synth) -{ - int chan; - fluid_channel_t* channel; - - for (chan = 0; chan < synth->midi_channels; chan++) { - channel = synth->channel[chan]; - fluid_channel_set_preset(channel, - fluid_synth_get_preset(synth, - fluid_channel_get_sfontnum(channel), - fluid_channel_get_banknum(channel), - fluid_channel_get_prognum(channel))); - } -} - - -/* - * fluid_synth_update_gain - */ -int fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value) -{ - fluid_synth_set_gain(synth, (float) value); - return 0; -} - -/* - * fluid_synth_set_gain - */ -void fluid_synth_set_gain(fluid_synth_t* synth, float gain) -{ - int i; - - fluid_clip(gain, 0.0f, 10.0f); - synth->gain = gain; - - for (i = 0; i < synth->polyphony; i++) { - fluid_voice_t* voice = synth->voice[i]; - if (_PLAYING(voice)) { - fluid_voice_set_gain(voice, gain); - } - } -} - -/* - * fluid_synth_get_gain - */ -float fluid_synth_get_gain(fluid_synth_t* synth) -{ - return synth->gain; -} - -/* - * fluid_synth_update_polyphony - */ -int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value) -{ - fluid_synth_set_polyphony(synth, value); - return 0; -} - -/* - * fluid_synth_set_polyphony - */ -int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony) -{ - int i; - - if (polyphony < 1 || polyphony > synth->nvoice) { - return FLUID_FAILED; - } - - /* turn off any voices above the new limit */ - for (i = polyphony; i < synth->nvoice; i++) { - fluid_voice_t* voice = synth->voice[i]; - if (_PLAYING(voice)) { - fluid_voice_off(voice); - } - } - - synth->polyphony = polyphony; - - return FLUID_OK; -} - -/* - * fluid_synth_get_polyphony - */ -int fluid_synth_get_polyphony(fluid_synth_t* synth) -{ - return synth->polyphony; -} - -/* - * fluid_synth_get_internal_buffer_size - */ -int fluid_synth_get_internal_bufsize(fluid_synth_t* synth) -{ - return FLUID_BUFSIZE; -} - -/* - * fluid_synth_program_reset - * - * Resend a bank select and a program change for every channel. This - * function is called mainly after a SoundFont has been loaded, - * unloaded or reloaded. */ -int -fluid_synth_program_reset(fluid_synth_t* synth) -{ - int i; - /* try to set the correct presets */ - for (i = 0; i < synth->midi_channels; i++){ - fluid_synth_program_change(synth, i, fluid_channel_get_prognum(synth->channel[i])); - } - return FLUID_OK; -} - -/* - * fluid_synth_set_reverb_preset - */ -int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num) -{ - int i = 0; - while (revmodel_preset[i].name != NULL) { - if (i == num) { - fluid_revmodel_setroomsize(synth->reverb, revmodel_preset[i].roomsize); - fluid_revmodel_setdamp(synth->reverb, revmodel_preset[i].damp); - fluid_revmodel_setwidth(synth->reverb, revmodel_preset[i].width); - fluid_revmodel_setlevel(synth->reverb, revmodel_preset[i].level); - return FLUID_OK; - } - i++; - } - return FLUID_FAILED; -} - -/* - * fluid_synth_set_reverb - */ -void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping, - double width, double level) -{ -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - fluid_revmodel_setroomsize(synth->reverb, roomsize); - fluid_revmodel_setdamp(synth->reverb, damping); - fluid_revmodel_setwidth(synth->reverb, width); - fluid_revmodel_setlevel(synth->reverb, level); -} - -/* - * fluid_synth_set_chorus - */ -void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, - double speed, double depth_ms, int type) -{ -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - fluid_chorus_set_nr(synth->chorus, nr); - fluid_chorus_set_level(synth->chorus, (fluid_real_t)level); - fluid_chorus_set_speed_Hz(synth->chorus, (fluid_real_t)speed); - fluid_chorus_set_depth_ms(synth->chorus, (fluid_real_t)depth_ms); - fluid_chorus_set_type(synth->chorus, type); - fluid_chorus_update(synth->chorus); -} - -/****************************************************** - -#define COMPRESS 1 -#define COMPRESS_X1 4.0 -#define COMPRESS_Y1 0.6 -#define COMPRESS_X2 10.0 -#define COMPRESS_Y2 1.0 - - len2 = 2 * len; - alpha1 = COMPRESS_Y1 / COMPRESS_X1; - alpha2 = (COMPRESS_Y2 - COMPRESS_Y1) / (COMPRESS_X2 - COMPRESS_X1); - if (COMPRESS_X1 == COMPRESS_Y1) { - for (j = 0; j < len2; j++) { - if (buf[j] > COMPRESS_X1) { - if (buf[j] > COMPRESS_X2) { - buf[j] = COMPRESS_Y2; - } else { - buf[j] = COMPRESS_Y1 + alpha2 * (buf[j] - COMPRESS_X1); - } - } else if (buf[j] < -COMPRESS_X1) { - if (buf[j] < -COMPRESS_X2) { - buf[j] = -COMPRESS_Y2; - } else { - buf[j] = -COMPRESS_Y1 + alpha2 * (buf[j] + COMPRESS_X1); - } - } - } - } else { - for (j = 0; j < len2; j++) { - if ((buf[j] >= -COMPRESS_X1) && (buf[j] <= COMPRESS_X1)) { - buf[j] *= alpha1; - } else if (buf[j] > COMPRESS_X1) { - if (buf[j] > COMPRESS_X2) { - buf[j] = COMPRESS_Y2; - } else { - buf[j] = COMPRESS_Y1 + alpha2 * (buf[j] - COMPRESS_X1); - } - } else { - if (buf[j] < -COMPRESS_X2) { - buf[j] = -COMPRESS_Y2; - } else { - buf[j] = -COMPRESS_Y1 + alpha2 * (buf[j] + COMPRESS_X1); - } - } - } - } - -***************************************************/ - -/* - * fluid_synth_nwrite_float - */ -int -fluid_synth_nwrite_float(fluid_synth_t* synth, int len, - float** left, float** right, - float** fx_left, float** fx_right) -{ - fluid_real_t** left_in = synth->left_buf; - fluid_real_t** right_in = synth->right_buf; - int i, num, available, count, bytes; - - /* make sure we're playing */ - if (synth->state != FLUID_SYNTH_PLAYING) { - return 0; - } - - /* First, take what's still available in the buffer */ - count = 0; - num = synth->cur; - if (synth->cur < FLUID_BUFSIZE) { - available = FLUID_BUFSIZE - synth->cur; - - num = (available > len)? len : available; - bytes = num * sizeof(float); - - for (i = 0; i < synth->audio_channels; i++) { - FLUID_MEMCPY(left[i], left_in[i] + synth->cur, bytes); - FLUID_MEMCPY(right[i], right_in[i] + synth->cur, bytes); - } - count += num; - num += synth->cur; /* if we're now done, num becomes the new synth->cur below */ - } - - /* Then, run one_block() and copy till we have 'len' samples */ - while (count < len) { - fluid_synth_one_block(synth, 1); - - num = (FLUID_BUFSIZE > len - count)? len - count : FLUID_BUFSIZE; - bytes = num * sizeof(float); - - for (i = 0; i < synth->audio_channels; i++) { - FLUID_MEMCPY(left[i] + count, left_in[i], bytes); - FLUID_MEMCPY(right[i] + count, right_in[i], bytes); - } - - count += num; - } - - synth->cur = num; - -/* printf("CPU: %.2f\n", synth->cpu_load); */ - - return 0; -} - - -int fluid_synth_process(fluid_synth_t* synth, int len, - int nin, float** in, - int nout, float** out) -{ - if (nout==2) { - return fluid_synth_write_float(synth, len, out[0], 0, 1, out[1], 0, 1); - } - else { - float **left, **right; - int i; - left = FLUID_ARRAY(float*, nout/2); - right = FLUID_ARRAY(float*, nout/2); - for(i=0; ileft_buf[0]; - fluid_real_t* right_in = synth->right_buf[0]; - - /* make sure we're playing */ - if (synth->state != FLUID_SYNTH_PLAYING) { - return 0; - } - - l = synth->cur; - - for (i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr) { - /* fill up the buffers as needed */ - if (l == FLUID_BUFSIZE) { - fluid_synth_one_block(synth, 0); - l = 0; - } - - left_out[j] = (float) left_in[l]; - right_out[k] = (float) right_in[l]; - } - - synth->cur = l; - -/* printf("CPU: %.2f\n", synth->cpu_load); */ - - return 0; -} - -#define DITHER_SIZE 48000 -#define DITHER_CHANNELS 2 - -static float rand_table[DITHER_CHANNELS][DITHER_SIZE]; - -static void init_dither(void) -{ - float d, dp; - int c, i; - - for (c = 0; c < DITHER_CHANNELS; c++) { - dp = 0; - for (i = 0; i < DITHER_SIZE-1; i++) { - d = rand() / (float)RAND_MAX - 0.5f; - rand_table[c][i] = d - dp; - dp = d; - } - rand_table[c][DITHER_SIZE-1] = 0 - dp; - } -} - -/* A portable replacement for roundf(), seems it may actually be faster too! */ -//removed inline -static int -roundi (float x) -{ - if (x >= 0.0f) - return (int)(x+0.5f); - else - return (int)(x-0.5f); -} - -/* - * fluid_synth_write_s16 - */ -int -fluid_synth_write_s16(fluid_synth_t* synth, int len, - void* lout, int loff, int lincr, - void* rout, int roff, int rincr) -{ - int i, j, k, cur; - signed short* left_out = (signed short*) lout; - signed short* right_out = (signed short*) rout; - fluid_real_t* left_in = synth->left_buf[0]; - fluid_real_t* right_in = synth->right_buf[0]; - fluid_real_t left_sample; - fluid_real_t right_sample; - int di = synth->dither_index; - - /* make sure we're playing */ - if (synth->state != FLUID_SYNTH_PLAYING) { - return 0; - } - - cur = synth->cur; - - for (i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr) { - - /* fill up the buffers as needed */ - if (cur == FLUID_BUFSIZE) { - fluid_synth_one_block(synth, 0); - cur = 0; - } - - left_sample = roundi (left_in[cur] * 32766.0f + rand_table[0][di]); - right_sample = roundi (right_in[cur] * 32766.0f + rand_table[1][di]); - - di++; - if (di >= DITHER_SIZE) di = 0; - - /* digital clipping */ - if (left_sample > 32767.0f) left_sample = 32767.0f; - if (left_sample < -32768.0f) left_sample = -32768.0f; - if (right_sample > 32767.0f) right_sample = 32767.0f; - if (right_sample < -32768.0f) right_sample = -32768.0f; - - left_out[j] = (signed short) left_sample; - right_out[k] = (signed short) right_sample; - } - - synth->cur = cur; - synth->dither_index = di; /* keep dither buffer continous */ - -/* printf("CPU: %.2f\n", synth->cpu_load); */ - - return 0; -} - -/* - * fluid_synth_dither_s16 - * Converts stereo floating point sample data to signed 16 bit data with - * dithering. 'dither_index' parameter is a caller supplied pointer to an - * integer which should be initialized to 0 before the first call and passed - * unmodified to additional calls which are part of the same synthesis output. - * Only used internally currently. - */ -void -fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin, - void* lout, int loff, int lincr, - void* rout, int roff, int rincr) -{ - int i, j, k; - signed short* left_out = (signed short*) lout; - signed short* right_out = (signed short*) rout; - fluid_real_t left_sample; - fluid_real_t right_sample; - int di = *dither_index; - - for (i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr) { - - left_sample = roundi (lin[i] * 32766.0f + rand_table[0][di]); - right_sample = roundi (rin[i] * 32766.0f + rand_table[1][di]); - - di++; - if (di >= DITHER_SIZE) di = 0; - - /* digital clipping */ - if (left_sample > 32767.0f) left_sample = 32767.0f; - if (left_sample < -32768.0f) left_sample = -32768.0f; - if (right_sample > 32767.0f) right_sample = 32767.0f; - if (right_sample < -32768.0f) right_sample = -32768.0f; - - left_out[j] = (signed short) left_sample; - right_out[k] = (signed short) right_sample; - } - - *dither_index = di; /* keep dither buffer continous */ -} - -/* - * fluid_synth_one_block - */ -int -fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) -{ - int i, auchan; - fluid_voice_t* voice; - fluid_real_t* left_buf; - fluid_real_t* right_buf; - fluid_real_t* reverb_buf; - fluid_real_t* chorus_buf; - int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t); - -/* fluid_mutex_lock(synth->busy); /\* Here comes the audio thread. Lock the synth. *\/ */ - - /* clean the audio buffers */ - for (i = 0; i < synth->nbuf; i++) { - FLUID_MEMSET(synth->left_buf[i], 0, byte_size); - FLUID_MEMSET(synth->right_buf[i], 0, byte_size); - } - - for (i = 0; i < synth->effects_channels; i++) { - FLUID_MEMSET(synth->fx_left_buf[i], 0, byte_size); - FLUID_MEMSET(synth->fx_right_buf[i], 0, byte_size); - } - - /* Set up the reverb / chorus buffers only, when the effect is - * enabled on synth level. Nonexisting buffers are detected in the - * DSP loop. Not sending the reverb / chorus signal saves some time - * in that case. */ - reverb_buf = synth->with_reverb ? synth->fx_left_buf[0] : NULL; - chorus_buf = synth->with_chorus ? synth->fx_left_buf[1] : NULL; - - /* call all playing synthesis processes */ - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - - if (_PLAYING(voice)) { - /* The output associated with a MIDI channel is wrapped around - * using the number of audio groups as modulo divider. This is - * typically the number of output channels on the 'sound card', - * as long as the LADSPA Fx unit is not used. In case of LADSPA - * unit, think of it as subgroups on a mixer. - * - * For example: Assume that the number of groups is set to 2. - * Then MIDI channel 1, 3, 5, 7 etc. go to output 1, channels 2, - * 4, 6, 8 etc to output 2. Or assume 3 groups: Then MIDI - * channels 1, 4, 7, 10 etc go to output 1; 2, 5, 8, 11 etc to - * output 2, 3, 6, 9, 12 etc to output 3. - */ - auchan = fluid_channel_get_num(fluid_voice_get_channel(voice)); - auchan %= synth->audio_groups; - left_buf = synth->left_buf[auchan]; - right_buf = synth->right_buf[auchan]; - - fluid_voice_write(voice, left_buf, right_buf, reverb_buf, chorus_buf); - } - } - - /* if multi channel output, don't mix the output of the chorus and - reverb in the final output. The effects outputs are send - separately. */ - - if (do_not_mix_fx_to_out) { - - /* send to reverb */ - if (reverb_buf) { - fluid_revmodel_processreplace(synth->reverb, reverb_buf, - synth->fx_left_buf[0], synth->fx_right_buf[0]); - } - - /* send to chorus */ - if (chorus_buf) { - fluid_chorus_processreplace(synth->chorus, chorus_buf, - synth->fx_left_buf[1], synth->fx_right_buf[1]); - } - - } else { - - /* send to reverb */ - if (reverb_buf) { - fluid_revmodel_processmix(synth->reverb, reverb_buf, - synth->left_buf[0], synth->right_buf[0]); - } - - /* send to chorus */ - if (chorus_buf) { - fluid_chorus_processmix(synth->chorus, chorus_buf, - synth->left_buf[0], synth->right_buf[0]); - } - } - - -#ifdef LADSPA - /* Run the signal through the LADSPA Fx unit */ - fluid_LADSPA_run(synth->LADSPA_FxUnit, synth->left_buf, synth->right_buf, synth->fx_left_buf, synth->fx_right_buf); - fluid_check_fpe("LADSPA"); -#endif - - synth->ticks += FLUID_BUFSIZE; - - /* Testcase, that provokes a denormal floating point error */ -#if 0 - {float num=1;while (num != 0){num*=0.5;};}; -#endif - -/* fluid_mutex_unlock(synth->busy); /\* Allow other threads to touch the synth *\/ */ - - return 0; -} - - -/* - * fluid_synth_free_voice_by_kill - * - * selects a voice for killing. the selection algorithm is a refinement - * of the algorithm previously in fluid_synth_alloc_voice. - */ -fluid_voice_t* -fluid_synth_free_voice_by_kill(fluid_synth_t* synth) -{ - int i; - fluid_real_t best_prio = 999999.; - fluid_real_t this_voice_prio; - fluid_voice_t* voice; - int best_voice_index=-1; - -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - for (i = 0; i < synth->polyphony; i++) { - - voice = synth->voice[i]; - - /* safeguard against an available voice. */ - if (_AVAILABLE(voice)) { - return voice; - } - - /* Determine, how 'important' a voice is. - * Start with an arbitrary number */ - this_voice_prio = 10000.; - - /* Is this voice on the drum channel? - * Then it is very important. - * Also, forget about the released-note condition: - * Typically, drum notes are triggered only very briefly, they run most - * of the time in release phase. - */ - if (_RELEASED(voice)){ - /* The key for this voice has been released. Consider it much less important - * than a voice, which is still held. - */ - this_voice_prio -= 2000.; - } - - if (_SUSTAINED(voice)){ - /* The sustain pedal is held down on this channel. - * Consider it less important than non-sustained channels. - * This decision is somehow subjective. But usually the sustain pedal - * is used to play 'more-voices-than-fingers', so it shouldn't hurt - * if we kill one voice. - */ - this_voice_prio -= 1000; - } - - /* We are not enthusiastic about releasing voices, which have just been started. - * Otherwise hitting a chord may result in killing notes belonging to that very same - * chord. - * So subtract the age of the voice from the priority - an older voice is just a little - * bit less important than a younger voice. - * This is a number between roughly 0 and 100.*/ - this_voice_prio -= (synth->noteid - fluid_voice_get_id(voice)); - - /* take a rough estimate of loudness into account. Louder voices are more important. */ - if (voice->volenv_section != FLUID_VOICE_ENVATTACK){ - this_voice_prio += voice->volenv_val * 1000.; - } - - /* check if this voice has less priority than the previous candidate. */ - if (this_voice_prio < best_prio) - best_voice_index = i, - best_prio = this_voice_prio; - } - - if (best_voice_index < 0) { - return NULL; - } - - voice = synth->voice[best_voice_index]; - fluid_voice_off(voice); - - return voice; -} - -/* - * fluid_synth_alloc_voice - */ -fluid_voice_t* -fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, int key, int vel) -{ - int i, k; - fluid_voice_t* voice = NULL; - fluid_channel_t* channel = NULL; - -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - /* check if there's an available synthesis process */ - for (i = 0; i < synth->polyphony; i++) { - if (_AVAILABLE(synth->voice[i])) { - voice = synth->voice[i]; - break; - } - } - - /* No success yet? Then stop a running voice. */ - if (voice == NULL) { - voice = fluid_synth_free_voice_by_kill(synth); - } - - if (voice == NULL) { - FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key); - return NULL; - } - - if (synth->verbose) { - k = 0; - for (i = 0; i < synth->polyphony; i++) { - if (!_AVAILABLE(synth->voice[i])) { - k++; - } - } - - FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t\t%.3f\t%d", - chan, key, vel, synth->storeid, - (float) synth->ticks / 44100.0f, - 0.0f, - k); - } - - if (chan >= 0) { - channel = synth->channel[chan]; - } else { - FLUID_LOG(FLUID_WARN, "Channel should be valid"); - return NULL; - } - - if (fluid_voice_init(voice, sample, channel, key, vel, - synth->storeid, synth->ticks, synth->gain) != FLUID_OK) { - FLUID_LOG(FLUID_WARN, "Failed to initialize voice"); - return NULL; - } - - /* add the default modulators to the synthesis process. */ - fluid_voice_add_mod(voice, &default_vel2att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.1 */ - fluid_voice_add_mod(voice, &default_vel2filter_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.2 */ - fluid_voice_add_mod(voice, &default_at2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.3 */ - fluid_voice_add_mod(voice, &default_mod2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.4 */ - fluid_voice_add_mod(voice, &default_att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.5 */ - fluid_voice_add_mod(voice, &default_pan_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.6 */ - fluid_voice_add_mod(voice, &default_expr_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.7 */ - fluid_voice_add_mod(voice, &default_reverb_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.8 */ - fluid_voice_add_mod(voice, &default_chorus_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.9 */ - fluid_voice_add_mod(voice, &default_pitch_bend_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.10 */ - - return voice; -} - -/* - * fluid_synth_kill_by_exclusive_class - */ -void fluid_synth_kill_by_exclusive_class(fluid_synth_t* synth, fluid_voice_t* new_voice) -{ - /** Kill all voices on a given channel, which belong into - excl_class. This function is called by a SoundFont's preset in - response to a noteon event. If one noteon event results in - several voice processes (stereo samples), ignore_ID must name - the voice ID of the first generated voice (so that it is not - stopped). The first voice uses ignore_ID=-1, which will - terminate all voices on a channel belonging into the exclusive - class excl_class. - */ - - int i; - int excl_class = _GEN(new_voice,GEN_EXCLUSIVECLASS); - - /* Check if the voice belongs to an exclusive class. In that case, - previous notes from the same class are released. */ - - /* Excl. class 0: No exclusive class */ - if (excl_class == 0) { - return; - } - - // FLUID_LOG(FLUID_INFO, "Voice belongs to exclusive class (class=%d, ignore_id=%d)", excl_class, ignore_ID); - - /* Kill all notes on the same channel with the same exclusive class */ - - for (i = 0; i < synth->polyphony; i++) { - fluid_voice_t* existing_voice = synth->voice[i]; - - /* Existing voice does not play? Leave it alone. */ - if (!_PLAYING(existing_voice)) { - continue; - } - - /* An exclusive class is valid for a whole channel (or preset). - * Is the voice on a different channel? Leave it alone. */ - if (existing_voice->chan != new_voice->chan) { - continue; - } - - /* Existing voice has a different (or no) exclusive class? Leave it alone. */ - if ((int)_GEN(existing_voice, GEN_EXCLUSIVECLASS) != excl_class) { - continue; - } - - /* Existing voice is a voice process belonging to this noteon - * event (for example: stereo sample)? Leave it alone. */ - if (fluid_voice_get_id(existing_voice) == fluid_voice_get_id(new_voice)) { - continue; - } - - // FLUID_LOG(FLUID_INFO, "Releasing previous voice of exclusive class (class=%d, id=%d)", - // (int)_GEN(existing_voice, GEN_EXCLUSIVECLASS), (int)fluid_voice_get_id(existing_voice)); - - fluid_voice_kill_excl(existing_voice); - }; -} - -/* - * fluid_synth_start_voice - */ -void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice) -{ -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - /* Find the exclusive class of this voice. If set, kill all voices - * that match the exclusive class and are younger than the first - * voice process created by this noteon event. */ - fluid_synth_kill_by_exclusive_class(synth, voice); - - /* Start the new voice */ - - fluid_voice_start(voice); -} - -/* - * fluid_synth_add_sfloader - */ -void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader) -{ - synth->loaders = fluid_list_prepend(synth->loaders, loader); -} - - -/* - * fluid_synth_sfload - */ -int -fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets) -{ - fluid_sfont_t* sfont; - fluid_list_t* list; - fluid_sfloader_t* loader; - -#if defined(MACOS9) - fluid_synth_sfunload_macos9(synth); -#endif - - if (filename == NULL) { - FLUID_LOG(FLUID_ERR, "Invalid filename"); - return FLUID_FAILED; - } - - for (list = synth->loaders; list; list = fluid_list_next(list)) { - loader = (fluid_sfloader_t*) fluid_list_get(list); - - sfont = fluid_sfloader_load(loader, filename); - if (sfont == NULL) - return -1; - - sfont->id = ++synth->sfont_id; - synth->sfont = fluid_list_prepend(synth->sfont, sfont); - - if (reset_presets) { - fluid_synth_program_reset(synth); - } - return (int) sfont->id; - } - - FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); - return -1; -} - - -/* - * fluid_synth_sfunload_macos9 - */ -void fluid_synth_sfunload_macos9(fluid_synth_t* synth) -{ -#if defined(MACOS9) - fluid_list_t *list, *next; - fluid_sfont_t* sfont; - - list = synth->unloading; - while (list) { - next = fluid_list_next(list); - sfont = (fluid_sfont_t*) fluid_list_get(list); - if (delete_fluid_sfont(sfont) == 0) { - synth->unloading = fluid_list_remove(synth->unloading, sfont); - } - list = next; - } -#endif -} - -/* - * fluid_synth_sfunload - */ -int -fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets) -{ - fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, id); - -#if defined(MACOS9) - fluid_synth_sfunload_macos9(synth); -#endif - - if (!sfont) { - FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); - return FLUID_FAILED; - } - - /* remove the SoundFont from the list */ - synth->sfont = fluid_list_remove(synth->sfont, sfont); - - /* reset the presets for all channels */ - if (reset_presets) { - fluid_synth_program_reset(synth); - } else { - fluid_synth_update_presets(synth); - } - - if (delete_fluid_sfont(sfont) != 0) { -#if defined(MACOS9) - synth->unloading = fluid_list_prepend(synth->unloading, sfont); -#else - int r = delete_fluid_sfont(sfont); - if (r == 0) { - FLUID_LOG(FLUID_DBG,"Unloaded SoundFont"); - } -#endif - } - - return FLUID_OK; -} - -/* fluid_synth_sfreload - * - */ -int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) -{ - char filename[1024]; - fluid_sfont_t* sfont; - int index = 0; - fluid_list_t *list; - fluid_sfloader_t* loader; - - - sfont = fluid_synth_get_sfont_by_id(synth, id); - if (!sfont) { - FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); - return FLUID_FAILED; - } - - /* find the index of the SoundFont */ - list = synth->sfont; - while (list) { - if (sfont == (fluid_sfont_t*) fluid_list_get(list)) { - break; - } - list = fluid_list_next(list); - index++; - } - - /* keep a copy of the SoundFont's filename */ - FLUID_STRCPY(filename, fluid_sfont_get_name(sfont)); - - if (fluid_synth_sfunload(synth, id, 0) != FLUID_OK) { - return FLUID_FAILED; - } - - for (list = synth->loaders; list; list = fluid_list_next(list)) { - loader = (fluid_sfloader_t*) fluid_list_get(list); - - sfont = fluid_sfloader_load(loader, filename); - - if (sfont != NULL) { - - sfont->id = id; - - /* insert the sfont at the same index */ - synth->sfont = fluid_list_insert_at(synth->sfont, index, sfont); - - /* reset the presets for all channels */ - fluid_synth_update_presets(synth); - - return sfont->id; - } - } - - FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); - return -1; -} - - -/* - * fluid_synth_add_sfont - */ -int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) -{ - sfont->id = ++synth->sfont_id; - - /* insert the sfont as the first one on the list */ - synth->sfont = fluid_list_prepend(synth->sfont, sfont); - - /* reset the presets for all channels */ - fluid_synth_program_reset(synth); - - return sfont->id; -} - - -/* - * fluid_synth_remove_sfont - */ -void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) -{ - int sfont_id = fluid_sfont_get_id(sfont); - - synth->sfont = fluid_list_remove(synth->sfont, sfont); - - /* remove a possible bank offset */ - fluid_synth_remove_bank_offset(synth, sfont_id); - - /* reset the presets for all channels */ - fluid_synth_program_reset(synth); -} - - -/* fluid_synth_sfcount - * - * Returns the number of loaded SoundFonts - */ -int -fluid_synth_sfcount(fluid_synth_t* synth) -{ - return fluid_list_size(synth->sfont); -} - -/* fluid_synth_get_sfont - * - * Returns SoundFont num - */ -fluid_sfont_t* -fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num) -{ - return (fluid_sfont_t*) fluid_list_get(fluid_list_nth(synth->sfont, num)); -} - -/* fluid_synth_get_sfont_by_id - * - */ -fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id) -{ - fluid_list_t* list = synth->sfont; - fluid_sfont_t* sfont; - - while (list) { - sfont = (fluid_sfont_t*) fluid_list_get(list); - if (fluid_sfont_get_id(sfont) == id) { - return sfont; - } - list = fluid_list_next(list); - } - return NULL; -} - -/* fluid_synth_get_sfont_by_name - * - */ -fluid_sfont_t* fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name) -{ - fluid_list_t* list = synth->sfont; - fluid_sfont_t* sfont; - - while (list) { - sfont = (fluid_sfont_t*) fluid_list_get(list); - if (FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0) { - return sfont; - } - list = fluid_list_next(list); - } - return NULL; -} - -/* - * fluid_synth_get_channel_preset - */ -fluid_preset_t* -fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan) -{ - if ((chan >= 0) && (chan < synth->midi_channels)) { - return fluid_channel_get_preset(synth->channel[chan]); - } - - return NULL; -} - -/* - * fluid_synth_get_voicelist - */ -void -fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsize, int ID) -{ - int i; - int count = 0; - for (i = 0; i < synth->polyphony; i++) { - fluid_voice_t* voice = synth->voice[i]; - if (count >= bufsize) { - return; - } - - if (_PLAYING(voice) && ((int)voice->id == ID || ID < 0)) { - buf[count++] = voice; - } - } - if (count >= bufsize) { - return; - } - buf[count++] = NULL; -} - -/* Purpose: - * Turns on / off the reverb unit in the synth */ -void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on) -{ - synth->with_reverb = on; -} - -/* Purpose: - * Turns on / off the chorus unit in the synth */ -void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on) -{ - synth->with_chorus = on; -} - -/* Purpose: - * Reports the current setting of the chorus unit. */ -int fluid_synth_get_chorus_nr(fluid_synth_t* synth) -{ - return fluid_chorus_get_nr(synth->chorus); -} - -double fluid_synth_get_chorus_level(fluid_synth_t* synth) -{ - return (double)fluid_chorus_get_level(synth->chorus); -} - -double fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth) -{ - return (double)fluid_chorus_get_speed_Hz(synth->chorus); -} - -double fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth) -{ - return (double)fluid_chorus_get_depth_ms(synth->chorus); -} - -int fluid_synth_get_chorus_type(fluid_synth_t* synth) -{ - return fluid_chorus_get_type(synth->chorus); -} - -/* Purpose: - * Returns the current settings_old of the reverb unit */ -double fluid_synth_get_reverb_roomsize(fluid_synth_t* synth) -{ - return (double)fluid_revmodel_getroomsize(synth->reverb); -} - -double fluid_synth_get_reverb_damp(fluid_synth_t* synth) -{ - return (double) fluid_revmodel_getdamp(synth->reverb); -} - -double fluid_synth_get_reverb_level(fluid_synth_t* synth) -{ - return (double) fluid_revmodel_getlevel(synth->reverb); -} - -double fluid_synth_get_reverb_width(fluid_synth_t* synth) -{ - return (double) fluid_revmodel_getwidth(synth->reverb); -} - -/* Purpose: - * - * If the same note is hit twice on the same channel, then the older - * voice process is advanced to the release stage. Using a mechanical - * MIDI controller, the only way this can happen is when the sustain - * pedal is held. In this case the behaviour implemented here is - * natural for many instruments. Note: One noteon event can trigger - * several voice processes, for example a stereo sample. Don't - * release those... - */ -void fluid_synth_release_voice_on_same_note(fluid_synth_t* synth, int chan, int key){ - int i; - fluid_voice_t* voice; - -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (_PLAYING(voice) - && (voice->chan == chan) - && (voice->key == key) - && (fluid_voice_get_id(voice) != synth->noteid)) { - fluid_voice_noteoff(voice); - } - } -} - -/* Purpose: - * Sets the interpolation method to use on channel chan. - * If chan is < 0, then set the interpolation method on all channels. - */ -int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method){ - int i; - for (i = 0; i < synth->midi_channels; i++) { - if (synth->channel[i] == NULL){ - FLUID_LOG(FLUID_ERR, "Channels don't exist (yet)!"); - return FLUID_FAILED; - }; - if (chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan){ - fluid_channel_set_interp_method(synth->channel[i], interp_method); - }; - }; - return FLUID_OK; -} - -/* Purpose: - * Returns the number of allocated midi channels - */ -int -fluid_synth_count_midi_channels(fluid_synth_t* synth) -{ - return synth->midi_channels; -} - -/* Purpose: - * Returns the number of allocated audio channels - */ -int -fluid_synth_count_audio_channels(fluid_synth_t* synth) -{ - return synth->audio_channels; -} - -/* Purpose: - * Returns the number of allocated audio channels - */ -int -fluid_synth_count_audio_groups(fluid_synth_t* synth) -{ - return synth->audio_groups; -} - -/* Purpose: - * Returns the number of allocated effects channels - */ -int -fluid_synth_count_effects_channels(fluid_synth_t* synth) -{ - return synth->effects_channels; -} - -static fluid_tuning_t* -fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog) -{ - if ((bank < 0) || (bank >= 128)) { - FLUID_LOG(FLUID_WARN, "Bank number out of range"); - return NULL; - } - if ((prog < 0) || (prog >= 128)) { - FLUID_LOG(FLUID_WARN, "Program number out of range"); - return NULL; - } - if ((synth->tuning == NULL) || - (synth->tuning[bank] == NULL) || - (synth->tuning[bank][prog] == NULL)) { - FLUID_LOG(FLUID_WARN, "No tuning at bank %d, prog %d", bank, prog); - return NULL; - } - return synth->tuning[bank][prog]; -} - -static fluid_tuning_t* -fluid_synth_create_tuning(fluid_synth_t* synth, int bank, int prog, const char* name) -{ - if ((bank < 0) || (bank >= 128)) { - FLUID_LOG(FLUID_WARN, "Bank number out of range"); - return NULL; - } - if ((prog < 0) || (prog >= 128)) { - FLUID_LOG(FLUID_WARN, "Program number out of range"); - return NULL; - } - if (synth->tuning == NULL) { - synth->tuning = FLUID_ARRAY(fluid_tuning_t**, 128); - if (synth->tuning == NULL) { - FLUID_LOG(FLUID_PANIC, "Out of memory"); - return NULL; - } - FLUID_MEMSET(synth->tuning, 0, 128 * sizeof(fluid_tuning_t**)); - } - - if (synth->tuning[bank] == NULL) { - synth->tuning[bank] = FLUID_ARRAY(fluid_tuning_t*, 128); - if (synth->tuning[bank] == NULL) { - FLUID_LOG(FLUID_PANIC, "Out of memory"); - return NULL; - } - FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t*)); - } - - if (synth->tuning[bank][prog] == NULL) { - synth->tuning[bank][prog] = new_fluid_tuning(name, bank, prog); - if (synth->tuning[bank][prog] == NULL) { - return NULL; - } - } - - if ((fluid_tuning_get_name(synth->tuning[bank][prog]) == NULL) - || (FLUID_STRCMP(fluid_tuning_get_name(synth->tuning[bank][prog]), name) != 0)) { - fluid_tuning_set_name(synth->tuning[bank][prog], name); - } - - return synth->tuning[bank][prog]; -} - -int fluid_synth_create_key_tuning(fluid_synth_t* synth, - int bank, int prog, - const char* name, double* pitch) -{ - fluid_tuning_t* tuning = fluid_synth_create_tuning(synth, bank, prog, name); - if (tuning == NULL) { - return FLUID_FAILED; - } - if (pitch) { - fluid_tuning_set_all(tuning, pitch); - } - return FLUID_OK; -} - - -int fluid_synth_create_octave_tuning(fluid_synth_t* synth, - int bank, int prog, - const char* name, const double* pitch) -{ - fluid_tuning_t* tuning; - - if(!(synth != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail - if(!(bank >= 0 && bank < 128)) return FLUID_FAILED; //fluid_return_val_if_fail - if(!(prog >= 0 && prog < 128)) return FLUID_FAILED; //fluid_return_val_if_fail - if(!(name != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail - if(!(pitch != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail - - tuning = fluid_synth_create_tuning(synth, bank, prog, name); - if (tuning == NULL) { - return FLUID_FAILED; - } - fluid_tuning_set_octave(tuning, pitch); - return FLUID_OK; -} - -/** - * Activate an octave tuning on every octave in the MIDI note scale. - * @param synth FluidSynth instance - * @param bank Tuning bank number (0-127), not related to MIDI instrument bank - * @param prog Tuning preset number (0-127), not related to MIDI instrument program - * @param name Label name for this tuning - * @param pitch Array of pitch values (length of 12 for each note of an octave - * starting at note C, values are number of offset cents to add to the normal - * tuning amount) - * @param apply TRUE to apply new tuning in realtime to existing notes which - * are using the replaced tuning (if any), FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise - * @since 1.1.0 - */ -int -fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog, - const char* name, const double* pitch, int apply) -{ - return fluid_synth_create_octave_tuning(synth,bank,prog,name,pitch); -} - - -int fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, - int len, int *key, double* pitch, int apply) -{ - fluid_tuning_t* tuning; - int i; - - if(!(synth != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail - if(!(bank >= 0 && bank < 128)) return FLUID_FAILED; //fluid_return_val_if_fail - if(!(prog >= 0 && prog < 128)) return FLUID_FAILED; //fluid_return_val_if_fail - if(!(len > 0)) return FLUID_FAILED; //fluid_return_val_if_fail - if(!(key != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail - if(!(pitch != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail - - tuning = fluid_synth_get_tuning (synth, bank, prog); - - if(!tuning) - tuning = new_fluid_tuning ("Unnamed", bank, prog); - - if (tuning == NULL) { - return FLUID_FAILED; - } - - for (i = 0; i < len; i++) { - fluid_tuning_set_pitch(tuning, key[i], pitch[i]); - } - - return FLUID_OK; -} - -int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, - int bank, int prog) -{ - fluid_tuning_t* tuning; - - if(!(synth != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail - if(!(bank >= 0 && bank < 128)) return FLUID_FAILED; //fluid_return_val_if_fail - if(!(prog >= 0 && prog < 128)) return FLUID_FAILED; //fluid_return_val_if_fail - - tuning = fluid_synth_get_tuning(synth, bank, prog); - - if (tuning == NULL) { - return FLUID_FAILED; - } - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - fluid_channel_set_tuning(synth->channel[chan], synth->tuning[bank][prog]); - - return FLUID_OK; -} - -/** - * Activate a tuning scale on a MIDI channel. - * @param synth FluidSynth instance - * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @param bank Tuning bank number (0-127), not related to MIDI instrument bank - * @param prog Tuning preset number (0-127), not related to MIDI instrument program - * @param apply TRUE to apply tuning change to active notes, FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise - * @since 1.1.0 - * - * NOTE: A default equal tempered scale will be created, if no tuning exists - * on the given bank and prog. - */ -int -fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog, - int apply) -{ - return fluid_synth_select_tuning(synth,chan,bank,prog); -} - -int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan) -{ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - fluid_channel_set_tuning(synth->channel[chan], NULL); - - return FLUID_OK; -} - -void fluid_synth_tuning_iteration_start(fluid_synth_t* synth) -{ - synth->cur_tuning = NULL; -} - -int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog) -{ - int b = 0, p = 0; - - if (synth->tuning == NULL) { - return 0; - } - - if (synth->cur_tuning != NULL) { - /* get the next program number */ - b = fluid_tuning_get_bank(synth->cur_tuning); - p = 1 + fluid_tuning_get_prog(synth->cur_tuning); - if (p >= 128) { - p = 0; - b++; - } - } - - while (b < 128) { - if (synth->tuning[b] != NULL) { - while (p < 128) { - if (synth->tuning[b][p] != NULL) { - synth->cur_tuning = synth->tuning[b][p]; - *bank = b; - *prog = p; - return 1; - } - p++; - } - } - p = 0; - b++; - } - - return 0; -} - -//workaround for snprint on MSVC <2015 from http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 -#if defined(_MSC_VER) && _MSC_VER < 1900 -#define snprintf c99_snprintf -#define vsnprintf c99_vsnprintf - -__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) -{ - int count = -1; - - if (size != 0) - count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); - if (count == -1) - count = _vscprintf(format, ap); - - return count; -} - -__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...) -{ - int count; - va_list ap; - - va_start(ap, format); - count = c99_vsnprintf(outBuf, size, format, ap); - va_end(ap); - - return count; -} -#endif - -int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, - char* name, int len, double* pitch) -{ - fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog); - - if (tuning == NULL) { - return FLUID_FAILED; - } - - if (name) { - snprintf(name, len - 1, "%s", fluid_tuning_get_name(tuning)); - name[len - 1] = 0; /* make sure the string is null terminated */ - } - if (pitch) { - FLUID_MEMCPY(pitch, fluid_tuning_get_all(tuning), 128 * sizeof(double)); - } - - return FLUID_OK; -} - -fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth) -{ - return synth->settings; -} - -int fluid_synth_setstr(fluid_synth_t* synth, char* name, char* str) -{ - return fluid_settings_setstr(synth->settings, name, str); -} - -int fluid_synth_getstr(fluid_synth_t* synth, char* name, char** str) -{ - return fluid_settings_getstr(synth->settings, name, str); -} - -int fluid_synth_setnum(fluid_synth_t* synth, char* name, double val) -{ - return fluid_settings_setnum(synth->settings, name, val); -} - -int fluid_synth_getnum(fluid_synth_t* synth, char* name, double* val) -{ - return fluid_settings_getnum(synth->settings, name, val); -} - -int fluid_synth_setint(fluid_synth_t* synth, char* name, int val) -{ - return fluid_settings_setint(synth->settings, name, val); -} - -int fluid_synth_getint(fluid_synth_t* synth, char* name, int* val) -{ - return fluid_settings_getint(synth->settings, name, val); -} - -int -fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value) -{ - int i; - fluid_voice_t* voice; - - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - if ((param < 0) || (param >= GEN_LAST)) { - FLUID_LOG(FLUID_WARN, "Parameter number out of range"); - return FLUID_FAILED; - } - - fluid_channel_set_gen(synth->channel[chan], param, value, 0); - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (voice->chan == chan) { - fluid_voice_set_param(voice, param, value, 0); - } - } - - return FLUID_OK; -} - -/** Change the value of a generator. This function allows to control - all synthesis parameters in real-time. The changes are additive, - i.e. they add up to the existing parameter value. This function is - similar to sending an NRPN message to the synthesizer. The - function accepts a float as the value of the parameter. The - parameter numbers and ranges are described in the SoundFont 2.01 - specification, paragraph 8.1.3, page 48. See also - 'fluid_gen_type'. - - Using the fluid_synth_set_gen2() function, it is possible to set - the absolute value of a generator. This is an extension to the - SoundFont standard. If 'absolute' is non-zero, the value of the - generator specified in the SoundFont is completely ignored and the - generator is fixed to the value passed as argument. To undo this - behavior, you must call fluid_synth_set_gen2 again, with - 'absolute' set to 0 (and possibly 'value' set to zero). - - If 'normalized' is non-zero, the value is supposed to be - normalized between 0 and 1. Before applying the value, it will be - scaled and shifted to the range defined in the SoundFont - specifications. - - */ -int -fluid_synth_set_gen2(fluid_synth_t* synth, int chan, int param, - float value, int absolute, int normalized) -{ - int i; - fluid_voice_t* voice; - float v; - - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - if ((param < 0) || (param >= GEN_LAST)) { - FLUID_LOG(FLUID_WARN, "Parameter number out of range"); - return FLUID_FAILED; - } - - v = (normalized)? fluid_gen_scale(param, value) : value; - - fluid_channel_set_gen(synth->channel[chan], param, v, absolute); - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (voice->chan == chan) { - fluid_voice_set_param(voice, param, v, absolute); - } - } - - return FLUID_OK; -} - -float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param) -{ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return 0.0; - } - - if ((param < 0) || (param >= GEN_LAST)) { - FLUID_LOG(FLUID_WARN, "Parameter number out of range"); - return 0.0; - } - - return fluid_channel_get_gen(synth->channel[chan], param); -} - -//midi_router disabled -/* The synth needs to know the router for the command line handlers (they only - * supply the synth as argument) - */ -//void fluid_synth_set_midi_router(fluid_synth_t* synth, fluid_midi_router_t* router){ -// synth->midi_router=router; -//}; - -/* Purpose: - * Any MIDI event from the MIDI router arrives here and is handed - * to the appropriate function. - */ - -//fluid_synth_handle_midi_event disabled - -int fluid_synth_start(fluid_synth_t* synth, unsigned int id, fluid_preset_t* preset, - int audio_chan, int midi_chan, int key, int vel) -{ - int r; - - /* check the ranges of the arguments */ - if ((midi_chan < 0) || (midi_chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - if ((key < 0) || (key >= 128)) { - FLUID_LOG(FLUID_WARN, "Key out of range"); - return FLUID_FAILED; - } - - if ((vel <= 0) || (vel >= 128)) { - FLUID_LOG(FLUID_WARN, "Velocity out of range"); - return FLUID_FAILED; - } - - //mutex disabled, must render in a single thread - //fluid_mutex_lock(synth->busy); /* One at a time, please */ - - synth->storeid = id; - r = fluid_preset_noteon(preset, synth, midi_chan, key, vel); - - //fluid_mutex_unlock(synth->busy); - - return r; -} - -int fluid_synth_stop(fluid_synth_t* synth, unsigned int id) -{ - int i; - fluid_voice_t* voice; - int status = FLUID_FAILED; - int count = 0; - - for (i = 0; i < synth->polyphony; i++) { - - voice = synth->voice[i]; - - if (_ON(voice) && (fluid_voice_get_id(voice) == id)) { - count++; - fluid_voice_noteoff(voice); - status = FLUID_OK; - } - } - - return status; -} - -fluid_bank_offset_t* -fluid_synth_get_bank_offset0(fluid_synth_t* synth, int sfont_id) -{ - fluid_list_t* list = synth->bank_offsets; - fluid_bank_offset_t* offset; - - while (list) { - - offset = (fluid_bank_offset_t*) fluid_list_get(list); - if (offset->sfont_id == sfont_id) { - return offset; - } - - list = fluid_list_next(list); - } - - return NULL; -} - -int -fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset) -{ - fluid_bank_offset_t* bank_offset; - - bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id); - - if (bank_offset == NULL) { - bank_offset = FLUID_NEW(fluid_bank_offset_t); - if (bank_offset == NULL) { - return -1; - } - bank_offset->sfont_id = sfont_id; - bank_offset->offset = offset; - synth->bank_offsets = fluid_list_prepend(synth->bank_offsets, bank_offset); - } else { - bank_offset->offset = offset; - } - - return 0; -} - -int -fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id) -{ - fluid_bank_offset_t* bank_offset; - - bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id); - return (bank_offset == NULL)? 0 : bank_offset->offset; -} - -void -fluid_synth_remove_bank_offset(fluid_synth_t* synth, int sfont_id) -{ - fluid_bank_offset_t* bank_offset; - - bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id); - if (bank_offset != NULL) { - synth->bank_offsets = fluid_list_remove(synth->bank_offsets, bank_offset); - } -} diff --git a/libraries/fluidlite/src/fluid_synth.h b/libraries/fluidlite/src/fluid_synth.h deleted file mode 100644 index f6befe1bc..000000000 --- a/libraries/fluidlite/src/fluid_synth.h +++ /dev/null @@ -1,206 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#ifndef _FLUID_SYNTH_H -#define _FLUID_SYNTH_H - - -/*************************************************************** - * - * INCLUDES - */ - -#include "fluid_config.h" -#include "fluidsynth_priv.h" -#include "fluid_list.h" -#include "fluid_rev.h" -#include "fluid_voice.h" -#include "fluid_chorus.h" -#include "fluid_sys.h" - -/*************************************************************** - * - * DEFINES - */ -#define FLUID_NUM_PROGRAMS 128 -#define DRUM_INST_BANK 128 - -#if defined(WITH_FLOAT) -#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_FLOAT -#else -#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_DOUBLE -#endif - - -/*************************************************************** - * - * ENUM - */ -enum fluid_loop { - FLUID_UNLOOPED = 0, - FLUID_LOOP_DURING_RELEASE = 1, - FLUID_NOTUSED = 2, - FLUID_LOOP_UNTIL_RELEASE = 3 -}; - -enum fluid_synth_status -{ - FLUID_SYNTH_CLEAN, - FLUID_SYNTH_PLAYING, - FLUID_SYNTH_QUIET, - FLUID_SYNTH_STOPPED -}; - - -typedef struct _fluid_bank_offset_t fluid_bank_offset_t; - -struct _fluid_bank_offset_t { - int sfont_id; - int offset; -}; - - -/* - * fluid_synth_t - */ - -struct _fluid_synth_t -{ - /* fluid_settings_old_t settings_old; the old synthesizer settings */ - fluid_settings_t* settings; /** the synthesizer settings */ - int polyphony; /** maximum polyphony */ - char with_reverb; /** Should the synth use the built-in reverb unit? */ - char with_chorus; /** Should the synth use the built-in chorus unit? */ - char verbose; /** Turn verbose mode on? */ - char dump; /** Dump events to stdout to hook up a user interface? */ - double sample_rate; /** The sample rate */ - int midi_channels; /** the number of MIDI channels (>= 16) */ - int audio_channels; /** the number of audio channels (1 channel=left+right) */ - int audio_groups; /** the number of (stereo) 'sub'groups from the synth. - Typically equal to audio_channels. */ - int effects_channels; /** the number of effects channels (= 2) */ - unsigned int state; /** the synthesizer state */ - unsigned int ticks; /** the number of audio samples since the start */ - - fluid_list_t *loaders; /** the soundfont loaders */ - fluid_list_t* sfont; /** the loaded soundfont */ - unsigned int sfont_id; - fluid_list_t* bank_offsets; /** the offsets of the soundfont banks */ - -#if defined(MACOS9) - fluid_list_t* unloading; /** the soundfonts that need to be unloaded */ -#endif - - double gain; /** master gain */ - fluid_channel_t** channel; /** the channels */ - int num_channels; /** the number of channels */ - int nvoice; /** the length of the synthesis process array */ - fluid_voice_t** voice; /** the synthesis processes */ - unsigned int noteid; /** the id is incremented for every new note. it's used for noteoff's */ - unsigned int storeid; - int nbuf; /** How many audio buffers are used? (depends on nr of audio channels / groups)*/ - - fluid_real_t** left_buf; - fluid_real_t** right_buf; - fluid_real_t** fx_left_buf; - fluid_real_t** fx_right_buf; - - fluid_revmodel_t* reverb; - fluid_chorus_t* chorus; - int cur; /** the current sample in the audio buffers to be output */ - int dither_index; /* current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */ - - char outbuf[256]; /** buffer for message output */ - - fluid_tuning_t*** tuning; /** 128 banks of 128 programs for the tunings */ - fluid_tuning_t* cur_tuning; /** current tuning in the iteration */ - - unsigned int min_note_length_ticks; /**< If note-offs are triggered just after a note-on, they will be delayed */ -}; - -/** returns 1 if the value has been set, 0 otherwise */ -int fluid_synth_setstr(fluid_synth_t* synth, char* name, char* str); - -/** returns 1 if the value exists, 0 otherwise */ -int fluid_synth_getstr(fluid_synth_t* synth, char* name, char** str); - -/** returns 1 if the value has been set, 0 otherwise */ -int fluid_synth_setnum(fluid_synth_t* synth, char* name, double val); - -/** returns 1 if the value exists, 0 otherwise */ -int fluid_synth_getnum(fluid_synth_t* synth, char* name, double* val); - -/** returns 1 if the value has been set, 0 otherwise */ -int fluid_synth_setint(fluid_synth_t* synth, char* name, int val); - -/** returns 1 if the value exists, 0 otherwise */ -int fluid_synth_getint(fluid_synth_t* synth, char* name, int* val); - - -int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num); - -int fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out); - -fluid_preset_t* fluid_synth_get_preset(fluid_synth_t* synth, - unsigned int sfontnum, - unsigned int banknum, - unsigned int prognum); - -fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth, - unsigned int banknum, - unsigned int prognum); - -int fluid_synth_all_voices_stop(fluid_synth_t* synth); -int fluid_synth_all_voices_pause(fluid_synth_t* synth); -int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan); -int fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan); -int fluid_synth_modulate_voices(fluid_synth_t* synth, int chan, int is_cc, int ctrl); -int fluid_synth_modulate_voices_all(fluid_synth_t* synth, int chan); -int fluid_synth_damp_voices(fluid_synth_t* synth, int chan); -int fluid_synth_kill_voice(fluid_synth_t* synth, fluid_voice_t * voice); -void fluid_synth_kill_by_exclusive_class(fluid_synth_t* synth, fluid_voice_t* voice); -void fluid_synth_release_voice_on_same_note(fluid_synth_t* synth, int chan, int key); -void fluid_synth_sfunload_macos9(fluid_synth_t* synth); - -void fluid_synth_print_voice(fluid_synth_t* synth); - -/** This function assures that every MIDI channels has a valid preset - * (NULL is okay). This function is called after a SoundFont is - * unloaded or reloaded. */ -void fluid_synth_update_presets(fluid_synth_t* synth); - - -int fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value); -int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value); - -fluid_bank_offset_t* fluid_synth_get_bank_offset0(fluid_synth_t* synth, int sfont_id); -void fluid_synth_remove_bank_offset(fluid_synth_t* synth, int sfont_id); - -void fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin, - void* lout, int loff, int lincr, - void* rout, int roff, int rincr); -/* - * misc - */ - -void fluid_synth_settings(fluid_settings_t* settings); - -#endif /* _FLUID_SYNTH_H */ diff --git a/libraries/fluidlite/src/fluid_sys.c b/libraries/fluidlite/src/fluid_sys.c deleted file mode 100644 index 09ef28b66..000000000 --- a/libraries/fluidlite/src/fluid_sys.c +++ /dev/null @@ -1,364 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#include "fluid_sys.h" - -static char fluid_errbuf[512]; /* buffer for error message */ - -static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL]; -static void* fluid_log_user_data[LAST_LOG_LEVEL]; -static int fluid_log_initialized = 0; - -static char* fluid_libname = "fluidsynth"; - - -void fluid_sys_config() -{ - fluid_log_config(); -} - - -unsigned int fluid_debug_flags = 0; - -#if DEBUG -/* - * fluid_debug - */ -int fluid_debug(int level, char * fmt, ...) -{ - if (fluid_debug_flags & level) { - fluid_log_function_t fun; - va_list args; - - va_start (args, fmt); - vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args); - va_end (args); - - fun = fluid_log_function[FLUID_DBG]; - if (fun != NULL) { - (*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]); - } - } - return 0; -} -#endif - -/** - * Installs a new log function for a specified log level. - * @param level Log level to install handler for. - * @param fun Callback function handler to call for logged messages - * @param data User supplied data pointer to pass to log function - * @return The previously installed function. - */ -fluid_log_function_t -fluid_set_log_function(int level, fluid_log_function_t fun, void* data) -{ - fluid_log_function_t old = NULL; - - if ((level >= 0) && (level < LAST_LOG_LEVEL)) { - old = fluid_log_function[level]; - fluid_log_function[level] = fun; - fluid_log_user_data[level] = data; - } - return old; -} - -/** - * Default log function which prints to the stderr. - * @param level Log level - * @param message Log message - * @param data User supplied data (not used) - */ -void -fluid_default_log_function(int level, char* message, void* data) -{ - FILE* out; - -#if defined(WIN32) - out = stdout; -#else - out = stderr; -#endif - - if (fluid_log_initialized == 0) { - fluid_log_config(); - } - - switch (level) { - case FLUID_PANIC: - FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message); - break; - case FLUID_ERR: - FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message); - break; - case FLUID_WARN: - FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message); - break; - case FLUID_INFO: - FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); - break; - case FLUID_DBG: -#if DEBUG - FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message); -#endif - break; - default: - FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); - break; - } - fflush(out); -} - -/* - * fluid_init_log - */ -void -fluid_log_config(void) -{ - if (fluid_log_initialized == 0) { - - fluid_log_initialized = 1; - - if (fluid_log_function[FLUID_PANIC] == NULL) { - fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL); - } - - if (fluid_log_function[FLUID_ERR] == NULL) { - fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL); - } - - if (fluid_log_function[FLUID_WARN] == NULL) { - fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL); - } - - if (fluid_log_function[FLUID_INFO] == NULL) { - fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL); - } - - if (fluid_log_function[FLUID_DBG] == NULL) { - fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL); - } - } -} - -/** - * Print a message to the log. - * @param level Log level (#fluid_log_level). - * @param fmt Printf style format string for log message - * @param ... Arguments for printf 'fmt' message string - * @return Always returns -1 - */ -int -fluid_log(int level, char* fmt, ...) -{ - fluid_log_function_t fun = NULL; - - va_list args; - va_start (args, fmt); - vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args); - va_end (args); - - if ((level >= 0) && (level < LAST_LOG_LEVEL)) { - fun = fluid_log_function[level]; - if (fun != NULL) { - (*fun)(level, fluid_errbuf, fluid_log_user_data[level]); - } - } - return FLUID_FAILED; -} - -/** - * An improved strtok, still trashes the input string, but is portable and - * thread safe. Also skips token chars at beginning of token string and never - * returns an empty token (will return NULL if source ends in token chars though). - * NOTE: NOT part of public API - * @internal - * @param str Pointer to a string pointer of source to tokenize. Pointer gets - * updated on each invocation to point to beginning of next token. Note that - * token char get's overwritten with a 0 byte. String pointer is set to NULL - * when final token is returned. - * @param delim String of delimiter chars. - * @return Pointer to the next token or NULL if no more tokens. - */ -char *fluid_strtok (char **str, char *delim) -{ - char *s, *d, *token; - char c; - - if (str == NULL || delim == NULL || !*delim) - { - FLUID_LOG(FLUID_ERR, "Null pointer"); - return NULL; - } - - s = *str; - if (!s) return NULL; /* str points to a NULL pointer? (tokenize already ended) */ - - /* skip delimiter chars at beginning of token */ - do - { - c = *s; - if (!c) /* end of source string? */ - { - *str = NULL; - return NULL; - } - - for (d = delim; *d; d++) /* is source char a token char? */ - { - if (c == *d) /* token char match? */ - { - s++; /* advance to next source char */ - break; - } - } - } while (*d); /* while token char match */ - - token = s; /* start of token found */ - - /* search for next token char or end of source string */ - for (s = s+1; *s; s++) - { - c = *s; - - for (d = delim; *d; d++) /* is source char a token char? */ - { - if (c == *d) /* token char match? */ - { - *s = '\0'; /* overwrite token char with zero byte to terminate token */ - *str = s+1; /* update str to point to beginning of next token */ - return token; - } - } - } - - /* we get here only if source string ended */ - *str = NULL; - return token; -} - -/* - * fluid_error - */ -char* -fluid_error() -{ - return fluid_errbuf; -} - - -/* - * - * fluid_is_midifile - */ -int -fluid_is_midifile(char* filename) -{ - FILE* fp = fopen(filename, "rb"); - char id[4]; - - if (fp == NULL) { - return 0; - } - if (fread((void*) id, 1, 4, fp) != 4) { - fclose(fp); - return 0; - } - fclose(fp); - - return strncmp(id, "MThd", 4) == 0; -} - -/* - * fluid_is_soundfont - * - */ -int -fluid_is_soundfont(char* filename) -{ - FILE* fp = fopen(filename, "rb"); - char id[4]; - - if (fp == NULL) { - return 0; - } - if (fread((void*) id, 1, 4, fp) != 4) { - fclose(fp); - return 0; - } - fclose(fp); - - return strncmp(id, "RIFF", 4) == 0; -} - -/*=============================================================*/ -/* */ -/* Win32 */ -/* */ -/*=============================================================*/ - -/*************************************************************** - * - * Timer - * - */ - -//timer disabled - - -/*************************************************************** - * - * Floating point exceptions - * - * The floating point exception functions were taken from Ircam's - * jMax source code. http://www.ircam.fr/jmax - * - * FIXME: check in config for i386 machine - * - * Currently not used. I leave the code here in case we want to pick - * this up again some time later. - */ - - - -/*************************************************************** - * - * Profiling (Linux, i586 only) - * - */ - - -/*************************************************************** - * - * Threads - * - */ - -//thread disabled - - -/*************************************************************** - * - * Sockets - * - */ - -//socket disabled diff --git a/libraries/fluidlite/src/fluid_sys.h b/libraries/fluidlite/src/fluid_sys.h deleted file mode 100644 index 289b85f64..000000000 --- a/libraries/fluidlite/src/fluid_sys.h +++ /dev/null @@ -1,141 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -/** - - This header contains a bunch of (mostly) system and machine - dependent functions: - - - timers - - current time in milliseconds and microseconds - - debug logging - - profiling - - memory locking - - checking for floating point exceptions - - */ - -#ifndef _FLUID_SYS_H -#define _FLUID_SYS_H - -#include "fluidsynth_priv.h" - - -void fluid_sys_config(void); -void fluid_log_config(void); - - -/* - * Utility functions - */ -char *fluid_strtok (char **str, char *delim); - - -/** - - Additional debugging system, separate from the log system. This - allows to print selected debug messages of a specific subsystem. - - */ - -extern unsigned int fluid_debug_flags; - -#if DEBUG - -enum fluid_debug_level { - FLUID_DBG_DRIVER = 1 -}; - -int fluid_debug(int level, char * fmt, ...); - -#else -#define fluid_debug -#endif - -//timer disabled - -/** - - Muteces - -*/ - - - -/** - Threads - -*/ - - -/** - Sockets - -*/ - - -/** - - Profiling - */ - - -/** - Profile numbers. List all the pieces of code you want to profile - here. Be sure to add an entry in the fluid_profile_data table in - fluid_sys.c -*/ -enum { - FLUID_PROF_WRITE_S16, - FLUID_PROF_ONE_BLOCK, - FLUID_PROF_ONE_BLOCK_CLEAR, - FLUID_PROF_ONE_BLOCK_VOICE, - FLUID_PROF_ONE_BLOCK_VOICES, - FLUID_PROF_ONE_BLOCK_REVERB, - FLUID_PROF_ONE_BLOCK_CHORUS, - FLUID_PROF_VOICE_NOTE, - FLUID_PROF_VOICE_RELEASE, - FLUID_PROF_LAST -}; - - -/* Profiling */ - - -/** - - Memory locking - - Memory locking is used to avoid swapping of the large block of - sample data. - */ - - -/** - - Floating point exceptions - - fluid_check_fpe() checks for "unnormalized numbers" and other - exceptions of the floating point processsor. -*/ - - -#endif /* _FLUID_SYS_H */ diff --git a/libraries/fluidlite/src/fluid_tuning.c b/libraries/fluidlite/src/fluid_tuning.c deleted file mode 100644 index 37e2ca414..000000000 --- a/libraries/fluidlite/src/fluid_tuning.c +++ /dev/null @@ -1,144 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#include "fluid_tuning.h" -#include "fluidsynth_priv.h" - - -fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog) -{ - fluid_tuning_t* tuning; - int i; - - tuning = FLUID_NEW(fluid_tuning_t); - if (tuning == NULL) { - FLUID_LOG(FLUID_PANIC, "Out of memory"); - return NULL; - } - - tuning->name = NULL; - - if (name != NULL) { - tuning->name = FLUID_STRDUP(name); - } - - tuning->bank = bank; - tuning->prog = prog; - - for (i = 0; i < 128; i++) { - tuning->pitch[i] = i * 100.0; - } - - return tuning; -} - -/* Duplicate a tuning */ -fluid_tuning_t * -fluid_tuning_duplicate (fluid_tuning_t *tuning) -{ - fluid_tuning_t *new_tuning; - int i; - - new_tuning = FLUID_NEW (fluid_tuning_t); - - if (!new_tuning) { - FLUID_LOG (FLUID_PANIC, "Out of memory"); - return NULL; - } - - if (tuning->name) - { - new_tuning->name = FLUID_STRDUP (tuning->name); - - if (!new_tuning->name) - { - FLUID_FREE (new_tuning); - FLUID_LOG (FLUID_PANIC, "Out of memory"); - return NULL; - } - } - else new_tuning->name = NULL; - - new_tuning->bank = tuning->bank; - new_tuning->prog = tuning->prog; - - for (i = 0; i < 128; i++) - new_tuning->pitch[i] = tuning->pitch[i]; - - return new_tuning; -} - -void delete_fluid_tuning(fluid_tuning_t* tuning) -{ - if (tuning == NULL) { - return; - } - if (tuning->name != NULL) { - FLUID_FREE(tuning->name); - } - FLUID_FREE(tuning); -} - -void fluid_tuning_set_name(fluid_tuning_t* tuning, const char* name) -{ - if (tuning->name != NULL) { - FLUID_FREE(tuning->name); - tuning->name = NULL; - } - if (name != NULL) { - tuning->name = FLUID_STRDUP(name); - } -} - -char* fluid_tuning_get_name(fluid_tuning_t* tuning) -{ - return tuning->name; -} - -void fluid_tuning_set_key(fluid_tuning_t* tuning, int key, double pitch) -{ - tuning->pitch[key] = pitch; -} - -void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv) -{ - int i; - - for (i = 0; i < 128; i++) { - tuning->pitch[i] = i * 100.0 + pitch_deriv[i % 12]; - } -} - -void fluid_tuning_set_all(fluid_tuning_t* tuning, double* pitch) -{ - int i; - - for (i = 0; i < 128; i++) { - tuning->pitch[i] = pitch[i]; - } -} - -void fluid_tuning_set_pitch(fluid_tuning_t* tuning, int key, double pitch) -{ - if ((key >= 0) && (key < 128)) { - tuning->pitch[key] = pitch; - } -} diff --git a/libraries/fluidlite/src/fluid_tuning.h b/libraries/fluidlite/src/fluid_tuning.h deleted file mode 100644 index 7919cf962..000000000 --- a/libraries/fluidlite/src/fluid_tuning.h +++ /dev/null @@ -1,65 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -/* - - More information about micro tuning can be found at: - - http://www.midi.org/about-midi/tuning.htm - http://www.midi.org/about-midi/tuning-scale.htm - http://www.midi.org/about-midi/tuning_extens.htm - -*/ - -#ifndef _FLUID_TUNING_H -#define _FLUID_TUNING_H - -#include "fluidsynth_priv.h" - -struct _fluid_tuning_t { - char* name; - int bank; - int prog; - double pitch[128]; /* the pitch of every key, in cents */ -}; - -fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog); -fluid_tuning_t* fluid_tuning_duplicate(fluid_tuning_t *tuning); -void delete_fluid_tuning(fluid_tuning_t* tuning); - -void fluid_tuning_set_name(fluid_tuning_t* tuning, const char* name); -char* fluid_tuning_get_name(fluid_tuning_t* tuning); - -#define fluid_tuning_get_bank(_t) ((_t)->bank) -#define fluid_tuning_get_prog(_t) ((_t)->prog) - -void fluid_tuning_set_pitch(fluid_tuning_t* tuning, int key, double pitch); -#define fluid_tuning_get_pitch(_t, _key) ((_t)->pitch[_key]) - -void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv); - -void fluid_tuning_set_all(fluid_tuning_t* tuning, double* pitch); -#define fluid_tuning_get_all(_t) (&(_t)->pitch[0]) - - - - -#endif /* _FLUID_TUNING_H */ diff --git a/libraries/fluidlite/src/fluid_voice.c b/libraries/fluidlite/src/fluid_voice.c deleted file mode 100644 index b41d7244b..000000000 --- a/libraries/fluidlite/src/fluid_voice.c +++ /dev/null @@ -1,1996 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#include "fluidsynth_priv.h" -#include "fluid_voice.h" -#include "fluid_mod.h" -#include "fluid_chan.h" -#include "fluid_conv.h" -#include "fluid_synth.h" -#include "fluid_sys.h" -#include "fluid_sfont.h" - -/* used for filter turn off optimization - if filter cutoff is above the - specified value and filter q is below the other value, turn filter off */ -#define FLUID_MAX_AUDIBLE_FILTER_FC 19000.0f -#define FLUID_MIN_AUDIBLE_FILTER_Q 1.2f - -/* Smallest amplitude that can be perceived (full scale is +/- 0.5) - * 16 bits => 96+4=100 dB dynamic range => 0.00001 - * 0.00001 * 2 is approximately 0.00003 :) - */ -#define FLUID_NOISE_FLOOR 0.00003 - -/* these should be the absolute minimum that FluidSynth can deal with */ -#define FLUID_MIN_LOOP_SIZE 2 -#define FLUID_MIN_LOOP_PAD 0 - -/* min vol envelope release (to stop clicks) in SoundFont timecents */ -#define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */ - -//removed inline -static void fluid_voice_effects (fluid_voice_t *voice, int count, - fluid_real_t* dsp_left_buf, - fluid_real_t* dsp_right_buf, - fluid_real_t* dsp_reverb_buf, - fluid_real_t* dsp_chorus_buf); -/* - * new_fluid_voice - */ -fluid_voice_t* -new_fluid_voice(fluid_real_t output_rate) -{ - fluid_voice_t* voice; - voice = FLUID_NEW(fluid_voice_t); - if (voice == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - voice->status = FLUID_VOICE_CLEAN; - voice->chan = NO_CHANNEL; - voice->key = 0; - voice->vel = 0; - voice->channel = NULL; - voice->sample = NULL; - voice->output_rate = output_rate; - - /* The 'sustain' and 'finished' segments of the volume / modulation - * envelope are constant. They are never affected by any modulator - * or generator. Therefore it is enough to initialize them once - * during the lifetime of the synth. - */ - voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].count = 0xffffffff; - voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].coeff = 1.0f; - voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].incr = 0.0f; - voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].min = -1.0f; - voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].max = 2.0f; - - voice->volenv_data[FLUID_VOICE_ENVFINISHED].count = 0xffffffff; - voice->volenv_data[FLUID_VOICE_ENVFINISHED].coeff = 0.0f; - voice->volenv_data[FLUID_VOICE_ENVFINISHED].incr = 0.0f; - voice->volenv_data[FLUID_VOICE_ENVFINISHED].min = -1.0f; - voice->volenv_data[FLUID_VOICE_ENVFINISHED].max = 1.0f; - - voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].count = 0xffffffff; - voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].coeff = 1.0f; - voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].incr = 0.0f; - voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].min = -1.0f; - voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].max = 2.0f; - - voice->modenv_data[FLUID_VOICE_ENVFINISHED].count = 0xffffffff; - voice->modenv_data[FLUID_VOICE_ENVFINISHED].coeff = 0.0f; - voice->modenv_data[FLUID_VOICE_ENVFINISHED].incr = 0.0f; - voice->modenv_data[FLUID_VOICE_ENVFINISHED].min = -1.0f; - voice->modenv_data[FLUID_VOICE_ENVFINISHED].max = 1.0f; - - return voice; -} - -/* - * delete_fluid_voice - */ -int -delete_fluid_voice(fluid_voice_t* voice) -{ - if (voice == NULL) { - return FLUID_OK; - } - FLUID_FREE(voice); - return FLUID_OK; -} - -/* fluid_voice_init - * - * Initialize the synthesis process - */ -int -fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, - fluid_channel_t* channel, int key, int vel, unsigned int id, - unsigned int start_time, fluid_real_t gain) -{ - /* Note: The voice parameters will be initialized later, when the - * generators have been retrieved from the sound font. Here, only - * the 'working memory' of the voice (position in envelopes, history - * of IIR filters, position in sample etc) is initialized. */ - - - voice->id = id; - voice->chan = fluid_channel_get_num(channel); - voice->key = (unsigned char) key; - voice->vel = (unsigned char) vel; - voice->channel = channel; - voice->mod_count = 0; - voice->sample = sample; - voice->start_time = start_time; - voice->ticks = 0; - voice->noteoff_ticks = 0; - voice->debug = 0; - voice->has_looped = 0; /* Will be set during voice_write when the 2nd loop point is reached */ - voice->last_fres = -1; /* The filter coefficients have to be calculated later in the DSP loop. */ - voice->filter_startup = 1; /* Set the filter immediately, don't fade between old and new settings */ - voice->interp_method = fluid_channel_get_interp_method(voice->channel); - - /* vol env initialization */ - voice->volenv_count = 0; - voice->volenv_section = 0; - voice->volenv_val = 0.0f; - voice->amp = 0.0f; /* The last value of the volume envelope, used to - calculate the volume increment during - processing */ - - /* mod env initialization*/ - voice->modenv_count = 0; - voice->modenv_section = 0; - voice->modenv_val = 0.0f; - - /* mod lfo */ - voice->modlfo_val = 0.0;/* Fixme: Retrieve from any other existing - voice on this channel to keep LFOs in - unison? */ - - /* vib lfo */ - voice->viblfo_val = 0.0f; /* Fixme: See mod lfo */ - - /* Clear sample history in filter */ - voice->hist1 = 0; - voice->hist2 = 0; - - /* Set all the generators to their default value, according to SF - * 2.01 section 8.1.3 (page 48). The value of NRPN messages are - * copied from the channel to the voice's generators. The sound font - * loader overwrites them. The generator values are later converted - * into voice parameters in - * fluid_voice_calculate_runtime_synthesis_parameters. */ - fluid_gen_init(&voice->gen[0], channel); - - voice->synth_gain = gain; - /* avoid division by zero later*/ - if (voice->synth_gain < 0.0000001){ - voice->synth_gain = 0.0000001; - } - - /* For a looped sample, this value will be overwritten as soon as the - * loop parameters are initialized (they may depend on modulators). - * This value can be kept, it is a worst-case estimate. - */ - - voice->amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / voice->synth_gain; - voice->amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / voice->synth_gain; - - /* Increment the reference count of the sample to prevent the - unloading of the soundfont while this voice is playing. */ - fluid_sample_incr_ref(voice->sample); - - return FLUID_OK; -} - -void fluid_voice_gen_set(fluid_voice_t* voice, int i, float val) -{ - voice->gen[i].val = val; - voice->gen[i].flags = GEN_SET; -} - -void fluid_voice_gen_incr(fluid_voice_t* voice, int i, float val) -{ - voice->gen[i].val += val; - voice->gen[i].flags = GEN_SET; -} - -float fluid_voice_gen_get(fluid_voice_t* voice, int gen) -{ - return voice->gen[gen].val; -} - -fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num) -{ - /* This is an extension to the SoundFont standard. More - * documentation is available at the fluid_synth_set_gen2() - * function. */ - if (voice->gen[num].flags == GEN_ABS_NRPN) { - return (fluid_real_t) voice->gen[num].nrpn; - } else { - return (fluid_real_t) (voice->gen[num].val + voice->gen[num].mod + voice->gen[num].nrpn); - } -} - - -/* - * fluid_voice_write - * - * This is where it all happens. This function is called by the - * synthesizer to generate the sound samples. The synthesizer passes - * four audio buffers: left, right, reverb out, and chorus out. - * - * The biggest part of this function sets the correct values for all - * the dsp parameters (all the control data boil down to only a few - * dsp parameters). The dsp routine is #included in several places (fluid_dsp_core.c). - */ -int -fluid_voice_write(fluid_voice_t* voice, - fluid_real_t* dsp_left_buf, fluid_real_t* dsp_right_buf, - fluid_real_t* dsp_reverb_buf, fluid_real_t* dsp_chorus_buf) -{ - fluid_real_t fres; - fluid_real_t target_amp; /* target amplitude */ - int count; - - fluid_real_t dsp_buf[FLUID_BUFSIZE]; - fluid_env_data_t* env_data; - fluid_real_t x; - - - /* make sure we're playing and that we have sample data */ - if (!_PLAYING(voice)) return FLUID_OK; - - /******************* sample **********************/ - - if (voice->sample == NULL) - { - fluid_voice_off(voice); - return FLUID_OK; - } - - if (voice->noteoff_ticks != 0 && voice->ticks >= voice->noteoff_ticks) - { - fluid_voice_noteoff(voice); - } - - /* Range checking for sample- and loop-related parameters - * Initial phase is calculated here*/ - fluid_voice_check_sample_sanity (voice); - - /******************* vol env **********************/ - - env_data = &voice->volenv_data[voice->volenv_section]; - - /* skip to the next section of the envelope if necessary */ - while (voice->volenv_count >= env_data->count) - { - // If we're switching envelope stages from decay to sustain, force the value to be the end value of the previous stage - if (env_data && voice->volenv_section == FLUID_VOICE_ENVDECAY) - voice->volenv_val = env_data->min * env_data->coeff; - - env_data = &voice->volenv_data[++voice->volenv_section]; - voice->volenv_count = 0; - } - - /* calculate the envelope value and check for valid range */ - x = env_data->coeff * voice->volenv_val + env_data->incr; - if (x < env_data->min) - { - x = env_data->min; - voice->volenv_section++; - voice->volenv_count = 0; - } - else if (x > env_data->max) - { - x = env_data->max; - voice->volenv_section++; - voice->volenv_count = 0; - } - - voice->volenv_val = x; - voice->volenv_count++; - - if (voice->volenv_section == FLUID_VOICE_ENVFINISHED) - { - fluid_voice_off (voice); - return FLUID_OK; - } - - /******************* mod env **********************/ - - env_data = &voice->modenv_data[voice->modenv_section]; - - /* skip to the next section of the envelope if necessary */ - while (voice->modenv_count >= env_data->count) - { - env_data = &voice->modenv_data[++voice->modenv_section]; - voice->modenv_count = 0; - } - - /* calculate the envelope value and check for valid range */ - x = env_data->coeff * voice->modenv_val + env_data->incr; - - if (x < env_data->min) - { - x = env_data->min; - voice->modenv_section++; - voice->modenv_count = 0; - } - else if (x > env_data->max) - { - x = env_data->max; - voice->modenv_section++; - voice->modenv_count = 0; - } - - voice->modenv_val = x; - voice->modenv_count++; - - /******************* mod lfo **********************/ - - if (voice->ticks >= voice->modlfo_delay) - { - voice->modlfo_val += voice->modlfo_incr; - - if (voice->modlfo_val > 1.0) - { - voice->modlfo_incr = -voice->modlfo_incr; - voice->modlfo_val = (fluid_real_t) 2.0 - voice->modlfo_val; - } - else if (voice->modlfo_val < -1.0) - { - voice->modlfo_incr = -voice->modlfo_incr; - voice->modlfo_val = (fluid_real_t) -2.0 - voice->modlfo_val; - } - } - - /******************* vib lfo **********************/ - - if (voice->ticks >= voice->viblfo_delay) - { - voice->viblfo_val += voice->viblfo_incr; - - if (voice->viblfo_val > (fluid_real_t) 1.0) - { - voice->viblfo_incr = -voice->viblfo_incr; - voice->viblfo_val = (fluid_real_t) 2.0 - voice->viblfo_val; - } - else if (voice->viblfo_val < -1.0) - { - voice->viblfo_incr = -voice->viblfo_incr; - voice->viblfo_val = (fluid_real_t) -2.0 - voice->viblfo_val; - } - } - - /******************* amplitude **********************/ - - /* calculate final amplitude - * - initial gain - * - amplitude envelope - */ - - if (voice->volenv_section == FLUID_VOICE_ENVDELAY) - goto post_process; /* The volume amplitude is in hold phase. No sound is produced. */ - - if (voice->volenv_section == FLUID_VOICE_ENVATTACK) - { - /* the envelope is in the attack section: ramp linearly to max value. - * A positive modlfo_to_vol should increase volume (negative attenuation). - */ - target_amp = fluid_atten2amp (voice->attenuation) - * fluid_cb2amp (voice->modlfo_val * -voice->modlfo_to_vol) - * voice->volenv_val; - } - else - { - fluid_real_t amplitude_that_reaches_noise_floor; - fluid_real_t amp_max; - - target_amp = fluid_atten2amp (voice->attenuation) - * fluid_cb2amp (960.0f * (1.0f - voice->volenv_val) - + voice->modlfo_val * -voice->modlfo_to_vol); - - /* We turn off a voice, if the volume has dropped low enough. */ - - /* A voice can be turned off, when an estimate for the volume - * (upper bound) falls below that volume, that will drop the - * sample below the noise floor. - */ - - /* If the loop amplitude is known, we can use it if the voice loop is within - * the sample loop - */ - - /* Is the playing pointer already in the loop? */ - if (voice->has_looped) - amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor_loop; - else - amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor_nonloop; - - /* voice->attenuation_min is a lower boundary for the attenuation - * now and in the future (possibly 0 in the worst case). Now the - * amplitude of sample and volenv cannot exceed amp_max (since - * volenv_val can only drop): - */ - - amp_max = fluid_atten2amp (voice->min_attenuation_cB) * voice->volenv_val; - - /* And if amp_max is already smaller than the known amplitude, - * which will attenuate the sample below the noise floor, then we - * can safely turn off the voice. Duh. */ - if (amp_max < amplitude_that_reaches_noise_floor) - { - fluid_voice_off (voice); - goto post_process; - } - } - - /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */ - voice->amp_incr = (target_amp - voice->amp) / FLUID_BUFSIZE; - - /* no volume and not changing? - No need to process */ - if ((voice->amp == 0.0f) && (voice->amp_incr == 0.0f)) - goto post_process; - - /* Calculate the number of samples, that the DSP loop advances - * through the original waveform with each step in the output - * buffer. It is the ratio between the frequencies of original - * waveform and output waveform.*/ - voice->phase_incr = fluid_ct2hz_real - (voice->pitch + voice->modlfo_val * voice->modlfo_to_pitch - + voice->viblfo_val * voice->viblfo_to_pitch - + voice->modenv_val * voice->modenv_to_pitch) / voice->root_pitch; - - /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */ - if (voice->phase_incr == 0) voice->phase_incr = 1; - - /*************** resonant filter ******************/ - - /* calculate the frequency of the resonant filter in Hz */ - fres = fluid_ct2hz(voice->fres - + voice->modlfo_val * voice->modlfo_to_fc - + voice->modenv_val * voice->modenv_to_fc); - - /* FIXME - Still potential for a click during turn on, can we interpolate - between 20khz cutoff and 0 Q? */ - - /* I removed the optimization of turning the filter off when the - * resonance frequence is above the maximum frequency. Instead, the - * filter frequency is set to a maximum of 0.45 times the sampling - * rate. For a 44100 kHz sampling rate, this amounts to 19845 - * Hz. The reason is that there were problems with anti-aliasing when the - * synthesizer was run at lower sampling rates. Thanks to Stephan - * Tassart for pointing me to this bug. By turning the filter on and - * clipping the maximum filter frequency at 0.45*srate, the filter - * is used as an anti-aliasing filter. */ - - if (fres > 0.45f * voice->output_rate) - fres = 0.45f * voice->output_rate; - else if (fres < 5) - fres = 5; - - /* if filter enabled and there is a significant frequency change.. */ - if ((fabs(fres - voice->last_fres) > 0.01)) - { - /* The filter coefficients have to be recalculated (filter - * parameters have changed). Recalculation for various reasons is - * forced by setting last_fres to -1. The flag filter_startup - * indicates, that the DSP loop runs for the first time, in this - * case, the filter is set directly, instead of smoothly fading - * between old and new settings. - * - * Those equations from Robert Bristow-Johnson's `Cookbook - * formulae for audio EQ biquad filter coefficients', obtained - * from Harmony-central.com / Computer / Programming. They are - * the result of the bilinear transform on an analogue filter - * prototype. To quote, `BLT frequency warping has been taken - * into account for both significant frequency relocation and for - * bandwidth readjustment'. */ - - fluid_real_t omega = (fluid_real_t) (2.0 * M_PI * (fres / ((float) voice->output_rate))); - fluid_real_t sin_coeff = (fluid_real_t) sin(omega); - fluid_real_t cos_coeff = (fluid_real_t) cos(omega); - fluid_real_t alpha_coeff = sin_coeff / (2.0f * voice->q_lin); - fluid_real_t a0_inv = 1.0f / (1.0f + alpha_coeff); - - /* Calculate the filter coefficients. All coefficients are - * normalized by a0. Think of `a1' as `a1/a0'. - * - * Here a couple of multiplications are saved by reusing common expressions. - * The original equations should be: - * voice->b0=(1.-cos_coeff)*a0_inv*0.5*voice->filter_gain; - * voice->b1=(1.-cos_coeff)*a0_inv*voice->filter_gain; - * voice->b2=(1.-cos_coeff)*a0_inv*0.5*voice->filter_gain; */ - - fluid_real_t a1_temp = -2.0f * cos_coeff * a0_inv; - fluid_real_t a2_temp = (1.0f - alpha_coeff) * a0_inv; - fluid_real_t b1_temp = (1.0f - cos_coeff) * a0_inv * voice->filter_gain; - /* both b0 -and- b2 */ - fluid_real_t b02_temp = b1_temp * 0.5f; - - if (voice->filter_startup) - { - /* The filter is calculated, because the voice was started up. - * In this case set the filter coefficients without delay. - */ - voice->a1 = a1_temp; - voice->a2 = a2_temp; - voice->b02 = b02_temp; - voice->b1 = b1_temp; - voice->filter_coeff_incr_count = 0; - voice->filter_startup = 0; -// printf("Setting initial filter coefficients.\n"); - } - else - { - - /* The filter frequency is changed. Calculate an increment - * factor, so that the new setting is reached after one buffer - * length. x_incr is added to the current value FLUID_BUFSIZE - * times. The length is arbitrarily chosen. Longer than one - * buffer will sacrifice some performance, though. Note: If - * the filter is still too 'grainy', then increase this number - * at will. - */ - -#define FILTER_TRANSITION_SAMPLES (FLUID_BUFSIZE) - - voice->a1_incr = (a1_temp - voice->a1) / FILTER_TRANSITION_SAMPLES; - voice->a2_incr = (a2_temp - voice->a2) / FILTER_TRANSITION_SAMPLES; - voice->b02_incr = (b02_temp - voice->b02) / FILTER_TRANSITION_SAMPLES; - voice->b1_incr = (b1_temp - voice->b1) / FILTER_TRANSITION_SAMPLES; - /* Have to add the increments filter_coeff_incr_count times. */ - voice->filter_coeff_incr_count = FILTER_TRANSITION_SAMPLES; - } - voice->last_fres = fres; - } - - - /*********************** run the dsp chain ************************ - * The sample is mixed with the output buffer. - * The buffer has to be filled from 0 to FLUID_BUFSIZE-1. - * Depending on the position in the loop and the loop size, this - * may require several runs. */ - - voice->dsp_buf = dsp_buf; - - switch (voice->interp_method) - { - case FLUID_INTERP_NONE: - count = fluid_dsp_float_interpolate_none (voice); - break; - case FLUID_INTERP_LINEAR: - count = fluid_dsp_float_interpolate_linear (voice); - break; - case FLUID_INTERP_4THORDER: - default: - count = fluid_dsp_float_interpolate_4th_order (voice); - break; - case FLUID_INTERP_7THORDER: - count = fluid_dsp_float_interpolate_7th_order (voice); - break; - } - - if (count > 0) - fluid_voice_effects (voice, count, dsp_left_buf, dsp_right_buf, - dsp_reverb_buf, dsp_chorus_buf); - - /* turn off voice if short count (sample ended and not looping) */ - if (count < FLUID_BUFSIZE) - { - fluid_voice_off(voice); - } - - post_process: - voice->ticks += FLUID_BUFSIZE; - return FLUID_OK; -} - - -/* Purpose: - * - * - filters (applies a lowpass filter with variable cutoff frequency and quality factor) - * - mixes the processed sample to left and right output using the pan setting - * - sends the processed sample to chorus and reverb - * - * Variable description: - * - dsp_data: Pointer to the original waveform data - * - dsp_left_buf: The generated signal goes here, left channel - * - dsp_right_buf: right channel - * - dsp_reverb_buf: Send to reverb unit - * - dsp_chorus_buf: Send to chorus unit - * - dsp_a1: Coefficient for the filter - * - dsp_a2: same - * - dsp_b0: same - * - dsp_b1: same - * - dsp_b2: same - * - voice holds the voice structure - * - * A couple of variables are used internally, their results are discarded: - * - dsp_i: Index through the output buffer - * - dsp_phase_fractional: The fractional part of dsp_phase - * - dsp_coeff: A table of four coefficients, depending on the fractional phase. - * Used to interpolate between samples. - * - dsp_process_buffer: Holds the processed signal between stages - * - dsp_centernode: delay line for the IIR filter - * - dsp_hist1: same - * - dsp_hist2: same - * - */ -static void -fluid_voice_effects (fluid_voice_t *voice, int count, - fluid_real_t* dsp_left_buf, fluid_real_t* dsp_right_buf, - fluid_real_t* dsp_reverb_buf, fluid_real_t* dsp_chorus_buf) -{ - /* IIR filter sample history */ - fluid_real_t dsp_hist1 = voice->hist1; - fluid_real_t dsp_hist2 = voice->hist2; - - /* IIR filter coefficients */ - fluid_real_t dsp_a1 = voice->a1; - fluid_real_t dsp_a2 = voice->a2; - fluid_real_t dsp_b02 = voice->b02; - fluid_real_t dsp_b1 = voice->b1; - fluid_real_t dsp_a1_incr = voice->a1_incr; - fluid_real_t dsp_a2_incr = voice->a2_incr; - fluid_real_t dsp_b02_incr = voice->b02_incr; - fluid_real_t dsp_b1_incr = voice->b1_incr; - int dsp_filter_coeff_incr_count = voice->filter_coeff_incr_count; - - fluid_real_t *dsp_buf = voice->dsp_buf; - - fluid_real_t dsp_centernode; - int dsp_i; - float v; - - /* filter (implement the voice filter according to SoundFont standard) */ - - /* Check for denormal number (too close to zero). */ - if (fabs (dsp_hist1) < 1e-20) dsp_hist1 = 0.0f; /* FIXME JMG - Is this even needed? */ - - /* Two versions of the filter loop. One, while the filter is - * changing towards its new setting. The other, if the filter - * doesn't change. - */ - - if (dsp_filter_coeff_incr_count > 0) - { - /* Increment is added to each filter coefficient filter_coeff_incr_count times. */ - for (dsp_i = 0; dsp_i < count; dsp_i++) - { - /* The filter is implemented in Direct-II form. */ - dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; - dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; - dsp_hist2 = dsp_hist1; - dsp_hist1 = dsp_centernode; - - if (dsp_filter_coeff_incr_count-- > 0) - { - dsp_a1 += dsp_a1_incr; - dsp_a2 += dsp_a2_incr; - dsp_b02 += dsp_b02_incr; - dsp_b1 += dsp_b1_incr; - } - } /* for dsp_i */ - } - else /* The filter parameters are constant. This is duplicated to save time. */ - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - { /* The filter is implemented in Direct-II form. */ - dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; - dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; - dsp_hist2 = dsp_hist1; - dsp_hist1 = dsp_centernode; - } - } - - /* pan (Copy the signal to the left and right output buffer) The voice - * panning generator has a range of -500 .. 500. If it is centered, - * it's close to 0. voice->amp_left and voice->amp_right are then the - * same, and we can save one multiplication per voice and sample. - */ - if ((-0.5 < voice->pan) && (voice->pan < 0.5)) - { - /* The voice is centered. Use voice->amp_left twice. */ - for (dsp_i = 0; dsp_i < count; dsp_i++) - { - v = voice->amp_left * dsp_buf[dsp_i]; - dsp_left_buf[dsp_i] += v; - dsp_right_buf[dsp_i] += v; - } - } - else /* The voice is not centered. Stereo samples have one side zero. */ - { - if (voice->amp_left != 0.0) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - dsp_left_buf[dsp_i] += voice->amp_left * dsp_buf[dsp_i]; - } - - if (voice->amp_right != 0.0) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - dsp_right_buf[dsp_i] += voice->amp_right * dsp_buf[dsp_i]; - } - } - - /* reverb send. Buffer may be NULL. */ - if ((dsp_reverb_buf != NULL) && (voice->amp_reverb != 0.0)) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_buf[dsp_i]; - } - - /* chorus send. Buffer may be NULL. */ - if ((dsp_chorus_buf != NULL) && (voice->amp_chorus != 0)) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_buf[dsp_i]; - } - - voice->hist1 = dsp_hist1; - voice->hist2 = dsp_hist2; - voice->a1 = dsp_a1; - voice->a2 = dsp_a2; - voice->b02 = dsp_b02; - voice->b1 = dsp_b1; - voice->filter_coeff_incr_count = dsp_filter_coeff_incr_count; -} - -/* - * fluid_voice_get_channel - */ -fluid_channel_t* -fluid_voice_get_channel(fluid_voice_t* voice) -{ - return voice->channel; -} - -/* - * fluid_voice_start - */ -void fluid_voice_start(fluid_voice_t* voice) -{ - /* The maximum volume of the loop is calculated and cached once for each - * sample with its nominal loop settings. This happens, when the sample is used - * for the first time.*/ - - fluid_voice_calculate_runtime_synthesis_parameters(voice); - - /* Force setting of the phase at the first DSP loop run - * This cannot be done earlier, because it depends on modulators.*/ - voice->check_sample_sanity_flag=FLUID_SAMPLESANITY_STARTUP; - - voice->status = FLUID_VOICE_ON; -} - -/* - * fluid_voice_calculate_runtime_synthesis_parameters - * - * in this function we calculate the values of all the parameters. the - * parameters are converted to their most useful unit for the DSP - * algorithm, for example, number of samples instead of - * timecents. Some parameters keep their "perceptual" unit and - * conversion will be done in the DSP function. This is the case, for - * example, for the pitch since it is modulated by the controllers in - * cents. */ -int -fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice) -{ - int i; - - int list_of_generators_to_initialize[35] = { - GEN_STARTADDROFS, /* SF2.01 page 48 #0 */ - GEN_ENDADDROFS, /* #1 */ - GEN_STARTLOOPADDROFS, /* #2 */ - GEN_ENDLOOPADDROFS, /* #3 */ - /* GEN_STARTADDRCOARSEOFS see comment below [1] #4 */ - GEN_MODLFOTOPITCH, /* #5 */ - GEN_VIBLFOTOPITCH, /* #6 */ - GEN_MODENVTOPITCH, /* #7 */ - GEN_FILTERFC, /* #8 */ - GEN_FILTERQ, /* #9 */ - GEN_MODLFOTOFILTERFC, /* #10 */ - GEN_MODENVTOFILTERFC, /* #11 */ - /* GEN_ENDADDRCOARSEOFS [1] #12 */ - GEN_MODLFOTOVOL, /* #13 */ - /* not defined #14 */ - GEN_CHORUSSEND, /* #15 */ - GEN_REVERBSEND, /* #16 */ - GEN_PAN, /* #17 */ - /* not defined #18 */ - /* not defined #19 */ - /* not defined #20 */ - GEN_MODLFODELAY, /* #21 */ - GEN_MODLFOFREQ, /* #22 */ - GEN_VIBLFODELAY, /* #23 */ - GEN_VIBLFOFREQ, /* #24 */ - GEN_MODENVDELAY, /* #25 */ - GEN_MODENVATTACK, /* #26 */ - GEN_MODENVHOLD, /* #27 */ - GEN_MODENVDECAY, /* #28 */ - /* GEN_MODENVSUSTAIN [1] #29 */ - GEN_MODENVRELEASE, /* #30 */ - /* GEN_KEYTOMODENVHOLD [1] #31 */ - /* GEN_KEYTOMODENVDECAY [1] #32 */ - GEN_VOLENVDELAY, /* #33 */ - GEN_VOLENVATTACK, /* #34 */ - GEN_VOLENVHOLD, /* #35 */ - GEN_VOLENVDECAY, /* #36 */ - /* GEN_VOLENVSUSTAIN [1] #37 */ - GEN_VOLENVRELEASE, /* #38 */ - /* GEN_KEYTOVOLENVHOLD [1] #39 */ - /* GEN_KEYTOVOLENVDECAY [1] #40 */ - /* GEN_STARTLOOPADDRCOARSEOFS [1] #45 */ - GEN_KEYNUM, /* #46 */ - GEN_VELOCITY, /* #47 */ - GEN_ATTENUATION, /* #48 */ - /* GEN_ENDLOOPADDRCOARSEOFS [1] #50 */ - /* GEN_COARSETUNE [1] #51 */ - /* GEN_FINETUNE [1] #52 */ - GEN_OVERRIDEROOTKEY, /* #58 */ - GEN_PITCH, /* --- */ - -1}; /* end-of-list marker */ - - /* When the voice is made ready for the synthesis process, a lot of - * voice-internal parameters have to be calculated. - * - * At this point, the sound font has already set the -nominal- value - * for all generators (excluding GEN_PITCH). Most generators can be - * modulated - they include a nominal value and an offset (which - * changes with velocity, note number, channel parameters like - * aftertouch, mod wheel...) Now this offset will be calculated as - * follows: - * - * - Process each modulator once. - * - Calculate its output value. - * - Find the target generator. - * - Add the output value to the modulation value of the generator. - * - * Note: The generators have been initialized with - * fluid_gen_set_default_values. - */ - - for (i = 0; i < voice->mod_count; i++) { - fluid_mod_t* mod = &voice->mod[i]; - fluid_real_t modval = fluid_mod_get_value(mod, voice->channel, voice); - int dest_gen_index = mod->dest; - fluid_gen_t* dest_gen = &voice->gen[dest_gen_index]; - dest_gen->mod += modval; - /* fluid_dump_modulator(mod); */ - } - - /* The GEN_PITCH is a hack to fit the pitch bend controller into the - * modulator paradigm. Now the nominal pitch of the key is set. - * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a - * non-realtime parameter. So we don't allow modulation (as opposed - * to _GEN(voice, GEN_SCALETUNE) When the scale tuning is varied, - * one key remains fixed. Here C3 (MIDI number 60) is used. - */ - if (fluid_channel_has_tuning(voice->channel)) { - /* pitch(60) + scale * (pitch(key) - pitch(60)) */ - #define __pitch(_k) fluid_tuning_get_pitch(tuning, _k) - fluid_tuning_t* tuning = fluid_channel_get_tuning(voice->channel); - voice->gen[GEN_PITCH].val = (__pitch(60) + (voice->gen[GEN_SCALETUNE].val / 100.0f * - (__pitch(voice->key) - __pitch(60)))); - } else { - voice->gen[GEN_PITCH].val = (voice->gen[GEN_SCALETUNE].val * (voice->key - 60.0f) - + 100.0f * 60.0f); - } - - /* Now the generators are initialized, nominal and modulation value. - * The voice parameters (which depend on generators) are calculated - * with fluid_voice_update_param. Processing the list of generator - * changes will calculate each voice parameter once. - * - * Note [1]: Some voice parameters depend on several generators. For - * example, the pitch depends on GEN_COARSETUNE, GEN_FINETUNE and - * GEN_PITCH. voice->pitch. Unnecessary recalculation is avoided - * by removing all but one generator from the list of voice - * parameters. Same with GEN_XXX and GEN_XXXCOARSE: the - * initialisation list contains only GEN_XXX. - */ - - /* Calculate the voice parameter(s) dependent on each generator. */ - for (i = 0; list_of_generators_to_initialize[i] != -1; i++) { - fluid_voice_update_param(voice, list_of_generators_to_initialize[i]); - } - - /* Make an estimate on how loud this voice can get at any time (attenuation). */ - voice->min_attenuation_cB = fluid_voice_get_lower_boundary_for_attenuation(voice); - - return FLUID_OK; -} - -/* - * calculate_hold_decay_buffers - */ -int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, - int gen_key2base, int is_decay) -{ - /* Purpose: - * - * Returns the number of DSP loops, that correspond to the hold - * (is_decay=0) or decay (is_decay=1) time. - * gen_base=GEN_VOLENVHOLD, GEN_VOLENVDECAY, GEN_MODENVHOLD, - * GEN_MODENVDECAY gen_key2base=GEN_KEYTOVOLENVHOLD, - * GEN_KEYTOVOLENVDECAY, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY - */ - - fluid_real_t timecents; - fluid_real_t seconds; - int buffers; - - /* SF2.01 section 8.4.3 # 31, 32, 39, 40 - * GEN_KEYTOxxxENVxxx uses key 60 as 'origin'. - * The unit of the generator is timecents per key number. - * If KEYTOxxxENVxxx is 100, a key one octave over key 60 (72) - * will cause (60-72)*100=-1200 timecents of time variation. - * The time is cut in half. - */ - timecents = (_GEN(voice, gen_base) + _GEN(voice, gen_key2base) * (60.0 - voice->key)); - - /* Range checking */ - if (is_decay){ - /* SF 2.01 section 8.1.3 # 28, 36 */ - if (timecents > 8000.0) { - timecents = 8000.0; - } - } else { - /* SF 2.01 section 8.1.3 # 27, 35 */ - if (timecents > 5000) { - timecents = 5000.0; - } - /* SF 2.01 section 8.1.2 # 27, 35: - * The most negative number indicates no hold time - */ - if (timecents <= -32768.) { - return 0; - } - } - /* SF 2.01 section 8.1.3 # 27, 28, 35, 36 */ - if (timecents < -12000.0) { - timecents = -12000.0; - } - - seconds = fluid_tc2sec(timecents); - /* Each DSP loop processes FLUID_BUFSIZE samples. */ - - /* round to next full number of buffers */ - buffers = (int)(((fluid_real_t)voice->output_rate * seconds) - / (fluid_real_t)FLUID_BUFSIZE - +0.5); - - return buffers; -} - -/* - * fluid_voice_update_param - * - * Purpose: - * - * The value of a generator (gen) has changed. (The different - * generators are listed in fluidlite.h, or in SF2.01 page 48-49) - * Now the dependent 'voice' parameters are calculated. - * - * fluid_voice_update_param can be called during the setup of the - * voice (to calculate the initial value for a voice parameter), or - * during its operation (a generator has been changed due to - * real-time parameter modifications like pitch-bend). - * - * Note: The generator holds three values: The base value .val, an - * offset caused by modulators .mod, and an offset caused by the - * NRPN system. _GEN(voice, generator_enumerator) returns the sum - * of all three. - */ -void -fluid_voice_update_param(fluid_voice_t* voice, int gen) -{ - double q_dB; - fluid_real_t x; - fluid_real_t y; - unsigned int count; - // Alternate attenuation scale used by EMU10K1 cards when setting the attenuation at the preset or instrument level within the SoundFont bank. - static const float ALT_ATTENUATION_SCALE = 0.4; - - switch (gen) { - - case GEN_PAN: - /* range checking is done in the fluid_pan function */ - voice->pan = _GEN(voice, GEN_PAN); - voice->amp_left = fluid_pan(voice->pan, 1) * voice->synth_gain / 32768.0f; - voice->amp_right = fluid_pan(voice->pan, 0) * voice->synth_gain / 32768.0f; - break; - - case GEN_ATTENUATION: - voice->attenuation = ((fluid_real_t)(voice)->gen[GEN_ATTENUATION].val*ALT_ATTENUATION_SCALE) + - (fluid_real_t)(voice)->gen[GEN_ATTENUATION].mod + (fluid_real_t)(voice)->gen[GEN_ATTENUATION].nrpn; - - /* Range: SF2.01 section 8.1.3 # 48 - * Motivation for range checking: - * OHPiano.SF2 sets initial attenuation to a whooping -96 dB */ - fluid_clip(voice->attenuation, 0.0, 1440.0); - break; - - /* The pitch is calculated from three different generators. - * Read comment in fluidlite.h about GEN_PITCH. - */ - case GEN_PITCH: - case GEN_COARSETUNE: - case GEN_FINETUNE: - /* The testing for allowed range is done in 'fluid_ct2hz' */ - voice->pitch = (_GEN(voice, GEN_PITCH) - + 100.0f * _GEN(voice, GEN_COARSETUNE) - + _GEN(voice, GEN_FINETUNE)); - break; - - case GEN_REVERBSEND: - /* The generator unit is 'tenths of a percent'. */ - voice->reverb_send = _GEN(voice, GEN_REVERBSEND) / 1000.0f; - fluid_clip(voice->reverb_send, 0.0, 1.0); - voice->amp_reverb = voice->reverb_send * voice->synth_gain / 32768.0f; - break; - - case GEN_CHORUSSEND: - /* The generator unit is 'tenths of a percent'. */ - voice->chorus_send = _GEN(voice, GEN_CHORUSSEND) / 1000.0f; - fluid_clip(voice->chorus_send, 0.0, 1.0); - voice->amp_chorus = voice->chorus_send * voice->synth_gain / 32768.0f; - break; - - case GEN_OVERRIDEROOTKEY: - /* This is a non-realtime parameter. Therefore the .mod part of the generator - * can be neglected. - * NOTE: origpitch sets MIDI root note while pitchadj is a fine tuning amount - * which offsets the original rate. This means that the fine tuning is - * inverted with respect to the root note (so subtract it, not add). - */ - if (voice->gen[GEN_OVERRIDEROOTKEY].val > -1) { //FIXME: use flag instead of -1 - voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f - - voice->sample->pitchadj; - } else { - voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj; - } - voice->root_pitch = fluid_ct2hz(voice->root_pitch); - if (voice->sample != NULL) { - voice->root_pitch *= (fluid_real_t) voice->output_rate / voice->sample->samplerate; - } - break; - - case GEN_FILTERFC: - /* The resonance frequency is converted from absolute cents to - * midicents .val and .mod are both used, this permits real-time - * modulation. The allowed range is tested in the 'fluid_ct2hz' - * function [PH,20021214] - */ - voice->fres = _GEN(voice, GEN_FILTERFC); - - /* The synthesis loop will have to recalculate the filter - * coefficients. */ - voice->last_fres = -1.0f; - break; - - case GEN_FILTERQ: - /* The generator contains 'centibels' (1/10 dB) => divide by 10 to - * obtain dB */ - q_dB = _GEN(voice, GEN_FILTERQ) / 10.0f; - - /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */ - fluid_clip(q_dB, 0.0f, 96.0f); - - /* Short version: Modify the Q definition in a way, that a Q of 0 - * dB leads to no resonance hump in the freq. response. - * - * Long version: From SF2.01, page 39, item 9 (initialFilterQ): - * "The gain at the cutoff frequency may be less than zero when - * zero is specified". Assume q_dB=0 / q_lin=1: If we would leave - * q as it is, then this results in a 3 dB hump slightly below - * fc. At fc, the gain is exactly the DC gain (0 dB). What is - * (probably) meant here is that the filter does not show a - * resonance hump for q_dB=0. In this case, the corresponding - * q_lin is 1/sqrt(2)=0.707. The filter should have 3 dB of - * attenuation at fc now. In this case Q_dB is the height of the - * resonance peak not over the DC gain, but over the frequency - * response of a non-resonant filter. This idea is implemented as - * follows: */ - q_dB -= 3.01f; - - /* The 'sound font' Q is defined in dB. The filter needs a linear - q. Convert. */ - voice->q_lin = (fluid_real_t) (pow(10.0f, q_dB / 20.0f)); - - /* SF 2.01 page 59: - * - * The SoundFont specs ask for a gain reduction equal to half the - * height of the resonance peak (Q). For example, for a 10 dB - * resonance peak, the gain is reduced by 5 dB. This is done by - * multiplying the total gain with sqrt(1/Q). `Sqrt' divides dB - * by 2 (100 lin = 40 dB, 10 lin = 20 dB, 3.16 lin = 10 dB etc) - * The gain is later factored into the 'b' coefficients - * (numerator of the filter equation). This gain factor depends - * only on Q, so this is the right place to calculate it. - */ - voice->filter_gain = (fluid_real_t) (1.0 / sqrt(voice->q_lin)); - - /* The synthesis loop will have to recalculate the filter coefficients. */ - voice->last_fres = -1.; - break; - - case GEN_MODLFOTOPITCH: - voice->modlfo_to_pitch = _GEN(voice, GEN_MODLFOTOPITCH); - fluid_clip(voice->modlfo_to_pitch, -12000.0, 12000.0); - break; - - case GEN_MODLFOTOVOL: - voice->modlfo_to_vol = _GEN(voice, GEN_MODLFOTOVOL); - fluid_clip(voice->modlfo_to_vol, -960.0, 960.0); - break; - - case GEN_MODLFOTOFILTERFC: - voice->modlfo_to_fc = _GEN(voice, GEN_MODLFOTOFILTERFC); - fluid_clip(voice->modlfo_to_fc, -12000, 12000); - break; - - case GEN_MODLFODELAY: - x = _GEN(voice, GEN_MODLFODELAY); - fluid_clip(x, -12000.0f, 5000.0f); - voice->modlfo_delay = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x)); - break; - - case GEN_MODLFOFREQ: - /* - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples - * - the delay into a sample delay - */ - x = _GEN(voice, GEN_MODLFOFREQ); - fluid_clip(x, -16000.0f, 4500.0f); - voice->modlfo_incr = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate); - break; - - case GEN_VIBLFOFREQ: - /* vib lfo - * - * - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples - * - the delay into a sample delay - */ - x = _GEN(voice, GEN_VIBLFOFREQ); - fluid_clip(x, -16000.0f, 4500.0f); - voice->viblfo_incr = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate); - break; - - case GEN_VIBLFODELAY: - x = _GEN(voice,GEN_VIBLFODELAY); - fluid_clip(x, -12000.0f, 5000.0f); - voice->viblfo_delay = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x)); - break; - - case GEN_VIBLFOTOPITCH: - voice->viblfo_to_pitch = _GEN(voice, GEN_VIBLFOTOPITCH); - fluid_clip(voice->viblfo_to_pitch, -12000.0, 12000.0); - break; - - case GEN_KEYNUM: - /* GEN_KEYNUM: SF2.01 page 46, item 46 - * - * If this generator is active, it forces the key number to its - * value. Non-realtime controller. - * - * There is a flag, which should indicate, whether a generator is - * enabled or not. But here we rely on the default value of -1. - * */ - x = _GEN(voice, GEN_KEYNUM); - if (x >= 0){ - voice->key = x; - } - break; - - case GEN_VELOCITY: - /* GEN_VELOCITY: SF2.01 page 46, item 47 - * - * If this generator is active, it forces the velocity to its - * value. Non-realtime controller. - * - * There is a flag, which should indicate, whether a generator is - * enabled or not. But here we rely on the default value of -1. */ - x = _GEN(voice, GEN_VELOCITY); - if (x > 0) { - voice->vel = x; - } - break; - - case GEN_MODENVTOPITCH: - voice->modenv_to_pitch = _GEN(voice, GEN_MODENVTOPITCH); - fluid_clip(voice->modenv_to_pitch, -12000.0, 12000.0); - break; - - case GEN_MODENVTOFILTERFC: - voice->modenv_to_fc = _GEN(voice,GEN_MODENVTOFILTERFC); - - /* Range: SF2.01 section 8.1.3 # 1 - * Motivation for range checking: - * Filter is reported to make funny noises now and then - */ - fluid_clip(voice->modenv_to_fc, -12000.0, 12000.0); - break; - - - /* sample start and ends points - * - * Range checking is initiated via the - * voice->check_sample_sanity flag, - * because it is impossible to check here: - * During the voice setup, all modulators are processed, while - * the voice is inactive. Therefore, illegal settings may - * occur during the setup (for example: First move the loop - * end point ahead of the loop start point => invalid, then - * move the loop start point forward => valid again. - */ - case GEN_STARTADDROFS: /* SF2.01 section 8.1.3 # 0 */ - case GEN_STARTADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 4 */ - if (voice->sample != NULL) { - voice->start = (voice->sample->start - + (int) _GEN(voice, GEN_STARTADDROFS) - + 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS)); - voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; - } - break; - case GEN_ENDADDROFS: /* SF2.01 section 8.1.3 # 1 */ - case GEN_ENDADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 12 */ - if (voice->sample != NULL) { - voice->end = (voice->sample->end - + (int) _GEN(voice, GEN_ENDADDROFS) - + 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS)); - voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; - } - break; - case GEN_STARTLOOPADDROFS: /* SF2.01 section 8.1.3 # 2 */ - case GEN_STARTLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 45 */ - if (voice->sample != NULL) { - voice->loopstart = (voice->sample->loopstart - + (int) _GEN(voice, GEN_STARTLOOPADDROFS) - + 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS)); - voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; - } - break; - - case GEN_ENDLOOPADDROFS: /* SF2.01 section 8.1.3 # 3 */ - case GEN_ENDLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 50 */ - if (voice->sample != NULL) { - voice->loopend = (voice->sample->loopend - + (int) _GEN(voice, GEN_ENDLOOPADDROFS) - + 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS)); - voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; - } - break; - - /* Conversion functions differ in range limit */ -#define NUM_BUFFERS_DELAY(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_delay(_v) / FLUID_BUFSIZE) -#define NUM_BUFFERS_ATTACK(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_attack(_v) / FLUID_BUFSIZE) -#define NUM_BUFFERS_RELEASE(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_release(_v) / FLUID_BUFSIZE) - - /* volume envelope - * - * - delay and hold times are converted to absolute number of samples - * - sustain is converted to its absolute value - * - attack, decay and release are converted to their increment per sample - */ - case GEN_VOLENVDELAY: /* SF2.01 section 8.1.3 # 33 */ - x = _GEN(voice, GEN_VOLENVDELAY); - fluid_clip(x, -12000.0f, 5000.0f); - count = NUM_BUFFERS_DELAY(x); - voice->volenv_data[FLUID_VOICE_ENVDELAY].count = count; - voice->volenv_data[FLUID_VOICE_ENVDELAY].coeff = 0.0f; - voice->volenv_data[FLUID_VOICE_ENVDELAY].incr = 0.0f; - voice->volenv_data[FLUID_VOICE_ENVDELAY].min = -1.0f; - voice->volenv_data[FLUID_VOICE_ENVDELAY].max = 1.0f; - break; - - case GEN_VOLENVATTACK: /* SF2.01 section 8.1.3 # 34 */ - x = _GEN(voice, GEN_VOLENVATTACK); - fluid_clip(x, -12000.0f, 8000.0f); - count = 1 + NUM_BUFFERS_ATTACK(x); - voice->volenv_data[FLUID_VOICE_ENVATTACK].count = count; - voice->volenv_data[FLUID_VOICE_ENVATTACK].coeff = 1.0f; - voice->volenv_data[FLUID_VOICE_ENVATTACK].incr = count ? 1.0f / count : 0.0f; - voice->volenv_data[FLUID_VOICE_ENVATTACK].min = -1.0f; - voice->volenv_data[FLUID_VOICE_ENVATTACK].max = 1.0f; - break; - - case GEN_VOLENVHOLD: /* SF2.01 section 8.1.3 # 35 */ - case GEN_KEYTOVOLENVHOLD: /* SF2.01 section 8.1.3 # 39 */ - count = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0); /* 0 means: hold */ - voice->volenv_data[FLUID_VOICE_ENVHOLD].count = count; - voice->volenv_data[FLUID_VOICE_ENVHOLD].coeff = 1.0f; - voice->volenv_data[FLUID_VOICE_ENVHOLD].incr = 0.0f; - voice->volenv_data[FLUID_VOICE_ENVHOLD].min = -1.0f; - voice->volenv_data[FLUID_VOICE_ENVHOLD].max = 2.0f; - break; - - case GEN_VOLENVDECAY: /* SF2.01 section 8.1.3 # 36 */ - case GEN_VOLENVSUSTAIN: /* SF2.01 section 8.1.3 # 37 */ - case GEN_KEYTOVOLENVDECAY: /* SF2.01 section 8.1.3 # 40 */ - y = 1.0f - 0.001f * _GEN(voice, GEN_VOLENVSUSTAIN); - fluid_clip(y, 0.0f, 1.0f); - count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */ - voice->volenv_data[FLUID_VOICE_ENVDECAY].count = count; - voice->volenv_data[FLUID_VOICE_ENVDECAY].coeff = 1.0f; - voice->volenv_data[FLUID_VOICE_ENVDECAY].incr = count ? -1.0f / count : 0.0f; - voice->volenv_data[FLUID_VOICE_ENVDECAY].min = y; - voice->volenv_data[FLUID_VOICE_ENVDECAY].max = 2.0f; - break; - - case GEN_VOLENVRELEASE: /* SF2.01 section 8.1.3 # 38 */ - x = _GEN(voice, GEN_VOLENVRELEASE); - fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f); - count = 1 + NUM_BUFFERS_RELEASE(x); - voice->volenv_data[FLUID_VOICE_ENVRELEASE].count = count; - voice->volenv_data[FLUID_VOICE_ENVRELEASE].coeff = 1.0f; - voice->volenv_data[FLUID_VOICE_ENVRELEASE].incr = count ? -1.0f / count : 0.0f; - voice->volenv_data[FLUID_VOICE_ENVRELEASE].min = 0.0f; - voice->volenv_data[FLUID_VOICE_ENVRELEASE].max = 1.0f; - break; - - /* Modulation envelope */ - case GEN_MODENVDELAY: /* SF2.01 section 8.1.3 # 25 */ - x = _GEN(voice, GEN_MODENVDELAY); - fluid_clip(x, -12000.0f, 5000.0f); - voice->modenv_data[FLUID_VOICE_ENVDELAY].count = NUM_BUFFERS_DELAY(x); - voice->modenv_data[FLUID_VOICE_ENVDELAY].coeff = 0.0f; - voice->modenv_data[FLUID_VOICE_ENVDELAY].incr = 0.0f; - voice->modenv_data[FLUID_VOICE_ENVDELAY].min = -1.0f; - voice->modenv_data[FLUID_VOICE_ENVDELAY].max = 1.0f; - break; - - case GEN_MODENVATTACK: /* SF2.01 section 8.1.3 # 26 */ - x = _GEN(voice, GEN_MODENVATTACK); - fluid_clip(x, -12000.0f, 8000.0f); - count = 1 + NUM_BUFFERS_ATTACK(x); - voice->modenv_data[FLUID_VOICE_ENVATTACK].count = count; - voice->modenv_data[FLUID_VOICE_ENVATTACK].coeff = 1.0f; - voice->modenv_data[FLUID_VOICE_ENVATTACK].incr = count ? 1.0f / count : 0.0f; - voice->modenv_data[FLUID_VOICE_ENVATTACK].min = -1.0f; - voice->modenv_data[FLUID_VOICE_ENVATTACK].max = 1.0f; - break; - - case GEN_MODENVHOLD: /* SF2.01 section 8.1.3 # 27 */ - case GEN_KEYTOMODENVHOLD: /* SF2.01 section 8.1.3 # 31 */ - count = calculate_hold_decay_buffers(voice, GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0); /* 1 means: hold */ - voice->modenv_data[FLUID_VOICE_ENVHOLD].count = count; - voice->modenv_data[FLUID_VOICE_ENVHOLD].coeff = 1.0f; - voice->modenv_data[FLUID_VOICE_ENVHOLD].incr = 0.0f; - voice->modenv_data[FLUID_VOICE_ENVHOLD].min = -1.0f; - voice->modenv_data[FLUID_VOICE_ENVHOLD].max = 2.0f; - break; - - case GEN_MODENVDECAY: /* SF 2.01 section 8.1.3 # 28 */ - case GEN_MODENVSUSTAIN: /* SF 2.01 section 8.1.3 # 29 */ - case GEN_KEYTOMODENVDECAY: /* SF 2.01 section 8.1.3 # 32 */ - count = calculate_hold_decay_buffers(voice, GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1); /* 1 for decay */ - y = 1.0f - 0.001f * _GEN(voice, GEN_MODENVSUSTAIN); - fluid_clip(y, 0.0f, 1.0f); - voice->modenv_data[FLUID_VOICE_ENVDECAY].count = count; - voice->modenv_data[FLUID_VOICE_ENVDECAY].coeff = 1.0f; - voice->modenv_data[FLUID_VOICE_ENVDECAY].incr = count ? -1.0f / count : 0.0f; - voice->modenv_data[FLUID_VOICE_ENVDECAY].min = y; - voice->modenv_data[FLUID_VOICE_ENVDECAY].max = 2.0f; - break; - - case GEN_MODENVRELEASE: /* SF 2.01 section 8.1.3 # 30 */ - x = _GEN(voice, GEN_MODENVRELEASE); - fluid_clip(x, -12000.0f, 8000.0f); - count = 1 + NUM_BUFFERS_RELEASE(x); - voice->modenv_data[FLUID_VOICE_ENVRELEASE].count = count; - voice->modenv_data[FLUID_VOICE_ENVRELEASE].coeff = 1.0f; - voice->modenv_data[FLUID_VOICE_ENVRELEASE].incr = count ? -1.0f / count : 0.0; - voice->modenv_data[FLUID_VOICE_ENVRELEASE].min = 0.0f; - voice->modenv_data[FLUID_VOICE_ENVRELEASE].max = 2.0f; - break; - - } /* switch gen */ -} - -/** - * fluid_voice_modulate - * - * In this implementation, I want to make sure that all controllers - * are event based: the parameter values of the DSP algorithm should - * only be updates when a controller event arrived and not at every - * iteration of the audio cycle (which would probably be feasible if - * the synth was made in silicon). - * - * The update is done in three steps: - * - * - first, we look for all the modulators that have the changed - * controller as a source. This will yield a list of generators that - * will be changed because of the controller event. - * - * - For every changed generator, calculate its new value. This is the - * sum of its original value plus the values of al the attached - * modulators. - * - * - For every changed generator, convert its value to the correct - * unit of the corresponding DSP parameter - * - * @fn int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl, int val) - * @param voice the synthesis voice - * @param cc flag to distinguish between a continous control and a channel control (pitch bend, ...) - * @param ctrl the control number - * */ -int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl) -{ - int i, k; - fluid_mod_t* mod; - int gen; - fluid_real_t modval; - -/* printf("Chan=%d, CC=%d, Src=%d, Val=%d\n", voice->channel->channum, cc, ctrl, val); */ - - for (i = 0; i < voice->mod_count; i++) { - - mod = &voice->mod[i]; - - /* step 1: find all the modulators that have the changed controller - * as input source. */ - if (fluid_mod_has_source(mod, cc, ctrl)) { - - gen = fluid_mod_get_dest(mod); - modval = 0.0; - - /* step 2: for every changed modulator, calculate the modulation - * value of its associated generator */ - for (k = 0; k < voice->mod_count; k++) { - if (fluid_mod_has_dest(&voice->mod[k], gen)) { - modval += fluid_mod_get_value(&voice->mod[k], voice->channel, voice); - } - } - - fluid_gen_set_mod(&voice->gen[gen], modval); - - /* step 3: now that we have the new value of the generator, - * recalculate the parameter values that are derived from the - * generator */ - fluid_voice_update_param(voice, gen); - } - } - return FLUID_OK; -} - -/** - * fluid_voice_modulate_all - * - * Update all the modulators. This function is called after a - * ALL_CTRL_OFF MIDI message has been received (CC 121). - * - */ -int fluid_voice_modulate_all(fluid_voice_t* voice) -{ - fluid_mod_t* mod; - int i, k, gen; - fluid_real_t modval; - - /* Loop through all the modulators. - - FIXME: we should loop through the set of generators instead of - the set of modulators. We risk to call 'fluid_voice_update_param' - several times for the same generator if several modulators have - that generator as destination. It's not an error, just a wast of - energy (think polution, global warming, unhappy musicians, - ...) */ - - for (i = 0; i < voice->mod_count; i++) { - - mod = &voice->mod[i]; - gen = fluid_mod_get_dest(mod); - modval = 0.0; - - /* Accumulate the modulation values of all the modulators with - * destination generator 'gen' */ - for (k = 0; k < voice->mod_count; k++) { - if (fluid_mod_has_dest(&voice->mod[k], gen)) { - modval += fluid_mod_get_value(&voice->mod[k], voice->channel, voice); - } - } - - fluid_gen_set_mod(&voice->gen[gen], modval); - - /* Update the parameter values that are depend on the generator - * 'gen' */ - fluid_voice_update_param(voice, gen); - } - - return FLUID_OK; -} - -/* - * fluid_voice_noteoff - */ -int -fluid_voice_noteoff(fluid_voice_t* voice) -{ - unsigned int at_tick; - - at_tick = fluid_channel_get_min_note_length_ticks (voice->channel); - - if (at_tick > voice->ticks) { - /* Delay noteoff */ - voice->noteoff_ticks = at_tick; - return FLUID_OK; - } - - if (voice->channel && fluid_channel_sustained(voice->channel)) { - voice->status = FLUID_VOICE_SUSTAINED; - } else { - if (voice->volenv_section == FLUID_VOICE_ENVATTACK) { - /* A voice is turned off during the attack section of the volume - * envelope. The attack section ramps up linearly with - * amplitude. The other sections use logarithmic scaling. Calculate new - * volenv_val to achieve equievalent amplitude during the release phase - * for seamless volume transition. - */ - if (voice->volenv_val > 0){ - fluid_real_t lfo = voice->modlfo_val * -voice->modlfo_to_vol; - fluid_real_t amp = voice->volenv_val * pow (10.0, lfo / -200); - fluid_real_t env_value = - ((-200 * log (amp) / log (10.0) - lfo) / 960.0 - 1); - fluid_clip (env_value, 0.0, 1.0); - voice->volenv_val = env_value; - } - } - voice->volenv_section = FLUID_VOICE_ENVRELEASE; - voice->volenv_count = 0; - voice->modenv_section = FLUID_VOICE_ENVRELEASE; - voice->modenv_count = 0; - } - - return FLUID_OK; -} - -/* - * fluid_voice_kill_excl - * - * Percussion sounds can be mutually exclusive: for example, a 'closed - * hihat' sound will terminate an 'open hihat' sound ringing at the - * same time. This behaviour is modeled using 'exclusive classes', - * turning on a voice with an exclusive class other than 0 will kill - * all other voices having that exclusive class within the same preset - * or channel. fluid_voice_kill_excl gets called, when 'voice' is to - * be killed for that reason. - */ -int -fluid_voice_kill_excl(fluid_voice_t* voice){ - - if (!_PLAYING(voice)) { - return FLUID_OK; - } - - /* Turn off the exclusive class information for this voice, - so that it doesn't get killed twice - */ - fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0); - - /* If the voice is not yet in release state, put it into release state */ - if (voice->volenv_section != FLUID_VOICE_ENVRELEASE){ - voice->volenv_section = FLUID_VOICE_ENVRELEASE; - voice->volenv_count = 0; - voice->modenv_section = FLUID_VOICE_ENVRELEASE; - voice->modenv_count = 0; - } - - /* Speed up the volume envelope */ - /* The value was found through listening tests with hi-hat samples. */ - fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, -200); - fluid_voice_update_param(voice, GEN_VOLENVRELEASE); - - /* Speed up the modulation envelope */ - fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200); - fluid_voice_update_param(voice, GEN_MODENVRELEASE); - - return FLUID_OK; -} - -/* - * fluid_voice_off - * - * Purpose: - * Turns off a voice, meaning that it is not processed - * anymore by the DSP loop. - */ -int -fluid_voice_off(fluid_voice_t* voice) -{ - voice->chan = NO_CHANNEL; - voice->volenv_section = FLUID_VOICE_ENVFINISHED; - voice->volenv_count = 0; - voice->modenv_section = FLUID_VOICE_ENVFINISHED; - voice->modenv_count = 0; - voice->status = FLUID_VOICE_OFF; - - /* Decrement the reference count of the sample. */ - if (voice->sample) { - fluid_sample_decr_ref(voice->sample); - voice->sample = NULL; - } - - return FLUID_OK; -} - -/* - * fluid_voice_add_mod - * - * Adds a modulator to the voice. "mode" indicates, what to do, if - * an identical modulator exists already. - * - * mode == FLUID_VOICE_ADD: Identical modulators on preset level are added - * mode == FLUID_VOICE_OVERWRITE: Identical modulators on instrument level are overwritten - * mode == FLUID_VOICE_DEFAULT: This is a default modulator, there can be no identical modulator. - * Don't check. - */ -void -fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode) -{ - int i; - - /* - * Some soundfonts come with a huge number of non-standard - * controllers, because they have been designed for one particular - * sound card. Discard them, maybe print a warning. - */ - - if (((mod->flags1 & FLUID_MOD_CC) == 0) - && ((mod->src1 != 0) /* SF2.01 section 8.2.1: Constant value */ - && (mod->src1 != 2) /* Note-on velocity */ - && (mod->src1 != 3) /* Note-on key number */ - && (mod->src1 != 10) /* Poly pressure */ - && (mod->src1 != 13) /* Channel pressure */ - && (mod->src1 != 14) /* Pitch wheel */ - && (mod->src1 != 16))) { /* Pitch wheel sensitivity */ - FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1); - return; - } - - if (mode == FLUID_VOICE_ADD) { - - /* if identical modulator exists, add them */ - for (i = 0; i < voice->mod_count; i++) { - if (fluid_mod_test_identity(&voice->mod[i], mod)) { - // printf("Adding modulator...\n"); - voice->mod[i].amount += mod->amount; - return; - } - } - - } else if (mode == FLUID_VOICE_OVERWRITE) { - - /* if identical modulator exists, replace it (only the amount has to be changed) */ - for (i = 0; i < voice->mod_count; i++) { - if (fluid_mod_test_identity(&voice->mod[i], mod)) { - // printf("Replacing modulator...amount is %f\n",mod->amount); - voice->mod[i].amount = mod->amount; - return; - } - } - } - - /* Add a new modulator (No existing modulator to add / overwrite). - Also, default modulators (FLUID_VOICE_DEFAULT) are added without - checking, if the same modulator already exists. */ - if (voice->mod_count < FLUID_NUM_MOD) { - fluid_mod_clone(&voice->mod[voice->mod_count++], mod); - } -} - -unsigned int fluid_voice_get_id(fluid_voice_t* voice) -{ - return voice->id; -} - -int fluid_voice_is_playing(fluid_voice_t* voice) -{ - return _PLAYING(voice); -} - -/* - * fluid_voice_get_lower_boundary_for_attenuation - * - * Purpose: - * - * A lower boundary for the attenuation (as in 'the minimum - * attenuation of this voice, with volume pedals, modulators - * etc. resulting in minimum attenuation, cannot fall below x cB) is - * calculated. This has to be called during fluid_voice_init, after - * all modulators have been run on the voice once. Also, - * voice->attenuation has to be initialized. - */ -fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice) -{ - int i; - fluid_mod_t* mod; - fluid_real_t possible_att_reduction_cB=0; - fluid_real_t lower_bound; - - for (i = 0; i < voice->mod_count; i++) { - mod = &voice->mod[i]; - - /* Modulator has attenuation as target and can change over time? */ - if ((mod->dest == GEN_ATTENUATION) - && ((mod->flags1 & FLUID_MOD_CC) || (mod->flags2 & FLUID_MOD_CC))) { - - fluid_real_t current_val = fluid_mod_get_value(mod, voice->channel, voice); - fluid_real_t v = fabs(mod->amount); - - if ((mod->src1 == FLUID_MOD_PITCHWHEEL) - || (mod->flags1 & FLUID_MOD_BIPOLAR) - || (mod->flags2 & FLUID_MOD_BIPOLAR) - || (mod->amount < 0)) { - /* Can this modulator produce a negative contribution? */ - v *= -1.0; - } else { - /* No negative value possible. But still, the minimum contribution is 0. */ - v = 0; - } - - /* For example: - * - current_val=100 - * - min_val=-4000 - * - possible_att_reduction_cB += 4100 - */ - if (current_val > v){ - possible_att_reduction_cB += (current_val - v); - } - } - } - - lower_bound = voice->attenuation-possible_att_reduction_cB; - - /* SF2.01 specs do not allow negative attenuation */ - if (lower_bound < 0) { - lower_bound = 0; - } - return lower_bound; -} - - -/* Purpose: - * - * Make sure, that sample start / end point and loop points are in - * proper order. When starting up, calculate the initial phase. - */ -void fluid_voice_check_sample_sanity(fluid_voice_t* voice) -{ - int min_index_nonloop=(int) voice->sample->start; - int max_index_nonloop=(int) voice->sample->end; - - /* make sure we have enough samples surrounding the loop */ - int min_index_loop=(int) voice->sample->start + FLUID_MIN_LOOP_PAD; - int max_index_loop=(int) voice->sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */ - - if (!voice->check_sample_sanity_flag){ - return; - } - -#if 0 - printf("Sample from %i to %i\n",voice->sample->start, voice->sample->end); - printf("Sample loop from %i %i\n",voice->sample->loopstart, voice->sample->loopend); - printf("Playback from %i to %i\n", voice->start, voice->end); - printf("Playback loop from %i to %i\n",voice->loopstart, voice->loopend); -#endif - - /* Keep the start point within the sample data */ - if (voice->start < min_index_nonloop){ - voice->start = min_index_nonloop; - } else if (voice->start > max_index_nonloop){ - voice->start = max_index_nonloop; - } - - /* Keep the end point within the sample data */ - if (voice->end < min_index_nonloop){ - voice->end = min_index_nonloop; - } else if (voice->end > max_index_nonloop){ - voice->end = max_index_nonloop; - } - - /* Keep start and end point in the right order */ - if (voice->start > voice->end){ - int temp = voice->start; - voice->start = voice->end; - voice->end = temp; - /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */ - } - - /* Zero length? */ - if (voice->start == voice->end){ - fluid_voice_off(voice); - return; - } - - if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) - || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) { - /* Keep the loop start point within the sample data */ - if (voice->loopstart < min_index_loop){ - voice->loopstart = min_index_loop; - } else if (voice->loopstart > max_index_loop){ - voice->loopstart = max_index_loop; - } - - /* Keep the loop end point within the sample data */ - if (voice->loopend < min_index_loop){ - voice->loopend = min_index_loop; - } else if (voice->loopend > max_index_loop){ - voice->loopend = max_index_loop; - } - - /* Keep loop start and end point in the right order */ - if (voice->loopstart > voice->loopend){ - int temp = voice->loopstart; - voice->loopstart = voice->loopend; - voice->loopend = temp; - /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */ - } - - /* Loop too short? Then don't loop. */ - if (voice->loopend < voice->loopstart + FLUID_MIN_LOOP_SIZE){ - voice->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED; - } - - /* The loop points may have changed. Obtain a new estimate for the loop volume. */ - /* Is the voice loop within the sample loop? */ - if ((int)voice->loopstart >= (int)voice->sample->loopstart - && (int)voice->loopend <= (int)voice->sample->loopend){ - /* Is there a valid peak amplitude available for the loop? */ - if (voice->sample->amplitude_that_reaches_noise_floor_is_valid){ - voice->amplitude_that_reaches_noise_floor_loop=voice->sample->amplitude_that_reaches_noise_floor / voice->synth_gain; - } else { - /* Worst case */ - voice->amplitude_that_reaches_noise_floor_loop=voice->amplitude_that_reaches_noise_floor_nonloop; - }; - }; - - } /* if sample mode is looped */ - - /* Run startup specific code (only once, when the voice is started) */ - if (voice->check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){ - if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){ - if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) - || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)){ - voice->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED; - } - } - - /* Set the initial phase of the voice (using the result from the - start offset modulators). */ - fluid_phase_set_int(voice->phase, voice->start); - } /* if startup */ - - /* Is this voice run in loop mode, or does it run straight to the - end of the waveform data? */ - if (((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) && (voice->volenv_section < FLUID_VOICE_ENVRELEASE)) - || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) { - /* Yes, it will loop as soon as it reaches the loop point. In - * this case we must prevent, that the playback pointer (phase) - * happens to end up beyond the 2nd loop point, because the - * point has moved. The DSP algorithm is unable to cope with - * that situation. So if the phase is beyond the 2nd loop - * point, set it to the start of the loop. No way to avoid some - * noise here. Note: If the sample pointer ends up -before the - * first loop point- instead, then the DSP loop will just play - * the sample, enter the loop and proceed as expected => no - * actions required. - */ - int index_in_sample = fluid_phase_index(voice->phase); - if (index_in_sample >= voice->loopend){ - /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */ - fluid_phase_set_int(voice->phase, voice->loopstart); - } - } -/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->start, voice->end, voice->loopstart, voice->loopend); */ - - /* Sample sanity has been assured. Don't check again, until some - sample parameter is changed by modulation. */ - voice->check_sample_sanity_flag=0; -#if 0 - printf("Sane? playback loop from %i to %i\n", voice->loopstart, voice->loopend); -#endif -} - - -int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t nrpn_value, int abs) -{ - voice->gen[gen].nrpn = nrpn_value; - voice->gen[gen].flags = (abs)? GEN_ABS_NRPN : GEN_SET; - fluid_voice_update_param(voice, gen); - return FLUID_OK; -} - -int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain) -{ - /* avoid division by zero*/ - if (gain < 0.0000001){ - gain = 0.0000001; - } - - voice->synth_gain = gain; - voice->amp_left = fluid_pan(voice->pan, 1) * gain / 32768.0f; - voice->amp_right = fluid_pan(voice->pan, 0) * gain / 32768.0f; - voice->amp_reverb = voice->reverb_send * gain / 32768.0f; - voice->amp_chorus = voice->chorus_send * gain / 32768.0f; - - return FLUID_OK; -} - -/* - Scan the loop - * - determine the peak level - * - Calculate, what factor will make the loop inaudible - * - Store in sample - */ -int fluid_voice_optimize_sample(fluid_sample_t* s) -{ - signed short peak_max = 0; - signed short peak_min = 0; - signed short peak; - fluid_real_t normalized_amplitude_during_loop; - double result; - int i; - - /* ignore ROM and other(?) invalid samples */ - if (!s->valid || (s->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) - return (FLUID_OK); - - if (!s->amplitude_that_reaches_noise_floor_is_valid){ /* Only once */ - /* Scan the loop */ - for (i = (int)s->loopstart; i < (int) s->loopend; i ++){ - signed short val = s->data[i]; - if (val > peak_max) { - peak_max = val; - } else if (val < peak_min) { - peak_min = val; - } - } - - /* Determine the peak level */ - if (peak_max > -peak_min){ - peak = peak_max; - } else { - peak = -peak_min; - }; - if (peak == 0){ - /* Avoid division by zero */ - peak = 1; - }; - - /* Calculate what factor will make the loop inaudible - * For example: Take a peak of 3277 (10 % of 32768). The - * normalized amplitude is 0.1 (10 % of 32768). An amplitude - * factor of 0.0001 (as opposed to the default 0.00001) will - * drop this sample to the noise floor. - */ - - /* 16 bits => 96+4=100 dB dynamic range => 0.00001 */ - normalized_amplitude_during_loop = ((fluid_real_t)peak)/32768.; - result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop; - - /* Store in sample */ - s->amplitude_that_reaches_noise_floor = (double)result; - s->amplitude_that_reaches_noise_floor_is_valid = 1; -#if 0 - printf("Sample peak detection: factor %f\n", (double)result); -#endif - }; - return FLUID_OK; -} diff --git a/libraries/fluidlite/src/fluid_voice.h b/libraries/fluidlite/src/fluid_voice.h deleted file mode 100644 index af6db899d..000000000 --- a/libraries/fluidlite/src/fluid_voice.h +++ /dev/null @@ -1,291 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#ifndef _FLUID_VOICE_H -#define _FLUID_VOICE_H - -#include "fluid_phase.h" -#include "fluid_gen.h" -#include "fluid_mod.h" - -#define NO_CHANNEL 0xff - -enum fluid_voice_status -{ - FLUID_VOICE_CLEAN, - FLUID_VOICE_ON, - FLUID_VOICE_SUSTAINED, - FLUID_VOICE_OFF -}; - - -/* - * envelope data - */ -struct _fluid_env_data_t { - unsigned int count; - fluid_real_t coeff; - fluid_real_t incr; - fluid_real_t min; - fluid_real_t max; -}; - -/* Indices for envelope tables */ -enum fluid_voice_envelope_index_t{ - FLUID_VOICE_ENVDELAY, - FLUID_VOICE_ENVATTACK, - FLUID_VOICE_ENVHOLD, - FLUID_VOICE_ENVDECAY, - FLUID_VOICE_ENVSUSTAIN, - FLUID_VOICE_ENVRELEASE, - FLUID_VOICE_ENVFINISHED, - FLUID_VOICE_ENVLAST -}; - -/* - * fluid_voice_t - */ -struct _fluid_voice_t -{ - unsigned int id; /* the id is incremented for every new noteon. - it's used for noteoff's */ - unsigned char status; - unsigned char chan; /* the channel number, quick access for channel messages */ - unsigned char key; /* the key, quick acces for noteoff */ - unsigned char vel; /* the velocity */ - fluid_channel_t* channel; - fluid_gen_t gen[GEN_LAST]; - fluid_mod_t mod[FLUID_NUM_MOD]; - int mod_count; - int has_looped; /* Flag that is set as soon as the first loop is completed. */ - fluid_sample_t* sample; - int check_sample_sanity_flag; /* Flag that initiates, that sample-related parameters - have to be checked. */ -#if 0 - /* Instead of keeping a pointer to a fluid_sample_t structure, - * I think it would be better to copy the sample data in the - * voice structure. SoundFont loader then do not have to - * allocate and maintain the fluid_sample_t structure. [PH] - * - * The notify callback may be used also for streaming samples. - */ - short* sample_data; /* pointer to the sample data */ - int sample_data_offset; /* the offset of data[0] in the whole sample */ - int sample_data_length; /* the length of the data array */ - unsigned int sample_start; - unsigned int sample_end; - unsigned int sample_loopstart; - unsigned int sample_loopend; - unsigned int sample_rate; - int sample_origpitch; - int sample_pitchadj; - int sample_type; - int (*sample_notify)(fluid_voice_t* voice, int reason); - void* sample_userdata; -#endif - - /* basic parameters */ - fluid_real_t output_rate; /* the sample rate of the synthesizer */ - - unsigned int start_time; - unsigned int ticks; - unsigned int noteoff_ticks; /* Delay note-off until this tick */ - - fluid_real_t amp; /* current linear amplitude */ - fluid_phase_t phase; /* the phase of the sample wave */ - - /* Temporary variables used in fluid_voice_write() */ - - fluid_real_t phase_incr; /* the phase increment for the next 64 samples */ - fluid_real_t amp_incr; /* amplitude increment value */ - fluid_real_t *dsp_buf; /* buffer to store interpolated sample data to */ - - /* End temporary variables */ - - /* basic parameters */ - fluid_real_t pitch; /* the pitch in midicents */ - fluid_real_t attenuation; /* the attenuation in centibels */ - fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation - * during the lifetime of the voice */ - fluid_real_t root_pitch; - - /* sample and loop start and end points (offset in sample memory). */ - int start; - int end; - int loopstart; - int loopend; /* Note: first point following the loop (superimposed on loopstart) */ - - /* master gain */ - fluid_real_t synth_gain; - - /* vol env */ - fluid_env_data_t volenv_data[FLUID_VOICE_ENVLAST]; - unsigned int volenv_count; - int volenv_section; - fluid_real_t volenv_val; - fluid_real_t amplitude_that_reaches_noise_floor_nonloop; - fluid_real_t amplitude_that_reaches_noise_floor_loop; - - /* mod env */ - fluid_env_data_t modenv_data[FLUID_VOICE_ENVLAST]; - unsigned int modenv_count; - int modenv_section; - fluid_real_t modenv_val; /* the value of the modulation envelope */ - fluid_real_t modenv_to_fc; - fluid_real_t modenv_to_pitch; - - /* mod lfo */ - fluid_real_t modlfo_val; /* the value of the modulation LFO */ - unsigned int modlfo_delay; /* the delay of the lfo in samples */ - fluid_real_t modlfo_incr; /* the lfo frequency is converted to a per-buffer increment */ - fluid_real_t modlfo_to_fc; - fluid_real_t modlfo_to_pitch; - fluid_real_t modlfo_to_vol; - - /* vib lfo */ - fluid_real_t viblfo_val; /* the value of the vibrato LFO */ - unsigned int viblfo_delay; /* the delay of the lfo in samples */ - fluid_real_t viblfo_incr; /* the lfo frequency is converted to a per-buffer increment */ - fluid_real_t viblfo_to_pitch; - - /* resonant filter */ - fluid_real_t fres; /* the resonance frequency, in cents (not absolute cents) */ - fluid_real_t last_fres; /* Current resonance frequency of the IIR filter */ - /* Serves as a flag: A deviation between fres and last_fres */ - /* indicates, that the filter has to be recalculated. */ - fluid_real_t q_lin; /* the q-factor on a linear scale */ - fluid_real_t filter_gain; /* Gain correction factor, depends on q */ - fluid_real_t hist1, hist2; /* Sample history for the IIR filter */ - int filter_startup; /* Flag: If set, the filter will be set directly. - Else it changes smoothly. */ - - /* filter coefficients */ - /* The coefficients are normalized to a0. */ - /* b0 and b2 are identical => b02 */ - fluid_real_t b02; /* b0 / a0 */ - fluid_real_t b1; /* b1 / a0 */ - fluid_real_t a1; /* a0 / a0 */ - fluid_real_t a2; /* a1 / a0 */ - - fluid_real_t b02_incr; - fluid_real_t b1_incr; - fluid_real_t a1_incr; - fluid_real_t a2_incr; - int filter_coeff_incr_count; - - /* pan */ - fluid_real_t pan; - fluid_real_t amp_left; - fluid_real_t amp_right; - - /* reverb */ - fluid_real_t reverb_send; - fluid_real_t amp_reverb; - - /* chorus */ - fluid_real_t chorus_send; - fluid_real_t amp_chorus; - - /* interpolation method, as in fluid_interp in fluidlite.h */ - int interp_method; - - /* for debugging */ - int debug; -}; - - -fluid_voice_t* new_fluid_voice(fluid_real_t output_rate); -int delete_fluid_voice(fluid_voice_t* voice); - -void fluid_voice_start(fluid_voice_t* voice); - -int fluid_voice_write(fluid_voice_t* voice, - fluid_real_t* left, fluid_real_t* right, - fluid_real_t* reverb_buf, fluid_real_t* chorus_buf); - -int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, - fluid_channel_t* channel, int key, int vel, - unsigned int id, unsigned int time, fluid_real_t gain); - -int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl); -int fluid_voice_modulate_all(fluid_voice_t* voice); - -/** Set the NRPN value of a generator. */ -int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t value, int abs); - - -/** Set the gain. */ -int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain); - - -/** Update all the synthesis parameters, which depend on generator - 'gen'. This is only necessary after changing a generator of an - already operating voice. Most applications will not need this - function.*/ -void fluid_voice_update_param(fluid_voice_t* voice, int gen); - -int fluid_voice_noteoff(fluid_voice_t* voice); -int fluid_voice_off(fluid_voice_t* voice); -int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice); -fluid_channel_t* fluid_voice_get_channel(fluid_voice_t* voice); -int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, - int gen_key2base, int is_decay); -int fluid_voice_kill_excl(fluid_voice_t* voice); -fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice); -fluid_real_t fluid_voice_determine_amplitude_that_reaches_noise_floor_for_sample(fluid_voice_t* voice); -void fluid_voice_check_sample_sanity(fluid_voice_t* voice); - -#define fluid_voice_set_id(_voice, _id) { (_voice)->id = (_id); } -#define fluid_voice_get_chan(_voice) (_voice)->chan - - -#define _PLAYING(voice) (((voice)->status == FLUID_VOICE_ON) || ((voice)->status == FLUID_VOICE_SUSTAINED)) - -/* A voice is 'ON', if it has not yet received a noteoff - * event. Sending a noteoff event will advance the envelopes to - * section 5 (release). */ -#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && (voice)->volenv_section < FLUID_VOICE_ENVRELEASE) -#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED) -#define _AVAILABLE(voice) (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF)) -#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL) -#define _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val) - - -fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num); - -#define _GEN(_voice, _n) \ - ((fluid_real_t)(_voice)->gen[_n].val \ - + (fluid_real_t)(_voice)->gen[_n].mod \ - + (fluid_real_t)(_voice)->gen[_n].nrpn) - -#define FLUID_SAMPLESANITY_CHECK (1 << 0) -#define FLUID_SAMPLESANITY_STARTUP (1 << 1) - - -/* defined in fluid_dsp_float.c */ - -void fluid_dsp_float_config (void); -int fluid_dsp_float_interpolate_none (fluid_voice_t *voice); -int fluid_dsp_float_interpolate_linear (fluid_voice_t *voice); -int fluid_dsp_float_interpolate_4th_order (fluid_voice_t *voice); -int fluid_dsp_float_interpolate_7th_order (fluid_voice_t *voice); - -#endif /* _FLUID_VOICE_H */ diff --git a/libraries/fluidlite/src/fluidsynth_priv.h b/libraries/fluidlite/src/fluidsynth_priv.h deleted file mode 100644 index 2daa296d6..000000000 --- a/libraries/fluidlite/src/fluidsynth_priv.h +++ /dev/null @@ -1,228 +0,0 @@ -/* FluidSynth - A Software Synthesizer - * - * Copyright (C) 2003 Peter Hanappe and others. - * - * This library 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 3 of - * the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * License along with this library; if not, write to the Free - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - - -#ifndef _FLUIDLITE_PRIV_H -#define _FLUIDLITE_PRIV_H - -#include "fluid_config.h" - -#if HAVE_STRING_H -#include -#endif - -#if HAVE_STDLIB_H -#include -#endif - -#if HAVE_STDIO_H -#include -#endif - -#if HAVE_MATH_H -#include -#endif - -#if HAVE_STDARG_H -#include -#endif - -#if HAVE_FCNTL_H -#include -#endif - -#if HAVE_LIMITS_H -#include -#endif - - -#include "fluidlite.h" - - -/*************************************************************** - * - * BASIC TYPES - */ - -#if defined(WITH_FLOAT) -typedef float fluid_real_t; -#else -typedef double fluid_real_t; -#endif - - -typedef enum { - FLUID_OK = 0, - FLUID_FAILED = -1 -} fluid_status; - - -//socket disabled - -/** Integer types */ - -#if 1 /**/ -typedef signed char sint8; -typedef unsigned char uint8; -typedef signed short sint16; -typedef unsigned short uint16; -typedef signed int sint32; -typedef unsigned int uint32; -typedef signed long long sint64; -typedef unsigned long long uint64; - -#if defined(__LP64__) || defined(_WIN64) -typedef uint64 uintptr; -#else -typedef uint32 uintptr; -#endif - -#else /**/ -#if defined(MINGW32) - -/* Windows using MinGW32 */ -typedef int8_t sint8; -typedef uint8_t uint8; -typedef int16_t sint16; -typedef uint16_t uint16; -typedef int32_t sint32; -typedef uint32_t uint32; -typedef int64_t sint64; -typedef uint64_t uint64; - -#elif defined(_WIN32) - -/* Windows */ -typedef signed __int8 sint8; -typedef unsigned __int8 uint8; -typedef signed __int16 sint16; -typedef unsigned __int16 uint16; -typedef signed __int32 sint32; -typedef unsigned __int32 uint32; -typedef signed __int64 sint64; -typedef unsigned __int64 uint64; - -#elif defined(MACOS9) - -/* Macintosh */ -typedef signed char sint8; -typedef unsigned char uint8; -typedef signed short sint16; -typedef unsigned short uint16; -typedef signed int sint32; -typedef unsigned int uint32; -/* FIXME: needs to be verified */ -typedef long long sint64; -typedef unsigned long long uint64; - -#else - -/* Linux & Darwin */ -typedef int8_t sint8; -typedef u_int8_t uint8; -typedef int16_t sint16; -typedef u_int16_t uint16; -typedef int32_t sint32; -typedef u_int32_t uint32; -typedef int64_t sint64; -typedef u_int64_t uint64; - -#endif -#endif /* */ - - -/*************************************************************** - * - * FORWARD DECLARATIONS - */ -typedef struct _fluid_env_data_t fluid_env_data_t; -typedef struct _fluid_adriver_definition_t fluid_adriver_definition_t; -typedef struct _fluid_channel_t fluid_channel_t; -typedef struct _fluid_tuning_t fluid_tuning_t; -typedef struct _fluid_hashtable_t fluid_hashtable_t; -typedef struct _fluid_client_t fluid_client_t; - -/*************************************************************** - * - * CONSTANTS - */ - -#define FLUID_BUFSIZE 64 - -#ifndef PI -#define PI 3.141592654 -#endif - -/*************************************************************** - * - * SYSTEM INTERFACE - */ -typedef FILE* fluid_file; - -#define FLUID_MALLOC(_n) malloc(_n) -#define FLUID_REALLOC(_p,_n) realloc(_p,_n) -#define FLUID_NEW(_t) (_t*)malloc(sizeof(_t)) -#define FLUID_ARRAY(_t,_n) (_t*)malloc((_n)*sizeof(_t)) -#define FLUID_FREE(_p) free(_p) -#define FLUID_FOPEN(_f,_m) fopen(_f,_m) -#define FLUID_FCLOSE(_f) fclose(_f) -#define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f) -#define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set) -#define FLUID_FTELL(_f) ftell(_f) -#define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n) -#define FLUID_MEMSET(_s,_c,_n) memset(_s,_c,_n) -#define FLUID_STRLEN(_s) strlen(_s) -#define FLUID_STRCMP(_s,_t) strcmp(_s,_t) -#define FLUID_STRNCMP(_s,_t,_n) strncmp(_s,_t,_n) -#define FLUID_STRCPY(_dst,_src) strcpy(_dst,_src) -#define FLUID_STRCHR(_s,_c) strchr(_s,_c) -#define FLUID_STRDUP(s) FLUID_STRCPY((char*)FLUID_MALLOC(FLUID_STRLEN(s) + 1), s) -#define FLUID_SPRINTF sprintf -#define FLUID_FPRINTF fprintf - -#define fluid_clip(_val, _min, _max) \ -{ (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); } - -#if WITH_FTS -#define FLUID_PRINTF post -#define FLUID_FLUSH() -#else -#define FLUID_PRINTF printf -#define FLUID_FLUSH() fflush(stdout) -#endif - -#define FLUID_LOG fluid_log - -#ifndef M_PI -#define M_PI 3.1415926535897932384626433832795 -#endif - - -#define FLUID_ASSERT(a,b) -#define FLUID_ASSERT_P(a,b) - -char* fluid_error(void); - - -/* Internationalization */ -#define _(s) s - - -#endif /* _FLUIDLITE_PRIV_H */ diff --git a/libraries/tsf/CMakeLists.txt b/libraries/tsf/CMakeLists.txt new file mode 100644 index 000000000..d3a24fc6d --- /dev/null +++ b/libraries/tsf/CMakeLists.txt @@ -0,0 +1,7 @@ +########################################## +# TinySoundFont +########################################## + +add_library(tsf tsf.cc) + +target_include_directories(tsf PUBLIC ./) \ No newline at end of file diff --git a/libraries/tsf/tsf.cc b/libraries/tsf/tsf.cc new file mode 100644 index 000000000..adcf12990 --- /dev/null +++ b/libraries/tsf/tsf.cc @@ -0,0 +1,3 @@ +#define TSF_NO_STDIO +#define TSF_IMPLEMENTATION +#include "tsf.h" \ No newline at end of file diff --git a/libraries/tsf/tsf.h b/libraries/tsf/tsf.h new file mode 100644 index 000000000..9b439e22c --- /dev/null +++ b/libraries/tsf/tsf.h @@ -0,0 +1,2073 @@ +/* TinySoundFont - v0.9 - SoundFont2 synthesizer - https://github.com/schellingb/TinySoundFont + no warranty implied; use at your own risk + Do this: + #define TSF_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + // i.e. it should look like this: + #include ... + #include ... + #define TSF_IMPLEMENTATION + #include "tsf.h" + + [OPTIONAL] #define TSF_NO_STDIO to remove stdio dependency + [OPTIONAL] #define TSF_MALLOC, TSF_REALLOC, and TSF_FREE to avoid stdlib.h + [OPTIONAL] #define TSF_MEMCPY, TSF_MEMSET to avoid string.h + [OPTIONAL] #define TSF_POW, TSF_POWF, TSF_EXPF, TSF_LOG, TSF_TAN, TSF_LOG10, TSF_SQRTF to avoid math.h + + NOT YET IMPLEMENTED + - Support for ChorusEffectsSend and ReverbEffectsSend generators + - Better low-pass filter without lowering performance too much + - Support for modulators + + LICENSE (MIT) + + Copyright (C) 2017-2023 Bernhard Schelling + Based on SFZero, Copyright (C) 2012 Steve Folta (https://github.com/stevefolta/SFZero) + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef TSF_INCLUDE_TSF_INL +#define TSF_INCLUDE_TSF_INL + +#ifdef __cplusplus +extern "C" { +# define CPP_DEFAULT0 = 0 +#else +# define CPP_DEFAULT0 +#endif + +//define this if you want the API functions to be static +#ifdef TSF_STATIC +#define TSFDEF static +#else +#define TSFDEF extern +#endif + +// The load functions will return a pointer to a struct tsf which all functions +// thereafter take as the first parameter. +// On error the tsf_load* functions will return NULL most likely due to invalid +// data (or if the file did not exist in tsf_load_filename). +typedef struct tsf tsf; + +#ifndef TSF_NO_STDIO +// Directly load a SoundFont from a .sf2 file path +TSFDEF tsf* tsf_load_filename(const char* filename); +#endif + +// Load a SoundFont from a block of memory +TSFDEF tsf* tsf_load_memory(const void* buffer, int size); + +// Stream structure for the generic loading +struct tsf_stream +{ + // Custom data given to the functions as the first parameter + void* data; + + // Function pointer will be called to read 'size' bytes into ptr (returns number of read bytes) + int (*read)(void* data, void* ptr, unsigned int size); + + // Function pointer will be called to skip ahead over 'count' bytes (returns 1 on success, 0 on error) + int (*skip)(void* data, unsigned int count); +}; + +// Generic SoundFont loading method using the stream structure above +TSFDEF tsf* tsf_load(struct tsf_stream* stream); + +// Copy a tsf instance from an existing one, use tsf_close to close it as well. +// All copied tsf instances and their original instance are linked, and share the underlying soundfont. +// This allows loading a soundfont only once, but using it for multiple independent playbacks. +// (This function isn't thread-safe without locking.) +TSFDEF tsf* tsf_copy(tsf* f); + +// Free the memory related to this tsf instance +TSFDEF void tsf_close(tsf* f); + +// Stop all playing notes immediately and reset all channel parameters +TSFDEF void tsf_reset(tsf* f); + +// Returns the preset index from a bank and preset number, or -1 if it does not exist in the loaded SoundFont +TSFDEF int tsf_get_presetindex(const tsf* f, int bank, int preset_number); + +// Returns the number of presets in the loaded SoundFont +TSFDEF int tsf_get_presetcount(const tsf* f); + +// Returns the name of a preset index >= 0 and < tsf_get_presetcount() +TSFDEF const char* tsf_get_presetname(const tsf* f, int preset_index); + +// Returns the name of a preset by bank and preset number +TSFDEF const char* tsf_bank_get_presetname(const tsf* f, int bank, int preset_number); + +// Supported output modes by the render methods +enum TSFOutputMode +{ + // Two channels with single left/right samples one after another + TSF_STEREO_INTERLEAVED, + // Two channels with all samples for the left channel first then right + TSF_STEREO_UNWEAVED, + // A single channel (stereo instruments are mixed into center) + TSF_MONO +}; + +// Thread safety: +// +// 1. Rendering / voices: +// +// Your audio output which calls the tsf_render* functions will most likely +// run on a different thread than where the playback tsf_note* functions +// are called. In which case some sort of concurrency control like a +// mutex needs to be used so they are not called at the same time. +// Alternatively, you can pre-allocate a maximum number of voices that can +// play simultaneously by calling tsf_set_max_voices after loading. +// That way memory re-allocation will not happen during tsf_note_on and +// TSF should become mostly thread safe. +// There is a theoretical chance that ending notes would negatively influence +// a voice that is rendering at the time but it is hard to say. +// Also be aware, this has not been tested much. +// +// 2. Channels: +// +// Calls to tsf_channel_set_... functions may allocate new channels +// if no channel with that number was previously used. Make sure to +// create all channels at the beginning as required if you call tsf_render* +// from a different thread. + +// Setup the parameters for the voice render methods +// outputmode: if mono or stereo and how stereo channel data is ordered +// samplerate: the number of samples per second (output frequency) +// global_gain_db: volume gain in decibels (>0 means higher, <0 means lower) +TSFDEF void tsf_set_output(tsf* f, enum TSFOutputMode outputmode, int samplerate, float global_gain_db CPP_DEFAULT0); + +// Set the global gain as a volume factor +// global_gain: the desired volume where 1.0 is 100% +TSFDEF void tsf_set_volume(tsf* f, float global_gain); + +// Set the maximum number of voices to play simultaneously +// Depending on the soundfond, one note can cause many new voices to be started, +// so don't keep this number too low or otherwise sounds may not play. +// max_voices: maximum number to pre-allocate and set the limit to +// (tsf_set_max_voices returns 0 if allocation failed, otherwise 1) +TSFDEF int tsf_set_max_voices(tsf* f, int max_voices); + +// Start playing a note +// preset_index: preset index >= 0 and < tsf_get_presetcount() +// key: note value between 0 and 127 (60 being middle C) +// vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full) +// bank: instrument bank number (alternative to preset_index) +// preset_number: preset number (alternative to preset_index) +// (tsf_note_on returns 0 if the allocation of a new voice failed, otherwise 1) +// (tsf_bank_note_on returns 0 if preset does not exist or allocation failed, otherwise 1) +TSFDEF int tsf_note_on(tsf* f, int preset_index, int key, float vel); +TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel); + +// Stop playing a note +// (bank_note_off returns 0 if preset does not exist, otherwise 1) +TSFDEF void tsf_note_off(tsf* f, int preset_index, int key); +TSFDEF int tsf_bank_note_off(tsf* f, int bank, int preset_number, int key); + +// Stop playing all notes (end with sustain and release) +TSFDEF void tsf_note_off_all(tsf* f); + +// Returns the number of active voices +TSFDEF int tsf_active_voice_count(tsf* f); + +// Render output samples into a buffer +// You can either render as signed 16-bit values (tsf_render_short) or +// as 32-bit float values (tsf_render_float) +// buffer: target buffer of size samples * output_channels * sizeof(type) +// samples: number of samples to render +// flag_mixing: if 0 clear the buffer first, otherwise mix into existing data +TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing CPP_DEFAULT0); +TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing CPP_DEFAULT0); + +// Higher level channel based functions, set up channel parameters +// channel: channel number +// preset_index: preset index >= 0 and < tsf_get_presetcount() +// preset_number: preset number (alternative to preset_index) +// flag_mididrums: 0 for normal channels, otherwise apply MIDI drum channel rules +// bank: instrument bank number (alternative to preset_index) +// pan: stereo panning value from 0.0 (left) to 1.0 (right) (default 0.5 center) +// volume: linear volume scale factor (default 1.0 full) +// pitch_wheel: pitch wheel position 0 to 16383 (default 8192 unpitched) +// pitch_range: range of the pitch wheel in semitones (default 2.0, total +/- 2 semitones) +// tuning: tuning of all playing voices in semitones (default 0.0, standard (A440) tuning) +// (tsf_set_preset_number and set_bank_preset return 0 if preset does not exist, otherwise 1) +// (tsf_channel_set_... return 0 if a new channel needed allocation and that failed, otherwise 1) +TSFDEF int tsf_channel_set_presetindex(tsf* f, int channel, int preset_index); +TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums CPP_DEFAULT0); +TSFDEF int tsf_channel_set_bank(tsf* f, int channel, int bank); +TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number); +TSFDEF int tsf_channel_set_pan(tsf* f, int channel, float pan); +TSFDEF int tsf_channel_set_volume(tsf* f, int channel, float volume); +TSFDEF int tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel); +TSFDEF int tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range); +TSFDEF int tsf_channel_set_tuning(tsf* f, int channel, float tuning); + +// Start or stop playing notes on a channel (needs channel preset to be set) +// channel: channel number +// key: note value between 0 and 127 (60 being middle C) +// vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full) +// (tsf_channel_note_on returns 0 on allocation failure of new voice, otherwise 1) +TSFDEF int tsf_channel_note_on(tsf* f, int channel, int key, float vel); +TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key); +TSFDEF void tsf_channel_note_off_all(tsf* f, int channel); //end with sustain and release +TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel); //end immediately + +// Apply a MIDI control change to the channel (not all controllers are supported!) +// (tsf_channel_midi_control returns 0 on allocation failure of new channel, otherwise 1) +TSFDEF int tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value); + +// Get current values set on the channels +TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel); +TSFDEF int tsf_channel_get_preset_bank(tsf* f, int channel); +TSFDEF int tsf_channel_get_preset_number(tsf* f, int channel); +TSFDEF float tsf_channel_get_pan(tsf* f, int channel); +TSFDEF float tsf_channel_get_volume(tsf* f, int channel); +TSFDEF int tsf_channel_get_pitchwheel(tsf* f, int channel); +TSFDEF float tsf_channel_get_pitchrange(tsf* f, int channel); +TSFDEF float tsf_channel_get_tuning(tsf* f, int channel); + +#ifdef __cplusplus +# undef CPP_DEFAULT0 +} +#endif + +// end header +// --------------------------------------------------------------------------------------------------------- +#endif //TSF_INCLUDE_TSF_INL + +#ifdef TSF_IMPLEMENTATION +#undef TSF_IMPLEMENTATION + +// The lower this block size is the more accurate the effects are. +// Increasing the value significantly lowers the CPU usage of the voice rendering. +// If LFO affects the low-pass filter it can be hearable even as low as 8. +#ifndef TSF_RENDER_EFFECTSAMPLEBLOCK +#define TSF_RENDER_EFFECTSAMPLEBLOCK 64 +#endif + +// When using tsf_render_short, to do the conversion a buffer of a fixed size is +// allocated on the stack. On low memory platforms this could be made smaller. +// Increasing this above 512 should not have a significant impact on performance. +// The value should be a multiple of TSF_RENDER_EFFECTSAMPLEBLOCK. +#ifndef TSF_RENDER_SHORTBUFFERBLOCK +#define TSF_RENDER_SHORTBUFFERBLOCK 512 +#endif + +// Grace release time for quick voice off (avoid clicking noise) +#define TSF_FASTRELEASETIME 0.01f + +#if !defined(TSF_MALLOC) || !defined(TSF_FREE) || !defined(TSF_REALLOC) +# include +# define TSF_MALLOC malloc +# define TSF_FREE free +# define TSF_REALLOC realloc +#endif + +#if !defined(TSF_MEMCPY) || !defined(TSF_MEMSET) +# include +# define TSF_MEMCPY memcpy +# define TSF_MEMSET memset +#endif + +#if !defined(TSF_POW) || !defined(TSF_POWF) || !defined(TSF_EXPF) || !defined(TSF_LOG) || !defined(TSF_TAN) || !defined(TSF_LOG10) || !defined(TSF_SQRTF) +# include +# if !defined(__cplusplus) && !defined(NAN) && !defined(powf) && !defined(expf) && !defined(sqrtf) +# define powf (float)pow // deal with old math.h +# define expf (float)exp // files that come without +# define sqrtf (float)sqrt // powf, expf and sqrtf +# endif +# define TSF_POW pow +# define TSF_POWF powf +# define TSF_EXPF expf +# define TSF_LOG log +# define TSF_TAN tan +# define TSF_LOG10 log10 +# define TSF_SQRTF sqrtf +#endif + +#ifndef TSF_NO_STDIO +# include +#endif + +#define TSF_TRUE 1 +#define TSF_FALSE 0 +#define TSF_BOOL char +#define TSF_PI 3.14159265358979323846264338327950288 +#define TSF_NULL 0 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef char tsf_fourcc[4]; +typedef signed char tsf_s8; +typedef unsigned char tsf_u8; +typedef unsigned short tsf_u16; +typedef signed short tsf_s16; +typedef unsigned int tsf_u32; +typedef char tsf_char20[20]; + +#define TSF_FourCCEquals(value1, value2) (value1[0] == value2[0] && value1[1] == value2[1] && value1[2] == value2[2] && value1[3] == value2[3]) + +struct tsf +{ + struct tsf_preset* presets; + float* fontSamples; + struct tsf_voice* voices; + struct tsf_channels* channels; + + int presetNum; + int voiceNum; + int maxVoiceNum; + unsigned int voicePlayIndex; + + enum TSFOutputMode outputmode; + float outSampleRate; + float globalGainDB; + int* refCount; +}; + +#ifndef TSF_NO_STDIO +static int tsf_stream_stdio_read(FILE* f, void* ptr, unsigned int size) { return (int)fread(ptr, 1, size, f); } +static int tsf_stream_stdio_skip(FILE* f, unsigned int count) { return !fseek(f, count, SEEK_CUR); } +TSFDEF tsf* tsf_load_filename(const char* filename) +{ + tsf* res; + struct tsf_stream stream = { TSF_NULL, (int(*)(void*,void*,unsigned int))&tsf_stream_stdio_read, (int(*)(void*,unsigned int))&tsf_stream_stdio_skip }; + #if __STDC_WANT_SECURE_LIB__ + FILE* f = TSF_NULL; fopen_s(&f, filename, "rb"); + #else + FILE* f = fopen(filename, "rb"); + #endif + if (!f) + { + //if (e) *e = TSF_FILENOTFOUND; + return TSF_NULL; + } + stream.data = f; + res = tsf_load(&stream); + fclose(f); + return res; +} +#endif + +struct tsf_stream_memory { const char* buffer; unsigned int total, pos; }; +static int tsf_stream_memory_read(struct tsf_stream_memory* m, void* ptr, unsigned int size) { if (size > m->total - m->pos) size = m->total - m->pos; TSF_MEMCPY(ptr, m->buffer+m->pos, size); m->pos += size; return size; } +static int tsf_stream_memory_skip(struct tsf_stream_memory* m, unsigned int count) { if (m->pos + count > m->total) return 0; m->pos += count; return 1; } +TSFDEF tsf* tsf_load_memory(const void* buffer, int size) +{ + struct tsf_stream stream = { TSF_NULL, (int(*)(void*,void*,unsigned int))&tsf_stream_memory_read, (int(*)(void*,unsigned int))&tsf_stream_memory_skip }; + struct tsf_stream_memory f = { 0, 0, 0 }; + f.buffer = (const char*)buffer; + f.total = size; + stream.data = &f; + return tsf_load(&stream); +} + +enum { TSF_LOOPMODE_NONE, TSF_LOOPMODE_CONTINUOUS, TSF_LOOPMODE_SUSTAIN }; + +enum { TSF_SEGMENT_NONE, TSF_SEGMENT_DELAY, TSF_SEGMENT_ATTACK, TSF_SEGMENT_HOLD, TSF_SEGMENT_DECAY, TSF_SEGMENT_SUSTAIN, TSF_SEGMENT_RELEASE, TSF_SEGMENT_DONE }; + +struct tsf_hydra +{ + struct tsf_hydra_phdr *phdrs; struct tsf_hydra_pbag *pbags; struct tsf_hydra_pmod *pmods; + struct tsf_hydra_pgen *pgens; struct tsf_hydra_inst *insts; struct tsf_hydra_ibag *ibags; + struct tsf_hydra_imod *imods; struct tsf_hydra_igen *igens; struct tsf_hydra_shdr *shdrs; + int phdrNum, pbagNum, pmodNum, pgenNum, instNum, ibagNum, imodNum, igenNum, shdrNum; +}; + +union tsf_hydra_genamount { struct { tsf_u8 lo, hi; } range; tsf_s16 shortAmount; tsf_u16 wordAmount; }; +struct tsf_hydra_phdr { tsf_char20 presetName; tsf_u16 preset, bank, presetBagNdx; tsf_u32 library, genre, morphology; }; +struct tsf_hydra_pbag { tsf_u16 genNdx, modNdx; }; +struct tsf_hydra_pmod { tsf_u16 modSrcOper, modDestOper; tsf_s16 modAmount; tsf_u16 modAmtSrcOper, modTransOper; }; +struct tsf_hydra_pgen { tsf_u16 genOper; union tsf_hydra_genamount genAmount; }; +struct tsf_hydra_inst { tsf_char20 instName; tsf_u16 instBagNdx; }; +struct tsf_hydra_ibag { tsf_u16 instGenNdx, instModNdx; }; +struct tsf_hydra_imod { tsf_u16 modSrcOper, modDestOper; tsf_s16 modAmount; tsf_u16 modAmtSrcOper, modTransOper; }; +struct tsf_hydra_igen { tsf_u16 genOper; union tsf_hydra_genamount genAmount; }; +struct tsf_hydra_shdr { tsf_char20 sampleName; tsf_u32 start, end, startLoop, endLoop, sampleRate; tsf_u8 originalPitch; tsf_s8 pitchCorrection; tsf_u16 sampleLink, sampleType; }; + +#define TSFR(FIELD) stream->read(stream->data, &i->FIELD, sizeof(i->FIELD)); +static void tsf_hydra_read_phdr(struct tsf_hydra_phdr* i, struct tsf_stream* stream) { TSFR(presetName) TSFR(preset) TSFR(bank) TSFR(presetBagNdx) TSFR(library) TSFR(genre) TSFR(morphology) } +static void tsf_hydra_read_pbag(struct tsf_hydra_pbag* i, struct tsf_stream* stream) { TSFR(genNdx) TSFR(modNdx) } +static void tsf_hydra_read_pmod(struct tsf_hydra_pmod* i, struct tsf_stream* stream) { TSFR(modSrcOper) TSFR(modDestOper) TSFR(modAmount) TSFR(modAmtSrcOper) TSFR(modTransOper) } +static void tsf_hydra_read_pgen(struct tsf_hydra_pgen* i, struct tsf_stream* stream) { TSFR(genOper) TSFR(genAmount) } +static void tsf_hydra_read_inst(struct tsf_hydra_inst* i, struct tsf_stream* stream) { TSFR(instName) TSFR(instBagNdx) } +static void tsf_hydra_read_ibag(struct tsf_hydra_ibag* i, struct tsf_stream* stream) { TSFR(instGenNdx) TSFR(instModNdx) } +static void tsf_hydra_read_imod(struct tsf_hydra_imod* i, struct tsf_stream* stream) { TSFR(modSrcOper) TSFR(modDestOper) TSFR(modAmount) TSFR(modAmtSrcOper) TSFR(modTransOper) } +static void tsf_hydra_read_igen(struct tsf_hydra_igen* i, struct tsf_stream* stream) { TSFR(genOper) TSFR(genAmount) } +static void tsf_hydra_read_shdr(struct tsf_hydra_shdr* i, struct tsf_stream* stream) { TSFR(sampleName) TSFR(start) TSFR(end) TSFR(startLoop) TSFR(endLoop) TSFR(sampleRate) TSFR(originalPitch) TSFR(pitchCorrection) TSFR(sampleLink) TSFR(sampleType) } +#undef TSFR + +struct tsf_riffchunk { tsf_fourcc id; tsf_u32 size; }; +struct tsf_envelope { float delay, attack, hold, decay, sustain, release, keynumToHold, keynumToDecay; }; +struct tsf_voice_envelope { float level, slope; int samplesUntilNextSegment; short segment, midiVelocity; struct tsf_envelope parameters; TSF_BOOL segmentIsExponential, isAmpEnv; }; +struct tsf_voice_lowpass { double QInv, a0, a1, b1, b2, z1, z2; TSF_BOOL active; }; +struct tsf_voice_lfo { int samplesUntil; float level, delta; }; + +struct tsf_region +{ + int loop_mode; + unsigned int sample_rate; + unsigned char lokey, hikey, lovel, hivel; + unsigned int group, offset, end, loop_start, loop_end; + int transpose, tune, pitch_keycenter, pitch_keytrack; + float attenuation, pan; + struct tsf_envelope ampenv, modenv; + int initialFilterQ, initialFilterFc; + int modEnvToPitch, modEnvToFilterFc, modLfoToFilterFc, modLfoToVolume; + float delayModLFO; + int freqModLFO, modLfoToPitch; + float delayVibLFO; + int freqVibLFO, vibLfoToPitch; +}; + +struct tsf_preset +{ + tsf_char20 presetName; + tsf_u16 preset, bank; + struct tsf_region* regions; + int regionNum; +}; + +struct tsf_voice +{ + int playingPreset, playingKey, playingChannel, heldSustain; + struct tsf_region* region; + double pitchInputTimecents, pitchOutputFactor; + double sourceSamplePosition; + float noteGainDB, panFactorLeft, panFactorRight; + unsigned int playIndex, loopStart, loopEnd; + struct tsf_voice_envelope ampenv, modenv; + struct tsf_voice_lowpass lowpass; + struct tsf_voice_lfo modlfo, viblfo; +}; + +struct tsf_channel +{ + unsigned short presetIndex, bank, pitchWheel, midiPan, midiVolume, midiExpression, midiRPN, midiData, sustain; + float panOffset, gainDB, pitchRange, tuning; +}; + +struct tsf_channels +{ + void (*setupVoice)(tsf* f, struct tsf_voice* voice); + int channelNum, activeChannel; + struct tsf_channel channels[1]; +}; + +static double tsf_timecents2Secsd(double timecents) { return TSF_POW(2.0, timecents / 1200.0); } +static float tsf_timecents2Secsf(float timecents) { return TSF_POWF(2.0f, timecents / 1200.0f); } +static float tsf_cents2Hertz(float cents) { return 8.176f * TSF_POWF(2.0f, cents / 1200.0f); } +static float tsf_decibelsToGain(float db) { return (db > -100.f ? TSF_POWF(10.0f, db * 0.05f) : 0); } +static float tsf_gainToDecibels(float gain) { return (gain <= .00001f ? -100.f : (float)(20.0 * TSF_LOG10(gain))); } + +static TSF_BOOL tsf_riffchunk_read(struct tsf_riffchunk* parent, struct tsf_riffchunk* chunk, struct tsf_stream* stream) +{ + TSF_BOOL IsRiff, IsList; + if (parent && sizeof(tsf_fourcc) + sizeof(tsf_u32) > parent->size) return TSF_FALSE; + if (!stream->read(stream->data, &chunk->id, sizeof(tsf_fourcc)) || *chunk->id <= ' ' || *chunk->id >= 'z') return TSF_FALSE; + if (!stream->read(stream->data, &chunk->size, sizeof(tsf_u32))) return TSF_FALSE; + if (parent && sizeof(tsf_fourcc) + sizeof(tsf_u32) + chunk->size > parent->size) return TSF_FALSE; + if (parent) parent->size -= sizeof(tsf_fourcc) + sizeof(tsf_u32) + chunk->size; + IsRiff = TSF_FourCCEquals(chunk->id, "RIFF"), IsList = TSF_FourCCEquals(chunk->id, "LIST"); + if (IsRiff && parent) return TSF_FALSE; //not allowed + if (!IsRiff && !IsList) return TSF_TRUE; //custom type without sub type + if (!stream->read(stream->data, &chunk->id, sizeof(tsf_fourcc)) || *chunk->id <= ' ' || *chunk->id >= 'z') return TSF_FALSE; + chunk->size -= sizeof(tsf_fourcc); + return TSF_TRUE; +} + +static void tsf_region_clear(struct tsf_region* i, TSF_BOOL for_relative) +{ + TSF_MEMSET(i, 0, sizeof(struct tsf_region)); + i->hikey = i->hivel = 127; + i->pitch_keycenter = 60; // C4 + if (for_relative) return; + + i->pitch_keytrack = 100; + + i->pitch_keycenter = -1; + + // SF2 defaults in timecents. + i->ampenv.delay = i->ampenv.attack = i->ampenv.hold = i->ampenv.decay = i->ampenv.release = -12000.0f; + i->modenv.delay = i->modenv.attack = i->modenv.hold = i->modenv.decay = i->modenv.release = -12000.0f; + + i->initialFilterFc = 13500; + + i->delayModLFO = -12000.0f; + i->delayVibLFO = -12000.0f; +} + +static void tsf_region_operator(struct tsf_region* region, tsf_u16 genOper, union tsf_hydra_genamount* amount, struct tsf_region* merge_region) +{ + enum + { + _GEN_TYPE_MASK = 0x0F, + GEN_FLOAT = 0x01, + GEN_INT = 0x02, + GEN_UINT_ADD = 0x03, + GEN_UINT_ADD15 = 0x04, + GEN_KEYRANGE = 0x05, + GEN_VELRANGE = 0x06, + GEN_LOOPMODE = 0x07, + GEN_GROUP = 0x08, + GEN_KEYCENTER = 0x09, + + _GEN_LIMIT_MASK = 0xF0, + GEN_INT_LIMIT12K = 0x10, //min -12000, max 12000 + GEN_INT_LIMITFC = 0x20, //min 1500, max 13500 + GEN_INT_LIMITQ = 0x30, //min 0, max 960 + GEN_INT_LIMIT960 = 0x40, //min -960, max 960 + GEN_INT_LIMIT16K4500 = 0x50, //min -16000, max 4500 + GEN_FLOAT_LIMIT12K5K = 0x60, //min -12000, max 5000 + GEN_FLOAT_LIMIT12K8K = 0x70, //min -12000, max 8000 + GEN_FLOAT_LIMIT1200 = 0x80, //min -1200, max 1200 + GEN_FLOAT_LIMITPAN = 0x90, //* .001f, min -.5f, max .5f, + GEN_FLOAT_LIMITATTN = 0xA0, //* .1f, min 0, max 144.0 + GEN_FLOAT_MAX1000 = 0xB0, //min 0, max 1000 + GEN_FLOAT_MAX1440 = 0xC0, //min 0, max 1440 + + _GEN_MAX = 59 + }; + #define _TSFREGIONOFFSET(TYPE, FIELD) (unsigned char)(((TYPE*)&((struct tsf_region*)0)->FIELD) - (TYPE*)0) + #define _TSFREGIONENVOFFSET(TYPE, ENV, FIELD) (unsigned char)(((TYPE*)&((&(((struct tsf_region*)0)->ENV))->FIELD)) - (TYPE*)0) + static const struct { unsigned char mode, offset; } genMetas[_GEN_MAX] = + { + { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, offset ) }, // 0 StartAddrsOffset + { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, end ) }, // 1 EndAddrsOffset + { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, loop_start ) }, // 2 StartloopAddrsOffset + { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, loop_end ) }, // 3 EndloopAddrsOffset + { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, offset ) }, // 4 StartAddrsCoarseOffset + { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modLfoToPitch ) }, // 5 ModLfoToPitch + { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, vibLfoToPitch ) }, // 6 VibLfoToPitch + { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modEnvToPitch ) }, // 7 ModEnvToPitch + { GEN_INT | GEN_INT_LIMITFC , _TSFREGIONOFFSET( int, initialFilterFc ) }, // 8 InitialFilterFc + { GEN_INT | GEN_INT_LIMITQ , _TSFREGIONOFFSET( int, initialFilterQ ) }, // 9 InitialFilterQ + { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modLfoToFilterFc ) }, //10 ModLfoToFilterFc + { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modEnvToFilterFc ) }, //11 ModEnvToFilterFc + { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, end ) }, //12 EndAddrsCoarseOffset + { GEN_INT | GEN_INT_LIMIT960 , _TSFREGIONOFFSET( int, modLfoToVolume ) }, //13 ModLfoToVolume + { 0 , (0 ) }, // Unused + { 0 , (0 ) }, //15 ChorusEffectsSend (unsupported) + { 0 , (0 ) }, //16 ReverbEffectsSend (unsupported) + { GEN_FLOAT | GEN_FLOAT_LIMITPAN , _TSFREGIONOFFSET( float, pan ) }, //17 Pan + { 0 , (0 ) }, // Unused + { 0 , (0 ) }, // Unused + { 0 , (0 ) }, // Unused + { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONOFFSET( float, delayModLFO ) }, //21 DelayModLFO + { GEN_INT | GEN_INT_LIMIT16K4500 , _TSFREGIONOFFSET( int, freqModLFO ) }, //22 FreqModLFO + { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONOFFSET( float, delayVibLFO ) }, //23 DelayVibLFO + { GEN_INT | GEN_INT_LIMIT16K4500 , _TSFREGIONOFFSET( int, freqVibLFO ) }, //24 FreqVibLFO + { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, modenv, delay ) }, //25 DelayModEnv + { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, modenv, attack ) }, //26 AttackModEnv + { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, modenv, hold ) }, //27 HoldModEnv + { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, modenv, decay ) }, //28 DecayModEnv + { GEN_FLOAT | GEN_FLOAT_MAX1000 , _TSFREGIONENVOFFSET( float, modenv, sustain ) }, //29 SustainModEnv + { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, modenv, release ) }, //30 ReleaseModEnv + { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, modenv, keynumToHold ) }, //31 KeynumToModEnvHold + { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, modenv, keynumToDecay) }, //32 KeynumToModEnvDecay + { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, ampenv, delay ) }, //33 DelayVolEnv + { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, ampenv, attack ) }, //34 AttackVolEnv + { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, ampenv, hold ) }, //35 HoldVolEnv + { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, ampenv, decay ) }, //36 DecayVolEnv + { GEN_FLOAT | GEN_FLOAT_MAX1440 , _TSFREGIONENVOFFSET( float, ampenv, sustain ) }, //37 SustainVolEnv + { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, ampenv, release ) }, //38 ReleaseVolEnv + { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, ampenv, keynumToHold ) }, //39 KeynumToVolEnvHold + { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, ampenv, keynumToDecay) }, //40 KeynumToVolEnvDecay + { 0 , (0 ) }, // Instrument (special) + { 0 , (0 ) }, // Reserved + { GEN_KEYRANGE , (0 ) }, //43 KeyRange + { GEN_VELRANGE , (0 ) }, //44 VelRange + { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, loop_start ) }, //45 StartloopAddrsCoarseOffset + { 0 , (0 ) }, //46 Keynum (special) + { 0 , (0 ) }, //47 Velocity (special) + { GEN_FLOAT | GEN_FLOAT_LIMITATTN , _TSFREGIONOFFSET( float, attenuation ) }, //48 InitialAttenuation + { 0 , (0 ) }, // Reserved + { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, loop_end ) }, //50 EndloopAddrsCoarseOffset + { GEN_INT , _TSFREGIONOFFSET( int, transpose ) }, //51 CoarseTune + { GEN_INT , _TSFREGIONOFFSET( int, tune ) }, //52 FineTune + { 0 , (0 ) }, // SampleID (special) + { GEN_LOOPMODE , _TSFREGIONOFFSET( int, loop_mode ) }, //54 SampleModes + { 0 , (0 ) }, // Reserved + { GEN_INT , _TSFREGIONOFFSET( int, pitch_keytrack ) }, //56 ScaleTuning + { GEN_GROUP , _TSFREGIONOFFSET(unsigned int, group ) }, //57 ExclusiveClass + { GEN_KEYCENTER , _TSFREGIONOFFSET( int, pitch_keycenter ) }, //58 OverridingRootKey + }; + #undef _TSFREGIONOFFSET + #undef _TSFREGIONENVOFFSET + if (amount) + { + int offset; + if (genOper >= _GEN_MAX) return; + offset = genMetas[genOper].offset; + switch (genMetas[genOper].mode & _GEN_TYPE_MASK) + { + case GEN_FLOAT: (( float*)region)[offset] = amount->shortAmount; return; + case GEN_INT: (( int*)region)[offset] = amount->shortAmount; return; + case GEN_UINT_ADD: ((unsigned int*)region)[offset] += amount->shortAmount; return; + case GEN_UINT_ADD15: ((unsigned int*)region)[offset] += amount->shortAmount<<15; return; + case GEN_KEYRANGE: region->lokey = amount->range.lo; region->hikey = amount->range.hi; return; + case GEN_VELRANGE: region->lovel = amount->range.lo; region->hivel = amount->range.hi; return; + case GEN_LOOPMODE: region->loop_mode = ((amount->wordAmount&3) == 3 ? TSF_LOOPMODE_SUSTAIN : ((amount->wordAmount&3) == 1 ? TSF_LOOPMODE_CONTINUOUS : TSF_LOOPMODE_NONE)); return; + case GEN_GROUP: region->group = amount->wordAmount; return; + case GEN_KEYCENTER: region->pitch_keycenter = amount->shortAmount; return; + } + } + else //merge regions and clamp values + { + for (genOper = 0; genOper != _GEN_MAX; genOper++) + { + int offset = genMetas[genOper].offset; + switch (genMetas[genOper].mode & _GEN_TYPE_MASK) + { + case GEN_FLOAT: + { + float *val = &((float*)region)[offset], vfactor, vmin, vmax; + *val += ((float*)merge_region)[offset]; + switch (genMetas[genOper].mode & _GEN_LIMIT_MASK) + { + case GEN_FLOAT_LIMIT12K5K: vfactor = 1.0f; vmin = -12000.0f; vmax = 5000.0f; break; + case GEN_FLOAT_LIMIT12K8K: vfactor = 1.0f; vmin = -12000.0f; vmax = 8000.0f; break; + case GEN_FLOAT_LIMIT1200: vfactor = 1.0f; vmin = -1200.0f; vmax = 1200.0f; break; + case GEN_FLOAT_LIMITPAN: vfactor = 0.001f; vmin = -0.5f; vmax = 0.5f; break; + case GEN_FLOAT_LIMITATTN: vfactor = 0.1f; vmin = 0.0f; vmax = 144.0f; break; + case GEN_FLOAT_MAX1000: vfactor = 1.0f; vmin = 0.0f; vmax = 1000.0f; break; + case GEN_FLOAT_MAX1440: vfactor = 1.0f; vmin = 0.0f; vmax = 1440.0f; break; + default: continue; + } + *val *= vfactor; + if (*val < vmin) *val = vmin; + else if (*val > vmax) *val = vmax; + continue; + } + case GEN_INT: + { + int *val = &((int*)region)[offset], vmin, vmax; + *val += ((int*)merge_region)[offset]; + switch (genMetas[genOper].mode & _GEN_LIMIT_MASK) + { + case GEN_INT_LIMIT12K: vmin = -12000; vmax = 12000; break; + case GEN_INT_LIMITFC: vmin = 1500; vmax = 13500; break; + case GEN_INT_LIMITQ: vmin = 0; vmax = 960; break; + case GEN_INT_LIMIT960: vmin = -960; vmax = 960; break; + case GEN_INT_LIMIT16K4500: vmin = -16000; vmax = 4500; break; + default: continue; + } + if (*val < vmin) *val = vmin; + else if (*val > vmax) *val = vmax; + continue; + } + case GEN_UINT_ADD: + { + ((unsigned int*)region)[offset] += ((unsigned int*)merge_region)[offset]; + continue; + } + } + } + } +} + +static void tsf_region_envtosecs(struct tsf_envelope* p, TSF_BOOL sustainIsGain) +{ + // EG times need to be converted from timecents to seconds. + // Pin very short EG segments. Timecents don't get to zero, and our EG is + // happier with zero values. + p->delay = (p->delay < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->delay)); + p->attack = (p->attack < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->attack)); + p->release = (p->release < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->release)); + + // If we have dynamic hold or decay times depending on key number we need + // to keep the values in timecents so we can calculate it during startNote + if (!p->keynumToHold) p->hold = (p->hold < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->hold)); + if (!p->keynumToDecay) p->decay = (p->decay < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->decay)); + + if (p->sustain < 0.0f) p->sustain = 0.0f; + else if (sustainIsGain) p->sustain = tsf_decibelsToGain(-p->sustain / 10.0f); + else p->sustain = 1.0f - (p->sustain / 1000.0f); +} + +static int tsf_load_presets(tsf* res, struct tsf_hydra *hydra, unsigned int fontSampleCount) +{ + enum { GenInstrument = 41, GenKeyRange = 43, GenVelRange = 44, GenSampleID = 53 }; + // Read each preset. + struct tsf_hydra_phdr *pphdr, *pphdrMax; + res->presetNum = hydra->phdrNum - 1; + res->presets = (struct tsf_preset*)TSF_MALLOC(res->presetNum * sizeof(struct tsf_preset)); + if (!res->presets) return 0; + else { int i; for (i = 0; i != res->presetNum; i++) res->presets[i].regions = TSF_NULL; } + for (pphdr = hydra->phdrs, pphdrMax = pphdr + hydra->phdrNum - 1; pphdr != pphdrMax; pphdr++) + { + int sortedIndex = 0, region_index = 0; + struct tsf_hydra_phdr *otherphdr; + struct tsf_preset* preset; + struct tsf_hydra_pbag *ppbag, *ppbagEnd; + struct tsf_region globalRegion; + for (otherphdr = hydra->phdrs; otherphdr != pphdrMax; otherphdr++) + { + if (otherphdr == pphdr || otherphdr->bank > pphdr->bank) continue; + else if (otherphdr->bank < pphdr->bank) sortedIndex++; + else if (otherphdr->preset > pphdr->preset) continue; + else if (otherphdr->preset < pphdr->preset) sortedIndex++; + else if (otherphdr < pphdr) sortedIndex++; + } + + preset = &res->presets[sortedIndex]; + TSF_MEMCPY(preset->presetName, pphdr->presetName, sizeof(preset->presetName)); + preset->presetName[sizeof(preset->presetName)-1] = '\0'; //should be zero terminated in source file but make sure + preset->bank = pphdr->bank; + preset->preset = pphdr->preset; + preset->regionNum = 0; + + //count regions covered by this preset + for (ppbag = hydra->pbags + pphdr->presetBagNdx, ppbagEnd = hydra->pbags + pphdr[1].presetBagNdx; ppbag != ppbagEnd; ppbag++) + { + unsigned char plokey = 0, phikey = 127, plovel = 0, phivel = 127; + struct tsf_hydra_pgen *ppgen, *ppgenEnd; struct tsf_hydra_inst *pinst; struct tsf_hydra_ibag *pibag, *pibagEnd; struct tsf_hydra_igen *pigen, *pigenEnd; + for (ppgen = hydra->pgens + ppbag->genNdx, ppgenEnd = hydra->pgens + ppbag[1].genNdx; ppgen != ppgenEnd; ppgen++) + { + if (ppgen->genOper == GenKeyRange) { plokey = ppgen->genAmount.range.lo; phikey = ppgen->genAmount.range.hi; continue; } + if (ppgen->genOper == GenVelRange) { plovel = ppgen->genAmount.range.lo; phivel = ppgen->genAmount.range.hi; continue; } + if (ppgen->genOper != GenInstrument) continue; + if (ppgen->genAmount.wordAmount >= hydra->instNum) continue; + pinst = hydra->insts + ppgen->genAmount.wordAmount; + for (pibag = hydra->ibags + pinst->instBagNdx, pibagEnd = hydra->ibags + pinst[1].instBagNdx; pibag != pibagEnd; pibag++) + { + unsigned char ilokey = 0, ihikey = 127, ilovel = 0, ihivel = 127; + for (pigen = hydra->igens + pibag->instGenNdx, pigenEnd = hydra->igens + pibag[1].instGenNdx; pigen != pigenEnd; pigen++) + { + if (pigen->genOper == GenKeyRange) { ilokey = pigen->genAmount.range.lo; ihikey = pigen->genAmount.range.hi; continue; } + if (pigen->genOper == GenVelRange) { ilovel = pigen->genAmount.range.lo; ihivel = pigen->genAmount.range.hi; continue; } + if (pigen->genOper == GenSampleID && ihikey >= plokey && ilokey <= phikey && ihivel >= plovel && ilovel <= phivel) preset->regionNum++; + } + } + } + } + + preset->regions = (struct tsf_region*)TSF_MALLOC(preset->regionNum * sizeof(struct tsf_region)); + if (!preset->regions) + { + int i; for (i = 0; i != res->presetNum; i++) TSF_FREE(res->presets[i].regions); + TSF_FREE(res->presets); + return 0; + } + tsf_region_clear(&globalRegion, TSF_TRUE); + + // Zones. + for (ppbag = hydra->pbags + pphdr->presetBagNdx, ppbagEnd = hydra->pbags + pphdr[1].presetBagNdx; ppbag != ppbagEnd; ppbag++) + { + struct tsf_hydra_pgen *ppgen, *ppgenEnd; struct tsf_hydra_inst *pinst; struct tsf_hydra_ibag *pibag, *pibagEnd; struct tsf_hydra_igen *pigen, *pigenEnd; + struct tsf_region presetRegion = globalRegion; + int hadGenInstrument = 0; + + // Generators. + for (ppgen = hydra->pgens + ppbag->genNdx, ppgenEnd = hydra->pgens + ppbag[1].genNdx; ppgen != ppgenEnd; ppgen++) + { + // Instrument. + if (ppgen->genOper == GenInstrument) + { + struct tsf_region instRegion; + tsf_u16 whichInst = ppgen->genAmount.wordAmount; + if (whichInst >= hydra->instNum) continue; + + tsf_region_clear(&instRegion, TSF_FALSE); + pinst = &hydra->insts[whichInst]; + for (pibag = hydra->ibags + pinst->instBagNdx, pibagEnd = hydra->ibags + pinst[1].instBagNdx; pibag != pibagEnd; pibag++) + { + // Generators. + struct tsf_region zoneRegion = instRegion; + int hadSampleID = 0; + for (pigen = hydra->igens + pibag->instGenNdx, pigenEnd = hydra->igens + pibag[1].instGenNdx; pigen != pigenEnd; pigen++) + { + if (pigen->genOper == GenSampleID) + { + struct tsf_hydra_shdr* pshdr; + + //preset region key and vel ranges are a filter for the zone regions + if (zoneRegion.hikey < presetRegion.lokey || zoneRegion.lokey > presetRegion.hikey) continue; + if (zoneRegion.hivel < presetRegion.lovel || zoneRegion.lovel > presetRegion.hivel) continue; + if (presetRegion.lokey > zoneRegion.lokey) zoneRegion.lokey = presetRegion.lokey; + if (presetRegion.hikey < zoneRegion.hikey) zoneRegion.hikey = presetRegion.hikey; + if (presetRegion.lovel > zoneRegion.lovel) zoneRegion.lovel = presetRegion.lovel; + if (presetRegion.hivel < zoneRegion.hivel) zoneRegion.hivel = presetRegion.hivel; + + //sum regions + tsf_region_operator(&zoneRegion, 0, TSF_NULL, &presetRegion); + + // EG times need to be converted from timecents to seconds. + tsf_region_envtosecs(&zoneRegion.ampenv, TSF_TRUE); + tsf_region_envtosecs(&zoneRegion.modenv, TSF_FALSE); + + // LFO times need to be converted from timecents to seconds. + zoneRegion.delayModLFO = (zoneRegion.delayModLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayModLFO)); + zoneRegion.delayVibLFO = (zoneRegion.delayVibLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayVibLFO)); + + // Fixup sample positions + pshdr = &hydra->shdrs[pigen->genAmount.wordAmount]; + zoneRegion.offset += pshdr->start; + zoneRegion.end += pshdr->end; + zoneRegion.loop_start += pshdr->startLoop; + zoneRegion.loop_end += pshdr->endLoop; + if (pshdr->endLoop > 0) zoneRegion.loop_end -= 1; + if (zoneRegion.loop_end > fontSampleCount) zoneRegion.loop_end = fontSampleCount; + if (zoneRegion.pitch_keycenter == -1) zoneRegion.pitch_keycenter = pshdr->originalPitch; + zoneRegion.tune += pshdr->pitchCorrection; + zoneRegion.sample_rate = pshdr->sampleRate; + if (zoneRegion.end && zoneRegion.end < fontSampleCount) zoneRegion.end++; + else zoneRegion.end = fontSampleCount; + + preset->regions[region_index] = zoneRegion; + region_index++; + hadSampleID = 1; + } + else tsf_region_operator(&zoneRegion, pigen->genOper, &pigen->genAmount, TSF_NULL); + } + + // Handle instrument's global zone. + if (pibag == hydra->ibags + pinst->instBagNdx && !hadSampleID) + instRegion = zoneRegion; + + // Modulators (TODO) + //if (ibag->instModNdx < ibag[1].instModNdx) addUnsupportedOpcode("any modulator"); + } + hadGenInstrument = 1; + } + else tsf_region_operator(&presetRegion, ppgen->genOper, &ppgen->genAmount, TSF_NULL); + } + + // Modulators (TODO) + //if (pbag->modNdx < pbag[1].modNdx) addUnsupportedOpcode("any modulator"); + + // Handle preset's global zone. + if (ppbag == hydra->pbags + pphdr->presetBagNdx && !hadGenInstrument) + globalRegion = presetRegion; + } + } + return 1; +} + +#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H +static int tsf_decode_ogg(const tsf_u8 *pSmpl, const tsf_u8 *pSmplEnd, float** pRes, tsf_u32* pResNum, tsf_u32* pResMax, tsf_u32 resInitial) +{ + float *res = *pRes, *oldres; tsf_u32 resNum = *pResNum; tsf_u32 resMax = *pResMax; stb_vorbis *v; + + // Use whatever stb_vorbis API that is available (either pull or push) + #if !defined(STB_VORBIS_NO_PULLDATA_API) && !defined(STB_VORBIS_NO_FROMMEMORY) + v = stb_vorbis_open_memory(pSmpl, (int)(pSmplEnd - pSmpl), TSF_NULL, TSF_NULL); + #else + { int use, err; v = stb_vorbis_open_pushdata(pSmpl, (int)(pSmplEnd - pSmpl), &use, &err, TSF_NULL); pSmpl += use; } + #endif + if (v == TSF_NULL) return 0; + + for (;;) + { + float** outputs; int n_samples; + + // Decode one frame of vorbis samples with whatever stb_vorbis API that is available + #if !defined(STB_VORBIS_NO_PULLDATA_API) && !defined(STB_VORBIS_NO_FROMMEMORY) + n_samples = stb_vorbis_get_frame_float(v, TSF_NULL, &outputs); + if (!n_samples) break; + #else + if (pSmpl >= pSmplEnd) break; + { int use = stb_vorbis_decode_frame_pushdata(v, pSmpl, (int)(pSmplEnd - pSmpl), TSF_NULL, &outputs, &n_samples); pSmpl += use; } + if (!n_samples) continue; + #endif + + // Expand our output buffer if necessary then copy over the decoded frame samples + resNum += n_samples; + if (resNum > resMax) + { + do { resMax += (resMax ? (resMax < 1048576 ? resMax : 1048576) : resInitial); } while (resNum > resMax); + res = (float*)TSF_REALLOC((oldres = res), resMax * sizeof(float)); + if (!res) { TSF_FREE(oldres); stb_vorbis_close(v); return 0; } + } + TSF_MEMCPY(res + resNum - n_samples, outputs[0], n_samples * sizeof(float)); + } + stb_vorbis_close(v); + *pRes = res; *pResNum = resNum; *pResMax = resMax; + return 1; +} + +static int tsf_decode_sf3_samples(const void* rawBuffer, float** pFloatBuffer, unsigned int* pSmplCount, struct tsf_hydra *hydra) +{ + const tsf_u8* smplBuffer = (const tsf_u8*)rawBuffer; + tsf_u32 smplLength = *pSmplCount, resNum = 0, resMax = 0, resInitial = (smplLength > 0x100000 ? (smplLength & ~0xFFFFF) : 65536); + float *res = TSF_NULL, *oldres; + int i, shdrLast = hydra->shdrNum - 1, is_sf3 = 0; + for (i = 0; i <= shdrLast; i++) + { + struct tsf_hydra_shdr *shdr = &hydra->shdrs[i]; + if (shdr->sampleType & 0x30) // compression flags (sometimes Vorbis flag) + { + const tsf_u8 *pSmpl = smplBuffer + shdr->start, *pSmplEnd = smplBuffer + shdr->end; + if (pSmpl + 4 > pSmplEnd || !TSF_FourCCEquals(pSmpl, "OggS")) + { + shdr->start = shdr->end = shdr->startLoop = shdr->endLoop = 0; + continue; + } + + // Fix up sample indices in shdr (end index is set after decoding) + shdr->start = resNum; + shdr->startLoop += resNum; + shdr->endLoop += resNum; + if (!tsf_decode_ogg(pSmpl, pSmplEnd, &res, &resNum, &resMax, resInitial)) { TSF_FREE(res); return 0; } + shdr->end = resNum; + is_sf3 = 1; + } + else // raw PCM sample + { + float *out; short *in = (short*)smplBuffer + resNum, *inEnd; tsf_u32 oldResNum = resNum; + if (is_sf3) // Fix up sample indices in shdr + { + tsf_u32 fix_offset = resNum - shdr->start; + in -= fix_offset; + shdr->start = resNum; + shdr->end += fix_offset; + shdr->startLoop += fix_offset; + shdr->endLoop += fix_offset; + } + inEnd = in + ((shdr->end >= shdr->endLoop ? shdr->end : shdr->endLoop) - resNum); + if (i == shdrLast || (tsf_u8*)inEnd > (smplBuffer + smplLength)) inEnd = (short*)(smplBuffer + smplLength); + if (inEnd <= in) continue; + + // expand our output buffer if necessary then convert the PCM data from short to float + resNum += (tsf_u32)(inEnd - in); + if (resNum > resMax) + { + do { resMax += (resMax ? (resMax < 1048576 ? resMax : 1048576) : resInitial); } while (resNum > resMax); + res = (float*)TSF_REALLOC((oldres = res), resMax * sizeof(float)); + if (!res) { TSF_FREE(oldres); return 0; } + } + + // Convert the samples from short to float + for (out = res + oldResNum; in < inEnd;) + *(out++) = (float)(*(in++) / 32767.0); + } + } + + // Trim the sample buffer down then return success (unless out of memory) + if (!(*pFloatBuffer = (float*)TSF_REALLOC(res, resNum * sizeof(float)))) *pFloatBuffer = res; + *pSmplCount = resNum; + return (res ? 1 : 0); +} +#endif + +static int tsf_load_samples(void** pRawBuffer, float** pFloatBuffer, unsigned int* pSmplCount, struct tsf_riffchunk *chunkSmpl, struct tsf_stream* stream) +{ + #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H + // With OGG Vorbis support we cannot pre-allocate the memory for tsf_decode_sf3_samples + tsf_u32 resNum, resMax; float* oldres; + *pSmplCount = chunkSmpl->size; + *pRawBuffer = (void*)TSF_MALLOC(*pSmplCount); + if (!*pRawBuffer || !stream->read(stream->data, *pRawBuffer, chunkSmpl->size)) return 0; + if (chunkSmpl->id[3] != 'o') return 1; + + // Decode custom .sfo 'smpo' format where all samples are in a single ogg stream + resNum = resMax = 0; + if (!tsf_decode_ogg((tsf_u8*)*pRawBuffer, (tsf_u8*)*pRawBuffer + chunkSmpl->size, pFloatBuffer, &resNum, &resMax, 65536)) return 0; + if (!(*pFloatBuffer = (float*)TSF_REALLOC((oldres = *pFloatBuffer), resNum * sizeof(float)))) *pFloatBuffer = oldres; + *pSmplCount = resNum; + return (*pFloatBuffer ? 1 : 0); + #else + // Inline convert the samples from short to float + float *res, *out; const short *in; + *pSmplCount = chunkSmpl->size / (unsigned int)sizeof(short); + *pFloatBuffer = (float*)TSF_MALLOC(*pSmplCount * sizeof(float)); + if (!*pFloatBuffer || !stream->read(stream->data, *pFloatBuffer, chunkSmpl->size)) return 0; + for (res = *pFloatBuffer, out = res + *pSmplCount, in = (short*)res + *pSmplCount; out != res;) + *(--out) = (float)(*(--in) / 32767.0); + return 1; + #endif +} + +static int tsf_voice_envelope_release_samples(struct tsf_voice_envelope* e, float outSampleRate) +{ + return (int)((e->parameters.release <= 0 ? TSF_FASTRELEASETIME : e->parameters.release) * outSampleRate); +} + +static void tsf_voice_envelope_nextsegment(struct tsf_voice_envelope* e, short active_segment, float outSampleRate) +{ + switch (active_segment) + { + case TSF_SEGMENT_NONE: + e->samplesUntilNextSegment = (int)(e->parameters.delay * outSampleRate); + if (e->samplesUntilNextSegment > 0) + { + e->segment = TSF_SEGMENT_DELAY; + e->segmentIsExponential = TSF_FALSE; + e->level = 0.0; + e->slope = 0.0; + return; + } + /* fall through */ + case TSF_SEGMENT_DELAY: + e->samplesUntilNextSegment = (int)(e->parameters.attack * outSampleRate); + if (e->samplesUntilNextSegment > 0) + { + if (!e->isAmpEnv) + { + //mod env attack duration scales with velocity (velocity of 1 is full duration, max velocity is 0.125 times duration) + e->samplesUntilNextSegment = (int)(e->parameters.attack * ((145 - e->midiVelocity) / 144.0f) * outSampleRate); + } + e->segment = TSF_SEGMENT_ATTACK; + e->segmentIsExponential = TSF_FALSE; + e->level = 0.0f; + e->slope = 1.0f / e->samplesUntilNextSegment; + return; + } + /* fall through */ + case TSF_SEGMENT_ATTACK: + e->samplesUntilNextSegment = (int)(e->parameters.hold * outSampleRate); + if (e->samplesUntilNextSegment > 0) + { + e->segment = TSF_SEGMENT_HOLD; + e->segmentIsExponential = TSF_FALSE; + e->level = 1.0f; + e->slope = 0.0f; + return; + } + /* fall through */ + case TSF_SEGMENT_HOLD: + e->samplesUntilNextSegment = (int)(e->parameters.decay * outSampleRate); + if (e->samplesUntilNextSegment > 0) + { + e->segment = TSF_SEGMENT_DECAY; + e->level = 1.0f; + if (e->isAmpEnv) + { + // I don't truly understand this; just following what LinuxSampler does. + float mysterySlope = -9.226f / e->samplesUntilNextSegment; + e->slope = TSF_EXPF(mysterySlope); + e->segmentIsExponential = TSF_TRUE; + if (e->parameters.sustain > 0.0f) + { + // Again, this is following LinuxSampler's example, which is similar to + // SF2-style decay, where "decay" specifies the time it would take to + // get to zero, not to the sustain level. The SFZ spec is not that + // specific about what "decay" means, so perhaps it's really supposed + // to specify the time to reach the sustain level. + e->samplesUntilNextSegment = (int)(TSF_LOG(e->parameters.sustain) / mysterySlope); + } + } + else + { + e->slope = -1.0f / e->samplesUntilNextSegment; + e->samplesUntilNextSegment = (int)(e->parameters.decay * (1.0f - e->parameters.sustain) * outSampleRate); + e->segmentIsExponential = TSF_FALSE; + } + return; + } + /* fall through */ + case TSF_SEGMENT_DECAY: + e->segment = TSF_SEGMENT_SUSTAIN; + e->level = e->parameters.sustain; + e->slope = 0.0f; + e->samplesUntilNextSegment = 0x7FFFFFFF; + e->segmentIsExponential = TSF_FALSE; + return; + case TSF_SEGMENT_SUSTAIN: + e->segment = TSF_SEGMENT_RELEASE; + e->samplesUntilNextSegment = tsf_voice_envelope_release_samples(e, outSampleRate); + if (e->isAmpEnv) + { + // I don't truly understand this; just following what LinuxSampler does. + float mysterySlope = -9.226f / e->samplesUntilNextSegment; + e->slope = TSF_EXPF(mysterySlope); + e->segmentIsExponential = TSF_TRUE; + } + else + { + e->slope = -e->level / e->samplesUntilNextSegment; + e->segmentIsExponential = TSF_FALSE; + } + return; + case TSF_SEGMENT_RELEASE: + default: + e->segment = TSF_SEGMENT_DONE; + e->segmentIsExponential = TSF_FALSE; + e->level = e->slope = 0.0f; + e->samplesUntilNextSegment = 0x7FFFFFF; + } +} + +static void tsf_voice_envelope_setup(struct tsf_voice_envelope* e, struct tsf_envelope* new_parameters, int midiNoteNumber, short midiVelocity, TSF_BOOL isAmpEnv, float outSampleRate) +{ + e->parameters = *new_parameters; + if (e->parameters.keynumToHold) + { + e->parameters.hold += e->parameters.keynumToHold * (60.0f - midiNoteNumber); + e->parameters.hold = (e->parameters.hold < -10000.0f ? 0.0f : tsf_timecents2Secsf(e->parameters.hold)); + } + if (e->parameters.keynumToDecay) + { + e->parameters.decay += e->parameters.keynumToDecay * (60.0f - midiNoteNumber); + e->parameters.decay = (e->parameters.decay < -10000.0f ? 0.0f : tsf_timecents2Secsf(e->parameters.decay)); + } + e->midiVelocity = midiVelocity; + e->isAmpEnv = isAmpEnv; + tsf_voice_envelope_nextsegment(e, TSF_SEGMENT_NONE, outSampleRate); +} + +static void tsf_voice_envelope_process(struct tsf_voice_envelope* e, int numSamples, float outSampleRate) +{ + if (e->slope) + { + if (e->segmentIsExponential) e->level *= TSF_POWF(e->slope, (float)numSamples); + else e->level += (e->slope * numSamples); + } + if ((e->samplesUntilNextSegment -= numSamples) <= 0) + tsf_voice_envelope_nextsegment(e, e->segment, outSampleRate); +} + +static void tsf_voice_lowpass_setup(struct tsf_voice_lowpass* e, float Fc) +{ + // Lowpass filter from http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/ + double K = TSF_TAN(TSF_PI * Fc), KK = K * K; + double norm = 1 / (1 + K * e->QInv + KK); + e->a0 = KK * norm; + e->a1 = 2 * e->a0; + e->b1 = 2 * (KK - 1) * norm; + e->b2 = (1 - K * e->QInv + KK) * norm; +} + +static float tsf_voice_lowpass_process(struct tsf_voice_lowpass* e, double In) +{ + double Out = In * e->a0 + e->z1; e->z1 = In * e->a1 + e->z2 - e->b1 * Out; e->z2 = In * e->a0 - e->b2 * Out; return (float)Out; +} + +static void tsf_voice_lfo_setup(struct tsf_voice_lfo* e, float delay, int freqCents, float outSampleRate) +{ + e->samplesUntil = (int)(delay * outSampleRate); + e->delta = (4.0f * tsf_cents2Hertz((float)freqCents) / outSampleRate); + e->level = 0; +} + +static void tsf_voice_lfo_process(struct tsf_voice_lfo* e, int blockSamples) +{ + if (e->samplesUntil > blockSamples) { e->samplesUntil -= blockSamples; return; } + e->level += e->delta * blockSamples; + if (e->level > 1.0f) { e->delta = -e->delta; e->level = 2.0f - e->level; } + else if (e->level < -1.0f) { e->delta = -e->delta; e->level = -2.0f - e->level; } +} + +static void tsf_voice_kill(struct tsf_voice* v) +{ + v->playingPreset = -1; +} + +static void tsf_voice_end(tsf* f, struct tsf_voice* v) +{ + // if maxVoiceNum is set, assume that voice rendering and note queuing are on separate threads + // so to minimize the chance that voice rendering would advance the segment at the same time + // we just do it twice here and hope that it sticks + int repeats = (f->maxVoiceNum ? 2 : 1); + while (repeats--) + { + tsf_voice_envelope_nextsegment(&v->ampenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); + tsf_voice_envelope_nextsegment(&v->modenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); + if (v->region->loop_mode == TSF_LOOPMODE_SUSTAIN) + { + // Continue playing, but stop looping. + v->loopEnd = v->loopStart; + } + } +} + +static void tsf_voice_endquick(tsf* f, struct tsf_voice* v) +{ + // if maxVoiceNum is set, assume that voice rendering and note queuing are on separate threads + // so to minimize the chance that voice rendering would advance the segment at the same time + // we just do it twice here and hope that it sticks + int repeats = (f->maxVoiceNum ? 2 : 1); + while (repeats--) + { + v->ampenv.parameters.release = 0.0f; tsf_voice_envelope_nextsegment(&v->ampenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); + v->modenv.parameters.release = 0.0f; tsf_voice_envelope_nextsegment(&v->modenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); + } +} + +static void tsf_voice_calcpitchratio(struct tsf_voice* v, float pitchShift, float outSampleRate) +{ + double note = v->playingKey + v->region->transpose + v->region->tune / 100.0; + double adjustedPitch = v->region->pitch_keycenter + (note - v->region->pitch_keycenter) * (v->region->pitch_keytrack / 100.0); + if (pitchShift) adjustedPitch += pitchShift; + v->pitchInputTimecents = adjustedPitch * 100.0; + v->pitchOutputFactor = v->region->sample_rate / (tsf_timecents2Secsd(v->region->pitch_keycenter * 100.0) * outSampleRate); +} + +static void tsf_voice_render(tsf* f, struct tsf_voice* v, float* outputBuffer, int numSamples) +{ + struct tsf_region* region = v->region; + float* input = f->fontSamples; + float* outL = outputBuffer; + float* outR = (f->outputmode == TSF_STEREO_UNWEAVED ? outL + numSamples : TSF_NULL); + + // Cache some values, to give them at least some chance of ending up in registers. + TSF_BOOL updateModEnv = (region->modEnvToPitch || region->modEnvToFilterFc); + TSF_BOOL updateModLFO = (v->modlfo.delta && (region->modLfoToPitch || region->modLfoToFilterFc || region->modLfoToVolume)); + TSF_BOOL updateVibLFO = (v->viblfo.delta && (region->vibLfoToPitch)); + TSF_BOOL isLooping = (v->loopStart < v->loopEnd); + unsigned int tmpLoopStart = v->loopStart, tmpLoopEnd = v->loopEnd; + double tmpSampleEndDbl = (double)region->end, tmpLoopEndDbl = (double)tmpLoopEnd + 1.0; + double tmpSourceSamplePosition = v->sourceSamplePosition; + struct tsf_voice_lowpass tmpLowpass = v->lowpass; + + TSF_BOOL dynamicLowpass = (region->modLfoToFilterFc || region->modEnvToFilterFc); + float tmpSampleRate = f->outSampleRate, tmpInitialFilterFc, tmpModLfoToFilterFc, tmpModEnvToFilterFc; + + TSF_BOOL dynamicPitchRatio = (region->modLfoToPitch || region->modEnvToPitch || region->vibLfoToPitch); + double pitchRatio; + float tmpModLfoToPitch, tmpVibLfoToPitch, tmpModEnvToPitch; + + TSF_BOOL dynamicGain = (region->modLfoToVolume != 0); + float noteGain = 0, tmpModLfoToVolume; + + if (dynamicLowpass) tmpInitialFilterFc = (float)region->initialFilterFc, tmpModLfoToFilterFc = (float)region->modLfoToFilterFc, tmpModEnvToFilterFc = (float)region->modEnvToFilterFc; + else tmpInitialFilterFc = 0, tmpModLfoToFilterFc = 0, tmpModEnvToFilterFc = 0; + + if (dynamicPitchRatio) pitchRatio = 0, tmpModLfoToPitch = (float)region->modLfoToPitch, tmpVibLfoToPitch = (float)region->vibLfoToPitch, tmpModEnvToPitch = (float)region->modEnvToPitch; + else pitchRatio = tsf_timecents2Secsd(v->pitchInputTimecents) * v->pitchOutputFactor, tmpModLfoToPitch = 0, tmpVibLfoToPitch = 0, tmpModEnvToPitch = 0; + + if (dynamicGain) tmpModLfoToVolume = (float)region->modLfoToVolume * 0.1f; + else noteGain = tsf_decibelsToGain(v->noteGainDB), tmpModLfoToVolume = 0; + + while (numSamples) + { + float gainMono, gainLeft, gainRight; + int blockSamples = (numSamples > TSF_RENDER_EFFECTSAMPLEBLOCK ? TSF_RENDER_EFFECTSAMPLEBLOCK : numSamples); + numSamples -= blockSamples; + + if (dynamicLowpass) + { + float fres = tmpInitialFilterFc + v->modlfo.level * tmpModLfoToFilterFc + v->modenv.level * tmpModEnvToFilterFc; + float lowpassFc = (fres <= 13500 ? tsf_cents2Hertz(fres) / tmpSampleRate : 1.0f); + tmpLowpass.active = (lowpassFc < 0.499f); + if (tmpLowpass.active) tsf_voice_lowpass_setup(&tmpLowpass, lowpassFc); + } + + if (dynamicPitchRatio) + pitchRatio = tsf_timecents2Secsd(v->pitchInputTimecents + (v->modlfo.level * tmpModLfoToPitch + v->viblfo.level * tmpVibLfoToPitch + v->modenv.level * tmpModEnvToPitch)) * v->pitchOutputFactor; + + if (dynamicGain) + noteGain = tsf_decibelsToGain(v->noteGainDB + (v->modlfo.level * tmpModLfoToVolume)); + + gainMono = noteGain * v->ampenv.level; + + // Update EG. + tsf_voice_envelope_process(&v->ampenv, blockSamples, tmpSampleRate); + if (updateModEnv) tsf_voice_envelope_process(&v->modenv, blockSamples, tmpSampleRate); + + // Update LFOs. + if (updateModLFO) tsf_voice_lfo_process(&v->modlfo, blockSamples); + if (updateVibLFO) tsf_voice_lfo_process(&v->viblfo, blockSamples); + + switch (f->outputmode) + { + case TSF_STEREO_INTERLEAVED: + gainLeft = gainMono * v->panFactorLeft, gainRight = gainMono * v->panFactorRight; + while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) + { + unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); + + // Simple linear interpolation. + float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); + + // Low-pass filter. + if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); + + *outL++ += val * gainLeft; + *outL++ += val * gainRight; + + // Next sample. + tmpSourceSamplePosition += pitchRatio; + if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); + } + break; + + case TSF_STEREO_UNWEAVED: + gainLeft = gainMono * v->panFactorLeft, gainRight = gainMono * v->panFactorRight; + while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) + { + unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); + + // Simple linear interpolation. + float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); + + // Low-pass filter. + if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); + + *outL++ += val * gainLeft; + *outR++ += val * gainRight; + + // Next sample. + tmpSourceSamplePosition += pitchRatio; + if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); + } + break; + + case TSF_MONO: + while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) + { + unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); + + // Simple linear interpolation. + float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); + + // Low-pass filter. + if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); + + *outL++ += val * gainMono; + + // Next sample. + tmpSourceSamplePosition += pitchRatio; + if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); + } + break; + } + + if (tmpSourceSamplePosition >= tmpSampleEndDbl || v->ampenv.segment == TSF_SEGMENT_DONE) + { + tsf_voice_kill(v); + return; + } + } + + v->sourceSamplePosition = tmpSourceSamplePosition; + if (tmpLowpass.active || dynamicLowpass) v->lowpass = tmpLowpass; +} + +TSFDEF tsf* tsf_load(struct tsf_stream* stream) +{ + tsf* res = TSF_NULL; + struct tsf_riffchunk chunkHead; + struct tsf_riffchunk chunkList; + struct tsf_hydra hydra; + void* rawBuffer = TSF_NULL; + float* floatBuffer = TSF_NULL; + tsf_u32 smplCount = 0; + + if (!tsf_riffchunk_read(TSF_NULL, &chunkHead, stream) || !TSF_FourCCEquals(chunkHead.id, "sfbk")) + { + //if (e) *e = TSF_INVALID_NOSF2HEADER; + return res; + } + + // Read hydra and locate sample data. + TSF_MEMSET(&hydra, 0, sizeof(hydra)); + while (tsf_riffchunk_read(&chunkHead, &chunkList, stream)) + { + struct tsf_riffchunk chunk; + if (TSF_FourCCEquals(chunkList.id, "pdta")) + { + while (tsf_riffchunk_read(&chunkList, &chunk, stream)) + { + #define HandleChunk(chunkName) (TSF_FourCCEquals(chunk.id, #chunkName) && !(chunk.size % chunkName##SizeInFile)) \ + { \ + int num = chunk.size / chunkName##SizeInFile, i; \ + hydra.chunkName##Num = num; \ + hydra.chunkName##s = (struct tsf_hydra_##chunkName*)TSF_MALLOC(num * sizeof(struct tsf_hydra_##chunkName)); \ + if (!hydra.chunkName##s) goto out_of_memory; \ + for (i = 0; i < num; ++i) tsf_hydra_read_##chunkName(&hydra.chunkName##s[i], stream); \ + } + enum + { + phdrSizeInFile = 38, pbagSizeInFile = 4, pmodSizeInFile = 10, + pgenSizeInFile = 4, instSizeInFile = 22, ibagSizeInFile = 4, + imodSizeInFile = 10, igenSizeInFile = 4, shdrSizeInFile = 46 + }; + if HandleChunk(phdr) else if HandleChunk(pbag) else if HandleChunk(pmod) + else if HandleChunk(pgen) else if HandleChunk(inst) else if HandleChunk(ibag) + else if HandleChunk(imod) else if HandleChunk(igen) else if HandleChunk(shdr) + else stream->skip(stream->data, chunk.size); + #undef HandleChunk + } + } + else if (TSF_FourCCEquals(chunkList.id, "sdta")) + { + while (tsf_riffchunk_read(&chunkList, &chunk, stream)) + { + if ((TSF_FourCCEquals(chunk.id, "smpl") + #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H + || TSF_FourCCEquals(chunk.id, "smpo") + #endif + ) && !rawBuffer && !floatBuffer && chunk.size >= sizeof(short)) + { + if (!tsf_load_samples(&rawBuffer, &floatBuffer, &smplCount, &chunk, stream)) goto out_of_memory; + } + else stream->skip(stream->data, chunk.size); + } + } + else stream->skip(stream->data, chunkList.size); + } + if (!hydra.phdrs || !hydra.pbags || !hydra.pmods || !hydra.pgens || !hydra.insts || !hydra.ibags || !hydra.imods || !hydra.igens || !hydra.shdrs) + { + //if (e) *e = TSF_INVALID_INCOMPLETE; + } + else if (!rawBuffer && !floatBuffer) + { + //if (e) *e = TSF_INVALID_NOSAMPLEDATA; + } + else + { + #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H + if (!floatBuffer && !tsf_decode_sf3_samples(rawBuffer, &floatBuffer, &smplCount, &hydra)) goto out_of_memory; + #endif + res = (tsf*)TSF_MALLOC(sizeof(tsf)); + if (res) TSF_MEMSET(res, 0, sizeof(tsf)); + if (!res || !tsf_load_presets(res, &hydra, smplCount)) goto out_of_memory; + res->outSampleRate = 44100.0f; + res->fontSamples = floatBuffer; + floatBuffer = TSF_NULL; // don't free below + } + if (0) + { + out_of_memory: + TSF_FREE(res); + res = TSF_NULL; + //if (e) *e = TSF_OUT_OF_MEMORY; + } + TSF_FREE(hydra.phdrs); TSF_FREE(hydra.pbags); TSF_FREE(hydra.pmods); + TSF_FREE(hydra.pgens); TSF_FREE(hydra.insts); TSF_FREE(hydra.ibags); + TSF_FREE(hydra.imods); TSF_FREE(hydra.igens); TSF_FREE(hydra.shdrs); + TSF_FREE(rawBuffer); TSF_FREE(floatBuffer); + return res; +} + +TSFDEF tsf* tsf_copy(tsf* f) +{ + tsf* res; + if (!f) return TSF_NULL; + if (!f->refCount) + { + f->refCount = (int*)TSF_MALLOC(sizeof(int)); + if (!f->refCount) return TSF_NULL; + *f->refCount = 1; + } + res = (tsf*)TSF_MALLOC(sizeof(tsf)); + if (!res) return TSF_NULL; + TSF_MEMCPY(res, f, sizeof(tsf)); + res->voices = TSF_NULL; + res->voiceNum = 0; + res->channels = TSF_NULL; + (*res->refCount)++; + return res; +} + +TSFDEF void tsf_close(tsf* f) +{ + if (!f) return; + if (!f->refCount || !--(*f->refCount)) + { + struct tsf_preset *preset = f->presets, *presetEnd = preset + f->presetNum; + for (; preset != presetEnd; preset++) TSF_FREE(preset->regions); + TSF_FREE(f->presets); + TSF_FREE(f->fontSamples); + TSF_FREE(f->refCount); + } + TSF_FREE(f->channels); + TSF_FREE(f->voices); + TSF_FREE(f); +} + +TSFDEF void tsf_reset(tsf* f) +{ + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + for (; v != vEnd; v++) + if (v->playingPreset != -1 && (v->ampenv.segment < TSF_SEGMENT_RELEASE || v->ampenv.parameters.release)) + tsf_voice_endquick(f, v); + if (f->channels) { TSF_FREE(f->channels); f->channels = TSF_NULL; } +} + +TSFDEF int tsf_get_presetindex(const tsf* f, int bank, int preset_number) +{ + const struct tsf_preset *presets; + int i, iMax; + for (presets = f->presets, i = 0, iMax = f->presetNum; i < iMax; i++) + if (presets[i].preset == preset_number && presets[i].bank == bank) + return i; + return -1; +} + +TSFDEF int tsf_get_presetcount(const tsf* f) +{ + return f->presetNum; +} + +TSFDEF const char* tsf_get_presetname(const tsf* f, int preset) +{ + return (preset < 0 || preset >= f->presetNum ? TSF_NULL : f->presets[preset].presetName); +} + +TSFDEF const char* tsf_bank_get_presetname(const tsf* f, int bank, int preset_number) +{ + return tsf_get_presetname(f, tsf_get_presetindex(f, bank, preset_number)); +} + +TSFDEF void tsf_set_output(tsf* f, enum TSFOutputMode outputmode, int samplerate, float global_gain_db) +{ + f->outputmode = outputmode; + f->outSampleRate = (float)(samplerate >= 1 ? samplerate : 44100.0f); + f->globalGainDB = global_gain_db; +} + +TSFDEF void tsf_set_volume(tsf* f, float global_volume) +{ + f->globalGainDB = (global_volume == 1.0f ? 0 : -tsf_gainToDecibels(1.0f / global_volume)); +} + +TSFDEF int tsf_set_max_voices(tsf* f, int max_voices) +{ + int i = f->voiceNum; + int newVoiceNum = (f->voiceNum > max_voices ? f->voiceNum : max_voices); + struct tsf_voice *newVoices = (struct tsf_voice*)TSF_REALLOC(f->voices, newVoiceNum * sizeof(struct tsf_voice)); + if (!newVoices) return 0; + f->voices = newVoices; + f->voiceNum = f->maxVoiceNum = newVoiceNum; + for (; i < max_voices; i++) + f->voices[i].playingPreset = -1; + return 1; +} + +TSFDEF int tsf_note_on(tsf* f, int preset_index, int key, float vel) +{ + short midiVelocity = (short)(vel * 127); + int voicePlayIndex; + struct tsf_region *region, *regionEnd; + + if (preset_index < 0 || preset_index >= f->presetNum) return 1; + if (vel <= 0.0f) { tsf_note_off(f, preset_index, key); return 1; } + + // Play all matching regions. + voicePlayIndex = f->voicePlayIndex++; + for (region = f->presets[preset_index].regions, regionEnd = region + f->presets[preset_index].regionNum; region != regionEnd; region++) + { + struct tsf_voice *voice, *v, *vEnd; TSF_BOOL doLoop; float lowpassFilterQDB, lowpassFc; + if (key < region->lokey || key > region->hikey || midiVelocity < region->lovel || midiVelocity > region->hivel) continue; + + voice = TSF_NULL, v = f->voices, vEnd = v + f->voiceNum; + if (region->group) + { + for (; v != vEnd; v++) + if (v->playingPreset == preset_index && v->region->group == region->group) tsf_voice_endquick(f, v); + else if (v->playingPreset == -1 && !voice) voice = v; + } + else for (; v != vEnd; v++) if (v->playingPreset == -1) { voice = v; break; } + + if (!voice) + { + if (f->maxVoiceNum) + { + // Voices have been pre-allocated and limited to a maximum, try to kill a voice off in its release envelope + int bestKillReleaseSamplePos = -999999999; + for (v = f->voices; v != vEnd; v++) + { + if (v->ampenv.segment == TSF_SEGMENT_RELEASE) + { + // We're looking for the voice furthest into its release + int releaseSamplesDone = tsf_voice_envelope_release_samples(&v->ampenv, f->outSampleRate) - v->ampenv.samplesUntilNextSegment; + if (releaseSamplesDone > bestKillReleaseSamplePos) + { + bestKillReleaseSamplePos = releaseSamplesDone; + voice = v; + } + } + } + if (!voice) + continue; + tsf_voice_kill(voice); + } + else + { + // Allocate more voices so we don't need to kill one off. + struct tsf_voice* newVoices; + f->voiceNum += 4; + newVoices = (struct tsf_voice*)TSF_REALLOC(f->voices, f->voiceNum * sizeof(struct tsf_voice)); + if (!newVoices) return 0; + f->voices = newVoices; + voice = &f->voices[f->voiceNum - 4]; + voice[1].playingPreset = voice[2].playingPreset = voice[3].playingPreset = -1; + } + } + + voice->region = region; + voice->playingPreset = preset_index; + voice->playingKey = key; + voice->playIndex = voicePlayIndex; + voice->heldSustain = 0; + voice->noteGainDB = f->globalGainDB - (region->attenuation / 10.0f) - tsf_gainToDecibels(1.0f / vel) * 0.4f; + + if (f->channels) + { + f->channels->setupVoice(f, voice); + } + else + { + tsf_voice_calcpitchratio(voice, 0, f->outSampleRate); + // The SFZ spec is silent about the pan curve, but a 3dB pan law seems common. This sqrt() curve matches what Dimension LE does; Alchemy Free seems closer to sin(adjustedPan * pi/2). + voice->panFactorLeft = TSF_SQRTF(0.5f - region->pan); + voice->panFactorRight = TSF_SQRTF(0.5f + region->pan); + } + + // Offset/end. + voice->sourceSamplePosition = region->offset; + + // Loop. + doLoop = (region->loop_mode != TSF_LOOPMODE_NONE && region->loop_start < region->loop_end); + voice->loopStart = (doLoop ? region->loop_start : 0); + voice->loopEnd = (doLoop ? region->loop_end : 0); + + // Setup envelopes. + tsf_voice_envelope_setup(&voice->ampenv, ®ion->ampenv, key, midiVelocity, TSF_TRUE, f->outSampleRate); + tsf_voice_envelope_setup(&voice->modenv, ®ion->modenv, key, midiVelocity, TSF_FALSE, f->outSampleRate); + + // Setup lowpass filter. + lowpassFc = (region->initialFilterFc <= 13500 ? tsf_cents2Hertz((float)region->initialFilterFc) / f->outSampleRate : 1.0f); + lowpassFilterQDB = region->initialFilterQ / 10.0f; + voice->lowpass.QInv = 1.0 / TSF_POW(10.0, (lowpassFilterQDB / 20.0)); + voice->lowpass.z1 = voice->lowpass.z2 = 0; + voice->lowpass.active = (lowpassFc < 0.499f); + if (voice->lowpass.active) tsf_voice_lowpass_setup(&voice->lowpass, lowpassFc); + + // Setup LFO filters. + tsf_voice_lfo_setup(&voice->modlfo, region->delayModLFO, region->freqModLFO, f->outSampleRate); + tsf_voice_lfo_setup(&voice->viblfo, region->delayVibLFO, region->freqVibLFO, f->outSampleRate); + } + return 1; +} + +TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel) +{ + int preset_index = tsf_get_presetindex(f, bank, preset_number); + if (preset_index == -1) return 0; + return tsf_note_on(f, preset_index, key, vel); +} + +TSFDEF void tsf_note_off(tsf* f, int preset_index, int key) +{ + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = TSF_NULL, *vMatchLast = TSF_NULL; + for (; v != vEnd; v++) + { + //Find the first and last entry in the voices list with matching preset, key and look up the smallest play index + if (v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE) continue; + else if (!vMatchFirst || v->playIndex < vMatchFirst->playIndex) vMatchFirst = vMatchLast = v; + else if (v->playIndex == vMatchFirst->playIndex) vMatchLast = v; + } + if (!vMatchFirst) return; + for (v = vMatchFirst; v <= vMatchLast; v++) + { + //Stop all voices with matching preset, key and the smallest play index which was enumerated above + if (v != vMatchFirst && v != vMatchLast && + (v->playIndex != vMatchFirst->playIndex || v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE)) continue; + tsf_voice_end(f, v); + } +} + +TSFDEF int tsf_bank_note_off(tsf* f, int bank, int preset_number, int key) +{ + int preset_index = tsf_get_presetindex(f, bank, preset_number); + if (preset_index == -1) return 0; + tsf_note_off(f, preset_index, key); + return 1; +} + +TSFDEF void tsf_note_off_all(tsf* f) +{ + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + for (; v != vEnd; v++) if (v->playingPreset != -1 && v->ampenv.segment < TSF_SEGMENT_RELEASE) + tsf_voice_end(f, v); +} + +TSFDEF int tsf_active_voice_count(tsf* f) +{ + int count = 0; + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + for (; v != vEnd; v++) if (v->playingPreset != -1) count++; + return count; +} + +TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing) +{ + float outputSamples[TSF_RENDER_SHORTBUFFERBLOCK]; + int channels = (f->outputmode == TSF_MONO ? 1 : 2), maxChannelSamples = TSF_RENDER_SHORTBUFFERBLOCK / channels; + while (samples > 0) + { + int channelSamples = (samples > maxChannelSamples ? maxChannelSamples : samples); + short* bufferEnd = buffer + channelSamples * channels; + float *floatSamples = outputSamples; + tsf_render_float(f, floatSamples, channelSamples, TSF_FALSE); + samples -= channelSamples; + + if (flag_mixing) + while (buffer != bufferEnd) + { + float v = *floatSamples++; + int vi = *buffer + (v < -1.00004566f ? (int)-32768 : (v > 1.00001514f ? (int)32767 : (int)(v * 32767.5f))); + *buffer++ = (vi < -32768 ? (short)-32768 : (vi > 32767 ? (short)32767 : (short)vi)); + } + else + while (buffer != bufferEnd) + { + float v = *floatSamples++; + *buffer++ = (v < -1.00004566f ? (short)-32768 : (v > 1.00001514f ? (short)32767 : (short)(v * 32767.5f))); + } + } +} + +TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing) +{ + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + if (!flag_mixing) TSF_MEMSET(buffer, 0, (f->outputmode == TSF_MONO ? 1 : 2) * sizeof(float) * samples); + for (; v != vEnd; v++) + if (v->playingPreset != -1) + tsf_voice_render(f, v, buffer, samples); +} + +static void tsf_channel_setup_voice(tsf* f, struct tsf_voice* v) +{ + struct tsf_channel* c = &f->channels->channels[f->channels->activeChannel]; + float newpan = v->region->pan + c->panOffset; + v->playingChannel = f->channels->activeChannel; + v->noteGainDB += c->gainDB; + tsf_voice_calcpitchratio(v, (c->pitchWheel == 8192 ? c->tuning : ((c->pitchWheel / 16383.0f * c->pitchRange * 2.0f) - c->pitchRange + c->tuning)), f->outSampleRate); + if (newpan <= -0.5f) { v->panFactorLeft = 1.0f; v->panFactorRight = 0.0f; } + else if (newpan >= 0.5f) { v->panFactorLeft = 0.0f; v->panFactorRight = 1.0f; } + else { v->panFactorLeft = TSF_SQRTF(0.5f - newpan); v->panFactorRight = TSF_SQRTF(0.5f + newpan); } +} + +static struct tsf_channel* tsf_channel_init(tsf* f, int channel) +{ + int i; + if (f->channels && channel < f->channels->channelNum) return &f->channels->channels[channel]; + if (!f->channels) + { + f->channels = (struct tsf_channels*)TSF_MALLOC(sizeof(struct tsf_channels) + sizeof(struct tsf_channel) * channel); + if (!f->channels) return TSF_NULL; + f->channels->setupVoice = &tsf_channel_setup_voice; + f->channels->channelNum = 0; + f->channels->activeChannel = 0; + } + else + { + struct tsf_channels *newChannels = (struct tsf_channels*)TSF_REALLOC(f->channels, sizeof(struct tsf_channels) + sizeof(struct tsf_channel) * channel); + if (!newChannels) return TSF_NULL; + f->channels = newChannels; + } + i = f->channels->channelNum; + f->channels->channelNum = channel + 1; + for (; i <= channel; i++) + { + struct tsf_channel* c = &f->channels->channels[i]; + c->presetIndex = c->bank = c->sustain = 0; + c->pitchWheel = c->midiPan = 8192; + c->midiVolume = c->midiExpression = 16383; + c->midiRPN = 0xFFFF; + c->midiData = 0; + c->panOffset = 0.0f; + c->gainDB = 0.0f; + c->pitchRange = 2.0f; + c->tuning = 0.0f; + } + return &f->channels->channels[channel]; +} + +static void tsf_channel_applypitch(tsf* f, int channel, struct tsf_channel* c) +{ + struct tsf_voice *v, *vEnd; + float pitchShift = (c->pitchWheel == 8192 ? c->tuning : ((c->pitchWheel / 16383.0f * c->pitchRange * 2.0f) - c->pitchRange + c->tuning)); + for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++) + if (v->playingPreset != -1 && v->playingChannel == channel) + tsf_voice_calcpitchratio(v, pitchShift, f->outSampleRate); +} + +TSFDEF int tsf_channel_set_presetindex(tsf* f, int channel, int preset_index) +{ + struct tsf_channel *c = tsf_channel_init(f, channel); + if (!c) return 0; + c->presetIndex = (unsigned short)preset_index; + return 1; +} + +TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums) +{ + int preset_index; + struct tsf_channel *c = tsf_channel_init(f, channel); + if (!c) return 0; + if (flag_mididrums) + { + preset_index = tsf_get_presetindex(f, 128 | (c->bank & 0x7FFF), preset_number); + if (preset_index == -1) preset_index = tsf_get_presetindex(f, 128, preset_number); + if (preset_index == -1) preset_index = tsf_get_presetindex(f, 128, 0); + if (preset_index == -1) preset_index = tsf_get_presetindex(f, (c->bank & 0x7FFF), preset_number); + } + else preset_index = tsf_get_presetindex(f, (c->bank & 0x7FFF), preset_number); + if (preset_index == -1) preset_index = tsf_get_presetindex(f, 0, preset_number); + if (preset_index != -1) + { + c->presetIndex = (unsigned short)preset_index; + return 1; + } + return 0; +} + +TSFDEF int tsf_channel_set_bank(tsf* f, int channel, int bank) +{ + struct tsf_channel *c = tsf_channel_init(f, channel); + if (!c) return 0; + c->bank = (unsigned short)bank; + return 1; +} + +TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number) +{ + int preset_index; + struct tsf_channel *c = tsf_channel_init(f, channel); + if (!c) return 0; + preset_index = tsf_get_presetindex(f, bank, preset_number); + if (preset_index == -1) return 0; + c->presetIndex = (unsigned short)preset_index; + c->bank = (unsigned short)bank; + return 1; +} + +TSFDEF int tsf_channel_set_pan(tsf* f, int channel, float pan) +{ + struct tsf_voice *v, *vEnd; + struct tsf_channel *c = tsf_channel_init(f, channel); + if (!c) return 0; + for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++) + if (v->playingPreset != -1 && v->playingChannel == channel) + { + float newpan = v->region->pan + pan - 0.5f; + if (newpan <= -0.5f) { v->panFactorLeft = 1.0f; v->panFactorRight = 0.0f; } + else if (newpan >= 0.5f) { v->panFactorLeft = 0.0f; v->panFactorRight = 1.0f; } + else { v->panFactorLeft = TSF_SQRTF(0.5f - newpan); v->panFactorRight = TSF_SQRTF(0.5f + newpan); } + } + c->panOffset = pan - 0.5f; + return 1; +} + +TSFDEF int tsf_channel_set_volume(tsf* f, int channel, float volume) +{ + float gainDB = tsf_gainToDecibels(volume), gainDBChange; + struct tsf_voice *v, *vEnd; + struct tsf_channel *c = tsf_channel_init(f, channel); + if (!c) return 0; + if (gainDB == c->gainDB) return 1; + for (v = f->voices, vEnd = v + f->voiceNum, gainDBChange = gainDB - c->gainDB; v != vEnd; v++) + if (v->playingPreset != -1 && v->playingChannel == channel) + v->noteGainDB += gainDBChange; + c->gainDB = gainDB; + return 1; +} + +TSFDEF int tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel) +{ + struct tsf_channel *c = tsf_channel_init(f, channel); + if (!c) return 0; + if (c->pitchWheel == pitch_wheel) return 1; + c->pitchWheel = (unsigned short)pitch_wheel; + tsf_channel_applypitch(f, channel, c); + return 1; +} + +TSFDEF int tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range) +{ + struct tsf_channel *c = tsf_channel_init(f, channel); + if (!c) return 0; + if (c->pitchRange == pitch_range) return 1; + c->pitchRange = pitch_range; + if (c->pitchWheel != 8192) tsf_channel_applypitch(f, channel, c); + return 1; +} + +TSFDEF int tsf_channel_set_tuning(tsf* f, int channel, float tuning) +{ + struct tsf_channel *c = tsf_channel_init(f, channel); + if (!c) return 0; + if (c->tuning == tuning) return 1; + c->tuning = tuning; + tsf_channel_applypitch(f, channel, c); + return 1; +} + +TSFDEF int tsf_channel_set_sustain(tsf* f, int channel, int sustain) +{ + struct tsf_channel *c = tsf_channel_init(f, channel); + if (!c) return 0; + if (c->sustain == sustain) return 1; + c->sustain = sustain; + // Turning on sustain does no action now, just starts note_off behaving differently + if (sustain) return 1; + // Turning off sustain, actually end voices that got a note_off and were set to heldSustain status + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + for (; v != vEnd; v++) + if (v->playingPreset != -1 && v->playingChannel == channel && v->ampenv.segment < TSF_SEGMENT_RELEASE && v->heldSustain) + tsf_voice_end(f, v); + return 1; +} + +TSFDEF int tsf_channel_note_on(tsf* f, int channel, int key, float vel) +{ + if (!f->channels || channel >= f->channels->channelNum) return 1; + f->channels->activeChannel = channel; + if (!vel) + { + tsf_channel_note_off(f, channel, key); + return 1; + } + return tsf_note_on(f, f->channels->channels[channel].presetIndex, key, vel); +} + +TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key) +{ + int sustain = f->channels->channels[channel].sustain; + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = TSF_NULL, *vMatchLast = TSF_NULL; + for (; v != vEnd; v++) + { + //Find the first and last entry in the voices list with matching channel, key and look up the smallest play index + if (v->playingPreset == -1 || v->playingChannel != channel || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE || v->heldSustain) continue; + else if (!vMatchFirst || v->playIndex < vMatchFirst->playIndex) vMatchFirst = vMatchLast = v; + else if (v->playIndex == vMatchFirst->playIndex) vMatchLast = v; + } + if (!vMatchFirst) return; + for (v = vMatchFirst; v <= vMatchLast; v++) + { + //Stop all voices with matching channel, key and the smallest play index which was enumerated above + if (v != vMatchFirst && v != vMatchLast && + (v->playIndex != vMatchFirst->playIndex || v->playingPreset == -1 || v->playingChannel != channel || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE)) continue; + // Don't turn off if sustain is active, just mark as held by sustain so we don't forget it + if (sustain) + v->heldSustain = 1; + else + tsf_voice_end(f, v); + } +} + +TSFDEF void tsf_channel_note_off_all(tsf* f, int channel) +{ + // Ignore sustain channel settings, note_off_all overrides + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + for (; v != vEnd; v++) + if (v->playingPreset != -1 && v->playingChannel == channel && v->ampenv.segment < TSF_SEGMENT_RELEASE) + tsf_voice_end(f, v); +} + +TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel) +{ + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + for (; v != vEnd; v++) + if (v->playingPreset != -1 && v->playingChannel == channel && (v->ampenv.segment < TSF_SEGMENT_RELEASE || v->ampenv.parameters.release)) + tsf_voice_endquick(f, v); +} + +TSFDEF int tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value) +{ + struct tsf_channel* c = tsf_channel_init(f, channel); + if (!c) return 0; + switch (controller) + { + case 7 /*VOLUME_MSB*/ : c->midiVolume = (unsigned short)((c->midiVolume & 0x7F ) | (control_value << 7)); goto TCMC_SET_VOLUME; + case 39 /*VOLUME_LSB*/ : c->midiVolume = (unsigned short)((c->midiVolume & 0x3F80) | control_value); goto TCMC_SET_VOLUME; + case 11 /*EXPRESSION_MSB*/ : c->midiExpression = (unsigned short)((c->midiExpression & 0x7F ) | (control_value << 7)); goto TCMC_SET_VOLUME; + case 43 /*EXPRESSION_LSB*/ : c->midiExpression = (unsigned short)((c->midiExpression & 0x3F80) | control_value); goto TCMC_SET_VOLUME; + case 10 /*PAN_MSB*/ : c->midiPan = (unsigned short)((c->midiPan & 0x7F ) | (control_value << 7)); goto TCMC_SET_PAN; + case 42 /*PAN_LSB*/ : c->midiPan = (unsigned short)((c->midiPan & 0x3F80) | control_value); goto TCMC_SET_PAN; + case 6 /*DATA_ENTRY_MSB*/ : c->midiData = (unsigned short)((c->midiData & 0x7F) | (control_value << 7)); goto TCMC_SET_DATA; + case 38 /*DATA_ENTRY_LSB*/ : c->midiData = (unsigned short)((c->midiData & 0x3F80) | control_value); goto TCMC_SET_DATA; + case 0 /*BANK_SELECT_MSB*/ : c->bank = (unsigned short)(0x8000 | control_value); return 1; //bank select MSB alone acts like LSB + case 32 /*BANK_SELECT_LSB*/ : c->bank = (unsigned short)((c->bank & 0x8000 ? ((c->bank & 0x7F) << 7) : 0) | control_value); return 1; + case 101 /*RPN_MSB*/ : c->midiRPN = (unsigned short)(((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x7F ) | (control_value << 7)); return 1; + case 100 /*RPN_LSB*/ : c->midiRPN = (unsigned short)(((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x3F80) | control_value); return 1; + case 98 /*NRPN_LSB*/ : c->midiRPN = 0xFFFF; return 1; + case 99 /*NRPN_MSB*/ : c->midiRPN = 0xFFFF; return 1; + case 64 /*SUSTAIN*/ : tsf_channel_set_sustain(f, channel, control_value >= 64); return 1; + case 120 /*ALL_SOUND_OFF*/ : tsf_channel_sounds_off_all(f, channel); return 1; + case 123 /*ALL_NOTES_OFF*/ : tsf_channel_note_off_all(f, channel); return 1; + case 121 /*ALL_CTRL_OFF*/ : + c->midiVolume = c->midiExpression = 16383; + c->midiPan = 8192; + c->bank = 0; + c->midiRPN = 0xFFFF; + c->midiData = 0; + tsf_channel_set_volume(f, channel, 1.0f); + tsf_channel_set_pan(f, channel, 0.5f); + tsf_channel_set_pitchrange(f, channel, 2.0f); + tsf_channel_set_tuning(f, channel, 0); + return 1; + } + return 1; +TCMC_SET_VOLUME: + //Raising to the power of 3 seems to result in a decent sounding volume curve for MIDI + tsf_channel_set_volume(f, channel, TSF_POWF((c->midiVolume / 16383.0f) * (c->midiExpression / 16383.0f), 3.0f)); + return 1; +TCMC_SET_PAN: + tsf_channel_set_pan(f, channel, c->midiPan / 16383.0f); + return 1; +TCMC_SET_DATA: + if (c->midiRPN == 0) tsf_channel_set_pitchrange(f, channel, (c->midiData >> 7) + 0.01f * (c->midiData & 0x7F)); + else if (c->midiRPN == 1) tsf_channel_set_tuning(f, channel, (int)c->tuning + ((float)c->midiData - 8192.0f) / 8192.0f); //fine tune + else if (c->midiRPN == 2 && controller == 6) tsf_channel_set_tuning(f, channel, ((float)control_value - 64.0f) + (c->tuning - (int)c->tuning)); //coarse tune + return 1; +} + +TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel) +{ + return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].presetIndex : 0); +} + +TSFDEF int tsf_channel_get_preset_bank(tsf* f, int channel) +{ + return (f->channels && channel < f->channels->channelNum ? (f->channels->channels[channel].bank & 0x7FFF) : 0); +} + +TSFDEF int tsf_channel_get_preset_number(tsf* f, int channel) +{ + return (f->channels && channel < f->channels->channelNum ? f->presets[f->channels->channels[channel].presetIndex].preset : 0); +} + +TSFDEF float tsf_channel_get_pan(tsf* f, int channel) +{ + return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].panOffset - 0.5f : 0.5f); +} + +TSFDEF float tsf_channel_get_volume(tsf* f, int channel) +{ + return (f->channels && channel < f->channels->channelNum ? tsf_decibelsToGain(f->channels->channels[channel].gainDB) : 1.0f); +} + +TSFDEF int tsf_channel_get_pitchwheel(tsf* f, int channel) +{ + return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].pitchWheel : 8192); +} + +TSFDEF float tsf_channel_get_pitchrange(tsf* f, int channel) +{ + return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].pitchRange : 2.0f); +} + +TSFDEF float tsf_channel_get_tuning(tsf* f, int channel) +{ + return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].tuning : 0.0f); +} + +#ifdef __cplusplus +} +#endif + +#endif //TSF_IMPLEMENTATION diff --git a/source_files/edge/CMakeLists.txt b/source_files/edge/CMakeLists.txt index 46e266a21..d5fd0d532 100644 --- a/source_files/edge/CMakeLists.txt +++ b/source_files/edge/CMakeLists.txt @@ -86,7 +86,7 @@ set (EDGE_SOURCE_FILES s_music.cc s_ogg.cc s_emidi.cc - s_fluid.cc + s_tsf.cc s_fmm.cc s_ibxm.cc s_imf.cc @@ -141,7 +141,6 @@ set (EDGE_LINK_LIBRARIES almostequals crsid dr_libs - fluidlite fmmidi HandmadeMath ibxm @@ -153,6 +152,7 @@ set (EDGE_LINK_LIBRARIES pl_mpeg prns stb + tsf xxhash ) diff --git a/source_files/edge/i_sound.cc b/source_files/edge/i_sound.cc index a2fea62dc..282078d0f 100644 --- a/source_files/edge/i_sound.cc +++ b/source_files/edge/i_sound.cc @@ -29,7 +29,7 @@ #include "m_random.h" #include "s_blit.h" #include "s_cache.h" -#include "s_fluid.h" +#include "s_tsf.h" #include "s_sound.h" #include "w_wad.h" @@ -262,8 +262,8 @@ void StartupMusic(void) } } - if (!StartupFluid()) - fluid_disabled = true; + if (!StartupTSF()) + tsf_disabled = true; return; } diff --git a/source_files/edge/m_option.cc b/source_files/edge/m_option.cc index adcff07d7..ea1d3d8c4 100644 --- a/source_files/edge/m_option.cc +++ b/source_files/edge/m_option.cc @@ -108,7 +108,7 @@ #include "s_blit.h" #include "s_cache.h" #include "s_emidi.h" -#include "s_fluid.h" +#include "s_tsf.h" #include "s_fmm.h" #include "s_music.h" #include "s_sound.h" @@ -526,9 +526,9 @@ static OptionMenuItem soundoptions[] = { {kOptionMenuItemTypePlain, "", nullptr, 0, nullptr, nullptr, nullptr}, {kOptionMenuItemTypeSwitch, "Stereo", "Off/On/Swapped", 3, &var_sound_stereo, nullptr, "NeedRestart"}, {kOptionMenuItemTypePlain, "", nullptr, 0, nullptr, nullptr, nullptr}, - {kOptionMenuItemTypeSwitch, "MIDI Player", "Fluidlite/FMMIDI/Emu de MIDI (OPLL Mode)/Emu de MIDI (SCC-PSG Mode)", 4, &var_midi_player, OptionMenuChangeMidiPlayer, + {kOptionMenuItemTypeSwitch, "MIDI Player", "TinySoundFont/FMMIDI/Emu de MIDI (OPLL Mode)/Emu de MIDI (SCC-PSG Mode)", 4, &var_midi_player, OptionMenuChangeMidiPlayer, nullptr}, - {kOptionMenuItemTypeFunction, "Fluidlite Soundfont", nullptr, 0, nullptr, OptionMenuChangeSoundfont, nullptr}, + {kOptionMenuItemTypeFunction, "TinySoundFont Bank", nullptr, 0, nullptr, OptionMenuChangeSoundfont, nullptr}, {kOptionMenuItemTypeBoolean, "PC Speaker Mode", YesNo, 2, &pc_speaker_mode, OptionMenuChangePCSpeakerMode, "Music will be Off while this is enabled"}, {kOptionMenuItemTypePlain, "", nullptr, 0, nullptr, nullptr, nullptr}, @@ -2037,7 +2037,7 @@ static void OptionMenuChangeLanguage(int key_pressed, ConsoleVariable *console_v static void OptionMenuChangeMidiPlayer(int key_pressed, ConsoleVariable *console_variable) { if (var_midi_player == 0) - RestartFluid(); + RestartTSF(); else if (var_midi_player == 1) RestartFMM(); else @@ -2086,7 +2086,7 @@ static void OptionMenuChangeSoundfont(int key_pressed, ConsoleVariable *console_ // update console_variable midi_soundfont = available_soundfonts.at(sf_pos); - RestartFluid(); + RestartTSF(); } // diff --git a/source_files/edge/s_fluid.cc b/source_files/edge/s_fluid.cc deleted file mode 100644 index 28fbe7772..000000000 --- a/source_files/edge/s_fluid.cc +++ /dev/null @@ -1,438 +0,0 @@ -//---------------------------------------------------------------------------- -// EDGE FluidLite Music Player -//---------------------------------------------------------------------------- -// -// Copyright (c) 2022-2024 The EDGE Team. -// -// 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 3 -// 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. -// -//---------------------------------------------------------------------------- - -#include "s_fluid.h" - -#include - -#include "HandmadeMath.h" -#include "dm_state.h" -#include "epi_file.h" -#include "epi_filesystem.h" -#include "fluidlite.h" -#include "i_movie.h" -#include "i_system.h" -#include "m_misc.h" -// clang-format off -#define MidiFraction FluidFraction -#define MidiSequencer FluidSequencer -typedef struct MidiRealTimeInterface FluidInterface; -#include "midi_sequencer_impl.hpp" -// clang-format on -#include "epi_str_compare.h" -#include "epi_str_util.h" -#include "s_blit.h" -#include "s_music.h" - -extern int sound_device_frequency; - -bool fluid_disabled = false; - -fluid_synth_t *edge_fluid = nullptr; -fluid_settings_t *edge_fluid_settings = nullptr; -fluid_sfloader_t *edge_fluid_sf2_loader = nullptr; - -EDGE_DEFINE_CONSOLE_VARIABLE(midi_soundfont, "", (ConsoleVariableFlag)(kConsoleVariableFlagArchive | kConsoleVariableFlagFilepath)) - -EDGE_DEFINE_CONSOLE_VARIABLE(fluid_player_gain, "0.6", kConsoleVariableFlagArchive) - -extern std::vector available_soundfonts; - -static void FluidError(int level, char *message, void *data) -{ - (void)level; - (void)data; - FatalError("Fluidlite: %s\n", message); -} - -static void *edge_fluid_fopen(fluid_fileapi_t *fileapi, const char *filename) -{ - FILE *fp = epi::FileOpenRaw(filename, epi::kFileAccessRead | epi::kFileAccessBinary); - if (!fp) - return nullptr; - return fp; -} - -bool StartupFluid(void) -{ - LogPrint("Initializing FluidLite...\n"); - - // Check for presence of previous CVAR value's file - bool cvar_good = false; - for (size_t i = 0; i < available_soundfonts.size(); i++) - { - if (epi::StringCaseCompareASCII(midi_soundfont.s_, available_soundfonts.at(i)) == 0) - { - cvar_good = true; - break; - } - } - - if (!cvar_good) - { - LogWarning("Cannot find previously used soundfont %s, falling back to " - "default!\n", - midi_soundfont.c_str()); - midi_soundfont = epi::SanitizePath(epi::PathAppend(game_directory, "soundfont/Default.sf2")); - if (!epi::FileExists(midi_soundfont.s_)) - FatalError("Fluidlite: Cannot locate default soundfont (Default.sf2)! " - "Please check the /soundfont directory " - "of your EDGE-Classic install!\n"); - } - - // Initialize settings and change values from default if needed - fluid_set_log_function(FLUID_PANIC, FluidError, nullptr); - fluid_set_log_function(FLUID_ERR, nullptr, nullptr); - fluid_set_log_function(FLUID_WARN, nullptr, nullptr); - fluid_set_log_function(FLUID_DBG, nullptr, nullptr); - edge_fluid_settings = new_fluid_settings(); - fluid_settings_setstr(edge_fluid_settings, "synth.reverb.active", "no"); - fluid_settings_setstr(edge_fluid_settings, "synth.chorus.active", "no"); - fluid_settings_setnum(edge_fluid_settings, "synth.gain", fluid_player_gain.f_); - fluid_settings_setnum(edge_fluid_settings, "synth.sample-rate", sound_device_frequency); - fluid_settings_setnum(edge_fluid_settings, "synth.polyphony", 64); - edge_fluid = new_fluid_synth(edge_fluid_settings); - - // Register loader that uses our custom function to provide - // a FILE pointer - edge_fluid_sf2_loader = new_fluid_defsfloader(); - edge_fluid_sf2_loader->fileapi = new fluid_fileapi_t; - fluid_init_default_fileapi(edge_fluid_sf2_loader->fileapi); - edge_fluid_sf2_loader->fileapi->fopen = edge_fluid_fopen; - fluid_synth_add_sfloader(edge_fluid, edge_fluid_sf2_loader); - - if (fluid_synth_sfload(edge_fluid, midi_soundfont.c_str(), 1) == -1) - { - LogWarning("FluidLite: Initialization failure.\n"); - delete_fluid_synth(edge_fluid); - delete_fluid_settings(edge_fluid_settings); - return false; - } - - fluid_synth_program_reset(edge_fluid); - - return true; // OK! -} - -// Should only be invoked when switching soundfonts -void RestartFluid(void) -{ - if (fluid_disabled) - return; - - LogPrint("Restarting FluidLite...\n"); - - int old_entry = entry_playing; - - StopMusic(); - - delete_fluid_synth(edge_fluid); - delete_fluid_settings(edge_fluid_settings); - edge_fluid = nullptr; - edge_fluid_settings = nullptr; - edge_fluid_sf2_loader = nullptr; // This is already deleted upon invoking delete_fluid_synth - - if (!StartupFluid()) - { - fluid_disabled = true; - return; - } - - ChangeMusic(old_entry, - true); // Restart track that was playing when switched - - return; // OK! -} - -class FluidPlayer : public AbstractMusicPlayer -{ - private: - enum Status - { - kNotLoaded, - kPlaying, - kPaused, - kStopped - }; - - int status_; - bool looping_; - - FluidInterface *fluid_interface_; - - public: - FluidPlayer(uint8_t *data, int _length, bool looping) : status_(kNotLoaded), looping_(looping) - { - SequencerInit(); - } - - ~FluidPlayer() - { - Close(); - } - - public: - FluidSequencer *fluid_sequencer_; - - static void rtNoteOn(void *userdata, uint8_t channel, uint8_t note, uint8_t velocity) - { - fluid_synth_noteon(edge_fluid, channel, note, velocity); - } - - static void rtNoteOff(void *userdata, uint8_t channel, uint8_t note) - { - fluid_synth_noteoff(edge_fluid, channel, note); - } - - static void rtNoteAfterTouch(void *userdata, uint8_t channel, uint8_t note, uint8_t atVal) - { - fluid_synth_key_pressure(edge_fluid, channel, note, atVal); - } - - static void rtChannelAfterTouch(void *userdata, uint8_t channel, uint8_t atVal) - { - fluid_synth_channel_pressure(edge_fluid, channel, atVal); - } - - static void rtControllerChange(void *userdata, uint8_t channel, uint8_t type, uint8_t value) - { - fluid_synth_cc(edge_fluid, channel, type, value); - } - - static void rtPatchChange(void *userdata, uint8_t channel, uint8_t patch) - { - fluid_synth_program_change(edge_fluid, channel, patch); - } - - static void rtPitchBend(void *userdata, uint8_t channel, uint8_t msb, uint8_t lsb) - { - fluid_synth_pitch_bend(edge_fluid, channel, (msb << 7) | lsb); - } - - static void rtSysEx(void *userdata, const uint8_t *msg, size_t size) - { - fluid_synth_sysex(edge_fluid, (const char *)msg, (int)size, nullptr, nullptr, nullptr, 0); - } - - static void rtDeviceSwitch(void *userdata, size_t track, const char *data, size_t length) - { - (void)userdata; - (void)track; - (void)data; - (void)length; - } - - static size_t rtCurrentDevice(void *userdata, size_t track) - { - (void)userdata; - (void)track; - return 0; - } - - static void playSynth(void *userdata, uint8_t *stream, size_t length) - { - fluid_synth_write_s16(edge_fluid, (int)length / 2 / sizeof(int16_t), stream, 0, 2, stream + 2, 0, 2); - } - - void SequencerInit() - { - fluid_sequencer_ = new FluidSequencer; - fluid_interface_ = new FluidInterface; - memset(fluid_interface_, 0, sizeof(MidiRealTimeInterface)); - - fluid_interface_->rtUserData = this; - fluid_interface_->rt_noteOn = rtNoteOn; - fluid_interface_->rt_noteOff = rtNoteOff; - fluid_interface_->rt_noteAfterTouch = rtNoteAfterTouch; - fluid_interface_->rt_channelAfterTouch = rtChannelAfterTouch; - fluid_interface_->rt_controllerChange = rtControllerChange; - fluid_interface_->rt_patchChange = rtPatchChange; - fluid_interface_->rt_pitchBend = rtPitchBend; - fluid_interface_->rt_systemExclusive = rtSysEx; - - fluid_interface_->onPcmRender = playSynth; - fluid_interface_->onPcmRender_userdata = this; - - fluid_interface_->pcmSampleRate = sound_device_frequency; - fluid_interface_->pcmFrameSize = 2 /*channels*/ * sizeof(int16_t) /*size of one sample*/; - - fluid_interface_->rt_deviceSwitch = rtDeviceSwitch; - fluid_interface_->rt_currentDevice = rtCurrentDevice; - - fluid_sequencer_->SetInterface(fluid_interface_); - } - - bool LoadTrack(const uint8_t *data, int length) - { - return fluid_sequencer_->LoadMidi(data, length); - } - - void Close(void) - { - if (status_ == kNotLoaded) - return; - - // Stop playback - if (status_ != kStopped) - Stop(); - - if (fluid_sequencer_) - { - delete fluid_sequencer_; - fluid_sequencer_ = nullptr; - } - if (fluid_interface_) - { - delete fluid_interface_; - fluid_interface_ = nullptr; - } - - status_ = kNotLoaded; - } - - void Play(bool loop) - { - if (!(status_ == kNotLoaded || status_ == kStopped)) - return; - - status_ = kPlaying; - looping_ = loop; - - // Load up initial buffer data - Ticker(); - } - - void Stop(void) - { - if (!(status_ == kPlaying || status_ == kPaused)) - return; - - fluid_synth_all_voices_stop(edge_fluid); - - SoundQueueStop(); - - status_ = kStopped; - } - - void Pause(void) - { - if (status_ != kPlaying) - return; - - fluid_synth_all_voices_pause(edge_fluid); - - status_ = kPaused; - } - - void Resume(void) - { - if (status_ != kPaused) - return; - - status_ = kPlaying; - } - - void Ticker(void) - { - if (fluid_player_gain.CheckModified()) - { - fluid_player_gain.f_ = HMM_Clamp(0.0, fluid_player_gain.f_, 2.0f); - fluid_player_gain = fluid_player_gain.f_; - fluid_synth_set_gain(edge_fluid, fluid_player_gain.f_); - } - - while (status_ == kPlaying && !pc_speaker_mode && !playing_movie) - { - SoundData *buf = SoundQueueGetFreeBuffer(kMusicBuffer); - - if (!buf) - break; - - if (StreamIntoBuffer(buf)) - { - SoundQueueAddBuffer(buf, sound_device_frequency); - } - else - { - // finished playing - SoundQueueReturnBuffer(buf); - - Stop(); - } - } - } - - private: - bool StreamIntoBuffer(SoundData *buf) - { - bool song_done = false; - - int played = fluid_sequencer_->PlayStream((uint8_t *)buf->data_, kMusicBuffer); - - if (fluid_sequencer_->PositionAtEnd()) - song_done = true; - - buf->length_ = played / 2 / sizeof(int16_t); - - if (song_done) /* EOF */ - { - if (!looping_) - return false; - fluid_sequencer_->Rewind(); - return true; - } - - return true; - } -}; - -AbstractMusicPlayer *PlayFluidMusic(uint8_t *data, int length, bool loop) -{ - if (fluid_disabled) - { - delete[] data; - return nullptr; - } - - FluidPlayer *player = new FluidPlayer(data, length, loop); - - if (!player) - { - LogDebug("FluidLite player: error initializing!\n"); - delete[] data; - return nullptr; - } - - if (!player->LoadTrack(data, - length)) // Lobo: quietly log it instead of completely exiting EDGE - { - LogDebug("FluidLite player: failed to load MIDI file!\n"); - delete[] data; - delete player; - return nullptr; - } - - delete[] data; - - player->Play(loop); - - return player; -} - -//--- editor settings --- -// vi:ts=4:sw=4:noexpandtab diff --git a/source_files/edge/s_music.cc b/source_files/edge/s_music.cc index 948ade3ac..822b164d3 100644 --- a/source_files/edge/s_music.cc +++ b/source_files/edge/s_music.cc @@ -33,7 +33,7 @@ #include "m_misc.h" #include "s_emidi.h" #include "s_flac.h" -#include "s_fluid.h" +#include "s_tsf.h" #include "s_fmm.h" #include "s_ibxm.h" #include "s_mp3.h" @@ -214,7 +214,7 @@ void ChangeMusic(int entry_number, bool loop) delete F; if (var_midi_player == 0) { - music_player = PlayFluidMusic(data, length, loop); + music_player = PlayTSFMusic(data, length, loop); } else if (var_midi_player == 1) { diff --git a/source_files/edge/s_tsf.cc b/source_files/edge/s_tsf.cc new file mode 100644 index 000000000..5dc6d2d33 --- /dev/null +++ b/source_files/edge/s_tsf.cc @@ -0,0 +1,400 @@ +//---------------------------------------------------------------------------- +// EDGE TinySoundFont Music Player +//---------------------------------------------------------------------------- +// +// Copyright (c) 2022-2024 The EDGE Team. +// +// 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 3 +// 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. +// +//---------------------------------------------------------------------------- + +#include "s_tsf.h" + +#include + +#include "HandmadeMath.h" +#include "dm_state.h" +#include "epi_file.h" +#include "epi_filesystem.h" +#include "i_movie.h" +#include "i_system.h" +#include "m_misc.h" +// clang-format off +#define MidiFraction TSFFraction +#define MidiSequencer TSFSequencer +typedef struct MidiRealTimeInterface TSFInterface; +#include "midi_sequencer_impl.hpp" +// clang-format on +#include "epi_str_compare.h" +#include "epi_str_util.h" +#include "s_blit.h" +#include "s_music.h" +#include "tsf.h" + +extern int sound_device_frequency; + +bool tsf_disabled = false; + +tsf *edge_tsf = nullptr; + +EDGE_DEFINE_CONSOLE_VARIABLE(midi_soundfont, "", (ConsoleVariableFlag)(kConsoleVariableFlagArchive | kConsoleVariableFlagFilepath)) + +EDGE_DEFINE_CONSOLE_VARIABLE(tsf_player_gain, "0.6", kConsoleVariableFlagArchive) + +extern std::vector available_soundfonts; + +bool StartupTSF(void) +{ + LogPrint("Initializing TinySoundFont...\n"); + + // Check for presence of previous CVAR value's file + bool cvar_good = false; + for (size_t i = 0; i < available_soundfonts.size(); i++) + { + if (epi::StringCaseCompareASCII(midi_soundfont.s_, available_soundfonts.at(i)) == 0) + { + cvar_good = true; + break; + } + } + + if (!cvar_good) + { + LogWarning("Cannot find previously used soundfont %s, falling back to " + "default!\n", + midi_soundfont.c_str()); + midi_soundfont = epi::SanitizePath(epi::PathAppend(game_directory, "soundfont/Default.sf2")); + if (!epi::FileExists(midi_soundfont.s_)) + FatalError("TinySoundFont: Cannot locate default soundfont (Default.sf2)! " + "Please check the /soundfont directory " + "of your EDGE-Classic install!\n"); + } + + epi::File *raw_sf2 = epi::FileOpen(midi_soundfont.s_, epi::kFileAccessBinary|epi::kFileAccessRead); + size_t raw_sf2_size = raw_sf2->GetLength(); + uint8_t *raw_sf2_data = raw_sf2->LoadIntoMemory(); + + edge_tsf = tsf_load_memory(raw_sf2_data, raw_sf2_size); + + delete[] raw_sf2_data; + delete raw_sf2; + + for(int ch = 0; ch < 16; ch++) + tsf_channel_set_bank(edge_tsf, ch, 0); + tsf_channel_set_bank_preset(edge_tsf, 9, 128, 0); + tsf_set_output(edge_tsf, TSF_STEREO_INTERLEAVED, sound_device_frequency); + tsf_set_volume(edge_tsf, tsf_player_gain.f_); + + return true; // OK! +} + +// Should only be invoked when switching soundfonts +void RestartTSF(void) +{ + if (tsf_disabled) + return; + + LogPrint("Restarting TinySoundFont...\n"); + + int old_entry = entry_playing; + + StopMusic(); + + tsf_close(edge_tsf); + edge_tsf = nullptr; + + if (!StartupTSF()) + { + tsf_disabled = true; + return; + } + + ChangeMusic(old_entry, + true); // Restart track that was playing when switched + + return; // OK! +} + +class TSFPlayer : public AbstractMusicPlayer +{ + private: + enum Status + { + kNotLoaded, + kPlaying, + kPaused, + kStopped + }; + + int status_; + bool looping_; + + TSFInterface *tsf_interface_; + + public: + TSFPlayer(uint8_t *data, int _length, bool looping) : status_(kNotLoaded), looping_(looping) + { + SequencerInit(); + } + + ~TSFPlayer() + { + Close(); + } + + public: + TSFSequencer *tsf_sequencer_; + + static void rtNoteOn(void *userdata, uint8_t channel, uint8_t note, uint8_t velocity) + { + tsf_channel_note_on(edge_tsf, channel, note, static_cast(velocity) / 127.0f); + } + + static void rtNoteOff(void *userdata, uint8_t channel, uint8_t note) + { + tsf_channel_note_off(edge_tsf, channel, note); + } + + static void rtNoteAfterTouch(void *userdata, uint8_t channel, uint8_t note, uint8_t atVal) + { + (void)userdata; (void)channel; (void)note; (void)atVal; + } + + static void rtChannelAfterTouch(void *userdata, uint8_t channel, uint8_t atVal) + { + (void)userdata; (void)channel; (void)atVal; + } + + static void rtControllerChange(void *userdata, uint8_t channel, uint8_t type, uint8_t value) + { + tsf_channel_midi_control(edge_tsf, channel, type, value); + } + + static void rtPatchChange(void *userdata, uint8_t channel, uint8_t patch) + { + tsf_channel_set_presetnumber(edge_tsf, channel, patch, channel == 9); + } + + static void rtPitchBend(void *userdata, uint8_t channel, uint8_t msb, uint8_t lsb) + { + tsf_channel_set_pitchwheel(edge_tsf, channel, (msb << 7) | lsb); + } + + static void rtSysEx(void *userdata, const uint8_t *msg, size_t size) + { + (void)userdata; (void)msg; (void)size; + } + + static void rtDeviceSwitch(void *userdata, size_t track, const char *data, size_t length) + { + (void)userdata; (void)track; (void)data; (void)length; + } + + static size_t rtCurrentDevice(void *userdata, size_t track) + { + (void)userdata; (void)track; + return 0; + } + + static void playSynth(void *userdata, uint8_t *stream, size_t length) + { + tsf_render_short(edge_tsf, (short *)stream, length / 2 / sizeof(int16_t), 0); + } + + void SequencerInit() + { + tsf_sequencer_ = new TSFSequencer; + tsf_interface_ = new TSFInterface; + memset(tsf_interface_, 0, sizeof(MidiRealTimeInterface)); + + tsf_interface_->rtUserData = this; + tsf_interface_->rt_noteOn = rtNoteOn; + tsf_interface_->rt_noteOff = rtNoteOff; + tsf_interface_->rt_noteAfterTouch = rtNoteAfterTouch; + tsf_interface_->rt_channelAfterTouch = rtChannelAfterTouch; + tsf_interface_->rt_controllerChange = rtControllerChange; + tsf_interface_->rt_patchChange = rtPatchChange; + tsf_interface_->rt_pitchBend = rtPitchBend; + tsf_interface_->rt_systemExclusive = rtSysEx; + + tsf_interface_->onPcmRender = playSynth; + tsf_interface_->onPcmRender_userdata = this; + + tsf_interface_->pcmSampleRate = sound_device_frequency; + tsf_interface_->pcmFrameSize = 2 /*channels*/ * sizeof(int16_t) /*size of one sample*/; + + tsf_interface_->rt_deviceSwitch = rtDeviceSwitch; + tsf_interface_->rt_currentDevice = rtCurrentDevice; + + tsf_sequencer_->SetInterface(tsf_interface_); + } + + bool LoadTrack(const uint8_t *data, int length) + { + return tsf_sequencer_->LoadMidi(data, length); + } + + void Close(void) + { + if (status_ == kNotLoaded) + return; + + // Stop playback + if (status_ != kStopped) + Stop(); + + if (tsf_sequencer_) + { + delete tsf_sequencer_; + tsf_sequencer_ = nullptr; + } + if (tsf_interface_) + { + delete tsf_interface_; + tsf_interface_ = nullptr; + } + + status_ = kNotLoaded; + } + + void Play(bool loop) + { + if (!(status_ == kNotLoaded || status_ == kStopped)) + return; + + status_ = kPlaying; + looping_ = loop; + + // Load up initial buffer data + Ticker(); + } + + void Stop(void) + { + if (!(status_ == kPlaying || status_ == kPaused)) + return; + + tsf_note_off_all(edge_tsf); + for(int ch = 0; ch < 16; ch++) + tsf_channel_sounds_off_all(edge_tsf, ch); + + SoundQueueStop(); + + status_ = kStopped; + } + + void Pause(void) + { + if (status_ != kPlaying) + return; + + tsf_note_off_all(edge_tsf); + + status_ = kPaused; + } + + void Resume(void) + { + if (status_ != kPaused) + return; + + status_ = kPlaying; + } + + void Ticker(void) + { + if (tsf_player_gain.CheckModified()) + { + tsf_player_gain.f_ = HMM_Clamp(0.0, tsf_player_gain.f_, 2.0f); + tsf_player_gain = tsf_player_gain.f_; + tsf_set_volume(edge_tsf, tsf_player_gain.f_); + } + + while (status_ == kPlaying && !pc_speaker_mode && !playing_movie) + { + SoundData *buf = SoundQueueGetFreeBuffer(kMusicBuffer); + + if (!buf) + break; + + if (StreamIntoBuffer(buf)) + { + SoundQueueAddBuffer(buf, sound_device_frequency); + } + else + { + // finished playing + SoundQueueReturnBuffer(buf); + + Stop(); + } + } + } + + private: + bool StreamIntoBuffer(SoundData *buf) + { + bool song_done = false; + + int played = tsf_sequencer_->PlayStream((uint8_t *)buf->data_, kMusicBuffer); + + if (tsf_sequencer_->PositionAtEnd()) + song_done = true; + + buf->length_ = played / 2 / sizeof(int16_t); + + if (song_done) /* EOF */ + { + if (!looping_) + return false; + tsf_sequencer_->Rewind(); + return true; + } + + return true; + } +}; + +AbstractMusicPlayer *PlayTSFMusic(uint8_t *data, int length, bool loop) +{ + if (tsf_disabled) + { + delete[] data; + return nullptr; + } + + TSFPlayer *player = new TSFPlayer(data, length, loop); + + if (!player) + { + LogDebug("TinySoundFont player: error initializing!\n"); + delete[] data; + return nullptr; + } + + if (!player->LoadTrack(data, + length)) // Lobo: quietly log it instead of completely exiting EDGE + { + LogDebug("TinySoundFont player: failed to load MIDI file!\n"); + delete[] data; + delete player; + return nullptr; + } + + delete[] data; + + player->Play(loop); + + return player; +} + +//--- editor settings --- +// vi:ts=4:sw=4:noexpandtab diff --git a/source_files/edge/s_fluid.h b/source_files/edge/s_tsf.h similarity index 82% rename from source_files/edge/s_fluid.h rename to source_files/edge/s_tsf.h index 4c1394af7..bc701b28f 100644 --- a/source_files/edge/s_fluid.h +++ b/source_files/edge/s_tsf.h @@ -1,5 +1,5 @@ //---------------------------------------------------------------------------- -// EDGE FluidLite Music Player +// EDGE TinySoundFont Music Player //---------------------------------------------------------------------------- // // Copyright (c) 2022-2024 The EDGE Team. @@ -20,13 +20,13 @@ #include "s_music.h" -extern bool fluid_disabled; +extern bool tsf_disabled; -bool StartupFluid(void); +bool StartupTSF(void); -void RestartFluid(void); +void RestartTSF(void); -AbstractMusicPlayer *PlayFluidMusic(uint8_t *data, int length, bool loop); +AbstractMusicPlayer *PlayTSFMusic(uint8_t *data, int length, bool loop); //--- editor settings --- // vi:ts=4:sw=4:noexpandtab From 2786f6d6a6d2a0d5d15bc128f00b1ce173970ec1 Mon Sep 17 00:00:00 2001 From: dashodanger <> Date: Thu, 19 Dec 2024 13:58:30 -0700 Subject: [PATCH 3/3] Add TSF license --- docs/licenses/License Attribution.txt | 5 +++-- libraries/tsf/LICENSE | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 libraries/tsf/LICENSE diff --git a/docs/licenses/License Attribution.txt b/docs/licenses/License Attribution.txt index f23252c94..b5414f68d 100644 --- a/docs/licenses/License Attribution.txt +++ b/docs/licenses/License Attribution.txt @@ -64,8 +64,6 @@ EDGE-Classic - Copyright (c) 1999-2024 The EDGE Team EPI library - Copyright (c) 2002-2024 The EDGE Team -Fluidlite - Copyright (c) 2016 Robin Lobel - SLADE (IMF->WAV conversion and sky drawing routines) - Copyright (c) 2008 - 2023 Simon Judd XMIDI library (MIDI conversion) - Copyright (c) 2015-2022 Vitaly Novichkov @@ -113,6 +111,9 @@ PL_MPEG library - Copyright(c) 2019 Dominic Szablewski PNPOLY algorithm - Copyright (c) 1970-2003 Wm. Randolph Franklin +TinySoundFont library - Copyright (C) 2017-2023 Bernhard Schelling + (Based on SFZero, Copyright (C) 2012 Steve Folta, https://github.com/stevefolta/SFZero) + =========================================================================================== Public Domain =========================================================================================== diff --git a/libraries/tsf/LICENSE b/libraries/tsf/LICENSE new file mode 100644 index 000000000..533da73eb --- /dev/null +++ b/libraries/tsf/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2017-2023 Bernhard Schelling (Based on SFZero, Copyright (C) 2012 Steve Folta, https://github.com/stevefolta/SFZero) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.