From e35755707ee765c746b457b41f06f926cb984c5c Mon Sep 17 00:00:00 2001 From: netpipe Date: Mon, 24 Jan 2022 12:31:41 -0700 Subject: [PATCH 1/2] new --- Examples/Tutorial10Fluidlite/CMakeLists.txt | 54 + .../Tutorial10Fluidlite/cAudioEngineLog.html | 107 + .../Tutorial10Fluidlite/cmake_install.cmake | 52 + .../src/Tutorial10fluidlite.cbp | 183 + .../Tutorial10Fluidlite/src/Tutorial2.cbp | 96 + .../Tutorial10Fluidlite/src/Tutorial2.layout | 10 + .../Tutorial10Fluidlite/src/cAudioDemo.cpp | 136 + .../src/cAudioEngineLog.html | 155 + Examples/Tutorial10Fluidlite/src/sitar.sf2 | Bin 0 -> 716378 bytes .../src/standalone/LICENSE | 15 + .../src/standalone/Makefile.os2 | 53 + .../src/standalone/README.md | 65 + .../src/standalone/fluid_chan.c | 455 ++ .../src/standalone/fluid_chan.h | 114 + .../src/standalone/fluid_chorus.c | 606 ++ .../src/standalone/fluid_chorus.h | 56 + .../src/standalone/fluid_config.h | 38 + .../src/standalone/fluid_conv.c | 320 + .../src/standalone/fluid_conv.h | 63 + .../src/standalone/fluid_defsfont.c | 3419 ++++++++++ .../src/standalone/fluid_defsfont.h | 603 ++ .../src/standalone/fluid_dsp_float.c | 685 +++ .../src/standalone/fluid_dsp_simple.c | 120 + .../src/standalone/fluid_gen.c | 149 + .../src/standalone/fluid_gen.h | 44 + .../src/standalone/fluid_hash.c | 388 ++ .../src/standalone/fluid_hash.h | 64 + .../src/standalone/fluid_init.c | 5 + .../src/standalone/fluid_list.c | 257 + .../src/standalone/fluid_list.h | 61 + .../src/standalone/fluid_midi.h | 247 + .../src/standalone/fluid_mod.c | 434 ++ .../src/standalone/fluid_mod.h | 40 + .../src/standalone/fluid_phase.h | 115 + .../src/standalone/fluid_ramsfont.c | 1117 ++++ .../src/standalone/fluid_ramsfont.h | 114 + .../src/standalone/fluid_rev.c | 561 ++ .../src/standalone/fluid_rev.h | 67 + .../src/standalone/fluid_settings.c | 822 +++ .../src/standalone/fluid_settings.h | 55 + .../src/standalone/fluid_sfont.h | 77 + .../src/standalone/fluid_synth.c | 3550 +++++++++++ .../src/standalone/fluid_synth.h | 204 + .../src/standalone/fluid_sys.c | 364 ++ .../src/standalone/fluid_sys.h | 141 + .../src/standalone/fluid_tuning.c | 144 + .../src/standalone/fluid_tuning.h | 65 + .../src/standalone/fluid_voice.c | 1996 ++++++ .../src/standalone/fluid_voice.h | 291 + .../src/standalone/fluidsynth.c | 710 +++ .../src/standalone/fluidsynth_priv.h | 228 + .../src/standalone/include/fluidlite.h | 102 + .../src/standalone/include/fluidsynth/gen.h | 135 + .../src/standalone/include/fluidsynth/log.h | 83 + .../src/standalone/include/fluidsynth/misc.h | 65 + .../src/standalone/include/fluidsynth/mod.h | 112 + .../standalone/include/fluidsynth/ramsfont.h | 113 + .../standalone/include/fluidsynth/settings.h | 202 + .../src/standalone/include/fluidsynth/sfont.h | 252 + .../src/standalone/include/fluidsynth/synth.h | 713 +++ .../src/standalone/include/fluidsynth/types.h | 67 + .../standalone/include/fluidsynth/version.h | 44 + .../src/standalone/include/fluidsynth/voice.h | 97 + .../src/standalone/main.cbp | 135 + .../src/standalone/stb/stb_vorbis.c | 5480 +++++++++++++++++ .../src/Tutorial1_2DSound.cbp | 91 + .../src/Tutorial2_3DSound.cbp | 91 + cAudio/cAudio.cbp | 306 + cAudioWorkSpace.workspace | 9 + 69 files changed, 27812 insertions(+) create mode 100644 Examples/Tutorial10Fluidlite/CMakeLists.txt create mode 100644 Examples/Tutorial10Fluidlite/cAudioEngineLog.html create mode 100644 Examples/Tutorial10Fluidlite/cmake_install.cmake create mode 100644 Examples/Tutorial10Fluidlite/src/Tutorial10fluidlite.cbp create mode 100644 Examples/Tutorial10Fluidlite/src/Tutorial2.cbp create mode 100644 Examples/Tutorial10Fluidlite/src/Tutorial2.layout create mode 100644 Examples/Tutorial10Fluidlite/src/cAudioDemo.cpp create mode 100644 Examples/Tutorial10Fluidlite/src/cAudioEngineLog.html create mode 100644 Examples/Tutorial10Fluidlite/src/sitar.sf2 create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/LICENSE create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/Makefile.os2 create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/README.md create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_config.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_float.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_simple.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_init.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_list.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_list.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_midi.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_phase.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_sfont.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluidsynth.c create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/fluidsynth_priv.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidlite.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/gen.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/log.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/misc.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/mod.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/ramsfont.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/settings.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/sfont.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/synth.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/types.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/version.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/voice.h create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/main.cbp create mode 100644 Examples/Tutorial10Fluidlite/src/standalone/stb/stb_vorbis.c create mode 100644 Examples/Tutorial1_2DSound/src/Tutorial1_2DSound.cbp create mode 100644 Examples/Tutorial2_3DSound/src/Tutorial2_3DSound.cbp create mode 100644 cAudio/cAudio.cbp create mode 100755 cAudioWorkSpace.workspace diff --git a/Examples/Tutorial10Fluidlite/CMakeLists.txt b/Examples/Tutorial10Fluidlite/CMakeLists.txt new file mode 100644 index 0000000..77290d7 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/CMakeLists.txt @@ -0,0 +1,54 @@ +#------------------------------------------------------------------- +# This file is part of the CMake build system for CAUDIO +#------------------------------------------------------------------- + +############################################################ +# Tutorial1_3DSound Player +############################################################ + +PROJECT(Tutorial1_3DSound) + +set (SOURCE_FILES + src/main.cpp +) + + if(CAUDIO_IOS_BUILD) + # TODO add ios appdelegate + endif() + + +include_directories (include ${CAUDIO_INCLUDE_DIR} ) +add_executable(Tutorial1_3DSound ${SOURCE_FILES}) + +target_link_libraries(Tutorial1_3DSound cAudio) +add_dependencies(Tutorial1_3DSound cAudio) + +if(CAUDIO_IOS_BUILD) + set_source_files_properties(src/main.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++") +endif() + +if(${CAUDIO_STATIC}) + ADD_DEFINITIONS(-DCAUDIO_STATIC_LIB=1) +endif() + +if (WIN32) + # append _d for debug builds + set_property(TARGET Tutorial1_3DSound APPEND PROPERTY DEBUG_POSTFIX "_d") +endif() + +if (APPLE) + # On OS X, create .app bundle + set_property(TARGET Tutorial1_3DSound PROPERTY MACOSX_BUNDLE TRUE) + set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.YOUR_COMPANY.\${PRODUCT_NAME:rfc1034identifier}") + set_property(TARGET Tutorial1_3DSound PROPERTY MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist) + + if(CAUDIO_IOS_BUILD) + set_target_properties(Tutorial1_3DSound PROPERTIES XCODE_ATTRIBUTE_GCC_THUMB_SUPPORT "NO") + set_target_properties(Tutorial1_3DSound PROPERTIES XCODE_ATTRIBUTE_GCC_UNROLL_LOOPS "YES") + set_target_properties(Tutorial1_3DSound PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer") + endif() + +endif () + +install_all_targets(Tutorial1_3DSound) + diff --git a/Examples/Tutorial10Fluidlite/cAudioEngineLog.html b/Examples/Tutorial10Fluidlite/cAudioEngineLog.html new file mode 100644 index 0000000..45d5b54 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/cAudioEngineLog.html @@ -0,0 +1,107 @@ + + + +cAudio Log + + + + +

cAudio Log

+

2.2.0

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Tutorial10Fluidlite/cmake_install.cmake b/Examples/Tutorial10Fluidlite/cmake_install.cmake new file mode 100644 index 0000000..b134ab8 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/cmake_install.cmake @@ -0,0 +1,52 @@ +# Install script for directory: /home/Dev/libs/Media/cAudio/cAudio-master2.3/Examples/Tutorial2_3DSound + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "RelWithDebInfo") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "0") +endif() + +if(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified") + if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/Tutorial1_3DSound" AND + NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/Tutorial1_3DSound") + file(RPATH_CHECK + FILE "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/Tutorial1_3DSound" + RPATH "") + endif() + file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/home/Dev/libs/Media/cAudio/cAudio-master2.3/Examples/Tutorial2_3DSound/Tutorial1_3DSound") + if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/Tutorial1_3DSound" AND + NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/Tutorial1_3DSound") + file(RPATH_REMOVE + FILE "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/Tutorial1_3DSound") + if(CMAKE_INSTALL_DO_STRIP) + execute_process(COMMAND "/usr/bin/strip" "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/Tutorial1_3DSound") + endif() + endif() +endif() + diff --git a/Examples/Tutorial10Fluidlite/src/Tutorial10fluidlite.cbp b/Examples/Tutorial10Fluidlite/src/Tutorial10fluidlite.cbp new file mode 100644 index 0000000..59b5df5 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/Tutorial10fluidlite.cbp @@ -0,0 +1,183 @@ + + + + + + diff --git a/Examples/Tutorial10Fluidlite/src/Tutorial2.cbp b/Examples/Tutorial10Fluidlite/src/Tutorial2.cbp new file mode 100644 index 0000000..0f2495c --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/Tutorial2.cbp @@ -0,0 +1,96 @@ + + + + + + diff --git a/Examples/Tutorial10Fluidlite/src/Tutorial2.layout b/Examples/Tutorial10Fluidlite/src/Tutorial2.layout new file mode 100644 index 0000000..a1e71c6 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/Tutorial2.layout @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Examples/Tutorial10Fluidlite/src/cAudioDemo.cpp b/Examples/Tutorial10Fluidlite/src/cAudioDemo.cpp new file mode 100644 index 0000000..28f46e5 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/cAudioDemo.cpp @@ -0,0 +1,136 @@ + + +#include +#include +#include "cAudio.h" + +using namespace std; + +#define SAMPLE_RATE 44100 +#define SAMPLE_SIZE 2 //4: Float Buffer 2: Signed Int Buffer +#define NUM_FRAMES SAMPLE_RATE +#define NUM_CHANNELS 2 +#define NUM_SAMPLES (NUM_FRAMES * NUM_CHANNELS) +#define TIME_INTERVAL 1000000 //1500000:duration + +int main() +{ + fluid_settings_t* settings = new_fluid_settings(); + fluid_synth_t* synth = new_fluid_synth(settings); + int res = fluid_synth_sfload(synth, "sitar.sf2", 1); + + double dlength = (double)(SAMPLE_RATE * NUM_CHANNELS * SAMPLE_SIZE) * TIME_INTERVAL / 1000000; + long length = (long)dlength; + char* audio_buf = (char*)calloc(1, length); + + if (res <= 0) + { + printf("Could not soundfont.sf2\n"); + return -1; + } + + //Some fancy text + cout << "cAudio 2.3.0 Tutorial 2: Basic 3D Audio. \n \n"; + //Hold audio source x position + float rot = 0; + + //Create an uninitialized Audio Manager + cAudio::IAudioManager* audioMgr = cAudio::createAudioManager(false); + + if (audioMgr) + { + //Allow the user to choose a playback device + cout << "\nAvailable Playback Devices: \n"; + + cAudio::IAudioDeviceList* pDeviceList = cAudio::createAudioDeviceList(); + unsigned int deviceCount = pDeviceList->getDeviceCount(); + cAudio::cAudioString defaultDeviceName = pDeviceList->getDefaultDeviceName(); + for (unsigned int i = 0; i < deviceCount; ++i) + { + cAudio::cAudioString deviceName = pDeviceList->getDeviceName(i); + if (deviceName.compare(defaultDeviceName) == 0) + cout << i << "): " << deviceName.c_str() << " [DEFAULT] \n"; + else + cout << i << "): " << deviceName.c_str() << " \n"; + } + + cout << std::endl; + cout << "Choose a device by number: "; + unsigned int deviceSelection = 0; + cin >> deviceSelection; + cout << std::endl; + + //Initialize the manager with the user settings + audioMgr->initialize(pDeviceList->getDeviceName(deviceSelection).c_str()); + CAUDIO_DELETE pDeviceList; + pDeviceList = 0; + + //Grab the listener object, which allows us to manipulate where "we" are in the world + //It's useful to bind this to a camera if you are using a 3d graphics engine + cAudio::IListener* listener = audioMgr->getListener(); + + while (true) + { + int nKey = 60 + rand() % 30; + fluid_synth_noteon(synth, 0, nKey, 127); + fluid_synth_write_s16(synth, SAMPLE_RATE, audio_buf, 0, NUM_CHANNELS, audio_buf, 1, NUM_CHANNELS); + fluid_synth_noteoff(synth, 0, 60); + + //Create a IAudio object and load a sound from a file + //cAudio::IAudioSource* mysound = audioMgr->create("bling", "ec7_stereo.ogg", true); + cAudio::IAudioSource* mysound = audioMgr->createFromRaw( + "bling", audio_buf, length, SAMPLE_RATE, cAudio::EAF_16BIT_STEREO); + + //Set the IAudio Sound to play3d and loop + //play3d takes 4 arguments play3d(toloop,x,y,z,strength) + if (mysound && listener) + { + listener->setPosition(cAudio::cVector3(0, 0, 0)); + mysound->play3d(cAudio::cVector3(0, 0, 0), 2.0f, true); + mysound->setVolume(1.0f); + mysound->setMinDistance(1.0f); + mysound->setMaxAttenuationDistance(100.0f); + + //Play for 10 seconds + const int ticksToPlay = 400; + int currentTick = 0; + int currentSecTick = 0; + + while (mysound->isPlaying() && currentTick < ticksToPlay) + { + //Figure out the location of our rotated sound + rot += 0.1f * 0.017453293f; //0.1 degrees a frame + + //Sound "starts" at x=5, y=0, z=0 + float x = 5.0f * cosf(rot) - 0.0f * sinf(rot); + float z = 0.0f * cosf(rot) + 5.0f * sinf(rot); + mysound->move(cAudio::cVector3(x, 0.0, z)); + + ++currentTick; + + if (currentTick / 1000 > currentSecTick) + { + ++currentSecTick; + std::cout << "."; + } + + //Sleep for 1 ms to free some CPU + cAudio::cAudioSleep(1); + } + } + //audioMgr->releaseAllSources(); + } + + std::cout << std::endl; + + cAudio::destroyAudioManager(audioMgr); + } + else + { + std::cout << "Failed to create audio playback manager. \n"; + } + + free(audio_buf); + + std::cout << "Press any key to quit \n"; +} diff --git a/Examples/Tutorial10Fluidlite/src/cAudioEngineLog.html b/Examples/Tutorial10Fluidlite/src/cAudioEngineLog.html new file mode 100644 index 0000000..89b5ffc --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/cAudioEngineLog.html @@ -0,0 +1,155 @@ + + + +cAudio Log + + + + +

cAudio Log

+

2.3.0

+
+
0.000293
+Audio Decoder for extension .wav registered.
+
0.000364
+Audio Decoder for extension .raw registered.
+
0.000409
+Data Source named FileSystem registered (Priority 0).
+
0.013912
+OpenAL Version: 1.1 ALSOFT 1.15.1
+
0.01417
+Vendor: OpenAL Community
+
0.014328
+Renderer: OpenAL Soft
+
0.01443
+Supported Extensions: AL_EXT_ALAW AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_MULAW AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET AL_EXT_source_distance_model AL_LOKI_quadriphonic AL_SOFT_buffer_samples AL_SOFT_buffer_sub_data AL_SOFTX_deferred_updates AL_SOFT_direct_channels AL_SOFT_loop_points AL_SOFT_source_latency
+
0.015375
+Audio Source (bling) created from Data Source FileSystem.
+
1.48142
+Manager successfully shutdown.
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Tutorial10Fluidlite/src/sitar.sf2 b/Examples/Tutorial10Fluidlite/src/sitar.sf2 new file mode 100644 index 0000000000000000000000000000000000000000..bf926be78ce6899d18acef330740ac18114884a5 GIT binary patch literal 716378 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%0A8VtSK?#oddX8l#6&?ql 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

{jP^Gl|JFB9!JMUc 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


7W)I3#Pz|HL{OyNpZ^=eUm5 zJhX3fPYW7x79$rV`8R~ugnXe#cv7*;+%su}8P(hsG$uC$YuoqKxb4rK0*}|7?Rw~a z4>w@B#mls|6lty5Noo4a(o8-ZdPuy2zx(Pq;x(vU>r>%PcsdPkx?-{FAsUQ_me1i(+gp2cQ4P17=B)Wm($BqVUqd$nPXekS7(5;sN?qo^ zRh}{qGY!<~WHW#|Fyfx;9fkB~GzCBLlIJ<0%lh5P9>vob8g_z`O56j6~+f)GifLJHz_Ds3}vu3Gp@v~=$p_-|G%E+K1KwN z$v6uXr%ej8R|m>093>I*KZHme9PzFiitdgePF|I2S`IF>x47VQ9KxgrCo72Z1) znSc9ym;LyYtE`yokHkK3&M11B>n2Z1ny4QqJ;dok^a$iw{}i^(t6kW!fGA{C&}SfA z9`6c%p5m_U|BXBZyb`^xDCJ&yvm}QuOWKA16kI~k{DyCx zY{(0b-IZa>~GTk$vTECbI%}s>DUQzpVT!YgM=f^M`Pt zG$>1#mT`Xqa~RjiO|hnEv(O@c%yZh$LeRL0S6j8&Jj^^*+ecCdj7A&!_E(=S+gIG9 zWNvv!>nJ**dc9zop?S)-T1V1fn7c`r5-U7UD~j{({J8MrY;H#R2;YU+Vs4_+VOFMW zPin4jL%aSRAhLo-s(%zc%v)P%E$CIWtKwgK-|%um4N`o)_=W6(EX+@1|BWZ0@z6+T zUiBlJ&V4vI3*Sb0*#cgBK`)V8^hmNzy}*JbEHu7X1$je>`JwubZ#7-)ZpSUxLw9}m zb^kf^8rv;>rfZ#YDlH?it}a({l=qgsIldLP_}R`3N3kn6kQ5gP_NmtxJD3UmP|XxY z4&TVih^ z)$YSjRNgS88_(-D$d&+9xSNOJpM^zPOSoF$2hk{TZ}C{kM&)Yb`;>X9RfZ4z`_W~d zkyUl7cG+9P6|8JtHy+I03>;)~SnF8>p*U2+a-kO-k4gp=+$(MAXow7g{-{2hyCifl z_SdS^-{tSbBcK$bWk7E|RW$FfqM&chC3qIILU>VfPP#*kf)0j|{1zV+^+lcr2l#G7UN_Eqpa{jPkm>7$5&qfFXrT^x|q)= z>5}^y*$SH2V!Vl+jyw*22wx4Y4X+7z3?7S2q*n5_DURrO8D{9(OD8c$hB7@<1BY=T zy9IZ);DRVg)JSwm+*I+z_$B2_%4@?depXcH8BjIUTIAHBZNXvuF}w;c2N=jKWU^RZ zusg(Jy+I-8fwHm16%}H45NpKyuIXY?B-A!^pxyK|@^<1+&}E`~@Md-65eD zdZzqQsj)0xKB6kyTN5`6t;UZj+FHD{UB@791*iT`6%K4rdZHpUmGbQp#S*XA0kfYRZju z3y*Mu)xny7U0IyPus z;7<1d>$mC#p8EJX2<9!~?c(Ch!Hf(>Q${Or0(gsY2wChP%it2o8uP}Z6rZWBXLgw_ zI;ZNOGAtb>ih}itl#r;VtmJ*6vUG%dCO(AyNBlqOIcX~q2q?%^@viZav7<<3uy>%I ze_41eev;WlvR^Mt>SM+fdVUr)E>iA3VQo{PDj#3|nBKhZ@7qD7iN2UhQwx{Z>l;BjI=uxZVl;thFy3#@ra z;j+qBb|~C2Udb%w?B`1*4EZZzTgXD4MXm=F_B&N6HK&~Sg1jh$iUa9T3*Jc41aX;M ztUsC1FY$>%q&Nss(I=sft|#^?N0mL(#qpH6&x8)d(zv8@pHY>Pk$Ti@)-)Gi;2dMU zk1CNnK_`v!4)syt+ten(W7TKF5tG)~Njq9j<6?~M(c2-l_l;}1QxhB#KhHg(uAR^$ zC7ANXxJurYcZqQ+b~W-VR16;t7s3O=cLQ~4-Ty6aq5QPgsDG+GDn_Z6pOr3_i32WRb&W*G3epAq z1!j|%;MsPhoLerhG5CYA!-6!;BI9f0KsBJ8rw~cj3$odD;y}n%^R@I$QC`^r@75>_ zEER{u3u(m1#`qrZ66+UJ##qRO;G;ll;4pkW`UjXH(;I&#`Ay5@JGt8l1N^~VZT(!i zxMFa{v`U$^pz4bMfBfZ|m<^URajsHp%va1EYsS&0S7gU%SE9z&XM_ z#O(^@#A2K)$`yu>N#^7QMybj!SOT_Yi1Ce)iNPbDt)5Tb7vb&HMZQG2S^v_QV9;yM zN?&t?)CjyDop+buTIloyf5p|@DeAYD@hPWLni${9FY{V4+Q&LY;L!Jo8$KA>6wV90 zM(orNZXd-|?F;=NomD)5X@n;S4#L6s7jQJMr@$_J&g;UTFG^EfGUuf1PPFLebB(yt zTV3OK{2LrXq;VJU=5V(|8b)`@Lf$8HfvLcLLIKZkoT$1{1v;kFeh?PXN)2ML8@Q_G z3b*`>_#po$;EUyk%5Ce)E|sV%zxq$c{-gCFTf|mTU*30edGryE;EE_G@;WdgU=E~2 zI>$DE8FIC8chYpDT$%|H(OiKW0GI zh9ti0-rBwi@Hs|ve!1eZ-etUM7^u#XfY4#Wf^`Z|?jx=X&gP-n@mG*rb;5!sUrkmS ziewwO<0)~pGdwxeFftI1!A(Nv{DYBuF|t|BLLKXN;8DR2q(i@Zye5?Wv{YdcXB z?(f8^SJs4GRpF!5CGm2N!4TIol`i>I`6f|EJ`5zrnXub_x58ic)Y>fMi|aY}#Tv0# z^o_HQc#5at1My3E3|#N{jOrj{v0!XEtr z$t$o2=?K=hVI>U;lttqU`;}%^=i1XlW1@>F7iXnlkW8hb#HHNh3=LWlcx*4L{?`t= zZ-xG1w+R*Sll>oWnIubgO)WL{wCuM$(>;?d26sjSA)hN~-)`Su)603p^TT^5GL{(4 zlWE=nl8$;XE;qDNiNqrCdwd}6 zM4I4nx(>J&hC31>+HN)>VO`3k6t&@&EX=J(k@#VlAMS-5jcAd+p_6`3WH-5o!=~rQ zm-;-dN;H-697clMB61=VOyQ2 zlh*4u3X50*Y)2sE8e09;T2MK&f~sg^?H>3;-I0tE^d)w$3Uo zX7i$&@MQNtjwSYqHRqiBJU@L=WD$9je@64wNF+2*sH3yW5Ae$XFPVz3Lx;lG{1Shq zKMM`87=p3NdwRBMy5X|YBRl|vV|H|D;I(Iydz^bFT#tYti+WvxEcJKFUA;s$lslRn zi!X(f;T1?>WJp9D%<(lqhLLhkQ`s{*{{hyl6JDj(z!`KpS}M^Hba1Bdn(@zqS2+I( zw#n+5{+F=U_))cpQ!Dz%-`!c|eH1xJL7WFrCujz+foL3W6CD~m&p1T2j~xrIcHMJm zoL9UZkbK52@fX!H?QeCmbe6;_KEUrz=c}Z}Z$xxXy|q)-R=X_{rG9b?#WtaXX9Ffg z+hI+y%eX1JfOhnghL(j7;VqeugafoL%Z{W(eVVWpD~R+8oN-lGH>=@Tjg_q`uUUTu zYz&>OLsB&TLb@d-Tm6Oy;OAX=)#r*|N)%4D^H!P~C6@egzw_zG!wNGdRodL;j(LbMxdEH7^VW z+84qpjM2y~cpH{aZijfhv3!I-6PnC5i$Zd>IX@xE)LHYBmlIzX8tZ8uY>bo4)!a=S z5oa0jjyM_*M{{BzvngYDYzds`Y3lyzP7QRzrm<#A8meq+RH+e<6@^3`KFFl+Y@cZpbfm5>^np6r2=n8Rp_#<{VMJHp}uXp|j>a zH;J-`!`@rY|5cx?E~`u|50nEuA3=20sSU7hkn~;l<5l7idGj9Wy@{999IJ`km2za5X#S% zf$GMx>4Lhfg~)XO3wv|hZf6tEwb1|2@9_)FFlz$uu5_#NroORBW!_~Tq2+>)wEX{gJVf?Oe)jQ@b2 z!&zuD!hl(U)xIp`4OzpPBWt0~rL*8Z^KVnZ2p_IO!^A~)Uyg}RzD0o$s1hX0M$nr9 zzx6#-^|&u$J?I&5N6O zli}0BU~mHLkJ^9?nv2LxXq;eE?d0^ObnsZ;7}v+@)iqPA`d198WY+vIw3_u%dLm(1 znk{u*QZrR^-T}OcdvNubBIoaCzrO!jUFLT&@G)Gqyp^$IB4L`VZ!KHK`9$^%EwcTu zEUlS;c&T_tS^zQoUk0qLrK{dZL#emZp& zX&%wx{m7^6DAbBGnWF|u!P`lY+2?8s9RczCOCU|13z%eV;M0R90o7-M3J zXjAMcev32_{qTCQ-Z#^~(r*rDMIW;oiU-K=C~nBk2-*tLghROFICdbBIEprR6Sg6a z+P<>r1Yj+{L{!F0p#5+xh!9~sBONX_e*y`rNo8cIoqr9wMG9Fe<}V@Hn66~Hpkl(c0?5r!oM!%(*OAx z{O-(^5w(Ayqp9tr^NK4g&@-|o`h%LxJj)p%xk=9vvy45BXN@{FS6mkmW9`Cao+?M8 z{aN*YPS7_o2xDoC9m2V~J%;1Pu%4?NEtw2Wq*li&u`bayXks`N^n|y@2=*z#Hs$~H zT@AOiL2(KPV(Mc?dJneBm*(&3PenG9dTyEaSduGMnet3GjD9bNlPhpCG7b^pr;+>N zpMe8`-`IS{Y91+{Oe_EMRo(e*nKSTXXt&rA@;O_^sf0dpgzR(B3}LPe(;qQ*)2&c_ z<I;ni@!*CbFk&^LTP>SvV-J4lyE zHR9I1p*#nFIOi4A6!;Qbj|_7UwEuAO0+q4Bz)r4Lu$%LRwLLx$cEQ6V15rKpEQAFg zh6$vMxWuWi7;5U6u*ZBr*%~@WXhSDG8Ls>q%yy!>u=3xkvGz6LvA_kXB0-scChcp| zW#wIN8vfM9p|RFMf9m~F=jW8cu6<}N&T3f)!{3BF(?A7s4B2Z=jii|71bcWtmX;gmnsW`CmGtc8&Xl+ZmV;86I6qwPdd1u8>_& zYmEiQKL(wVQpvQ(c|?yF#1rTa0zWuDZqs(vYJ&Cf_K!#;#8` ziV@iI=moS@cnO`&vp4pFjq-C8QSCI{Ahkf42)?EM;FZXu;4I&3AL_dYk0(}egxUs4 z^-}jGt=E2+4CT}%E@59Il@SScHnKjHAE*cz@Jz-;UIY0Sb(+3N`JJz1MtLwZ)-fD>#QCv)X-_T^6-2lFE){(2A2YDfMnXEA{`4NZSneK zRlHAh3$iOXHS7%KL=41j;H_}E#3Fehy2RVWBlz<<`=Jp)Nj!=^^gMA&JdcALh`a1| zJXG)tddw(}UJQ?fS4B=E-_aod8+m79yd7Mn^_Dt_9!*pGXThl`^RA-T{ltEV>0cM4U2ANd`a z=gxI5^n7-23~Y#GME|4OvZ{Fqo#eO2SYrHX$Tr?rCrO;_ruY^3nD35L<51WzSDV0r z@Tq7&);G}+oy73f5Y%3iT@-!;t`R?CUfdj$;p1Tg{F`>!?*Nwas}$XJx3zi7k^FT) zL+Uy1q$iD?zS{n#eiN)B$~mpI^^;Ol#wMQA)RAbRy74OXp9m|m7JUil1@XZ1z%lG7 z)q~5D4^V&5K2u!g4QDlt>99tz$wXTq0=58oP-kEgG)34^c2p}j(66pKU)Ym*7_SSr z!AfIysI!2QwT@Xy{=iSrf{%CDS>k+rZFDsDI9wAchGU3`)U%%oJ>vc1&7ySPWo|zI zHs=_pHmD|>U_tLN&q#kYyoyplLVi7AF-VZPc>i#61Va2sH!LHZ5YCEJWBZtY1>-g5 zgpC%RF-blf7$3VA=;rC+&a@x1Gpj#T{Hf~exBz>AlTs+5eOh+P+XRLjg<{xNm&E$0 z@OSRNe`@lsmUVQcAVa~);v~&L(;P#F{=Do2)RU|XX4{i0twjxrt4olwIW^pxu(K?v z#>at70Y}0$0QdVil`3^D?6gIUK8ltdE>R|HQk+C9%e|`^ab{ zEiw*WN?L(hg4e>Qg4TkooS~fSJOTX%KV`Qgx}u3b1MP%A6xqYL4`uM`^OJ!l@hNEM z&}_JOBpAs;T7))+3gNZ*CRPJc3tH>KGN0GAl)h!Y!Mg@>-D&QlwkEc|*8UaatO92d z908t5{N}5vyOZ}?I?4Y+bSjIpch&5|S%1F$omz0KJj-=FGLZdJ6j$+0tBi9EujL~- zW5^+)6i1z^AtilEd&*v?`cFvrRj`;W6V+Q6U}Bn$C4t${2QI1bG1FkjR(Gk%cI|b3F2qE z{`%g!DQdYSjenCFik~8`#+MK+VoAs@#EWUkn^3Xvw(_0!lX|Co6W7N2L208e&{pu; zfF^j0-cnyl&EoIUpG;jgVXiTU$G&_2mkc>8(Rp|enXx|VOCr|h-y z@5n~~NdK4cR_qaTKeUT`n5$;ai5@_vg=)cPBWogE5qq$AI3H2QmVv7z**b25$dpQV z<1JXjqMzwn-gF0Tw`}dIzgLd0`Rz(X7P5CrQ!S&?8l;R&NLIL@RQ!tbOx4ArjJ)~< z-HPs2baqw46M$`^*Q)!bE~ZvSyJ9ZKP2aH==W=W3(()2d*_;ZsjgHH6E~n9ghv06( zA!$3+J=s&?A;5#K2+j4Txhmb;U0;2J!b#{J@(z=p9OOqdU5&4d9Sma)N0nw_HXy}n zN3#7roKZ)2`%70t`dl83O=UM0U;6)47xjL{B4Ky#Va7jn9c#x8#Ovs#$OEJlyGXDh ztDuvzy=I{DjU*HFGv1Oq{40_Y4*7v#v%p(;Aw}{#=(Cd2l2%v_sa>M`?61*J=uGlP#p=fj<1jhH^}e))X$8_fiH3;rH(6FDimG$y4tx7L8K03P@PSObj{l}V1N zp6hCKiMj*QAXrVV#0SR~#8Ziz%sPxrsxm$mxf$7uNbsY?oA};%-*{&%4Ihgx!7nim z0gZSEcwM;ZybjQE$i&NmV$g7!iQSHV2+R$(MN*=pSY4pooD0x7axL~SQW4A!?}O{n zKX-zs!zIYV*b;E3M5~)_&NRf-m0~NMYP;LF%Q?@5*0i(LwW=yN)NFH2h+wR15{vm< zYEE)1i&EAFtc_iwPu$`nL;k%2N8y)plXGbpWL*+YRkky^jbn`w#Xin%${ITA++`hH zTEDb+MXaK-rb|tC=bn%pKMMXQK&3S*m#ncU4m85Ww6o_F_jnJ_-OyhY9)Nnt*R0q4 zPRh9UhDmA$jNc4p%3|SNc4D+M+B5jgb=_6Zx!iM<_6jH@jzeQ4Z?tRa{2-(9if}Nu z4s#+w2LzHANLo^h&cF^uEmT`h9Z{ANSMwBg#f#ZX8F^$7%SG0Q7X{cMb&wl*L~RkA z)SHqTCiXF}Q{h4vFd+H_MUno<7vys2Lm)rIguT%mMu@Xi)>*YseMTndp8|Cx1D8ZU zMPD-}utC;N;5-WfPYCyjYZPA1R$U9-cj;=V2bB`l#a_og#%@v<$aTcDSVrU>d><*n zbVNGQkQhj0<7N09tVMJ*GXZ$XdBv4;?r{0gbFdyyN_)mVW8a8h!ls2E!dZC#cm=Qx zs^YYP4CFv;QY0QM4}XGBhvmrDFe@?{tBD`x9Fe*7>n!QUBFzO!BW8PivcJH&#l6t} zkNs=)v8srzq317hkG)6gH*3;nB=anbWNX1+*iEO>y12MoK}})3Vnv0_83=h9`TSvu ze@#UunBI&Y#hFTW4UKWGv)(HWm$s=GU(v3*i}ko&6Vzg-*vI$|$sFZbX|*5&D8s74 z3jcFgBe&LxdKv`@xF3BZW$@c5erT>3ju~yb*T{ke}IFG zBB}tdKv|Jyp|9bB&^AQDnk_nDIFfWUL1I>`uZWhhbEB8AQ78|+MCZ(W53YtgN6yFE zGxu`$$_J<}tA0xJ=(O9}pqAU~s&X9l^bDSZKNFu>-}!eG^)xpOTa4@VeqDkh zkDm>^#?p|l0mS*j<#e|6YzXXx7ZSChO3{1OHsxM9ByGXF4o1i;@hWO4d5XLq%Rr0p zhVhfsW6nqsN6|rfPaNQ7F@xkZ@_*QSWHzh`{Rn>ywMIbJ5YY}jl;}6Nr1KGW2!{X& zJ_|d979i7+b)nC}ev$V`O}q!|5BH-yUA0v;LN;5_hFeb6iGtC!(eaFb*xgy1*(fj( zx+`oYa?0;$FVQp7CZ(R=6PQet$DTx=;|1h)LPTtgu7syVTA=CCqr?ufhV)Z=VmG2~ zqlS1kD}nWn=BMufT4+5m2rzNhLysUQ`(3OLmL2(x7RFnX^}zMeT~N%*!VU1(P)mcVZuM7DG z^paXl^-^0TJvL8}{g1r^d+eBMr5&@16H5HWRQUtDEeKL?c&Un7=I-XWair1%O`)9O z3U_5qO2zDQW#xg2WNS~W+1@fV2b;+L!9OOcqg*R(FIo$V<0p_V!KkZ_Yqlfi`5oK@ zHzNyJQh`NTs_A6RGN}zub-ff0ek1n6=ufm`2zO|;s zK&s?qOaVSjC?YmJdN!^mq&Vqm&4=3 zImla9rr4u5BwRK1Gfq|71$itxJ_GBH3XmO<9ii2sTF3(wjibykCquqkxnKF8Y?`12 zcR%I9USJn+2Xzi;#mol=0-wPTf{lVA*<xVP!-Pl-sS$Lp_Tj$Pdd-^N&D{ zsEQ~|tHF#=Z{!Cu zHa3OfhX%|3QA||)BmK(n#<3B*(Fdp%8%n-p4Pks{)v%mwH~%>Qtt?$P&(y@UL}eBD z*fw%qY&vd7X*F3aA@&z@hMyu^aAkY~=_PBZ4CX;%1o4r0PSUQ^%q4)0wVPQ0c4R+e zr*KDePC~uFI>e&b`dAgQob{Y_g42(4fSt(bk4=V0hG&P$!=2$J!7dRP{(xk~x-bhw zJ=I-JqXa&1Q z9`HwIGx~EMh-&0a=_+9#a8WD~`4o8IR5^mSLe~y|BzPrO%52IfE@NMDF~M?b%6!<`SEH1g+sza%MAzQqvh*qa*2#V;b@f1<+#rG}u#mUw_8Z&{S<)t?DA= z0hv)2z6!fR_p-;}nUVH*2p>rf0$%V|C~hjtlu2@2c#gM|dWy}&>qPUYGr$YRDqs$< zmY$oP{BN>9x=!ZVrZ(zb!lU3%QWG7GE{NcGV|*;$oYowEi1fi&(JL_(v60-yxE1di zzZh>pY-6q?&rn0CJ!C!BJmv^yD%c+su#d9lMh8Y`#8~7j<~s(?KFr?B+#E;X?ZMK3 zI- zpe@_6cSZiK8suVF6SPa1%hjJvF%z*#WD|H}vEIr{0A!Is;a`QqlB85HgKjIxm4(Js;T z(E(ULSca~`cgAKD=SYn4j&+EvP5vR*QwLeM$zIel>IXTU)q$mAO{Z&Fz&;54h>eRc zC08=?fzzx$&=@exxE2e*Z-R#4l0f~?(a=u+=x|ZEKN6!SmhqD2nhnNzruurOYOD|- z|Bc|DxD9u{vJ>{-_MdjGH#N`)c@1=utv2mQ-jguU)LT}UosXV!N~&L$zbQLV{=C#! z9;un=pB!BaHkBrr2ATUA&nWwHep2ruy?q%DUe&mYHlr$U>t4$ zABcRwjZqWPiZzOxBhxC>iuJN1!UXOs@+dk2TZXqKFS7d6ckCk|hwY}f*AK}-F8RnEwM7QBrCoownYLHHF+l`bZsl zn;B(hvL1mb*qi27*2KAFYi4_(HnfiY5ZVb&WvrxA>C1z3Mo%z3)IM~`zcV~MQV&IA zje(8QE?S3ig=vz$O1WFGoEQ$j^E|SJoIFRhUEly5+q|WLDM&J4midjnlE)`JG?LQ0 zED1WsDX!*M{wP;eq?e|ZJ*`gmW#J%ui$rbAHT}__Qn(=#V>R-}+uJdyvO#&LibZAj z%f?i6tl1UVi8P=JIK4%Wog>OIys9CMAb*S1?-F@L?#$=G(_mEf>~OY^?( zj00#yXPs2ZI|?RIhIBwZ+6!@mYr(%;_IOa<5p zXve%jJtljx9Bc_Rjo(_7ArkRx0Wszfas&}4zv4C6M(iuH6Mc>TjwV9$C70Fz)0b<< zYe&m)&L&Dl^SqUj^YAkGGTaD}My1iw#4F}-E+y?M|0o|Ws}R=Zye6(Ar_lCz7sAOr zNL^r-u)M$>_M;~n!Wb20b~tjEp(RD^&U3n1)7PLNGN0ro=bC!P%d z3aWy#;QC;fP@bO!H$i@*IwHdM$X4reX^xO@7^`v$P@)H1=ox4q<(lmr=Dg;t<2vvA z8{CBm*cwHF$&su~EH&oQwcL$9bN#6KQ8m6&Q@OWvdzrT8uTO!O0FY##L1KQYU$4}0 zCo>%xf!rDQL^^MzZKyc8dPS>qZ-4 zv^Ezv(FwO|=?rzI{<^k}cDfAZlu{3|7U)RiY@`oz0cnG!#CpaYgpIYHmn~B&REj*A zOmqS|Laau2(=XpzqB&DZshQJ(t?Vq`Il)bZ(6lX~za>@MTk@O3q3)tPBU5N5bTL{R z9fdF>6S3`4BE}+~l6lNpz;0SCyPP?Qk&YD5bY4_PPf!3^1%nHU88Yi6vR&oyt&hTDA zcNk^VCc++TN7jxiqAh5>%zbP~ltZ-O7$t9%0-amK*Jz{>x?f4gCL;+**T_|*DS7}K zO?Q$CS_N6k>noch-z#^>n4J=DUjnE5UPtzM8Cz) zfRp5JbtT3^8eKsYt$5vH%uqMa2HRTaROcPXVP~;(mG5NmES(HlAfIVGoY=sUXqYX% z#N?m~H^Y`=tzG4-TvV1f0&&5^Qt%eAnO-CH_+GB(K@ywtFlA+ zwXy}3&21M0r;x|QWzHZmqUfM#BAq9=#PZ^^;fFTmK6GW$*gn0<;NJ&2#b#ghPa>TrSiISi))yRI&&* zm)(=DrGycA!J=Agas>G4*K z=TtE>&b|qb<=aH(Rc`aO#FLh#y0A>mAI)fueTsa5bCE~rBXkcoo94-K;)jT?WIp2u zyAu2ifWSZO>8v;f#g5{|F;+a4+Qcko^4RUc)wG5-n-OI#L?mFU@EOV3JMk)ye@%%OFi zaGmsR36>+LfEV%yh9?Pw%@N&2$#=#OWCC5w!PPsfm{l*z63cd3bsjhTnVKVPp$i%x z=vFDY{Dv$A?)U$4&ZzQ~WtYp#oF#?jZ>#6|KEd_l`=CyuiE^u~j$}H&g6Y9lhlhGH z?APt-t{=X#z(hO^=*ZtE>#G9vMFy4Dqv#+#%zwf8&U#9gN4tlH2EO~R`0j=vd@ZYp z_eL;ASR+6<1UP|xm0cT}0ltR5g9q6C*+&5;G?BBF)0+Pe{rODx1crq1iF^`2LXL~? zj01!*mKHY=7DfZ!8ObEYevL=HQ#DC49DG6i5Bmoh9@!J&VZ-sS(H3!A>^#v7IKv|( zE94X8?PNOP1orUQa-wbqi-!`yddA7nuI zZ>TQKPfd+{Lgt|!d{pdc?09@WH5+)wP6c|gNY-6OBC#Ethn~QbqGbFU)q=5tH61{i z3RWwI1bEBCc!PP*Avu)Cnio9?_YV5~Y=61$gKxY4vhP>00kReAM}Fe8RX)> z)mJL6aPLMJ1z&s4(>u!RTti(M?ljLL|CUf1rU2cF8AgAC!Ln7qNqU_*1^vf6#F1>j zSiRNiDj!ni@SC?BVuXO%-!1wwGNfeOA$|Cc*zV zvOjL%{1APW?~~_Ci~@>Pf=`BzcvrV0$`uH|xJFbU&j;G;=KnuG+@Gsi|jsr8Ga<+sW15N-h!7!8u zt>k9%4ukDkGe{1(kC+iFj=zt!i*JfIiZSCm2onS5prXC9X6pKCw$dUJ0LSBJ(2hvA zh!9zZeZy;_J7cS2KZ%>b7hYXyGkIJ2GTB>!32;RBAX>BtTOJcqjTi>jAaDx3hdD~@ zRXsO9qLnhAwa4U{yra}`R0E$6AAlo~Lx>GqgiC4O#2P1w2*U(sK}xVG`+w}e%-+O8 z><`*Gx-8n87|vM5{6C7$F;0%`@8V^)J&5gWu=&T%#Vg4 z3MPHMimWYk)v;HP!!=S}f;Zg(=Yssfc~s7yztZxJek^fDy`{P7$jdB~Ue@+jQvvHK zevGv9mCSFRv-DT}>@iu6oTmk&g1W>s={PY*yT>%rG(ayi5L!q09v>T==VsjPJ#7Om z!*#i7fTMQL*w6afQ7^rsZG)+?0WdtMujp`fzL<<&2<-_i1(Ig!KU3N3YDIb=Hhy~J9iB#GuiWrqnDZSy9 z6sIk0Tx8vEJ!)FZdSFy4$E{7SOsq(ve1Bo4_(ohO7Rbf1;ml2ab#PnGGob7b_&on9 zIWT47e+&OA!=ds>6?`WCoj$4m%QmraNua8H<5*#8OJnNeWb4?Ys40FTUL+ahzVaQ# zhEhbjE=y1aydQBB|A=kGGT@&gog!0|@IPUKbXQ#p<-!>H7S2U3q4$Zf=C*_&Rius`#sk(J!y)yg6X6FqUH(p5^bt##YHhGv@j!8pPJ}z3}g*is@2jtxU}Eryha_r<|cp zp3$yv`P1?i=2Xjhn711cHTBASriE=)hLk3O96lHyE^Uq82inVpzshGX$x38x&k+i; z1N9QsB@v&l(Hp-Rzv+i)7_613#8-uifX|cMMt_a)ozx1r6g|eU&LY`|rpeaork;jn z+EX-!uYtx%trEfTnDDBw6mKQy;rhf7;ye)}H{##W;`n>AE4_rmDKpuM{Esw}m8cKY zQfeT0|YYt^?ht~GSjZJ{)(ncJFdn<~kb15@7y5P^2VVWJu{$Z){c zrcnFBWMgcjYcEKqdQ`MC13C-gonao@uPH7nFa5|e-VR-HuxH}K2%dBTl&&(KCUSJ2w^pZ9Jsk{Aln zR7khUT*datKFYG!VAdbd9HA;>)uHF&nRqm$34IF*F@>w9PC!?q3owjmkCjIR)Ie6D z){qN{qTq>qP9mfp=%1RCCyAeU0aRQLNav+i(pQ0!Fm*jN99pi9hJx@SY%bkSvqV?J zG#4=5zfzB(QDO}~m|Bv=xu4=@sewFAcFI-YSHxOPQ9~KiaN{~%0W}lQY-V#cfH$X} z_(NHZs2D`rsWF<@rnQb;g;x}*SZHTjRm%uwtF$sFhuTFeO9OPzjr}>}qDCin=1;SwpT}rH=Biel3zw9Nd3{f7gmf8_1 z8)^k|r4D*3_$CFO2D3s3f*cL@;U$KJ>|RY zrbJi0O6fw~Q-NE2y4`4HTu+sN~T8UGJjD&3N1NY}*Cf?KSn zwtyGGMZvfA4u-G`3Cc~h!YDu~s0dn^JWFk-R*{{tBG53tU!pvyN8fOi@JSv9anJ^6DclgG ziVtOb=<6CVZ8N4Ni6hrir2(~eeW04Zm)Gn+7|aVj58D%qg>O(ZHevN=>@GB}&|=34 zeHuDCl?;9MJ#}Aob@jCkJn>Hr+zJm#JQ2gV#<z56*hYfE;)uM!0_eLG@r z1RN`1MBNYrpCnnGCEgWBTy%DNfMB&@46C%H2@Hnlay@-L*)um>nBca#6I?~KcBOh(O2 zSNbM)9G zZUi@qpC&t?H&6)DB9F0U^dn7Bf8Y38x17x*%ObnDE>U-g4xaa4@ILZ)3VjOqjTB2B zmR2CAG)?X63pFS*xv=bz^oP(;YC@z+;I6lrhxWG$9uMROt45*JV|fYL+!%E1%(#}} zw|CJ8(CPeI;08GA>E?o+U-I|=E}37+Q#sm1T0k8z4^DrRp*R+pW)OCXi>~nX$;W>= zfBu(M`&X-+Y6b0l9b+d16WWIPs@)Vkzc z?Q7F8OGPVW-e7p8FQ*roAYKpA%8!%9BI`n~aF_U4t|9n+UxzG6G3)@^3#ovAr%cQ} z`WbbM`b;gPYf>Z02)T#M$19;1mDYkyG>LX_mhI(t@c*zA=zjPdauJyQBBoRR6oztg>UgR@|4ue2MdZ2iG_^j8QFAo+bUMRs-DM^Rlfa|^ZPKwkG@d;=3kw&sO+XWOzwt)HNNRFN-TwLWDs3F!$SRjZBOE~yfHZ=a@#w% zpt$pw=Shf7qR_u&JMB%A-D0ilZPz)&{f^6iHkuKq7k*dw8X5~bmTb_ zL+$u}a0>6)(Yltpqx2)hC3WUIa|=>qQy4!~_Q*3OtE>lmw3p<2wzGD;POn`A{8!h3 z0_9Zd3D=bK@Ku!hXj5V>d7AcWADXY)#q{=>hcenZnwxwSCihI%jtz`RLnou zv)DDkdpR%;@LA79xa1f)fCqF>Y=5R-PpfKctTUqvc__X!FxFGTRna-1;OXy?d2`*@ zBHP8Lq{p-*y;SCrv@7P{fVloK*2_P>U~Nu?>~Glva`E4bosIo>Vsk~XXJk6+z8S_E zhU*59-;`5;HuT$vIQuz^c{heZ?G8O8wc0Kw-h9+@$NZP+xv{WujP?zA9-X6Xk-&4$7ViLFGN6R+ss445QQjp?#XoLNr~0;=Jz#7T54^iwL}?(!vs zB0^GlEI&qESUv1BvK7=zgYltc4)v0Gr}?CLLH386NR9YFs$!}r_lv(Q?@@kAf6Gmv zH0*zrMzczDNsDPFke6UiSdqGv`j0EhRTTCrFi>1Y$^Oh%U0cfr;I{pg(J%uC865TK z8S<-SskkHNjNMDT;*ZG9m8w!bSy2b0ZGi^%H?x}=NNq+q`7$>n(Knf$>dy5BI`;u; zVQ7)s3bB&KHN*6jak#mTv5cE_fx>DO@d4UmlLl(6+ZF zGW!&cW@K6CX#CK+V)-+FEz0VUeJ!V0 zuBD)tZ%z~z*C1Xh3Emx5^`*4s$VN)dRCc7guW!Nif{5qOaIa)B^&oMRO*4KsT{JH; zFEW)eNk&miQS-p$=L~RrWku|qj1DifUNR73LX>Xwo&? znSA`p6v+`YQQ~Nsh={;39ZEstlW9LH-y=|T>e;PvD#reu({Tq6Hr zO6mrfA=?>;JMCcF5L=f1GX zSCG{d7hj+Foot;NnCcaE-fnK6 zcS&%3_-c4}f>2uE#dO8&s|u|toR^`u_SH0jE2MTtCI|f9hwcmB2f;wFekd;*1B#ZJ zL~}#h#I_7EZ$1CvRNdW1uW}E>t0Q8zslAjQ<=TOlOIp zFeh5irz`05t8Uhdtf4vSxfk*`dM(lBLOnQ0Ze*|OR_M-aUV`MW*GWr+^nK1>oNx5( z4Tq1`ZDfGb^uwJ7nQ5fVWd0S z8Dq(fOfB$jQ*4;kX}s((<_`IcJV%h&Oh}ZL@c#qdVmEP;JO}!UN65OQfHx)bi7Mn) zDn?h*WNI(7ZOC(QH+g_)5FYay@w9|MxzPXAhR`Fl8R?=cXufJ-?I-3Lz5#j*bXWtq z!F(+~DBM(s;bW+obO%kIzPBacRy6HMdcSnaP8q6G2jrd zas^05O96^(8K6^dN_2p%(#BK~uq~~bSPr_(TyCj!QtmHz1hq54{;TU`_}kRSxJnbi z=c-a7Cs5Bf+dakg-C5r=%x?>32Mc1nv=nQm9dA9Kk;wcf&1D+O0A5LAVR)p!xYr5N zKY9fp2Q{I`(TChlW+h-+wrkBi=!7QeKVOa9dwfw~1P% zZsIJl`TnoY^3FQmsbM9NCwIV?F_jH@MgkBSXPMWV-Wkv6a;Zz$5jDYIi}j6+kG_sa zlX={2X_Gu#JqJHQ+F>zbFf;N0cP4BWJD)|_?(`I56mbRb1C$u6#JXG`t}Hmu+VWng z1OA@aNc@Fw#$}u&W&kg&l_|~ErO#m&=#)5Vh8Qg^vkT%J&%e#Ir~CcQvY-_X=j-Zt7%B>hBM#5P>7kTxYdc{<)T zW{ZDIwiTMo<&=SvUTFriI845zY^;~gCThUfr6ji@u{7aNR83w^diYmzRN1JsM=DUC zG+IMX(?!c5^K0FA;=Y=foEkjotKjMFZt6PXu>>XupN9(KvYfzq?FPWTxtpmvuABDI zQ&c|DAJ8?{d!nwzo@n4|FdSSPz0L9RX>71&m6>tuvt}1vQ&-h3+=!Sa@YPwz*}&T({4&u^*5h@VE&370@HX&#HJ z!-cK!snO}N%?UWwhwCgQFeRFWDh2IK(VpWH+hV=FK}h|kbR=@fS~p-oIlph-Mc zPZ%ZVDX)R6<{Gt4JKMO%JjdF^GFp$2I8-%tJNUrY)Dv*cbxrjY3iJ-W2!BtQ)JMbz z-7l+>p)ZthEH&XwdFWo!9s!A?ApNC;*BQJJIv5%p`-88n9KmF^vAMHjoxPOxpza4o ziofISgUdamT$P>GTylQ<{E2RDrAJ~45@SMZ~su|u$)HO8?%+% zx_NRzD}VjiEx`kAB*rlR0#XgF)zRG{7kDoJ@!xXpE|}@58eW+=DD}tmv|C%saKlJj zBuiP#9n%K=YkCey*}g9HPP~lnkBv$6Np48J6+g@MRU2{={e##=PXp<-itd)yukEO9 zr^#ljP{+YUdLMF7`7QS1HGFTOwj`>j(c7es&ZRDr70AipPZ`Qf9ib=E>xd7?Ky`&k z3w!tm{2$Uid7m19HmZlAfq>q-k1Ptdi0>$zh(l$hA6(z$gM<!3;0G5rhMkMFELqYEA5d<*n*zM zar!X(NxMKdQ@dQtXnf2?x(GRzkT4_sOgbXe^X(tO) z7pP2nCUp-ljXY4+iPeSP!f;`-{8UMYa^QSuJfb7wve_sr$2t84-LE<0)&uc<=JQ5L5b0kXm5_^wn`Jk3o_nByJeXX5pM>+64FU)e9uMo6#^9cT=jk6O!F2kA=? zvz6TB?tsG^a$R)(a;|VM^|K*9d^{Obdy_kL%K#IgNM={ZSL1nlJ2WR%D{{|Y-aE}h z`C5e1L26)$I4#swT4UWc*=E0kNxNtLsc%KJmwqPhgu>oTPg#(W_QrL^`QFP%o#Ihk z(9g6#N+0KVW12$KiaAjxxW{!mx92Z=&VsxT1+Cq3;C$>H-%VYG?WZ$zF5NWkWqKf- z&mD|53LJ1vbH4PH3;APN{5CiS&N4^W!T8*K&eF|%#W+B}lrDq;>bYP~42ph;RE({Q zRftdJpNcD_Z|ZzxB7TJ`&Cb`h)y>oX&~#?YFxBXj#9%ytZd7-R4xubiQO@UAN;9B8 zu~pouC1E~8YN}LJ$Dw_g z6Mz=I->SFww%s&PtU^Fv2{M`>#<{dat+crE@mnDlIO zL+&l^m9FB>k={haE7U-MY!Ue{0m_GnRV zulx$d=@HtXc9rHi4N|)JRWWCvw7aKso#$fkVC)8e3cgHCW~=D$7>`?CSZi2Hn3fod zv#s#sP#3XI(hxftT^Oqze-)q2ACzFZ2DA!<H()nqj)7`pdfVy5X9a%nkZA@eHev z-c=uqErbo+_S8n6mDAxZz?I&QLg-gh9*;P)? zR-UNe;lc1?Z97z&`&0$<-X^`c+ zb*=3j5X!w`uHpG=1AYP6Y@bQo=MD?GVsYiU@&OuwRVTYpCFwG(Oy}TL;F#1%_?$c+ z9~XL`WTLtX=5sc}QQY3u_>%anTuP+}wcb)dO19p0#y!&iGc-M_ z;XXh^sI9ub))(o|GtSs+8Gq6V=t=5hc(;GEccZt8Z*8!Cgaz?c!GAps-C3^P?vJjm&e`6a2rf)OI%$?#Mml!b>KJEJgnTzP9^9AH zatnU-%{iUd$+_Hh)W0AqrwW5^{XN-HQ%1W$qh~rJANg6a?E%6)%2~`)A^14Dn)5@Y zi1tivT}$Iq%NuK5%R!HPA204k5)YOs!3J;}!@B z*^i7O-_p<720Fo@n$DOvTZ&oVntK>-(UtMaP&58n@=ijNXwT(?{-vWlL8%P&MjI1_ z$%#}q<`8AZJAn@8K0iG*obZIz0g+*TY?|V&vv%4qQ zKQb%~2;Tfcc{O^TZEkMmnB%Bs`B$gl%fxr_PN7+VCb!tbc^Y_%xVQMqM>h)pLp_=z z*2|7}wqnNibUWo?{8!+#>)`MAIdyZ7YeezsvNpx7WLabtZ z0QVcHc1EZp(PHFsrk?hiezD=HzOk;h<^p|``c9m}E+KY67C9$O0o#xL!UUxUGLINd zHK2Df8BBTFPH!c@6K%;n{22NgTBo?>zoZ+&b78W$Sa|{!f;*y1(PG#mVmrBj*iK9* z3*)C@uY8za2^=Z)laslR!gtw(mL)CVop4t7+0ejr%QVTHVR>)5r5{7@!wk?yzFDeO zaz$btm*m?CdijAe7`lnhB{XDLO2hm|t-)GAQE@gOOAxV!v9$QwgqmE>Kb7t)bI~G9 zP}j#i-G0%*+TR+g6F=pFsTYB#?lms0vyE$%JJVM&q>o+ZG=K#a(4Mj^ORJZ@&6YHr zp~u5hxi1mXf7d(PH_Nv**eOyaayeF?&yp{pkmf)0ZpUNCbjux`k$5XI2`IeE_sUxa z__JPs*fMpYx7RzUif{ifby*I77pe`-zLwB z+u)PrI?Y1eU)q1zN0?4r8)rfZkQ%J@P7eyvd#N63Q+zYEUfaVMv^Z>!EGJBbjkh$V ziQmv%acgo%ta4Nv9Ubi%+mb3THW9VTS)?!dmieD{nZA~xm;SzX63bDaNe;gS^s*I{ z&B7X<ziE`dM3o}H;MIU@bvv?KjmcN!Eb)U-AiNgqtFz$& z@FvuW-NGu9BsGhePE;i)VMAbEUdgN6fmBhT-+2t4Uk4T@Jq)h3>GKSgOcTuu%?0Lm zrg^&Bw2YO5?+c#PiqyJf1%8F_L^vd0RlC5=u`Gh6s?r;oGt>?20dxUm6dy~@jE#@2 zk7LQgsVE>T%}{0^Zs^US;K)9qR_ zqHT&NgbML;;Y^BFT4y4iWi)-TYBAj9rCI5M~3W_B`)q_ia#Tmx>-vK2Z*05;;)Y z+W6QKu`RIHGS4y&){Z2~!n`;+WsBd6UXF^Ea&ohN8#jQj;|gbWX!oV-v$& z-Cvrw^aV;ouE6ReqB2puCR`K#1>^t<^ooCzC~af5uoGC3?nZ~miC}JEz#(Lw+E>Yy z>qw==tKunX5YS21L26?AaUDLFd_}^92cJPqN1H%HI`DBp3 zp?RxYW*BX3Wtw72H}xF8!!j{r$E7L023mJ=&rl*7qv(KAtYDf(KLmwf)Sa9lO&~)|$p#MufxMX|VIk^=mBDmp`>^GiwsdW( zDdEM_u;Orc^$+EYycxLbs!270KJPH{2&s=RBTnNKP$~2y;`j)nD|$dZAzk5Cq!P&z z$x1vdKUQY>kNv&dOk0bE^r#0}+2r}_cKfRleCZHGp| zuhH&=fgDCXq+gJG(3Q}9$;0nRlCl4y2Vz_2niytl`ruWdf#Ry<$XF`k z3*QS*ij0nH`EJ5^KsxG1ePoTg96e+#W;mzq!;Ydp5a;k0$R((R@LZ#LT$G)YDYQn5SFM-lq=ZgU}E1 zN&X7o7R(Pn2_uBgU`{_7SqB(;OUZ1iE(1_Em=kIy7ZJE*vDi_d3VI$VlP!TdrKh?X ztID=Ecr2gO;%TpKkZ}?DQo%V_@R;kU^JV^uf|YKoPY5+nqzm;S8yRNprXlu1jty3y z;T*%k#rTVnZ~nF38{WRY3jS#5Vx)I$A@@Tbj1FRZn1))-HrmupGY5DkYsGJdfBP)n zkax9rga4pEE!Z#io6CTnkcIUd%!kaK^v~&hs7mrm@U*AC^J0Eh!8g}p?`dDnfIF-L zzKLEvk2{&g+KHMn>_*~{G#7BD5Bq0%M|$)8FT<qG5hzv}cxqfuv^WWc)6eOENKPR zIUN5Izmk~D6_>)QADheGF`TtlPtQ#s2)aCpDyO#P4unboHS36i4}cim5YVmuOs*4d zLEFh6tlN0X_QGDxw%fRaeTdBCZ$*OsJaCqaeV_g7!!4tWV%NDt%0=`E`_xp=`nNS^ zY^6Dle-oRH)}TUm#-qW5Y(vtyZw#A|7DGCPv+U-QCV*#9DQD~5_2kUGQ_ zcBQeF^`ot{?U;Flv5W2+`3APh?KwH#Iu?(lB0E9;=^c(1X{8-Lj@hCm4VR3KO(zVT zb}6WRMv+G1Z=^r8NBJa90b0h|BC2cvZ_V>$lG?)5)5KYrNl{~|LwFxR0zuWmiYosn zbp&4YrqVq{Q5PXc(7AY+{D;s2=EEd>GqxA&2rZL#3d>TDlg8weL|2}dpFIc*8oOQGh>$>GF79Ws>itw;nVI$uA>ES z3MPAs_@_lyrfQ0AXc_s0{be-R6OOs|Po}4Un-t_%N5#Mq-wxjkf4M+T_-DdhJ zyePReG}LPYSwAM%8lZ(A=YQmX6Pgq~l58z!V2i2M>{K>PZztx<_mU-|n!qCOF;CQ6 zF;q5INPv*qxS3T9Ve@8N#F}ZDWt^^c6BPVOGH|=&qX4TV9^M>z8wC}=uvInyH`jH| zay@NaVce)6sj0}6qin<_tS)R*c^MK93sZq&S+6uee4y^=Le*qs_6_@nUPPTHVbFsn z;a>`&m=r;(EHxJ&Nlnx>kciB}EW}=F9@(650HsL)>y5R9#!DB4^4y(d7;9f=97FF>>vL1X zqhHn`i5SZiJ;>+-pffC_q z(cW>6%T%vnC)lng+|u3B)YzRpj^CB8$4^H_1vu|`e+Pf3pes}mo)=%r^;E*xBDR_V zGtLLeVdaswse7R!N3qO_a8&c=SNv zkyr9`^34maiap?K!!e9vn;KVHGVCjDBQ4jB&%oaL54fUi=368jv9gi%;a8Eh(cj6Q z0;~MOw=oF~YM5y(X565!pfNJ7sa*UXRvI3rR8UxHvRGHDEk%{Os1Y}TEYUm6pPKz( z<5Z0LLQcjfqX*!-N>?zGG0PIDV(&=L)J(V}Ivi}KX3z}Pm*_;cBi>@Auz$e}X{HcP zJxv}=en=h^wt>0+0_-Do2JHXG8P9@eQg7(48>^Ya3?Ww$qOo~UM98*#gu>XL(CfRY@9MIHGI(ZWgF9N$YUTo>$=)Owkco4gb)zN zNp9sd@)D~^JS1Px)7X3_pDIoUi9T2{q!z5otz}wyAch29C@yVMPeK=w_gFJR1ls9; z@q7XyY*-$$OM%7L{Ek$!WZxu|DiCtiUC3!HNd2YFFibV38|Uet>6`23u9$)t6q1=frlv&rm|#CT)bzBiGRzKx^NIK1)v}7NEbNqH-I4e{x;yL-cD*h)s+S zNcqImY7X8)^V|5`j%3uySOV_Loy;DnA$K!e)qTUc+Bvnr>1pmS8)=$y2z68)(TLR= z_t_{%!1mVESF;zX#gC4@4upKQd@FqEftBG#u{UuIuT?d8Hq*=a)b!OKUyFZ$BDvCJa`=H`Xh`saH#xhA-Odp*7` zK|^3Ma5PVimk}Sporum%F^xo5A_=uT_gCB(Wc*dVb$xw94*+eX67)BwWqKMLS&rCV zfbh@arX|{k4<0h%#6LxuTpCcJrH4Rg=S#rBc~~5%K_?vx)Aay#=z_`WU>r zpw6cC(Nm~=d^Wxm*8^V6cWJLUNZiituvnf!>8@AqHrs^pJa> zXdSB_tr{B>J0I(moG6}BH{%>&3*(N(>4uDs_EkpE;6Zm>DYNU+hiu*+N%=f47Yq zLod@Mj1lu+wy15ewTkJub|fXjb(DibyTrcew=fglA2|}Kl^6uhvI^0L)oLk2j-i(U z0s71Ts4HY`Y&UX8-6{W4ipn#jesW1=Hnba5Gt-HWWD}+bdmE^{3?xa`#QpG};B#ln zV*%mmyf90=B`t+s0S7>9q6Ybju1Y_`UlT2f6uJ~?0OYi#`O~Q>Nni3|>Y=y8ey?Vgd1#=nFVbT|w&BIq3i|^Cv`EY6zD@r=ge0=TrrH z0DT3=(StyTjqq)gb7Fg%ul|B^Y0}e1AW|{+9&5Lkb zPif~u=lA@!o+^Ro;nu00d{n-Uhk=)+s;z|mu^!@uaR$jpgWip zIOEH?%}NBT!FctTj8lz|4Xv3%*g0u#oQgVw(|jueGednNH)D09*|CGEqT(9ZN_NsD zbnmoPnH$J2u0nKwu(1Dz$L;CszvLerd>+7q>mtkJ4TL2090T(gjhXS0+0bZyO!81T z0y@^IzT+WltSLClde|H~WN2r3YI|cJU`v?@-B8MgEK%BtQld(1c_c5?F0v+aJbp@C zpu)srHdk|Bf5Fhxa8K8Y=}vVb7onw*i>h0Cta#-&(oT7&k^qy?>bMKfA_Md|b|8~Z zKP7e$AbK3O!-tew@*R1ixK&syew98$l@J&mLHt8rq({(YfG#ML=!K@kt>mRV#SKh} zi9yLXsR`l_HGtH>F}j6rf#INWjv=O9q8-l`qSlf#@VWSP;s|O+o+(?UjnWtXA-@dx zXm>%m$aHiv=>$iXeDqkfQfyB0s#pt}Lu}G$O${Bh(>tUe zw!4i5?0n=kH!%WvKRau?dKIkqd<}Gs>`hsPF3LK57Tei4#&*^Ir+u`!jbJhdfR6LD#}3)Mj!E)vgYZ+bDml z4dF-VQG7YjdlY6`u@jgo^lGv*d5~Cu)P|EveVJ8mivi%BohTdNDAEnZ$P<)G*JQ2| zKp{*lLVrW0<;8q9hjKTPby5|$QsDW0j+UD(EN5Qlu5dWFoVmQ!u11U3t)3=NXbag~yh*tY27Xw~S6Xs4ti z_JcYAw(vaTFME;nCu!U4bxcJx4)g-oC{o9J#7VoJ73e$%1DO$h>I%?)cEiW9cMZIC zi~XfdHGN=x$aH={%ojZB@8~PxEhgTieCv%LmyGr!Y+D)=T~ z3oH-UjV()+Q1&B#lFQiy-H;p&XY*Ofosj{7y}nMqf?)CJABon|CPYUqFl1UZw#oK) z)+;817R=j_VM-_QSK@fIMWkKmV%QbY#OsOe<@janO$;eRj#F`0Ny9-@OKl|ch&a*-E~lQ6yC@Z;fN)kKlmYNm z^d5KxS$pWy=X;ES6!xIlP*is zkv@kT@e@cUR8pk?&*!q*T9K5B&;UTn*^f=d2M`aa4@@_D0abxq3%GL$_#iY{DGWS7 z9Ylo}#oF>Gr~{w{>?M2BQThqf8g!Qld?wOGohcOq^SeF31r|y^;Yvz-pxRhIag8ab zpK6?GIj^owbO9aBL?W5t^BXCg-cOTP=) z8Q*Z93Dk`|NFL(X%4_iDY+u7H>r$I&{li>dQxB~vbcojo9{>~Cn!Y;0_u=ue%E|ux zdu26#iY~8hW9X^xreDDH1xX9ZM2Xm=h$iR^){2&leNL2#PmX0L*YFLMAo4fagB_^( zo4$db5L5Bv5pQsi|Aco;U{G*cuz4^Scsln-KPJyez2Mn+N9HVjfoKMDV#{%>VvT}k z{~F)>K(k0ztd#H)u0)>Jm$7WI{%PM~J!d+m*+oKVB_$=kO>T|83r`Gv3bl>Qi0u$a z=phEmRn`T3lxMZ)HHRq^z8DL^D^-s&R<5NUh03Wj)&5WxJPEykEd}24uT(o`75$Ow zMs5PG+d0Tv$fNX@bn*tVnb1%AApeB^!a86t$rSCPFEats0kqM(5Liu$mH9lt1F4c4 znySSIq<^4t*bcHSyGZ}hILpZCZ)v}?S@c2TJbnt_f!82g0utIB^tReh$pi>2Na`(j zRu3Snu*%eEW(=K9wa1&I+0a0V;SVNj#&oni#|Eb60Xj();t)H>c-d}D|2M6- zW1Sh-Ca~{dGS|?5*44sw+PM>KgXcx|Bvt3&@k?V|#{f*HZ$!Bc)R)F6r_Pm5!r)!0S) z8=X%yLbr-{xFpzNk^!^-cwk^88vD)r;Hl(#{Y}eK+iQDq8)BZOT>*UVom8K+mRlLi zk4z0;48M%RiD%+XI2ZgbUuwqbngj37Y0YVRJbnf{0vLyXLa2HODv3CO?_?!>1;Mc~ zxE)M!NV+6Ur$j@S(Uv5~eAPWNG&d~JtfyCL^*W?7gnG7Mv@cTetc!EBL4Uqe#9>j`7(BMno-ay?@LC_K!6QIE- zPfMH-&Z2dh?dSYk!i8gxIXzs3bm?|m@~p6Zgtdq%qVbSf zXqsx4#MC3e+|-22hk-IaenRj=E3uADlC@}G>FioBGmgr{jv+_krs@Rso-#%44Yz@I zs+XX1um`CEs0Jm7KI8@ZH}#GjLk=Vg0Y1k_Xtd%L&x=>Vv~9LjLg@!tuxr?5aw5~1 zxx<_V8;^t7Lb$A)BSg6KscxxU(2>mJPf7}8#3-t&rmwz|ai^iY9&7{|Fjd0+*bv-` z4<#<+m#{5ZF?f;kRXWd`#Od-71w)471F3IJ5t<<9VIz^C8WR7z&NzIB`GJ8Y@awVD3=cz`a@{)hBi=+&Fm2mlhZl z`Zu^bDEt2mmW*V_3h||s(Fg(5jPLQ%Xl1DucPo(*8t4C?Z=!DrAgQILG|(cVuS{gAO5{Rxh&$j~?L8c8 z#3Xs^xf6hGJBUA`<#;Q;%M4+3fk?>1kIywF~e4ODwa9Gy>q1Tnb?M?BQ=_M zsn=AhNhO5?k+RSm(<6FS-fa9JO5u^y0$}gQJV*MqC8A%WiuZ5Q^Qq-9ulR_F#pgK zxFL?9dy{9O>p#9NCxlu6+i{`bDj{)eF? zKAvSa z<%zg1J5squNTVK6FNtzwB6*59K*dnS$s}qDRh%A%p5Z;&Waxc``2Fn_cp zK3hNZK3aP%q|P>KSzAE`Hy_Vuk2!Ag6Zt{#z3fZtRSz#vup}kWoxOe)Hf;-1@QqUo%smK zKs_Lv>^n0TZ=kynsTOCn)K+PIv;#&}yCcya?P31qIy;v+;-Iy=4GvOI?8V?38m@kn z6=|hhR_UkJ22`*Bn#vu4mY8)hC1JbFxC}fB$a#ciCUf*D~T2SE+N&*U)bA#Iqr;XX4g`N%8mG9qZM&1ZbWVcq_x=w!wmMFilD;1Qw zNoAw8go$!~NS|&-dhrO(1!}>fc1Q5xXNi0Xp7SsA=L-EBoDt~f|Iarh2y>{^&1g#& zVJJ`}H%Algduk!2dgO~gzdyI{azKf+lA({6ed(T>kSo)qOpe4`vF$xG`O)|cQQG{d zWtFZ*Rz*CKUIMMuGcYv=@8@5;M!UT3AI^n*HEt$qLk*z{K`OyhDoPS`XWC1Br*hEE z>7^(ibDJ@lja)hYK9_}`#IA=~^D(i+YHf7WHfag^cVmRD5KEAUeZoz4zH?Z70lqJ8 zPc5+*=z@AqJuBCjm&%lKUek>dL~XPg96S#_3^<*7xIa4o0Y_40)RaDeGUF@w4f6~) zU~EnOve{%kf^_2m|%?^bzQ>jdjN}hGG>k{%M z9EfY;PQ&x93@K}5AKaI7eP06yBhAErfKh2;jiJYK5Bc>lCr$1OnlFi*a#6YD^iz$=6lhaF?7HN9;k@UV!=#F$uaCTNI3Tr>E>+xm13()v^GXII~TEpuEOr& zlAN6L52PMEL_GP!ih?q8wt7O&DPuWV$*WOjRbn4n%(Zh>@wD}J^X_tOaKg#WOhe7+ z0%#h(g%j8}%s3`Qud^N)Uaf}O+>EvJQ{7mbJI`n1Q}8;vBiRQ2yG!KVq8WV>=@Q8X zi6e!TlX_`lBWlbm-l~bvHg7h#y%JC$vw4X@Nn1TzVXSfL{Ay#OKu}= z8AsIDVh;FoJ#tuRDRqI4of1NC!6#)>C?jg$rMIzP*o7FAzqLcsndqbNf58cX9HAA# z%z;_{Cca()=sN`7YY;I79b{KA9newYv-(ahC?vy^o|m!GzcCV!YTK8Ye_a*hhbHDv z%$eYfP4ax^&*L{l3*(^{BOi~hiK1wC@qzMCH;GE<4V&g1=)B=51>0aOBpJ3McajSr zU7{FWhFVT-r)yJBs1;OUXc8%brN>5l6Bbh!I{#1E^SzFUo|EtPsQ4Jn0}K_U5#_>AiEf~%Tmw|AM#~}C1$&NxL?P-AZw%00I1qg}y#g5_YI!^E`;I;pd^(=>8LOCer z5b8xgM(Rax2uqaxfJ?SP_qcc7W{F3VtmLst4H93wXXB;jD#015?K}9VNP6*fkFR5R zl<-vPWX`AN@Vw_#Opb)E2{+! zyK|I#uH!ji*454L>PU$ai;0gS9fUQ~Na?e5RoEVVDdv)QXnE~z)EXv%&4PQ9IrW>8 z5&ae>LInc@g6)EX{r~#!WOVgE3mp-r>96eEw8E5OTA?B2X-$-m36}$w?_@?v|BVQe z?^z|7~?2UXC=o5 zel2^IX@gFXzsQ2ra4JMUgDI#e`a`{^^3l`bsaC6(U zVT#RSy0uDjk~lazC{ieLJd!5dksoS@tn+jSXcQika6I$u|W%E=k`M)Z83eKJVab3RRuT8WI2=ERIV(lF#Qyk`)Lll9n~B3 zpRe&Js+sXh{ww$+lf#vQeCSzlgMW^{j;|SXjlB>=?WuhU=76Hi8PFa!&@*IKWP>&R z^L=st?U7iyij{`l&X2Jh6R#$SaZO^zy1kCvcrPe!^5}1sg5oLRh_Fwb2AJDw+e4oO zU0+W}0{@X)3^|o`&;oJ=`3DrPcWDjYOK6v)mQi8qCuH`1MzM^AQ<+iRBo6a8SRWuW zKZ!l&KgM&lI^^-@FxwLosB35?_pf7%bG5S(KZmQ1FA=}3v-%HphE`IIgOsMx@N;Q< z0XYCy;!Rg)c#>_sZQUN1&L3q`@iMd?O=b=<4Y;SEx$chh6Z6fmzFC`YEh7igBRGh^ zoYos4n%LuSB)=3H`WK8izvQ#{GHgvZk{P?KbP}D1N{j95&zsE z6)7rylACG8%^~C#<_>p|Kk1y$&tfckkJ(fkDw{%E$t%J>o&hjyB| zl5C5|Fk`SrC74;1x?;WP*YMcj(%_ij0+@jo`L6oDhrS3ew72#k>J`4nq`{=p&&Z~f zmzsrk`SsVMajU8Jvo@-0e9R9@Oe4W*BxYd9Bk7}R*L zxI5fU_88hkl?GiSZJY#ND!)+)w6Wu;_NYFGAPc{ja|QQ{O~NnjTyVw~R{yAzloRqn z$kXqvx3Xf%tau!E#F^8*&wbYO+Laq7ucAy29Hzg~ogg9Y5ZjhJ#%w{GZI{^&oP+Pp zaggTym>tER=Q214PN6;#narZl6}MO{1`NfA$YIzX>L^dNYt}aUE!*AmJKoH6FKJ%p zK?w&uC-7IRku)WA&>#8p`)@EkHX}XwD4HM#3^$p?{^wfiy&bnP{z+__`#Q7B9;A$k z$iaGnj)8)qUXf{HHf5$Z$!toFWd7sc^7EXF`6bLy^u{Wok5R6Ro#nyee?nckpj<^p z(wOKD!4U5$hs-u)Nnme(;u2`S)lwZOwH1U&@zBBGo?wQ*t^bPeg|A>ZD%{ZAgh6(~ z26(JupyB+H5-;ZtcMoj!FZMg4Z4}CWz$|cfkL{cICm|_*P|PLwCr1mWDmC4DZ|v2J zDQ)DAa$Pw|&t?9w@6dzsaX#JA4^X+2%wGHjU8BxWgXz)qb7Vk=OJUTKPNiDYFUdnx zE%X}YXL>S);l8}aeq)Bvo5&S*IiojVt+Fx$^6zx(Ct04(#!lql@uM9Z*Z{6icd<4Z z9kgfi0A;^?O4=*WR{qnLnin7qy)&E7@z6OBkc!LT6gKl;{o@G@2AdVI)M5jk5!A^f)`Kh(Bi=z$P zGf%CA9+~$fmrwF1%=DIFr-HH|E)o;GnI4~!=*#W<8e+s2io+^QO=l0e+sFKff0ZyI z?ve*{naRECwdlX$*ig>Ux^UO%E(xpi_3su-on(sgDu3Jglz-0*zzM`Z#yoY4)J!=b ztrx$_TcNplm2@@gi*6G0DJ3Bhpb>h_gm5+V#A>1Xq>q9xk~fqbJQA4Z8|&-s>jY_( z{|Y75lU8A(7d-)ka)Z2SJdi!oqmbzv<$IK|JzP?TzGXCqALwlnABoE!>-V@_0+$W9 zA|JpTG)FI>^pjpl&!oayA7i;ym#Tx#u|xS?%mL;K{)IlHA+VE-qifPV(LN+oD!r0^ zNPVFl)K}^?nt;=pE^uFF<%h%5ya4az8sepy2scLq?E!Qy79|?cPF#|2>+J6Q&sm%o zm`&)Z{l&bmS5Yy{vU`<<>OifA@zbh7K0&3qH;!p8)}81%;QHp6&vyg9>nGin&dPLV zs)NIG6q66_C+a{R-&cLV$y3>B5fZnafOo7P&O;3#+8X85g7Q0IX>=R-r=@5S@hxzY zA(No1vEN+#PR~~>8D397!Rg*Tk&dMc`E4UufRDxI&wzwT00?qsh#&je6{!vu?@Uc zUB~$HOfBlRb;a0joP)B6O3GomxAxH(V*Mib)4Q3oTq`CkGXb|nYte3M2E3P<>3(Py zV(B?_VR|xEg%(L2@~<&2!n|Zyt~4Nc)1b9v19=zJ=->3(S~>NY{>k`eJ*M`dcy5=Y zkfSfCrWfL|^lj^u;na^Q2f-KARynTr)CwDOt*1mUG?abEmviNGBhPi0?C>}iFthMO zbe*2Z5TH(<$?bwWWH7l3ax(Lnqky%|hsLqD_?cW9*Mhl4gaLi#hH3%XB2Vs#PFyB`NegSS@@#QcEnW5_(zC*_Xv$t~Mw0&$#-G z6VD=F7$3Cd@_OZ>QdL?4=_n`V60!sGGO~bsaGf!ku;{AnLnZ@sI2RiRs7P$$bdlpK+xYMq9}&B5DWZPPrdlT zagRaSSH^XQPhq-K+w87p2lE!7-1XEZ$^p%QWR4bOQTjO^%ss=saT{njD~!6)AE7g( z6WtLVL@%jT^duTV&es3_IZNL_)tOODA9g9boIwmv(`0$Oqft|5HNPThIpDi-kZOx& zvr&FOe~F*THbAwh9G0Sg(sCYyN#=zV-x>{ znT;KIFQ~-sGYvpF`J73@)hWteV_BwT#nEYKEL+uKa{2kIObmD*%9kObDbxWDVAmyX*W|30R@yDytWZqZ5$pTfUF%|RDML^q11)or?IRv}!FEH#|H1DIDM z+z>Sb$ID!;J0wq)RYr(Oav!;wG*i44IRu`C!{Szrw2M)dx&xgP(C)7+;x2Nz`9tgqXpS02-5?snTa&}+s}(a0vpDe% z&0^~FqnxW9_Z(?}9O_gpJ8Y!ugVny^D2P@2X<76JkPxwi7)y@^2mE74TURr8*tN^? zkMbsniYWQDgHU|&o5}9F1kUiknX+bX67TkYWV4YO+ObI9;I@nhf3x}yX3Pnk zimsJz>m!J2sJxT$evV0vqhiLnin61L_1bWOic|~l3pE2=Fq8BWdKUUyoryd2D0U9} znp+R1w}E&hHPw6tcgHj3q#6s1!9_)rUP`+F8-5!dAvMzpVg$Vtu*fs`Aa&h{QFlob zgpJ|D0Xk5?*WLHbU*2Cllp^Gm2O8DwC&X8}DIEi;50{jm;;YC^$Wp%-_z@nfM2uhb zb?zbPSn|d8@vd=~ajxY)gVw1dvEBM; z8FfaVP%QGJiMR;LjJlzQv`UYqyHS&Y!>G!>g{;}p+#04YGaFqd&)GA~ImSjkr?w9o zfw$Vt;aTqGaymJO!Z+bYp}CabdSc`Ue7vQqt0mR%+H`%r@yj|vOrnc|m-3Y3m@DWm z?FP|_;|+TdzDeW3SCobQz;pmt(N17(4%r*5B36HE6!jLimCT^Bz6WVhTgejk9o?4m z$Q?xkPT0S2#8G2`UlKt^gt z$X_g)AR+xXO!6fhO+5)QLToSZBj*EV0?|f$CiufM!bYfNq=Z0A_f$=P1Sd&ndNXr} zDa0M(B-{bdqW6QgjMl~gBbP-9Ln3@u`I6)khDB~f7f22DJj8XHW+$@wnTxb&hSh&% zO~@Dd7}(_x`d0X|1_}kng>DEn;QA#!-OVj62aE2*uK^d!`B)O;@!i@ESABvGy1;kup#blt>>z3+eJG z3@s&H&_tL)Fy0FD>`QtH?3W^-?6>J1s4|nn)`5K4pLjm53hta~cAD{5pQQg%8|nSc z33iaa#uR`jl*-rVKe21*Rpe{yfbmElseV^PwY1hnR{lu)bdCE)*UJ?v3O zK=FH*U4+-8$5bh+gb`AoE4{Te`4@N#J4&0xm}o1=eVZ+Q2IZcIF2S4tXVh)Fto2Ea zNW+BY;c0;}{w}^4pDVC6&@{ABs4Ab=tJpsKGIfMHLJqXQseh%$Lc@?9+#4DhA=LlO z(ez3#)>*<^A?BRN;SK`JJA_$;(x`Oe7_r4J4wGRGJx!ZpRs}6j8abY7js?^ZQQ&NM zYtT&(5*HM6avllsrovzzkQoTs3)UwJDoM$ zgI&uUW%)Vm8#EfNr>~>mu*GJuqnL8&ES;Ae4c%QE?JV>$+=E%lJNe(N%rv6f*e#80 z>TP+8)Kc6dP7<$5``}q({iQh!oT6i$NwF&v#%0Q#d1S)6m~IY|=FG0*_)zx1vWyP? z3H}p-5<)3i)KZAvD6gZ0H)l+FNL<JyrpmQhjd3pdfjVMOUvYVTR(L&Dy^JGs*#cNL>aw(k|rl{Pw zJZgj<;TxztFv!>F#W)L|gr=hudOlqn6{XYYq1eS%;PP=B*gcrQyU;6`W7nHojOT_& zKcaWAzS%Y!58G!U$6KD`-?07Y!-U5^ZT!@;Xup+h;PvUQw=~KcY34m>Sj&T2F{Sw& zt_tqfuBr|ncbAwxd6l`fswPMEV|d3=@5}hziH$S8i=X6e>PVx$8QH|_@GKW+ z6o5UvYoKM6l`n$>Q>KscUp(`@L14t|I|EE6k~jR49vvNR8+8aL#5VF|ZJ;sDTxTC5 zFQeml1Cx{6&5Xmp(E*}@HNq&WP147z5m{CaNipKU=)CZ&$Yb%a`U9F}-$9nA2Xxys z0oniZTA^OJT;P!JSw{B^Z(x7$Z>WLLSGuO|hPy5sQJ-9Hud_T_Q(#+GK(phlaE-`X zxtd`RW0?WmROc#p4Oa)iMFplJR_Xgx1@a8hjtH2=%}@G$b+g{es0V5IU+l5eOgcaP z7^NT=Vj;KNLhX=`{)TtqA7~YdrT5V-&}q@m-TI$nkN9csHC*O0 z{ggffIw}(JJ?*tIH#!qvH{g-Tn1~Zj- z$vNAd4>S!k_^Vtl$l9BVI6Q}K%5G)qeVO8SJ_~G7Xaas3sN&5rKjk zt$Y*xFa66Rb)>zJel>)i&6RVC8ZqZn&s@BO~Kp$eVb8nbY zcno>c@T$*+GvP;pvHpPXtgn9XR_H;P5TnvFRs4TvzzTAKUB>41lCoR;FWfHtEs_z< zp+F}KF_kILG0v9m0 z$_MY@NSLB~BaN;_KSCFvH^N8zsqxf&`ZYBi=39>OvBkiPHwm4D^!;9B5xao-Uax07 z)Bc5C;?AHSS_%o@1$oSWXMMmW=OZ3l$SeRTT#UL?o1s6`4;uZ9o8}683YiZ1+>5w7 z&IRtPuBDC#{8&i2-;3X)Lbw@QnH>WewsoL|q&hvGYzDveVR|p~nJK_;=j(A1W*;@r znhz7zJzzwG;u~=|I01{x-(|1X#N1B4hb)z`-m&oo64xeXNpQpr1sy%8-=X7 zb|qko2BDVh9wrWtM}5eN;L>;u=_z~FT#%dlNE{Ho7wHf_6`3feDtWBo)Dc{m_2EaT z8hJ}cN(|hC5B+z2wR|5kx(7CfxX2};iF85P0_mE?Y`^`&DrseCo8(!bVvdhYjn)&! z$d!x<1i>`nNJo9w3UE#phpfVn_zxb8GXT$-PL&}B+h@)9+I#&AWCjkfd)Q^j{B&75 z9aV-lp2mRE&Bje}ZIp`JGjC8AR02@;%_tcXotMGiBybnl4BQ%BhJK+QWNAB#8H8ll z`T8KelobaEz+~=PWfhWNtYf|RNc^?Lq)ew05@L@yhojZj4mn$-N@#=s zoWF3eUvP5t95ia4u)CtI+!nXfQ_-WldpSBm(%XIGl5#-2CQKB*2Z|Abn=u?ZyYfcn2`(ua5~i9OyUh<)HM5}k5;oRG;;hKoaO3ET z=yPd|zSQ21mN1pLB9LVr!)FJaWGKFX|KcOS+YhB~6ZPynW}4O$ns_psi>=XCCHP$9 z0k!Lh`@kKR#(cy#@qUDvEWp{EMDJlI>4MWy0v-U^ngehTH|SS8qS@#e)rn|pl{Dv? znsLjhW~Y+taDY1u$@XWtHf$qi9sR*RZIw57>tD6XYANloei6FzY8tcuhaN&+-Vk(y zDeTDPYUSJm=_P026FQCZpc}LcoC~jU4dw%CkFwHT$@9ca;s*5%4`cSSM>szqR4jGH zbQ`VIdUBlnS{y8%7te@2;mtgx{xH4~`SDh6vU_pd#Dpse+v87rKRSw_K4t~^W_T9x zN%MSN198F0kcfX&U2S!sC$mW|(Z#rnyYKMRu#3E8d;#}ZN0^PviEjD5l1JO9|Iurk z5it-_vrSNXxFs98+FX; zrq=`~GYR@0?lx?2ltqmBK>C=;76jgs~bXbNWW5J-yZ z+y9vd`HKObHx=Xleyf$j)XY>nEY9X`WdHX5zu*ztmN_Jlv1- z@Ev9mY>&@T6a17J3ERn2XgZyOa^P1e8QgSp*mEqwl!Up~OTPuIfUu513(QR;%_wUx zA!nf~Twic}@Z3&jIc`W_wrgAM%nkY%?X22It7jw_r=c5anK95xwN`RWY7SNy z<)@Tcnl4R-T-qWEuWdDd5L?i7?x3p=IMTZ$Y>BTFGuqh-9Wk%U`y$;!SN&&w*8_Wk zMI+;-oN8HfBUPAr$^V9g+xw1I+$@wrTr#$+N2J1HRG2TON%NE#Xqbx8o@#!>WuGT5 zQUzgVxQiF!$#eqI!k%ml0S#ht;7EFiT6kZWj8uz$7H=qb%vY3vFSCyr3V)|Epo8#x z^gwXA|CE29zmI=!a7mIQ%Hy8%}bU| zb%6VEH0yz!$Tr+a)?{w5Ke?CeKaeF_o&HNsC6=4d^jt<$V~n{IGUQD13iy<}GBfZX z<~DdMpD@*#)=UpjG*87EOt)QeKE?J~gC1xd`wl!t1F%9Bx1XA4jZFGvZMqs($Lk~Xjz(@{Iq+?7tjR<%zy$@if+NB4 zi!<0h>@z^09?@s$%cvf3HajqfO9E=Kfy_a+A@5NeumQb}bGTmYCup#qZOt*>YX3{? zuO>|sy%HlmkYW^7EoNRL#-kNn)Wya%Ot_Yy#Gi=C>uQ9TTD)3OxD(z0%p?=~9x4$% z0*MXNty^>=yU4NBne4m{C;b}ip&DB?bz7+=GxA@lwbDV`sjb#dYnSwXW+!_gnM!|Q z68X;H-hPcNax1ah?5da4PRq75Nn953Mm|Rx3!E$|Bg}NFBxADGAwMn(jSc@Qtne!M z!2jO=(+`ad!L5;g!dnqbogiQH8YE(!fZmW~xPz*w9p%H~0s%{FBwKQ7CCvlGdip$F z2iIl#F?r#M6lS_Y-s>iI7k&ayVG{Wh@S5F_iT^|k>v@ddMnNJQwTbSE&!S6sIJ=hV z&fEa+oXIqW`C|d1Q6D@Co^1hk75kZe$#!6_p)r6Ucxi?BXnCzdRxfZzw;~=}7sNGg7|Y_B)MM+F5!OxuCwfQBB`y)W zi!Xr}&ZRap*4Y2jz1dsNb1}K&|HieBtLxqC7=UJ*1(c=HTcJgP?*6nutjjP3S6q~R1ARVctO@EWp<8HE;BO!? z_#x0Sv^Uy8Y$q8owS9xU!TDNK-Kpo+hik?YmKwxJuon3Zm8c zExy9sWF|uX#yqYP_k`&OP2X3^{Di|Culd!}D%LybC5^6jP3Y`hhFYO7XctVfWr2Ts z0gBlAxHP&=H>Q0s-(JSen9EFv$zTpc1M?1QAoYYyv(H-d%^K!)bAfrv&PDx$wy=%4 zJ`kiI$fiL4IBh zXl+DXlsm@Nx}X}U=UYTq3%%sA>UeVvJ(0=Bt!KO8*P!$5sFo47gw_Vu2Py>L z2L^^dM;D7{B)@b@N>TQxGt|ObL+!Fw(y$@ttdVjMx|MP&zvWEY6r-XYOYMYd#zI?g z01}g8SdN>>Z{_oI8<=A#hAu`%pe3T8Rzy3Xjnn%Yt?hE;9;y(oh*#j!EW`E#WseU{ z#rtqGG!y*=hPg7sFtfpha2N2kUHB3Gjv7TxAqx=mt#0N-^N#u2lt8bPgz`W-#6|Wm zOtGucXX>~OK3hPUBHD3{*4r8jjVVSW^Cu+tzp+?w$8xYsdif|X@dx;E{1)~W?ulu* zi*kV@@IFJrvn>UPf0&v^S3wt<VW_*KqD!xHX&^Zg2u}5k<#H`Vap|-)xun#PX7Lz44-jv8W&;&7$ zYsh^EL~A}uBo9~uNX^jXk1(2CMA%V*{efNuXC)rM?h zwbo2!laxz*EU=MI;W<%N=q}$<8$iuk1aD$yPG)*-^j}79%!Lkk{l7WBIZdmhtA%cdb`$I5vO(T+D)q|# zZaz13Evh}xYQa7-*qC9|H-8%)%$`=|b2DBH~RB7d$908w5TeG0uoT|W-fMoLAYx2Ql zO9A#`D?5c1m_+6~nn}-~=8>m~iS{b1qa|CPt^bIv^aOO3$pw@1VeU9n51j{|t(A4j zD5z6n@sl!_}9*9Y#)LC)Wh&d%w1 zLB6s@)6&~&`_v(7eXXUYD`gc@*#Ng%C)i57z`GuUHh|LHc=jjCpz7Pna3ZgiJR&cq zM88Cw&@&+^RrL(B4m}gj*qQ8d{Fv%$Pt*#C!@}i*`-6EyR_H@Gr?^SJ14*1WrS(z_ zuw_@}7U~PNxpCB(rkB>|E7O!5&@S0fouH){o2(1=G(sd@bPt@9-Oiri&hoq?jxWef zWG>LL)FaE*o+{mBhtflRt+p}`+O^2i=qmE!DNKK65L1mQfV;sQ5~L5Ko9GWX5EjDZ z@dtK=3pg_k~rXN9^Bu~_2KF%h)o_nvv zevQ2dP3f7Phd>W_OMfRbVp_xw?GKNPGU8yluPW*Nh{aSZXy%>89A#ntM$1UaE@w?K zvg(i3(!igW*0w6kWlO54)PnCxL%R?)ff>N>;7>p)PApzVt+bK8N|8lXcqz1qI-|u! zPR^mW&=p9W{fHN^4OtIfPF=9dYh%O#;h13EV1rPZaK%VlXp}oFH&iN13&qdSWXEU#J#amZSL_d<#diBR@Zst&VzA zpDmBBD(#^ut+?t_L(m4i0z8o&aesV**~3g=mcb;u7qFc^NCC~(ZF~q$Jm{%q2QeZJ zARZl|I+5py)pkppu?5qxD%cB&$#g!H4)65<_>Q-L6X+ZH+(y<>(8cV~`sgvn1lTP9 zGe(&6&0?Tdd1Ah{Z&CeGQKl_4iS^*?@~h##`~#Y7xq#iI49#Nj_m8^BdRU~njU7KSj#buAH6*tj) z#R>zXqCxUWbQ7RN;dBpZrwL3aA7)afMjvfy}7~bf-DU zSYkBSMyiFhR7C|1YH4k@amg$NyKQIc6k5SfWqKj4ooG`QfhI0W#*?)pA+y$N@% zepZdsmMYs|`s$(f(OFQ*WZ3P=4=|7H;x6*z90Q&6oeRKMJPYzA9@;TRYc)fcfou<|T8Exy4Mj>R8K6 z#h7mvwV#3>-if=j#rUiIDZtm~FdLWv&H$X?EIi8;CLMjI$I-*-J!l7<4hBrJE%^Vq zrGTZ}0{xO^d{sF)yEIm;C{`D9NPp!?I<|a}R`rdY=uGlXkCD6$Vsd)MJGL@GP-nK# zy2`m=mR%hEBS2zuHlVFT*v%@5wLLYPE}8QG6TSEy&S)LK#3<2dl-5-qsKDEh@q^ zXZAw^M8Nz<`zXT63eMXs;m?uV(Mh5rmr-;%3Ffg+(LiLM@KEZbRMKadugsu%SHGz~ zQ?~`6zWr;B%y$Lk&M-ouwkRpZXAz8> z8*h>}&+J7G#tS&F;~^yI#^UUdGk8sTAZ-*lXnolWseFY1VO^!VjNfKWG7;Uxv#>$$ zBTJd5;RJaY5koCPIe-J{EmV{iDXWyJ${7g>O{03`me55GDp&Lt)&`4*T&4GrUcW{4 zDQA_{ilvs(W59L(n#hfsvp4ug&d*NO)y}=rwZu7%eF9s_QnQLS5%SMRK(8ooFE=w9IM+K@fz z6*!(N&)4P~aV&$GYrv>AM>_NTwkXBeJB_w#ZTTWJMtzg= z$+?w&>NIVlInd_Go1h_;u@7*^HuPh%D0#zfXjL|sLbm1|wHqYA^%pCMwUicmKdT|t zoY~2jaDL+Zaij1&Xc(BNewJc+y%)o0T%kCAqaUKl(5d4Zn>pl}acH`fqcBRRU0M5oU}jYHQ_zY)Fli z*Q(cmt_dOwsG=9}_Z-WdbzL!_$H?xi$8M%K5$}!9>IW$&Fz14@Nm~wR<9I5bY0G%o zpMVoJfbJk4J(PB!G+cwN$P!!|P!o3KZa^YvW|T&+rv?+B;jU~)q!D?D@}M+9)KJ)F zFX2z%J4}Zbnn`qLGL|@F6)|thE~#h)@tjZ`580@w$TUL4Z6}CGla|M zhPDZXH-IW|9!wJVfhXw!KHk$9yb;7^b24Pe%@=P9 zvjtYv#CYY7z7Ckt6qLpuc64wr^xSn5o{sJx{8wfom0_JWUaFg+hamudnkP~t&|3x7 zTE;}nv=>kZ>8GeH6GcE4Q3FW7z0cZgKG4@|8&p!Jq*~%HAw$ligD{NV$h_qTIak3c zf|)Mh=v4qg9V%{#g3eAH0<&}t^@+C691Q5oEBX$)gT~UINQYHWyC!4daJY6D05Rd7 z_(fU;?OLw^E65Zb0(@oLs8{mISG7^l-cb#dKgINIT1Ry;V3hmCR4KP&XpJGWp$P1a zsoWAr8K>J-%-!ClI*PEB>G#l5w@94^&4fLq)kXJRIr!J&VI)iK%cjQx4ii;vxTaOMZ`iv3SL1KC_6GvfrO^h|k6uV7SiQBdyjkcGxffQ$2ZWJQ1NoLR z2>c5dq|4Fuk<{?ZNKGkTS*w)={lZ-0w!PdauOCp4$(nQv*pvc_23|94)6`p>gKrER z-Feqo_do8^E(d4QRY}!Yrp}Tk3#Rx=4yyIc&DLzPFRsN*XBshg@nU=h&7lubhoI-Z z7aPMh;aYQBxRKm1K-+uKf2r>z4LoQ7FmgmLChkJksDqk{So{>#hCQtr+m@+7|0dkT z46A~P4HEJ$|3EusUu(V9%syZ}H>c;ky=Z}4@wRZMLp6SvLB>ME(9SU|WWkYbWVs6X@;b^^H! z)#Um)%DXzaGhBJx2V5s0pS?7?2AM@Ij2c=AB~5-McZF?ijj~v6pg%WT*$t?%bO~hQ zjyOm^qAE}ndD!l2%`+-!P1O@pW>FB7XnUE4CY_&DJtmpI;TY^F&(#BUP-)WwpWSw0 zXT%{K6&p%V^tn%b!;O_r0W0;qC5^M(7Kpm2ZCIfom;{x1U z*lV)#rP=v7M1QAPswMHCty|TI>11j0B@rZV!mjuPSnSKpA~r9uwg1p*#A*AT)!mFY zvKceYik4_Sw}x5JM`SfLtLrI%1Sgv_>`&wjL~*5{u^K}QbO6rt9B@-F2i9~sJ)Pb| zb*Dc-pVSqsV1k_ieHLH&Bw<(Um^c%h#ZAUHVkO|Y-<5|ggQyj#f9NsNd`pA zLuLVVa*5Fp@RZJy8`P3r1h4GYuUSsI0@I4`;M5&mApPqTanH!3HkW1!ha!!HUZ7N& ztd!9<=u6?oDo3;=UsB27cbrNrGYe`-$_DXXBr(!IQdL+1=v@be1!iD`ls(!z+$A(6 ztO*n#QG?C&f<{@Z1j*vx+`rCcu8^y-hxDYn8uNWo zTe5?htO?K<+Ce-9xL-G8i?yFf!kZwCP=l=_20RlQT@dh=V@x^D%jbp-a0%ZEK9$G# zJ$;KRMYSd_+K=t`L|IB99b>~{CGNd%)vR6oRj&Y@HEeX%5Mv3tRe3KPKIiYdCaB7wS1^+qevQuvb(_*~Iwv^xI!17QR2Ce{nbfNAJLHV^6+TF}<#MX1wF75O zE}}azjXX;Aq32PtoDJ_*`b*8#asV?^ys{bEH=Nu(jvUTye zMrKyqwl!@V)77?Z+qP{^+wN)G)3$AQRb}S&jrjKaR;~VL*6Y_tIcQ<7jPy)h704Hw7it+(H0Eva zly*+6$qn#pIWcBi=t@o*FRZg}TAH44px!l<3P}=fmJi@p*#RFdk!@wU8ij{7yJoBR zWJ@d{%;7>@PnMh2VJS$M^Q~R;g)4M@Oa6QCcN-6!4{u(BIpS{G%r)QImwzQASO+4*f1v$~nLj9g|?<8GbH&z@?-_Vnx9~PwOqK zM>}|>?W@qBbco!*iTnnMCaLKW-jb%N3*lqiu1Mk@cGJBF^4YD35Y{5;P0L&Y5B~;a zH~!0ZuvO#?SC7BPZ6Ra8R>*C;#`s9V@W}A+XkKf-_0#%hg)A9QKu@sazQvD6?RLYt zfai6AKgG{t``q$K3kAJ4QXz6OYFfR#j&uk2OWLYk4mONA0Wv#h7K+&Xm!UKK*_YN7FZF$Pq7iWtp^gr@oQ_(plD0=*rmxjQa5CqI z4)c*PncK_e`Da|)&Tgl)a+{aT;+AVIaPz=bJdrOYToxBgxsiobT)4pHBWT-sZ|!VW zBcn~^U8G`UT=csgq__E(QZqH1o}l$tP6@qPKDWNrJK8g1C0q~xf)3#m$i>CISNHXxLn3ZG@kwJMM&qBdg=D#ifov9l37yanjQ- zWDviSU(H&$o9v|KDs*{+V7{+5HFuZ)1Uu(RwQ(>MGYToF*%SR6N~`~ng8X=@xf!fU z(FRbPeXzdaW|o!gg=Y1T)LGgnmKTW_5jsHKJ5x5*kX}`v3sv7BO;eXh^@VO+X|6ok z&Hf_K$Zo!n&|PRDZiXgvDBgoASU-d0F;YYr!et|~**&mqx4`~l~tgTyRqkqChB0Hiftb^7}YpylaJZpS`*4>Cp8J`4GxYEvT-y{Qg2i(`) zZdrRX2yq`0E}*VDWz5G-rzbfhj)bQ8MW}6zkmzYlhfrgEk>qn1=v60`H8Xk)SrEOg zkIoe+=|1q;&^dnwg>!wOs_>5g&5yzDubtXSgAYsVq#acSrLkC!FF`i3($GsbVyVe( zZWMo*za@y+m-|UsrH$|kRFnRJu4pNj8E0c(de#@{P1=!dVaLdGHWmM6oLAbNWRJHm zIX(P6$O`MGl)?R12wn;_hf4Yn=98+>8yvUBd<%Y#@Dh%te(3Y=2YaHOo*5eV;ff1q z(tUrZ^UKH|tsVIpE)p$oKDY0BSJ)e5(+`jbD1$M7S%LX`M!lO>Kz**Xkk0WodOFMe z3GOaCyYjb4wo zbDq)dd^@?TnpA%S)$tj@L%u^2t84V%2p`E8>1%$q-#N2ASj2ovXVJ(05X`Fj6P0YC zi`^a|kDZDNk;;*=(JQc=H?vAwE6n1C5SbqTAntM8x^P=_rrp`!O6o%sGKO4m9czKn z49Nf&<1>bf7{#4baGuu{2daGoG;}d$f6T-fGx)duQyR(lV2$08)~hHN9d8t{mOC^3 zR;(@mOSHuwVr6kKTqJ{q4Z;|49<)lGQJItsY}aRkA2LD;2>VD?e8PL|EN++SgqmUn zv6D0e&+#XIen}hUcakBN6X){DxWXie>i!a~#ds1VjBF>**dp4-i#jDSQ`+h*@zRlT zLMx>kQr`{+)&^E;JCt(LKE4Y7gJdI1x%b>VzO9%}swHnx#;FUn6#4^rRWd6>#A=-E z-?QV4oss+DN#R?OtmZO1uUCx~;nDTO-O58MZgVJg52IuCN9m$;l{WIJxx{QB$i*q` z5oU5jH-;FU%%XMxsedc6pO1yNtueYzt%cnDQE2QY``a95&M=ZjW1=T8nL1~?)a9qk z15`(Q0sYo?;U&HAw6d}z=lFCaE|MJHq?zFSAMmgFkI{>}3s*pX(vN`?E<6|^4; zYRwhs6dX6Nf^vNdcU&X-IjkjIjElyN4@>3nfs=7H)EGB89aLn15`k`_91^E@f18&rF)@;1jm5i%q6X%RSlg;Nwic`g0 z=()V*OAFV9b3#WkEB4rNdX2#T|FNXCDT*y_;_H$5Y&>hkhLc3xS3aY-S1cyAmr_HC z@EdfVBJu?E#>a~f__C;e+A)nKLx*T4SweK|&1aBeJHtEV46_s29h@TGDRx>&t7O%e z1_lTJ1P*CKm6B2qejzuJ5K@4n++)6im_({0pH((t&Pl)`zJm144dPBB`I$u{)c;qe+a5 z#xgT%CAi<|Zf-MQ8@=J>QbX~$aF|cUHDT2M;J9WQb4|2v)Qfg7c;|1HUFa=$Q$K1m zw5-Z7VFi8ZjDlOQM|5QLpXdQ=rrX}@@8_m1>0Nq-e)2ETLEwG1C3V;pF9&L_ndYSE zuxNjyrd0*Jqp{Wz^a{>K+9Zrd%G#HNHfA0$$tHq8_=i`x2Hqa4mQg)iGJbIUfP^$r z5!zpkd=!|nIADi<#q5j;#rzCT(sL;9`5UZ?m&879yo?-=PBK^98TIW}DGC1xs!ts!i4QK!1Y;8bp;%Q!HnP@*R&Us+pvR>F}z1!@f&{Y|wR|)!{_minyb1g>rtNG6;8!&VB}`l6f<-K71^k zA-VuM^vRgrt>q@+4Xlf4%Xg_N+@djR4fPxPif83%;tKvMiKE@Tf_8IrZ*+aMDKa+R zSdHDuGzWK#|G{q-J7C&Y7_<8Ga5WkJA16IL4s)VjWOH0CLH zV9@W*BI~wsA)3dig!6ilm)?KvU!jxfK02T7@IkpJcS$O;l$G*VpjZCG)QvOP2Xk2k z;7BcGJ%z$0Wpo4Rw`bzLgrM2dk^Su0Axa@T;)Qq8N@x6=kSV@ue3^vjkr~!^uP|vV zc2MC+j7gE`Zj2I>HS|cIsC*H?di8pOayb|i`PSxO`<3U=zmfX$A5O}~!c;yTA1h23 z#=}2*UtOS=42(tg!en(C$e?q$4(z^vkiMhu*d#73Uk)_pFJgYl5Xy=Iyjk_o315V& z_BOu=Gn85^8*9uuu(K>ca)T3Hf|bQYQ*gdo1@LwzMM_F3;ktZF^8?$1WrA&V6W)W$ z{7vlQ`PpDXv4?*_AMuL3T%D&S(MRE=s)zhbL%hd@v2My(hoajecR`A5U_E!5`FB`P zP>HUJd!>rfb7=?kyu*-Wt7yrzIjRMxZf(9iSp}MmZtXYH8Fw%NPG?&#qaCa+|SO6GD8`I4AnB$vuanZ<2FGtm~pLME}A@KpH2hq&gb2W7UK z4PnpNM^=z@B_C*8zpnc)ezP;?LTkP=k{;ww%3HMqfyu$@!GJyroWJ*+PPVc~bTwPZ zO@|75J9uC1)e@Sd7l-Ti9kMSri<`MF{&c64wLjW4vOlsV`rcgc-1X+LPuwiwt1wzz zBg)b(=(Qu@MHGNPtQt~P&Vr9SmxSn9)HJW4!$j1tSqLPsC>_Ra5q6-;${|;hl8Q0H zY0hGUz=z9h&o-MG&!Qg;*&61oV8?_?a%puFs0r_-)%-Ae#A$CmHN^fHdSMLv@O!K|XrLEisxsp>*7qxL5J*BWKM5UK#RSNU17;?V+rRj>kkoi9+?z z2^k>$fEMo^vd`WaGmZ1+XuB5HWis-fTQB5B8tF~GD}Pg{0Vjg2RMo}>CIlMi$PGf7)M1>WZC(mQE{6pKz#4l${iTudh}6uw|p@8cE_RIiwcy~Q3| z0M$`7L%7C@F@WF{3pHp7hhkI`g45*_}>{a#`?sM~g?nr8$GnVSDZo z%Z_k|16Bibyzx5f8cEQdD+a&jd1;bz1Iul{^yqzS`LPaNopS^@tkA zy8H-2cmdG!+lW26C;kzO=3_43hd<6&giE3i+%YlAWVN?; zUE8PKR`yCygQF0x%6BNm!B)eeaEEp2Dw6x!+(3aX) z6D-ND;69{dgjq@-{RHSRe$dxLN(@vL$w&((;-uZm4S{Frs{BN04Q^jQ{a@|1nprKX zEEd!9)oDiemUY5thWp=@XwbR@e*a8b7JdH4{3C8RXmDFGzuu01a$9YdwhR2Vw@~df zGM*+lKg=dZt7s-eF@5C290$?19nbhFLRf4dUgB@SCzzYP^S2{~0XZCoXdHloW1Z85 zw&&MFYd8t6j?Q8tZlT`~v#f1K6?}soks{Xso!I*HIPKeP@AFUh-fX=uqoWV%!?gxEM6efXm|Ix2%&yJc2CE}+c zuarhwS{uFmTz}D0e0@yleT*N|FQ##*WUzr=9`nU5WTV&0*@6B36*QeUomY6P2C_Tc zOVl&t`9EA`;gWbu+Nz}2N(a2a=D;94%l#rMTymWr_RG@->?m2lcM*1ppQN+WU(!yY z5x;_e%MV2tx23Qi%7C?~t;djVNlrE+`msLnDeUxRjmzGA?bXu#qZRsign%El(K~@6$gSvh_0rQ-v^PylCwVXIlh$MNAGq;* z8~4oN)-Q7}`aYYZOCznqG2uO7!H5MBf1iKA zrxYFwiNzV>Wg$I(9DVqS+;FZb8BMOU*(@dN;l;Y5bH{pWO|bSkmFRywQI2TW0*`Pe zm)B8!2$i_obR--{RcQvUg^*9Gsk~PbYgP0@`Y7#``b){9#EE(Ma_p8n->zluitdQ+ zGe%ptzfiOtT74y*G?5?9GI~ewEK3?cqP@(- zcDy6wyqM14M)h+7HB(lhA~^Hwur4clO`P>O*V>sU%qR9V*Y!hug7A-IOO2$jn1-*! zx@4w}4D5iJ!tU&h_I$byPp+$f)a~NtcNu21&;4d}8*M|^d&5Ae?rE(t_Tjv&Y#xTk z;Vk~HiAi_I@YnGE$o;4Z7vV^DAA8s?*3n;LWr!X_($k5!3<-0f%`OURbrg5;H2RCc z(@?>fJ)s<-anKfUsv#cae));q6ZSN-tl1gvm#khBKOaS>h8xdKeBloe1agY&*h&!bn=qUssti0p_J`AsY{XzZ$GMe7<7qXW{+M|vkQ5j{n+ za1kUT6sq73_UhD(a#cR5g?r@|nb%WlkBzCFBd@-TBv<~EhsaoPd)?oU; zU9FnZS{%clBp&?bXx^yZ#( zzxh~cn_OM}rM=aQ2FBt}wHAGdTdb>h!J>v8T@rm`^sw$Ys~R6O62wH#dvqj^xH& zCDxwr<)kgh0k{FKcpI#%kx~h(;>yRZj?W!QY@R{-YH7id4uKvyAUGzJH&iV6BoL=R zMW)YDel1Jkb#y+NUyV8DVB8PRK)a{0r|8Vh=TC$3Ib1r5go?eI9M~TC0@g=zr4IJC zX5;|<>`$fwl1oPm*TpXI*d>(@ifx3=TyE|u`fhjdG^>i|KzfSBO;yD{Tc0aQzQeK5 z4;|;e-WA8N^V%8gI`$!F8NIff~8>Ur>DPD-_f2s!3=bXVG^%*o~xGd0MDd(jI`jAX#q#K*df0n>P=#Nmd7kJNvL;}*T>>|vr|wnnKK+X&rH%bA?%&RQ zXP^`3^!6J1cm4eEuvc-X+x@MNMqeYf(Z^V7o-((Z1Pw<-DfWf00V?zZ8!975^wa*7(Qa*;9U^R70(ypF#$$jAt1Q^b8aStA3^sLmqg$ zoqiUBmdM8GzQ%pz^`?7CX|4{R3zMv6QaP|YZfK`K-A)sDq3u?dVoLIsjA46d3U-BT z=Cj}iS_)3dDRM-Ixy<}l5ZWh0(HbvRl#h!C1dkVl9{dUH9PL;x)|BdgalEzX z?2&dhXQ%s$1_fRDtQ`r|33kVs{6j8_JvK4h?a!hTTSvAF_ob*ZN!za_#oLqx3Xzmb zRoR5c_B51u3Gg?SHFp|A%vfuKy~X_p1cpnrDC@#9khA18oZ8o1Q)`H|dCvV%=SS3~f+IOlEGs%gi# zFT7YfguLb}ib2!{zrenl;16;&JfF(eOKX(-%RA@~W}De3R65^W0@vwrr<yRS+mXCMowcx^k_6>B!F3w4Qv;QS@ekC-?Oa}(OvMr zoQ{hiE7diYIvP97my(_+#q#bEr*cfO2wq7~^ zxyRuVyN|m52j5YMLGSFhQbGHq*?M(-7I=dLz-UQBy3lieWjdDiLP~@d5Qbkb_YVfO(V4H?yW2NahXk&AEE8*i!Y!|SLJDS^+7UxsQ z8MJDFpHR{4(q_v^1fP^Yelb^pO%U&38_V{6PIhqPZSN*u`~1)fgHj6fv?(eMGzm8hkjOPHngd6 z(fVd(C<`=du&>+zA&W4WU&?0!HF^u#!)ns;el~B6a z)}<58W-c)q+@mL;3v3om75#zwZM8MrJ>z|bF5LH~xO=Q*(e4Qk<7&mti@zLxXEb!G z(7*X(Vqc{lI&p6T1%ji3ZG+zell8jlB&iBtg#PRHwHuh(P|E`K3ODCRKOGsxZ9~^5 zy;w+oti-9mwby#Tz%_lawqCKs8vIitko&YVJ4E*Ijm1S$4>=q71_wk6vR5LKlY79m zfb%md{wMt{R|Qq*E?=5o$H^okNE8eF;<$T#!0)*l88X$KW$r(;Ij_iHp`qCm_=GyO zhaAU0L>A)_e*kvb_2iC_Lr#It&3~G&&DUD0x0Tm&N~m<}2%FGL%mtX`ek*o zi@4Rik6tbMnC4{{*hO-YdnZ%}lc*o^bWaB!VAg#=DJ&i%H!)>CXl{T~wkdX_-L6Q3 zn6G63o@PMX^@ z!KavG4@FM8i1%nM+k(&j$DQG1bJ96mo$qc1|B>I1wx(M>W-qYT;2siykGi?J(d=Sf zMkm5HDjKDb*xTD&U{wIk>XP4r{_-ZcU#woy^5GisPvi0?42+~Ti#V-mSN?(cOy)4h z?GqRloDv)!>=#r6+g1L5D*zcz`=LdcV!StUTV?UowfB4E`H|d_#2`cpI zI7hqkJvbkotSf#_e~G7IEt&QkdxkyOdE%C%Yq+V>P|Ve?=~uOtY9{#tpO5UJU2w|$ z0U=z`wei% zD~UO96RrS!o5PeJTADz=!0|u>{i0G)yi2}Cr_>_Ez zpTea>9bAS~WCES!7xV_X8QqVLjfA*o^df5ax#&}m7HaeVkk@`%udZc641uf0gCiHto zN^?CjKTbP&z1yA!Z_-MqmenYFJs~b`6R5;L!s*Ow_GEt`w??=pHG%BoQMV4=?6ZKahio8+h%N>MQq#=9nPooxV!xa|m$X(RT zdJ=uTrXlYwgUTy~B2_-1Xr5$^8a%yfe$ywE(f72UD{m%SKCk zBb+3T?)1m%7zdZu1}H0v`-*GARiMMw&=kG+UuJV_u(iv)Y1TqM);JR!eyfm!f6aUB z1-yn%1*;_HVU6N{#yRmRBcgf1UJJ!fOWe_NfRW!^pBZR^?#fR5xuC@=*~Cw5oA=G` zXQnV>(9gPyRESmHKDq`g?vZd-yn~F`>}o}AB9amZXc^TP@)XhGHjrB&ugwN~Z3@=q z9Vl0;OYzW^4Ce!wZ{#FRxhTI2Yx=xAS81$dlP8Ni_=`}zJf;!aSG#UTw2F0-n8x*kjL1 zZK1IWaUq)4D}(x{sa4$4p;A2O+;fXy?v$Ml2j5~pv@`kPC0?j8y%t<M6R{(v zUS*vFaBhZ-FGf*23!L$JFe6IEt>y}Fqe(+j9$Ao!nL%gzb^Ruu>vqGf{|f3t0)=la z@(yf=)V#>`VCDT;ZoCa%fj!kx;rRHM{=t5gk3IFjxQCqu_6WPC!yF4qtbOPvn#b2L z{a<8a=3-<5@3w+D$U1F(f=(sI^2|QgLhG5;!1)IH-fl0Ychh+XZEi5qJ0V{}y08?D zwO%@({B>LdF}Zw63F!F(ql4{3A3|2BN~ljDUd=5p=T7_m-GQiN#~D4L7X9pG^i#9v zsJs`6qoty9Kjo~tN;`lmG8QhidGZ<2<$l6R(i+p{lcY8%?2S?3tN`t79X|j*)c3R= zCR?k($w~po{BgOO+(`OgGE8PN0#%{t4Ri%JsoNI4f}zMl9EWcpIeZ9$_){LKc7}(G zQ|d~s`Ttl(n$J&7+pw>si?CNpr54m=WDsTo O)3*_r=Qahx7tw2v^QM(r-F6qj*6Rz?$x0YP;J9&BC z@Ag7xn0v)LONWB%JCTiH^O3x~!?|w{1`qwSv&FmPkAsUQ)-T}hv^rxp(kr?<+R}Iq z8dEQ8gBcG`G;|I)^Picn)y;Y0JarB^Q!RC_ffK?eiCW2R3w4_oXmgB-`LjXbp|d1E!E7+*AIK*h+4!^i=QT zbi0UoNonpllEhp113{Bb?Jaea-~=k}-g2RJW5>8N;y^{!ZX?0(2r{O#3&%(i=KDM0 zrCiIwH!HP(Lw~N;PHTc1wxx0f-q1^uCyG!w^kBJs;=ZzvgU-JZ?{zn4fV&vPlG=Vb z+LrAkefUh+W3%dQ1NQ?NgCq3Z>NaUAcNaQl(@JAqg{LI5bHrOlFJZp&j{C`VXDd6SecR53UH_;*fmWbR{SNMbNR#Pqe1j){gHgr2Zf=F@xfWJry!khN zgRx+jez5mCGo2^)f9PiHirh%J6rUnK6C9f>jC^(n_X-__^STJSnrXCp`hr0A;0WX- zTKZ^pm9&-XA}PSD$cSz!k5SU8Cb9?8z{8qYeI#d)dP8UW ziOeET$XZnKImJiN-6?QNClOw7d*QCtNk1;V&|1m{O2SyU!}2I)qy)YmcbAQ#x&6Fe z0qn8I-8SwL_dmCv*NP^GT6(_NRLP_*)c#ewD6gf40?&14bFg1kB8RwM;xM_qdIb~6 zM5tm1!3kCv)YH$>1gW0*kk5_XtuMxx&A%svG)9R(UcqE zPBkvj2z9KYu9J4dowd+?W2LoPn?KAic6RSC+KSa8x5z_K6swcR>?)qfBj~dC(!sP5 zUFr|_?|3W0^2-2asLP&_6uirwBp2CEcx5)b8$saw4YzAzrozFv9*U6nw6NdQtqqra z84$I?UNu?>JuJ^}i2nLNW-2pmv@rUaAw0V?b|d?%HQ8Qax3d>wa?}tW+GfraX9c{M zea(*1H{r(#f8r-56awk6rd`YJhisRdLL*60{y~cIx;e&1q^5 zfd}O=)Rj{$-Yt(^b{n@`cqD#A3gt!ltCB|j0D{O%sL^+V(zFhw?;orr$i34Aj)lK*?iCxsWQhl9d9@*(AOAlF|=(8N5T^wXNDo)lhCLhEhQ(r@YB^69K#8M4JZ0-=neJ z`mM$#uwu;cM!U(}#!gFTsXGT`<(X^^JHReb(@*2obgI~0tUTbfl=CWp-CUN|_N%&C z>^$aQMkG2RS^$-7SM#|ggW2BDE@2nNbn~$_((PK;T5S;>|qt*IUfWD89Vy$}5i7S}r{1B4HBjl0t7WR!`Hi4HJl*q`09s2`j2 zFU8N&WjR*ag(q?v&dY-873Dv9nDies%qz)Sbl@*>7OLH+;#BDaw6x2(-b4m7?IZon zvg2=e0&}ghx?LGBN5mQ2GB$-;-T`-uGa3}x#MohXfko5aE8|yXpUGl=5FG65l#Yrg z9f0S$133RW9gQ58vs^YYUJ5Abw0y{V`-FLZI%S~p0aTNJw z`9Yp66|&M)nNYv2$kL*G-FjOKa@xnxy+t=`nq z!%fs4{=HqK1A2@vyi{H%cQ00E4RD)Qc(eUm(01R%UVTm4uOujw6&b9(VnP6JmATlL zR*Qa7%G-~Z6t?##41z}=t05|PT-3SI5v zuIQhHNBRc0Tl^}2Q0GA5+c+>+|A|g+0>6e<@$x{ue#DA{=kcglgEoPlUMF|iQuv)@ zkl`w`8tfi>$7-`~tT=mvoAD4#hCccm!8iE8Hj#o{7PwDW!^4)&yWoCyw?Z82ehc)&~5ExwZDK(KGS>RPp2>FE5Cx5(|Kt2HmahI?O`l7pMf9K)k)-3LWgLD zy~BQFC59%*hMVvV^dk$5FOh_B#&GV0P6=}(8eH+yF$p@%&TtQf!O|Y3uV&~gg9Ss$ zL-T{{^*u;LFV5<_r7aCU?VXWom;!!tHiMxp^HaqYk}HjnO{h$(!F{zuX{MaUS|}h+ z<~goCSBdYySAxE$h1f(IB`(Gd`xvPV&hQ-e2%gFXP*QYJ6Khx1cA)z$=5hXj5}M9i z=(YgT_2%k9EwL;oV zP%SDbR6eNu4VGpZFjUt_Nf5K6(F<-a=a$vTN?|3pu3NQmcOB>c@W#-A;7ZKGou+|$ zR==fh*VE}Ilp&JMpQAzV4@h^soy%SZ3f)!zGaU)N?>Nll4x@YO`I}I)j$r>lKe&#) zrVH>CyFo4a03N52tOz^7`j81Y|J(SUyULAocjM%L>9=Lo&?$-G`d|;5;}>&VIsanU zF9L0WLT5l*kPox%6HbT!X)tk{|6rs6CFHza%1!S=eT`(uBOt*{M?GHA+5*>5P4lKP z8_9D|kR;qHygf1juK2#rCh+l~h7uY|u}T>&K|da>9a1guV=FV$aw6T|H2(MTT89wRc=Xl`PXbL{p=+M8}E~o#a)iXnLVE66+^eJ zKFQ8s=4XI-`VRkAN_n?98G4_B=xtwO<ZbKc(Q7{sHNEsegcf?9c;-;9;Qth}tWSP7#;I44jAOq?NVU6~gbA=}ceewUi8Q~p~<;HUB zUnkll^y}-G`J6}KLN`EXeTEdH z)B}0oIsBBWmI}=JR4Ou{l!@SRJ%=``fv{D$C~Sm6>#?|A{DNnBh*Uu(f1VY=I;$sr z#yMLVc_NCkK{^4F>{oxBSH|7yq{l2M2`Ku-z46{;uxo3x#M}<<8*hm1rDf7QF{3aP zDSBntc63TAp}V`2KZ^2R&>ny^$o6o^ z&V;t{Upvkj==G-=F)0}U%}*LNs(nL`unbbM)5}kVWF(3m0+cRvga^4XetXg%z&-3D zO%O{7b@=i^dpP>aiTTCD;&!l`t|K#iHi;!^$$GB6a9bLq4%JeEz5NHY$x4DqO44}m zk^94$={EliWR^I#jiusx@j0QUTP*WP zMp>s$LX59CNE+`$~8qr>c+PEW8il9N+*Wo7P2GXhx2g>2$5HjGFL(Q4R7UYu{l(Sm4zwLS0@2Y z>6Oq;oX-!17B&xV*|p#Yt|6?HdaCcVh&DzO)LYV8OiDNT7rb@o-A#3JxegK*ui@-@ z3C>%D4d+tw2l=_;08lbtit7ZMKgs3Bv)st8;wJZv&mjFJkC4ZM;I>dMEPs&>B2~ME z606KmzRTCdsr)#$8_8NttpQN$6&(L?ky zCUFHp%v(W!(m4DtXL}OJ+TB5G9EcgpdYqOWxYTN=f^*4UZx3{?dbjB3Dlj9c6}vKpykUxl}# zF5MS@Lu;Lqhg*acCG)Xc_d);Zt{j1rmf?(Ss3hQ?R1z8B1}F;q)tz!Cu?N?KE=7;K zhjqc^tR-e6dx0|_ch{oq6ekGB#1`m3J=O*Vz9Ic@zqU~xgMQIb`olZx&T^Nz?T}+M z7&UWuFfrcIKR6K=`KA0m{t!$ei;(f?ARH!@aC1HfU1S!z!aoQt)?V=7{=%t9y*yrT zH|)0XZlRz57k=}@)thh9-BBCLf@_LEiDoC-ejZjfa5`?sJ+FD8yga_J5Ek98YiHNl3-M@o=7*k^hR)uqDfH*K_5 zK>M!LkzVk>*fpdPpLTA8yx7KR0j*(A@PkEYN&nF8=>JyWvh&LXSD1*2_C4eq1;`NG z3Zn3L|^W9!e>&NBlz9e~)q(-QEjiuixEK!D8-WTt^CbUvr-Q z%&CiP#;-(zt8yzcxUy--^mqD5y`%PzoKh^oCuN*}(o?-`sK5fKd&aXcs%T!SfJ&SQ}QePw9ZqrG^(af(eCCL5G(4V0-Vnef=_sjbO0Q%9&!cT7gXheoJ(l~ zN8SiA8^~tcK=1AjUB%zg<_wT~`(yG^P(KUT`gGhfE|Tqj5AQH; zB$J#Tm;_An&tVQSo|VB|Tjic`1^Lck=Km7^ftpVemhqF2737e=F$tZB6r6J4)Xc;# zdkbv04KkIRtDf3Z8-?`wYRVn)HmG#B-A{HFtAg3t{AI+O8*CZ5O%Lf6D3}w3&PaM& zrVi8R=m#}kD=I$}iXm51^ILe=+_&x+kcn&1#@J(z(-0D0MuAuS1SjB9KL`4Cm$A;0 za?{8-{BDcsdio9PwkSOSo^cCw_EO+U9>n~6mbb>gN}sX4V98ZOSH35m@4ZBQd%)g{ zF5WuSxC8u&{zasD>_DFVI=G24V^vnLb|cxX4$j)v{#3kI6EMI2iXQPSqi=LgbZYc+ zBpm4*sTLWEJvLROQFN$zA5^W{-U9F`Y6zL6bRbs+12se0V=9Ld1vBfqQbIURmpid= z5gtTGHnTb1j&m>5ZaidsKTmq#=Idmg(pbN8wJHTHS`ii%uJWv#kf&T8298yLrYt*Hhpx4(; z?WR&q;)Ta-pjXP-3Zg`1+_Arzy+A?;d%Nfla)|$f?7}qa1+}Wa9Eqv#2a13_Sd*nO`Pk%b#-6zzch0(GC@FzY@tv%OM=dRzP51kC{Nw%x z{|S2S!~6x_H7_2i=giv(>i7;epS&SHDMwb(iRjaocRygVnF?LSw)lP9`U&VOOmtpB zgXthkE)`~7RqREWS7!E`LC^Ns?ctVmR$7hB8_{>joBI{X9!Vc56q$pJI1;%SJ{Ngn zbhkoI4k%UXkPZAM(0YhATOSxa9m*f78*HH`S9%Ne;E=||$ZUXlUQWEzZ{0QY2}#6n z7d$Zy$RIf|YhI1-Fr{)>PNyuDcfbdKL5Sx=!Zm(6-xQkIxqK?*5e-HnUKMg5eV<0c zBI%5>2=vJq?U|BH+Qd&HW&GOi6X&9n#`)@a?htSDw!SRf;X7 z!>X*sf9D*Mn`?;*x1x|%>!68z2~TfX*eCu#*(0LqPo((9bD^oBT%H z8F!+xIfU=gWd4}oCecT z`&5D>H96@EC)+QUhUW8TyStsbn3Qz3n>Zc4hv09-dY|27a5HU!I(scB5jV_7)>vmA zI#lcYx85-Hs3$uu;8MtA6pv=ZB!x!I$eBnFc&?vCwnf%Q7otn_z{%$Cr}v2rHGfTX zHoFFDg-*t7iD?Qj?{zFaWGwD!E$&5 z!lADm097j*U=;s`+xfjqQ=r(%M?%l=cdP3p|mR z#dXpWrK;Ljo2(zwT=@OIAqy)9mlda-Mw8P5^egSpM!{1!8U%(!^dK}EMg6}17r!6d z#|iu!ur=0$XOaT`+Mg^HD@S|M@pO^}>D4_A%@pmP?p{AF$;qdNhwZs&fJOIMipZRL4!q{dps80@0d`>!bPKXL=&@G{m!K!?piF_X_K@5I zYUhgFNj3@^JJv_odNZ8EulQU?6iGGZanHU{vO!QpAa&Eax!MM!71484>6 z=*e{x1|VZj)*3+7ksK>$IUh@Y`Ac2ubahrcWzb=3=69q?*)&X+uCf?(7!@y4-#Sq5;^)~zpUUrbX$8zvDV&W zN{}13tJIi2tUgy89cvS?*frZ|LybM5lw+I4trh{3<*-d?masGAnX6($W|SD!@E92XgHv zaRig&v&w62HdwOlLMejn^j?_F?%}TcsocxZ-S#(4vkb^lo&0NTFxJXq5S1^95l})x za(=iD_bRfwLn(q;@CYftm{xej@8X;A)49}OuI?o^oX;&mf$I;S{8ljWZy~K>n_5Ik zjV$w#U_Oj?Pa#dI3u=LLE)OO1E83EYWEKcj-`E!NoFvD7+J~QnJ3s{L_gIc_S-26n zc}@TmuLj*fZHe8bTt>J&Faiz+3H}GEbbsZJ@*42i90~wQA4Lc4EfwrD7vPRpHKiT_R@GkZiR-{|=jA}3f5 zmcqY*J@X4XmXGYlPCl@>)}R}(-o4w!+ppf;9bXI439j0Zkpp&uuEdF4xES_{e z>n>Pge}njaK0G0uC!8i+AzU{y2;T4FxRWeGuf7)ai5<9s!a3;x_Sj5;Yr)u1wct*D zohr$j_*?Wb)^;AV(|jE+*3N1`eGDS|5ox_R zTPQDR!ZiLBcZ9pe4dkAYf3UuuazpV3)JK9?3hk{{K|83d0^O-K7onMv?3L7&QCl?i z-awyN2rS3s#Kn}!W4%z#766Sc8BX7N{4Z`Sm!EqLFF{JKAN*P;k(*Oij1`OGZdq8a ztbA0~Ytup1S`XIGd}V@ol50&xca5c*HKCLI2MKj`L8VJZGm!w_6K=Ia=t34lHc%t2 zBfOiVq}1X@&?EoHQpki_dKqhiiAYYi3#aF1^l4ML)7{3<_e79|G=*CU&G$=wA<|xD z?k8ITf5AlT#yz}J(8^8mO2EyY6*rC#P?>bc@6Z5k*LBcCHKK1k!TSeme7SQ1X^}y{ zgn!=~>{WxFC1SlnpDwR?()xn@kp`%nlG7Trm%q%L1BLWhYo?(_FQ5yXJv=+1Y{JEa zHVH+-GsAbo)j*x?W+ijJxI6t+MB!73>E+wXIqkdtI*=@|S=*~DlYVh+=_t6)Gg^(! zeP%0giO1od-5eUY!u-Aeclnf-rb#>HGav$YQ-{LKnqN`m#L`)zF#2sn`TJZv7t6&q+*bV8IjESauuc38&20zd-CR{xse*o4&lQO1$B-GG zPddR>q7_{QzV)rq;ZYNGfe%&<)XEP06vdEjaz{*s4C?>Qdwlt})I*%Z&)`y%Wi%;u zP`5zaKvO|$?&It$ie2#pCe+kf?mqU8&`V?^w~?<7Z|-640;$Q4`NThg^_T^7f_3f; zkN0c)3o!%vK?g&Rt8tA$?M%y!BLBzHSwKl~ZELu^Wd@hv?(XhRfWa-eySoN=3GM_4 z?oM!bg1fuJOm~lT`Te*n>)rLqTcVTtcil-b`b5G!t7X$eK^hekoeh zjoq+qZxWUy)I|%l+I(*D8#^7{LGazzt={GuJpl*WnOHltiM+M^v>i*K3b9>DSNtBg zD4~@8)Yxum68!d3LA<~h>P z+X>^~OZv+1{l-U!E7F^>*~UrEMZg2}_!kSehuF6rQnB>v*1Lvs)&JZ>3hG zw6|h&Xyj|?8_U~5>ZF*{y=tvAbDDqARC1g@e>~W=#+g)G2*dmJhG7uJzfq{Z2Z605 z<&a*Bs1f+u_j7L-!OwXJN6upPp;i_ZQCS}zqKImdZzG=jUwe~jZ>7K8?v|vF2FolT zADNIl9MCgc)18yrj&C)WowSv5v%)1`$AF!5f+-h)JhvBet z*~`p{#((+&{ecmp8|SNIphh|)t(AsLJ3xN=xL0{XGw{eZF~1w-$uk~>4rmeC1lPbm zCb%k0{2<|gd(Ay>cV)w@#r=0tA7RWj{xc6-d1%UTjZL}{_J-S$9b1&vf*0JSUF;e7 z{2DpK?KV~ybEm#D;aOboSc50oIJ7gEDx`&8g+|BTi0w!t%|bnFe6S|}VhyCT%6&ZF zM|_PU8k4~C#-G8L#4|+sCZr^>?S@_p72sa|gt5Td<6IT)vLk$u*O7Dgla#xbp1-`= ze5-tsz5>2Y-YAmJMk?(&eLhOHrQ*y$v0_OnJGWgXxtKCpy+P{iPw!pdN#9WKUt|Jp z700+MQS7fEnJTe$nP+*L9FnIX>j8J5y92ILWmeoR^ul3(Nn9%y6ORe4(VTo>WBnnt zCTZA|X3DR*`|heKJjcnp%i#alf6!meH^h@pT_P59nwcLG3de6J=Q%d+zxYk0_7t~g zI+FxLtSbkUtS~KJPkT>i6qyCpwem)3KF+g>=mwIx)!jol?Z!A0?3dPOTuV>&HM(nb zpy&RklN#N2cW_EcUMn>gQwqbJw00va#!P0tr{S=P)x@sN7x&TLmb$S&Rz zM&oLnVXv}Ea1LEJ+M5e_=6&r5d!u#1{L6T)hr+GHmB{#c3F8(`7xhgN<~qB7kei$V zjdL$f*+g+aLkB_yLZ@h9d=i{RrfT-Mx@2lC(Bs(|b2v}k8qzD$3yb0168w+-g(H$M z`;5|7$@4p{I#A ziiVC$5w9Z>N2ZSmd&g_F<)g8}*Ag^zzzAoy_7IsfPTA{1!iI zJ<%gnaIY7HvQMVy^{l&@Eok(6yICfP?Wvgr%EE@bb zm@gC+yDN4>oS)fulabAmo!ofRr^nE67WF?JBt}AHDay9qISps0~zq&?GKP$)$s0M$W>%xSy*^ z$w;rtqU3~e{MR$o+srosH9#FLhw?;>6zVwJtas*b)_LnMJF)YD4Ko{A2E|~?vcPZ+ zV3Iu{3>Q@rQI7I+KE`+2RtS=9xrM3du{f4wg#YC9AV9S}wPCpz`fEfCjEEp#`L%jg zn(lrulj{Z^n}12lo|y1F{LN@(zaqn|o-~5HcAUC|n|218gqhlIbtAsedeWcb?_}8KCU+a0xD@g3zfH2O=Qo4rXW93}2&?ix&9`D=Q`8VWZ^!#!d>Gr7*K6J`~w zqJ5k`k?f8G6MbArixZ^|9#oxKHidHyK1^iVsbfxo<=bPab{4A%>b0}_i||^K^LsFv z6f)cWQVKcM+~)379485Uw~NCM<7dWJ51kG64?YO&4HODq4&Dr1id`9ZH-1AnH46Ie z+_2w-WaLRmFj%th5{`tgH2Xf$+9{hw%lToZHJ0hOQL~NJm*f2JiOz7Guz<?YcYzu{+n=B2n*fCplH&Dl`SJ z?heCwft$OCGLxI{nWq#fvfc=THh@ng{eIF~z>j{i3K<_0dd63cKM?;YA&;KT9A|s+ z1viyO!?Qe54={-jr6KvGCXH?5j!&?_$zw!>?cLhJg`d0^kLZkkh#eR^O0cjKd( z3f0F&VTm-09RCke74+JR$>KV0ZKp$ah^d&*jXE^EpMrnvYhSnbIcvb;lapnb8uiv= z&daLSG`5nZ#$cnCQHm*{s z?UI}{{?);PK{K#Bpa#zd10gpyLwuhEos5uaaOQK`lr0kX| zO6jHhOq_$b;|fYIrR`EP`kGeYEbrqP>%B=6xZ>@v)mM_xM|>W2nr_66T});z#kBME<(cPxE^QdynFd zZ51&lqNsnXcaV08cE%|r!u7yEo|FFQmI>X%(~Nc2I@svVVs3EWmTEq2hn9yM^Sc(Q z#VL_EDEt542~;1g>k3Z#Ty7F{^$FaUwtftEL>e?Yh49R@llv=ol{89nzT_{$c+&hT z*l+H3>(;EseESb7!{Ojjrh%@&f)$UqL)7Y9GLKdn@JTW8n8X%E2N+R^bHOHhstm9G z01sFJSfW~>STj)Ax=uP6>Gy2YL(MVfee)1qI}iEpdxFUtx?l{WU#hQ>1;(wfGn!ts z{=COs`R;F9`;3<1f8vM5)(9;MRtgphP6-Br#bXWfqH=`u(YpP_%7i|t3wb=Hm5tn( z>All^HT(RkB|3DjSO7htL*h2ugkQ6U%TnzT^fOs4i(&DIur?$B%So{q(6@rpab zsc(Nbf0%V)4R)g|Kh93}nMD1K?s3?Xvcd#mI7+E({CBS78Ft}YNhv&cOQEeC#R**9Rrb_Wz~Y@q^JjQ!f=S+r63)Q1U(x>T^b)*u5!B(n z_rlnH{6BLyhOXy{${ReCa9?7yIGcRaY(iSxS)1rH>WY4@hkij9&D{1Q=O8IqEBSpN z$bG@LdI}$%1@=H|xM>(e&8^&nNv)FhG+T0-q19W)Z849%tTXfXa`Zu&_{z7T(+}c` z$ZD=K2U%I*LdF|k_1SuScsXsfjZt>ZMb|Lc@w=1wIrKme9yF^OQaC@Seq0aE!#qT`HUmXUpfmU64M#kMktC4vI z9^;B0ueUP`{Qphs#k2IwrT89(X|-q_UEnJVo~@(%S*-e%!EBdbabd&@4}~=1EK=92 z$&KU>WT;(I6M05?_RwrH5L_Xx@=mNI=zuP^+%PBl zMxJLRU+5NLC4CsP&~;64i=$fo#% zIw{N_;Z->Cx+T0w_!=%}EU}I{>4d>zPWcJ9#9lQ!I*AWp%BQu$noreH5jG{CAS)Rs zj2tMD{W!(TiObQa*Hh23 z*In`M^^Nxhy{YilJzy)jNTcahaSAxwcyT3ITXy+74HSEn^PI;cJ#SE!T=XXLuA~X* zr<9Wv;3du)8UYvD2kmrVplQe8bw74g8T zZb(=q-GeV`t2D#^URoUCwxkVUAPkFRj<>p7`>i>6@h3Q%+W`ky4yLr~%&`ZBx9&o> zozuXs&JB6foQBHIYwfZ2T4D5)-PwBj>Rm~+n4&i_2U z;hCAA?K9#lj?oR*=0NSLgygUcfHZ=>55H%>catyRo$tx2El^HMjfHpiNOBf_GlJp!;bUMD zx2$|{LFI+&_?+h`O_^u*dn9so4VvD&Yene=pNAWvl`t8+W)n&3JLwK;Bkz&hkuzbb93LcXU`B?sfW>!&0sN+FS1-x~AJhe15>2f78hVwGHQw-fp zIeRwKn}O@`wVR&}v$)U@)z=~6i?9%cU@pCVNjdZ0@w`tshe!da1ebDIXiO`^V?4n% zJ=uJp$Q)|K(_9m=&v%i2ZJ)HnU4`>zMz|G;^rsU7C}UQd({LXS7Y2wOz~Oc&jo`9> zs0FnKXov2p^*L$Ns=JlB@^=y^l8Ie}^_-rStpets#u8()(cc_nPjz#O3((56P=?XN zSwKopg2r>Fq1_#|$3nR1T-G>iCCvH>yPvZV7PL6q0m%8h4pN%*7wyZH%4?pGf{(Gf2(pQMCR(!8wBsXZDGy5c5wSGW~%EesR(px%2a zq!)+qW{d%y{^>^IgZ!0k{gp(JwvOnYa3>2{r8V*mB@J5H7363f^yiD{!iM>qf4irn zI!tQgzOd$Sro19I=OQh3>5O71Y3|ch8p+?doUFpc7E}+ZskIbZG3~WlUDcK3>JnwV zd)d{NK-cnhjTXj$ug-AZaHD=lKbzDV$w|8e%xjK$%!ok)v`X);Yx@1L zsqdz@Xr0pvjY>~8nsQ_yOf`Ry1~Mu(f3Rvy!|1Wm^Fu;iZ2bFTGRaJ{f;+_$E!AwO1i@>pOGWnDdBm1;e=YCofVCM zc?I0}x+{w|she-)@ybm&?Xv2>>Jv0+$<>h36Q!>zeG?xEOWo?u5Z>ishOP(6jL1aJ z)?W1f%bDY!%0J}Pa#`t?&_uX{)@Li)t>b1MD<%4Zv8aQ-GFRT_jZ4PU*h$WTEOck{ zH=Rv(UrV#v@jM^n)&6RB0&Qd1-AV=mHc>bw>=1vG;)FDANxQhY z7`N8y&<3hbd|(BcX_%gln|4R|FMS*76qV80Cn2%q8kt(Pxm)l!Hx2Mrz z_E6|3_7Q*Qrd-2)*$vz-jJx<4cTNX>^$yhgFPv+*_6mB8&NRA z@yGh^dlsuzWkEP-FEUHf?o=RQ8#6*%X7M4GYbOG^Plf_l<%tZ21Fetd_aXkG{$$yV z#<_bJFVYIs)tyjftwB}yk12EQI!VR;k)Rk z$;#+z+%zBAQ6!l66W$3`;aHYBiR_RuDEv4sZ>U({cP3eZhLR=0ys;zVg9(ZBltvq~ zlHJ`|05_3bD32C3nbHbhMiy@}-#^}iq&YNHo8x3G?=-?8SQ@+_tNwsJ_8++UEsg|| z7m_!seMpm-=H2KW>Ye9lNdDbhxt07~oWz+jOGqKU=I7;;r?XcNAsaa{8tLn}UgJ={ zuSV%5Gbd$)Woqxfc4)t}zrifDCLK5*EK3Wvnnq%2u>;StKQmK)|-fIx52_!+cuZbnB9#$>ncsc2=ioqptCN*%a!*uCMe%f(w3Asq%~^&8s*}ci zjZ>_Z@DG{aE986ZqH*GBcz`?3OZex$)@E}DXpYH#u#gR*ByZgu=bE#HEp8vG-#C;f zU7Z5J<;Bg?9$8`6xwoiAegnh2 zO2*1~p%2=xXDDi#an>sIEPe$6+YF<4fYiMq_F1xenmSX#RI-EHzrw5EgL%3i$-wpf zCH$p8uBListL5PBc9U8;Rezg60%bxzk^(Xr%}Hvh$nF|$9TqF;<(!B6ew}^~59bY|3z-xy9?1`ddK3bOb3FKxaJXCA9EqF(Dyzz%{ZRT-{IsvOQ>Vog&Y`ih&nr%S6{xlaG z2iQouk?nX;Z(vk0o7pi=E@3s@EzQA0`@4m0%Xl0v7C)J$uWB)N^uIB61ODLnP_wv$ z2{*z~Mnlf(_4a$arE{5SAh*d!+f{ZD@{|2;2Av2s|{+)JbkSJhXC zXN9ZlFZ6vzTKeO^p~R^#%n?VC%F|3aLK{x3dQa0lt+bw~`c9}Pl+rRvJ29^?*%@HB zM=5vG=wm!Eyk;*e2`SNy#V+WLhQgG1rA*=hGBGMRQS6vcnMzAo`Oqkzwp!w1s6kKb zPiD)TuqD&@8(bwd?H=s&FV*u36bR|edxmX9lhq&^VREIC=_Rb5_kX2c&6azsaeYM-M`RT@asP~es@XXvW_6ouOby*0d5z$(O9blcr8_$3R^ zN(tfz`>6_v`XgxH{afv)=3y#}CgEj*TiAJmr(+YG*Jj<&`x#B){PTjB`@t@zqu4D@ zbA4}~{(xJnN_z2qp#h%aPRxffb{12mllEimv|y*0OfjcoHU;tr zw}qz1T}x=CmoR=~dLCx4arWT{O(HxLb1QL?bSNO(k+Uoo>Y zXZjHRJgBT>m*Nh{$rP9tEnX#00?Fsiy+=JowR9wkq+*W!T^!3!QCZvvO5H;$DDReA zDHD~MickHMz2`1%7x(GjJ4zE_8!@%e!5zwuxe$M2DsYHaAXlYPaP=l9>AKjKi8hV| zv(982>~tnNC+uG6DwdJ**bp?a7?VL559vlyx2JlF`^v+Vq>Z@Y&*gtc)A)0xoK(df zVV^O->qYd6`VzglahGhJIC8aXxnJEEVDr`F)XEE#zQ1efJZEsp*QA?zt2S7zsAQmx zEvfjoJKiZ_-z4j)4Le~bwvCRCDr`h0auB{?qP$Of$!s#oRdCT(w0r;3Y+EbHJG1c# zeMTpen>lR}yYO1K3unq6CyMjuxD}25psP8`e8Qh+tFe#lo;~^~vK2ap4~2jIjE|xi z%;B7%6?Uy~1mtp&YtasRI9w_Ie&}i7PITqyC(#dMmIP)6XUC2uvu6qCavxCO4dj{( za7u9lE=M_$LCvkT_NMin^(OW%(MG9*<(I-tr?^!SmUS~5P_%x_cugApIMT10$$8a7 zXlCw^TrigI*d5wD?#qMnOG%P)qc0bwKk&VOl-i=(JH{6usg740YFD(pxKOre$+Yz7 zrAkYO;hqbV3E+1ed{H&osSe;jS%Lz!i}a6FQu@GF@)|G0JCYi%Ik}wG_8j}PT>`wo z@3vrOA1jq3*=-z+h`P51n#wP@@ZR~pdJ}tADyby!1T=ERjc;LvB)aW*1An=YuGw?p zdGiPrQ77f1OKI7YKR8$IwV)-JaSnVn#)G&HwljlZ zFB3EK=et7lZ&ALQQsg?`u{HZ2(ibYDSznDh_P&)snqnEJBNKNNw|z9vvxa-y*+hfT z473Fmt-96`v$L7sd}CZP8q!PmGyE*vU60XM;7|xy-<@$n9EoqU#N=W@VS=;XTub9v zs<WMI{7)`%LodqG$x?NTnQYw1)v;JNJ?;3=!URwQtrRcL-9 zMGM8=C$gaPNUx>CayIe{Rw?&LC9kQy)s|@oXuNPquZzSBHj380dro5K8F{xqox4oc zw}r#xSd1c*UBY+p2m58Tn~gnt28l=6@m=*`e^~}9;t>Qk*#G2bidUQLx!@K3P5eFl zQT{Eyp8xLx_e#&*X6#f+jY6a!tq-rzGa4<8zsV391dCA^zj_~W33`uqN*DD8nHnFo z##%4!AGIG7Tn{OSSWL*~-n183HO=$JF5{;$%Jf+)?d;&Wf5Wn`M%URz+DQ)kWOk)J zv{sb1>+nC_@l`g!@zuzQV@t?PyXPDBtfE2yti2@(B+JN7{hKF#&T3)ZMtA$e$P06_ zMNbHK)(eo_P>HE1g`J87DMw*kY&dxEu|F&q=?g}

u7ggh=m8>-FFOrGgWeszE8f$*L=CJCohfHSE* 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%0Aqy?d$9Fud3Zdi?F@ zEpvw#wfc7W&gU&d_Ck;eOmJK`1TunlY=v(UYsm~SMBv?FtGG3IcbZTQ-eh#pPGkRf zla>dS`vi7b*rk(mDDP3~aBAFvw%CL_<5Q3VMqG1tq->c_Egdt0$8UJ3`)I*19}9Aa zLAOJ{3#9!#P@ddATkylkcksc@GBY_{JQ~tKy5Y@k6%R=wqB;R~8>|lcMj9EE5={J& zE3=lILwtfcR228!Ip$ULJX$co=*S+Q$Ao$mv+6VuQnKNbDCP_^+iCBS4nZ?9=2yd^ zU5bR@2$<|_>QSMvW}q*X=O2n;+Ut&T=Gb%=zOZZ%2922k?p<42Od!{o>_K?sLvC7#+Ee zap#C<()V`wA@Me~ha;Dqg0AjC(i^F=w6|6hUi!t$ufOuYIbW~&MOGpxsB4nfB;Jdw zl0-Op^eocZ5D7DlJt}4=e|b;vXygKUp8k+y z7j{WAVzc8mpgtRsLoth~nF1M+S{es4qrG$kDyK~3ZM5^W{PiFlWeYCxQQu&zKF;3` zO+_u<^eNn$6|DW#15vhL>Dp-RL!QFbg^%W*yl8)szQ6%dy4W;qZo-{;52x7{U)TR9 z>D}X+zVG(`B%dUooDYydf=C7AARwUPgjJldiBno}!Y1D0Dt?7kT6Gm~wUyrL3QlOD z6ISWO6*^%RZ*j^hOz4VJI`FQvVucD60tt|FNFWJ0=XYhlhktC{;UR~2-tX7px}KNo z9%c+2-*Z1E1_a5x#ze2lZ3ee1m)FV3U@4ee&}&XJG&pB9j9e%|BA{;LF&@)1Xal~l zy;nWqs7^LJtoFN$Jqx?Qq3hF@;l0ryRi#*Y6X~IaNXRSE$nkf2&557~9d-|R|4s?` zxgxmjg5n@r@DUI->%nFMCy1X4W$;Nxz?0U>e3z*qcyvFljfz|eMkXtb)5$Ibheytn z2;un?+whae%m<`{cIXN!1QzTypMl!`n&06`a0eV$Ok1$+u5H}90c4h~E(+xuy&N;Q zKGCT7gt#zhWl)ibC#Yf*^w)eZyOdDSov^fQ;6PZoEY$Ki81X53~jSyb9ExApbYvdMOVL0Yon z)t3%OJzO0e8=CN=l=s)u*T0|oljP46|96ckW+C#Oh^w-CQ9HXFJJN9X6~_Q**=GSI zt3;~uWv>R#w*q!PeC&O=vD@*fsKG4r0PPd+Hb>XuT|>tF5ctMPQ-|OLSR7@HERImV z|LKc;!wVAY9_I)qXSqz0TP(_l_we9~>tV5BYWc2^yTPZ0QeHXh zF+)nuGI^{xRw^Up+sZ@4Yw6Ok*uH-Vy}6Cj9?*vff=)2@F%E#x^(%O4Wu855cy^KD-D++HcT1%y z(=@8($lG0uwpWoG)+Ueu;0_*&9w0fKtt&i?Ft$d_X_KU0S#mcsO=@f zG(j)wIwPGAZ^m`(vm?RYs095j-ByiMj@sqR-VEXZS-`J`#(QP(&Yg+YuSIRh*SXDlkjVh-YZ{l5>6*|Gg-cP*H zx43qo*W7GFsvk5^T}Ur|WaOIV@F$)_UUma&!FG`~^lEtWs&M$XR>!}eq)nY$|K_@% zq-Ya=zvflYX=JM+SMCVu5hSwD&_ASHcgvi&z>ZWe4OlTNay|9kCdx=DuUa4yH;eL+ zfP4%oW99S*)LU-7ZG$C8e+@a5{R$%zn`Xx@e$OBM&G$ck@zvNX6F*G2fB1OHI3t}u zZ4_D$I<9&nkU?C`9_2R%H6aVGIc!qC6KdtNlEffve7I7wic!eyhSJKyEXABYhnEA& zZiTQFyuu>!xOf9}SC8;DJb|V`#2w^xagmF}Orj>cR3IH2^npallgf$denr6yh#Klv zs3r2CS%_UqN2mGFHRj&$`N;cEI2U(QjnL#ZFeaGGuZ@sH&a8);i;Z&M2b zx>?pU4*CO5H7^nOxdv+TM|ky$ajWMDZRj~m1ZmjzXE5KTy$)5@b=Q8!J$of~jj^^p zxY6Fiy?ucyWfpRmB763Y)>CeTI3}5R~~>eZO)rm(oq`nro`#)4!ek zV&dm7{yOsVcOl;aEi?MJ7vGKjYhw0?Tt$RxP~E(64>YE4yM+at(k$sa{7=e;MOTOrY}??Rdc^}7c?5r-N{H0+yKQti$c&Y z5H5NmNw7=Vr;#AYV~>*AsFQ|ppIUIzdzrVHdC<_bGA(#rYlx4KzwPjbLkE8ZF0@vt z8$daM#yJgr=Ce8J?69&_)eX9`1&N+2* zo6*Tsb4pQ>T_iG~FWZgH!&jKiYVao)8hjtVcOSZ(Y;4*jqN1QaY%~XjErIF*6}=C% zmgC^dd`$n8a@2Rpv(>fNd3CwZegn?i2OFi;&aVN*f$j0dzb8W>Ne9*efy3z|C9$(_5R zyS!Mt{GNN>$A==ff;%kC3LchT5B)N<8q-Qia0j?-UF_YAFQ_isIA)|Zn4YF`Rd_Wk zgn>DD2Qq0u-;h2EIe{%ZAx=Y2kjaz8SnRulQiZh#@ z3k6@3*}N#S-CO3mkHEQj)%!JS+AwHby5SE*HHz$AB$7MGXq1&TAnI z>=R1@Inj?Iw#nK>7dRQn+`R8Q z=f3PX0T)>c{+7$m``!mM4YL|*fey?FQ$$WE8X{P;P!&FRtF5OOigcZ`#LVu=2jhpv zUKzdl-1FVfM_(CZP4ItsX>!X{!F0oHhX!d>_J^(+-#3_A4Us$fB(%px(h;dsT7;au zDbZY>>OvxfN^B)YXi8J@K$fyZu_^E1zD{t4a{c+Sq{`|brFpGX9uxL8yS?_F}pL8%p_ z&Lbiz*H6FWy8?Fa-X$*P8F}hPcn(>}59|hkuuxTue;M*anre%aQ8` zjkBm4rz}r;MS4!MA;bw6=MfQ!>MWbr$I0ZhFbim#e21}Jtv6-qzK2J{fOsdxNe`(O2kdvCe++oAS1+4Sw&UTvOEfzA2txo%a!?LMmM z(~W?r5WASQ^u+3P?(x3m|236QH2z0TCBnW=Bj^?8@p-&rZZsK9d`MYG=?|z>p>o>- zCG;75O`~)>iKGJPhWl}wH;J;udxJ;CGLZV4_z}>R_dz*i^I!=1|7XCgCM`Vn{P7L7mxab=a>vBfa;%N0Fmi3FqW6^0Nj(dMq_I z8FGzklXCGa=D|0qnXGZaj*zY48=$|Z#4y(UI_dW*>(>8h!^zZ{wSP!_KjBXaKZ;uu zRT*9+Ddd%raIGW5<32cM`lS;~r)?XMxcV_o!Mw()!L%<0m4y$Vs&aNay?{#f>TNVL zO@C%CZsyG7?lBYmCd9zGWzQuUs*ww2~eV}?#MpE3J*`nW=jbP|O|1mb-?@;6T`(d>tvA9+4O zYADrUj7ZRwU$W4g9&wo&<5`Aqcy=d5(L2EAq-CK`G3?Ye4X z%0d^M^<3*_I~3>mxz##1Ef3nb)-*F=BH@Ja843-<@KSAFszg5b7Fs`x6mAY32;cV7 zxyb*HeH}djecf-@-$d%(t4aS(peC$XGryYgl0-g)+swi+(l%1gdFDYik}V0Xt@f8) z9^W;>LY8nzVY{eRlnB*lGcS`hf#kGbxj@Q-g0^CIXgYfG(AcLhwtpWs8a8VBKJ15# zsSHK7;({VxX_${Pq?nJ`@4KJ-TyTtUWR!7xk#klXGz`u_2fSfD~(u5h<7I%nOO3sFiAV+Uo$Yy*hcY?8D=Ed?mxHarqW*qHh-v(#e;%QTz z;ekdmcU$?r;-cd5Oe2!k4TkgPbW4#%zrUzhU;M^W z7%ECQ!?)8)p{6uaUZJ%xqDckW#62x2!>kNRNg@N%-^zj|aHkaG9qr|&!@u0ZXGn#QZElyY|nizgz$Ih99qg zGv&XNLK6Qk;YVxUT3s7)H}r<^0jA3qeD_y9BgjO3v~&r3*kBLnD2!Y-Cegx`qAqmQ z6>y|xGuBaJyp?#Zdh}^(o~lr>eS-eteQcZDBX5ryU%c_d7N?y2pH4ArpahH)=A z$1=~$U`Ht^3c8$IEwEq~l!e@tUg2?QmGd~|>`sOejH%m{uLu{T5sqgC$SO{e2-7CB zqznB*EOL+K(i=hb;QtMCd%1P&A+THuC|`TdE?0w_!ZmattBkMwMqyVRP(^5H`g+q7 z_$w+GJ8YAVk35_F!M?+ux4=NX1-ir+_^tjOGmB}|`7CU=`k(=CBlDmEY+((fLW^eC zv5VMk?0R?zeXNa0eRESIXgmrSwIBi=Lv7=Ov+2ApPCE;&nIAVo1?ZX`hS&usGIe}l za)u*mT?~qsm^w-j-JMz zAXxWFVHr^hN<62oLqTl(>w<`*d3hat3?n9?bl7>K|isqpys< zIr7($pN<|H)BfQ?bDb`AQLWFV|G=Y6!N#q2nMCpP!DE4wOy{`c` z_8kwVlJMtFfj=dlD^`|H_s`_bD|7+hV*XOmlFa(hE_5Y>s%6G@j^PRDs9vG)Xo-Y| zv74cRcRCdWR7HSJ37P}wa5!(XNU*(t9jg@DwgR>bB>n^7aJ0dV67IZVwV4Nv3aA?M zprJ3Hw_tky#Z1d=qBc{1*I+Xlpvf75Z#~xOK(Coi`PL78C_Hf!Yz?O!DyQ45cII|w z0k}Tzc`t(zc-^lBll2264{xM5kS#0~CtCo@n@|oXL=KX;_6B!|GX-am1L5O#aL>b| zGRhL+hV7)bQ>>WJRD#v?j`x;(tLLFN9{$QOW*s)EMdGKDZ6S9~b2%b8h(*Sd%4&W9kRW?K9EJ0#)&B(`+XcT{Y+d(qnIc4PW({oD6E?X0S4#iNEi+LNR(4bLC$-Zn>ZPqv52=<{Sq% zIT5N9njnh11ruee7kulbQe%xKarUce-sJy#F*j--{qyLrzfT&Q{-FRBQnoTr`KhWz zLz;Rl=WK^v1D-wpPpQSk7IraG^YTT*P+@l=o266GhS}9k@-}e@9=3)684L=IE?$5G zSSi^daY}B5K9V(qu;G(D!7dcV+$sZQ%?QT)Iz2U_qDNOW%T8|28Xw9A()2f#Kk_(%fR#lS$*gjB*CNv6dUnMfgEa*L0&m8i<`*lXVpMEbesBsnuHw{L7Bwo z^OpBB{B3-qlrcm$a5DHML8qig;5|^wi^3*D&q_+f7H%I?Ou6o?g}-UsRIKZrJu`i1 zdiTs3l%CvkU`rvCf69k6a(UBIdpLKd${E6}{%Vzoq^%`MacFBx17(MG*C+ zgR;%ydi=JB;G22Pf6(Q%@3mFiA32qtA?h_o1S^V53TmOT%fhCll&uJ)UAi_b+mOt4 zeqOBNPw$4~Wc&ADe82cTe=L4{-K2P$oRKOCOq4QoLkoMc@tH)v?k#@-^(27=6}}2Q zmI_`!vNT$uiszySkdkp=cK;f;*)^7do5$nw>qP-td73moWETi+2Qka7m7IrnxI>uD zd&cpB?{)}02@p-4>DB>s0a3c!^Vygd*C~3Hb?P#Gf@uI7kw|!g#~eD$?X2EQ$e};% z+KO4wCd|*E@FEh4Zm`d)kd^R^<;Prh6uoB!{Ca0tY0!!kuz4U!%(5(ufOb2V`Z03B zIQAZk0j`reElX1c3bGU$hc3)CinX;ypV4dDX|A*sFP*>^+W|M@Mv!vX`zzqBtsw0< zzc%z8+3+zD@H6iL0n`De+3zUP)K|b(X`m4#(rh@bykcR8s6*H#$P$;}oF#w_5Kt*X z?}s9@Z2} zlN=Ehb2}M#d{!_a9$E5@Et=h!5ya!1RVkBHc4(+}&wQvXSGnd!HO1Hn4}%PT!eU)I zWsh{~T@M{1TN8FoNS!oY(C6t?+758|gW5q3^J67nhOdeGDEhsa->jh| z|04BYso$mWQ(mlHv-W=y|106&@vB!~T`3P87Upo%;J{8Lw)zI3*gj&HL38vLsA&bv z15g9W1rlL4G!0NoGjCEko~@2OHW3&`#Qbq(sp8{_x4-|_=;G-6-+%w&mGSn;Ez=t0 zghZ(J&7U=#fa)uG`7RXZYy7{V`c+KY3g z$SW4lu;z0DjC}Lcr3C9)@EVw|``%0V#NPp3D*-9b7eS)Rpt0x{xV$$q!pJsuB8SIj zkv!4@hh97GvQBmsCly<*Mx3@>mY+TgziL z_fP)qlrQmWT8IXgft>**TopE$IYLsP5@iJ=t41v4D>xBcDSVv@P8q7kz&X1}d`hbz zP)&nz@h)!7PjODq&@q!?M?n=*9wZFi1Lxku6@u`{6^}x>k||*;l7l|-!Z8dDY?;1o zzGh}ZQ8gWb-0m}~Y;bOFD%((H_F>MK(lOG4)>N48SHVv4XG^RwW#-S&R$-#yf!rT?UWCbf0X>%+FzsF6s!r2 zyAv6+0z`0rDW`zd%c!9u&%@DY51iv2pcsu31&jVA&lHH6rc)x4l8Tb(0RvteK*`AVMtF$A)3Jtw0KPRgVIfl)q8Hw-p zf_~^ABUopUVH)nKb&lA&EKcKb-Jlw}oSA*osdzQJ^=2d7vWutSvVLNJ4-*xc?>*!q zuW~VPgB$|0;v{7Y9AaYHailINSPFI=I=Bk5f^BD)f>NHy_OZ)x_EJGd)i6hixA8hD z(4{ z2S;f>I&6ZU$(198@&c6FCyATHhYTrn^DOEG>Kjma9LF@!$4cWKN7|n`SdA<+W!Tjf zx1hLcl2(c1csx={z2R(LqM2JkdT#&&-8fY`eQ~Bi8HPXIGc08;erx@p9-r3tfy@W$ zC^SK!hd9n+W`7V_ANi&{=(~Byb;*SvqQ#Ja+1XRa*FFMHx)RZe&|s*6?nQ5keKXlKBelH8=wXK#u0RU-RMBq(5Mw z1A-@@34FsmZa3=nHGaKw^Rf*}{5VwJZ8PqvUrzk(#T#QEk0Vp_hsRULXG&FZbH#JF z;Rrrv=34c(bjLZD%$EuhERX3zx@Imfo0Ep0YB8ok?VLtt4)!m9r!;~ENU$nER8YX( zpD0NWsSG`f9Gnt)uncOG;M3^5TJUOCv7strXldtsN5O9A+Da^)#sXcQTBEu!<5Fxy z)_S6$2)pzBi?vJJ><`iH@9<&z;oc0Q%@I$e-vP>Q0T^JnX&Z@?j8^=O5nMP8$SPLg ztJ1K>!IDMK zx!i=m^-GY%HlTXGk9pm5_aszMsZe~x(Gv;O%e32+=TN484r16Ha1~F1m0wD`fb8K; zY+Op0WK(GBQr%i*;v=Zc`WOJo;gZ}EW9hTH5WaRzF&525z;gIQGp z1$rZy2H#eZU`Qm9)}u0`LaD5mUkQ~5cL_S!x%7E&i9Hs1I(a$}W|U!yLyBwDVaf(& zgz{6IvR?F^TjqJ%ab20d9*)3Da5bFpAYNJCvCIUu@Ce9m{iY^Ei>_ZQLO#kB(_Ql= z+a|Y>5Vx$1{gKqwF)5{|2%)a#_Qu&Wia(Vj3pdS1`ve2|a?oJW6s!oFlFlNdy(#PE>Qp z8Aj?yNc}$S7y+Y5YE0AS%qOX?&7>-G=R5Srp#3_HB!(mQZH}w1pJAi93SOCmt{!l; z+^C)(Kohi^-T*4Jg%ok4_;TJBxIs(dJZl8=s-KgCyuTsr8}c|6oH|w~<1q*XEZTrK z-?7(v#B$dB#MFyausD6Lme9n(3qb0xm?R5au#pqY+ZNSJdu^K?GWXZ+v)Bk8MM~z! z;ASP^M6^*3Q1)OSF9Vt4Yge-Sy5}R`rtx7(Q>4>o%Wa|66}!dUGXAv$7;~c z;+K+spZY(k+f&@he@=cg`4?-~B>oV;W_8cX+RzhX12!S!coP?VAK;GI0Y8w^a|>E+ zJ53D2@&zPcRP(Ahoy=%L30g?ya+b}yK2 z_NFp=wn5Wmgr3^GYq1Brxg(BKNU3=XnLwvpAAkcF>G#7?5(d(10WBAwq6QKKD(-1a zq^@B%MBpn!x+rL<1bl;~tWxaQHAs8e2a@zf${lyDy=&38AVbdXpbmZ@?J;Oi<@49j zYaTNVo6?Ooa8G=e+9ey(f`zWP!2FMN%6t~^#@y65@LJnZ@4xPU?%Uj zy^W|Alj!~Ugh_cMKNI&^7Mz8Tgipj-aNJe$DxgCX;pOZEM`Jr{2o3^FW1wgGkUYtV z26ea$HRfr2#b#a~HYhnkOzD@h4J%HqkgO1f?Uga5DWVo`F0+6dj=B1{sYgGK^w7uC zrHXf_6*FykGbd)klr73xRpT5_a|O4wQ9G{9LvC0T66QtlsrJF+?pw$*)j^|?t}oSg zX$o`(Y(Ke6R*+Y>K;`8VaHX-~JED?TuZ`8kQ`Ro0{3GRG$-hYc=URU9Qqp&8f-pP# zU`3B~gXlc6K|4r45)Q672asF|$FX<8{{~G2y5vogfR7Db4Erm_8QOE-He@G1vU*Jy zppdIjeK_r(yf!{P7Bcbf54R_`E25R7v#$9DHh}6Sk%&|yNfjSn(EKirx|C8z-<4N}Ja6fS- zfJF7VpHKbV@9_C4hiJFKoXB8xA^i#vM^;FfQmT>)frNF9LVo>J%SS@o_SFdDwrXk?7PI5vcUOVhlrXe3PLeYH$zuz%=ND9>gp_HoACFLY3bNy9M=V zz;|68lqSH>jCjYl7ZZXb#+|xUHET9(nx&YXW+^+NJSJw0=-S$5>yQ>7i|VUhOMv5; z0wwKfoUvqE2?!kd3y+LxCKXaT2GM6X;>E~9ro7d;7nyr5?oM%R=>CY;qPNDZk6V@S z-^o8&_h#x`%5PJCmlB_HXzg6WTXAnj%2uRFCCCMS0D_eKF6l(inZMor7fRpQ$C(t zXR48hvd{+wQ?hNx@(p(zw!~+M8m5#jVn2WuK97AKrcwTDWy~_(4e^oCv5582@8FzK6aJF?RqCHnoBdbv;@Ur~{V4H|a5-I%I3i04 zju1+r%#VOB^&O;gop5uoo4JH3gP0aa)Y0XvLeTBXIMd=E+9GHBRNdB4DK_5!GrY3wsB7q*Bl zc)-o5i`>LFv~$*tf)u~|%rBlx+Te&#+9 zYi?67dxGJ;xNNPrJTkU`5UPL{G-=MIHXt2rz}#rrxpdk7iSw{q>3-Ea;CTwY_Cw6) zdEi!j?q5flpxvft;~tQsA}WQmz7f-nL2QMjfhq{+tPIsEi(L*1S{PJbbm~6pVgEMQ z6Ic0vy z$3x7AUk%Vm;2W~KpQ3*I01lNlID*4yos0``U1{+5?!=3^6EpETFu6!kAsnS`?0(J= zSA;2J9rHMJaW!N&*@=xsA8R}FD~18-aiu);Q*coXiyA=@ij=*z;`)lZ2B* znYA9lKBX9$-%$(OOy@y}tcGVLQNz>4nwX0v)*Z_-H)sy5op9HDCf@|o88!9~asNt~ zOa3XQ8vjcE_u4*;)_n_eoo|sx@pq7j_R$W} z)6kJg;R+stgX_NU5mFG8(ELv6MQRs1yo-u2e)zxfzmMOXI52r(Ds?6sYQJo?QGGy@ zVd%mRn}*)_lJhO!anNHeL^d-C#F~AWT9TMZ)}uPhhDXvu|Bx}xEWpfsB|ei8;uMK9 z#3`FXazK&vmQ*E4krW4|2T4RZyaIL{cu~iRFv?yy$#!B&Z8rBCj%imyp--a6J)rd& zOF$&vY>`;cEq~^E&wbFdF&p?2__L| zAG?v&&+;Kt<}s_6RfL<@h^*vXss-HbP0kVP=EV{Cw-1`?^fql75+LNdR`gy@$F56MXB7_2NtGJ*N-HgySkhO+LFk& z((&A#O`BlnA!YDUSa9TfaG(D!?w!Q%QvQ_6PfbdBJz2YU_1gOh&tqRiUSDw_H@7GA!x$mXoQz*tK%|){?F0gIzNE)CqE(HlJ!E+h9k}gZW-Zj6d8k#vi zJu&&aiEk!;46Rw)bgn9Oz7d|uD6LPs(?nbHVyfY=e+@FyMcPF|11fnj8Ad*XiZL7A zos^q_)T=j$XvQJt0VpG+&;sp*HZL7Y+ZZ`lmM2w9z^RoC1S`cU;v<4Q4pR5v-n~c@ z_>Z{imsf&x-MVm1|3Fg(YD)vUj}F}cHjH=8H$clj=LmMoJcscc-i+Vt4v*Knh#im8 z|Bf$&aud~H4YrB!hk`Xw1|5(bxmbDZJosMO^cDw>ff`R*70)6%rTsKYu+)nH)q(Ve+ zyV(73FGOQk(Tn8E7S0*4&MWXw3aCL-1SA+jU8v2jAT_o*Y(w}*VZ%rk8xN)hHS$wg zUx3i0`j9Bximbn*GHZx3 zxHZ@u+=KK85?PJqn5#4}KP1{|-PF&$T=@JumNr;oO*?hbntsqzRC7MH)OgEWVL1l2 z#EC#I9M0G&&zGKLRA~Egi|2XYK5bl$Z=)nT-X%snF#n@jDVXu>iT~j?9 zTpiHsMlnB&BQ1;ws9xUyyFLMo;U{1?7F+U7r@=d}*G3?1w-c_DT}Y;Wq*oZyq1U z8fJ$%f*}x0+XYo{0*v$JTo>-HBJKlj4(B*F7MZLEsMe}DQAnqTdlmhPk6$4y5g!v@ z4YCE51=B*F%EH5shI7Mqh3=GQNz7u*Nytwrr`#R3^XLLp+H9o5_!aJ{+-dvt&`iUO zfBHjZ#2gYjHI2yePSYuLX1yG{%_1b*oVSdC@{(yPHyMn_pl#&o$}~1@ld*a6GS1mk zr_k5SNaJS)c|)(PoQ!%M=j>-|{<`+_)cAEjNqsf>M{D0t3QhXWnwjXx2ydti$tR=y zPR!2M(O&qx$gzCY`_OyYcN7Ug#netrA|9}%oC-`I8mP&Bujjh+vORq%)-;N|@l@5j zGYZAV$xo3*ZJgv|ixF^umxC{tsLj)_gifQmvOOIt?FmjZH|(?&Igc36L+j7g8i^mK_X9mzYeBa0=Y6OwuMzgCP6u@psl0r_3lS%k_2fl zB}hnc=|s9J9rX3OI6W7i^g`rv?!pe@$YTAHXh{g2V>Nc^J)r1`R+f7VX>mG#E;vuusL`}i&{!+rZc0Oa)+lHa62dOwIGmbwf)cI)Iu9E+2VG|s zH0Jee4SE!$UV@@p$1O)SXFwucb5LuL3Xa1f$>q?MVajmf%DtGIO<@NgAtrej7%{$W z&dMc&X;=>u;Ox6IUriaOKAwIy;{vIUm}yhB%;kZ1kfy2A9)m(S#n1;|bpl8*22df> z;L=3)j=ohth?Mn2b+y)Je1f!&M>ew~+2>-IF{5n`O<4I_)Mu+-i~WCV{;~Gc)IX*E zG5O7;-z0vN_{)Sp#Llk@U$HXeMo^v*3OcsyKh@Ve-eBlVo_i1bmVBrDC#f|c)=c2$ z9>V^2h&t~dKuQZJk4t5yE=~7bziK0#k{hR{fB5OtHHClX_N*KlhH;Hin*yd$rzO(X zXIF!2wBMVH#F!-7W1^ce$`p}<!`Qv}&+hB?1F-tGej~Ef=JVU9QOGUDilT@xpn-IHvOX>bm(r65X)L2@UNq z66QBMHXuX#G8AW%?g88?k^XOxj+8;!3hK=|S~p`KnMPK@o7)Ws-67^MeuL?pGHB0^ zvGU=qCAh7eChi1#f{bEjq4cKC3FhQMYX~#elMzh9vpUsP&xE5%UP|cYCEwTc5z8eqE_mjSW8&}OEAwPS>vJ9nxaRZhdq zq3Pq(6{?202DMyMpeX~DDhu~AX*zG-1r`#>x~3?j!GNqg{fItMpQ$~7Y~gZqrIl+_ z+V8m5Q9xf4wt_^Pyec^6t+<)^nZ)0u{(1fT>*7;>pEQpQ?tdnH8+&in^RTNSKP`{zXqr3l@)(--E6*Cd@1Q31M zJn(Hti44++*=PkA2K|CEabHj?2x0q?KidI^(+woA8iK3D2JDivv28D>6;Lkuk(K4h zLVCxE1)K4Lex+6eFSTpFLgR$i&1X5aIIwu#w!=99ck5NmsrKNkJ%l45f~KW)Bc<~; z;s%c-Zkz-a_^xV}d9p!l+w4PnYk3 zZ%?xtke$?snR&O~qPv1k;HcpUlz+wgPGmal#oygz7U63UB2V=Q<{DIYxN{fwZXbC+ z@=yBa-NCMNAffBr??IpR0<(_S{0~6Fo@LoF9X^0LnOvX}@C1VbpJ0@)!0BQkmzRfZ zj&d*s!Fb>*;6tH-Z)wAyYADsNh_M3=-V6N{6Peo^aLTAFkAzvJ=HT?;7BCYNNgm~v zv%{8Uu^HQSuK8}&ndy^Lw-wuGZYwRyk7q`e^7%9kfyB*0=r;Rxg@#V!u;~%Lhe1#t zWeXjcW|SIjIA^0eHF`~#p~Ny^-ChL_I)>!S?1V^0Ut`^y)h2rGcE0Y0H%HhwpzlJ{3Kks%oE0Lv6-GZKlxOHBEtx zbj}{R%t2;(15_&(kgXmt6sTphkwgX7FIx-;wwS2`d8(M};tcW5i|+fsoQsHj{NdsvyTOCiW2JAx8keHjOyQa+sd!S4=WZY$G!kcv+ zN~D+JJAc_1Absxj&LbPuMKL0WYX-T;NBxg!$H4=NV-&)9UIR}+Bl^xFZZ0PaztwVd zJ(>JG%&SnXA&t2d{2ndxZ)ENh$VuB_TZzOMnlZ2gEJVU6fmGoX!xe*EpQ*!^U;&k( zMT0HpEmX=!m-jl6X9&+*isLfWXnWxrxrcn2X2%Yf89(3q$erKh-vZZ2F6av?whgl$ z7dHykSseT-MZ$Kd8&f$w93Qw07Wkp7SQSv_jSJLp;n+YMi4|hL7UT>zBb_!9DfeB{ zSlOMh;P7|CM`U`587UvdqD*cZeXDoZa^zCeLIE=L-k6grUsBMg4k?a9dB#`rW(;s3 z$~Dy>gcKqTk*BLeKO;ARAGlDB?8jzfmcgtq()Yr1)1|SfOYk#ZiTgtj|JV-i0eT`o zKR8x)D*Uy`{W14rJ@H>9xl`%uUP=i~`bEOO6W&hvdHnk5tC(rfBz@va4Mf0g zbQK=jJLrkt@@+N?E-a;|GK*wN{#x3B7L3O1;1bCOvLoN@S8MD zN2%)O%GD}OJLWGmeJhAYwN|zL{_-~Hi{^dDu~Ed-8~PXv*cs59aY#%@ldwOE1MBKE z^i^_h7k^j`#=G=xNNwm{Y|x#-w}MYd5+%oiP#2@q)X+bLR{5w8p8}*Y3?N-04YzuW zZd9XDA5g2*W!h3>t9j+(6%Z&BkjwlLCK?OwfJ2+fV zv5#?^Sp_{%HfMy>g>BeAb|$Bv*TyfxylR|Njufdpe8%F?0ln%*lA4`^WVcGQ!I*<& zOS!?ND@WBVGU9~k8{t8Jf>VBQ@lk-~f$zP?G2*hizYeJ0oSUGEdIrTutoy!m7u+IC z`1yKq#>V`q*y^UitsdZ@9_Kdlr;ssK2GT>mr~_*4100fD$two`?=(Aujr}Xsj}{~h zGzqf6nMx0O5+nlQN`?AjN-`jwl06E;ekW{0$OsY!DlykqknvPK6naW?t+5X-YL{x` z^t+QMC!^ul&zQM3eP()}QZ<*NN!K=LS~R)Z08_URQyOw19d2`<;h3Qw*@}0uCGdgv z*r7#U)PjC#CvJ~ZuGjr1$tclf>0SBdh~G!Qh+T?bofx0;KkNU!?zbsFPx>U`%|uhe zcX1CRkFM~A)X`ezf*G9R8leQU((=E`dWT5*>W2G|RAkNTsQ#XKs zJ7(Xuh^d{9rY7gwRc&yw<)W&rnkJD5HwxxV6Tape+`hL!6i>H5wBG`4;3MxoiWD<- zyuSGvFU#-Ojchh7tD z^Z34gOjwPQ4A=rCiJtdZ1JS;~6`~^Vl#};|9Uh3MVgvOk~(` z&hELtMCQ&@5M8GhM8-yahrSLQSxYKw@ z&|>F-?a&V`TrWD!)7UI!f^nF|&jXz*P3#PcmH6N`9FXQqZ%9W%F3b0>xPc_{yOMEn zBGTRSc-NTk_|H1dg9|hW?NtAKf$EULKKUuAhkOv?&dfZUX_|%q6g$mw^v@EV3ctBh z<2bZ*m7r(b#dlVxf21#kQoBW$qT}g@j5N!Xb;Pb*e%TX28)9z=qJ=(Ov3b?%)eqyo zUGplubl2a{Ij5|BU>P0Lwu6w9p_-mvr-+`SD~!|8P?a12DaWTN z)b;3XsLpz@Ki|B35A21j&;)7F!KY%o0DUg%p>`IJT}Fb!i0lLvHxo`Cl82v5uv{t& zIT(5)bbsi|kRsF^5~ThaL`_^CtA=rn$e@0Pjm;epl(8K|?QVJ>bCflVbJm18 zfeXHq987D|!B!I??WGWus$Se@$h@KL@ojb0E~nV_ASTu!*|J!FOwZG&qN3p%40@5_ z3TB(tCg(z-xoM#u(^~aXvu$8`0PM%j%eU-rIVRnQeKEdymwx#)sC6%UK8L$+gKr+y znV9yBk%n5|fWJM0+sh|}QE;f=lesi%DiFsR*Z%1kcn`6w2O`lqqq;~`@K7sCHQQ(@H+#cdFJNSg~<=6 zcFz>U50I<8rfi+FX)-}QO4Ir@d0Ly6Fti$^NK?tf2_p^Ncr)|#9oj+75PUafIuY_@ zrr?2^cT9SFiDq6ca$MJh?_VWYy((@w{!fX2P5w>liez^JJ3c)AcWZtX|2#ToGRXir4g`5aIEpvn(45h(wxKhHEa4~6ohEo;|F4zZt zrk9C)>=Nr~)Mid(7m=uxp6H5oIl69Wa~iQbZ?-Doph30mSwNk65Q#uNn9#oEe+x9i zOun z01<8x+6;pV@8lpTh&El0Ap`2u9wftcfD3!xY(?hEmBmXo=JH_&m=SPr?}a9H62!H6 zD94c`fW#k%cRjwI{r=bdA(Z39II{uuYCk4B{oHZ^4VkSXxWMw!2jqZ=)CmnoI}$W9 zk=0iPce+T_Bj^xzh?%(gH-t2YG=(Zd^Ks60hVGT=!>)!&fsD3GB7})IX+b z*ZwQvf8$rJSpx?;Cwd_KuP%K6nrN!Epp}Y$N>*+-7a;EN(ro6CSo|9?7i-2TBE{ zL^1Yo!fi<;6Zzxv{{&HK@RH@6%r~6rffk4N=DY1t(~& z!wcMigA;71&B!c$Xjj@K_M`Ci-3J5aI;cM0<+tFKi19H%Q+tk0dK>LAT$`gf(-ABv z-;giW&ws)ngaY&!uMi6LPHrQ&2W-n>HjmdMI4&f?erpkun3lCl%+ehpw9q5C`(vSN zNtb1XjmZX)zt9TrdM?tCdGr%-Kc2@|TxhUqS@XA{v7h*1-Q;e?0qoHE_~Tc$%-3so zg5WTUM05#GRW)j|6hodKY1g1S6zebGy^!F{mFfw@u&D_yMJf~tF}`SGh%fJ$#^@(W^&ZD^ZW%f?Xh>@o+)aI%iF{eEPHjibxSlKyS zJYTFX*0_)~LmH|~X3G&YP{8wH@>vA`?QxJ}G$0gZ3NR1g8NsGL4Q=5T#%|gMux-4KC-y#T2j&AV1Exs& zSX~iR^22(KK2x8CZop?KtfxTNJe;1tS7QO+g8BIJ9PFgOMXNAZ%7IEwOUBYHjqlgrh zgK9GZcm847plq{zP_{MnGQ3S^BL>nm_;S+v90q4lCBgGTK?|D@3)``VDDS7CZ{3{y0@ zc?t^UQ_fr96?fAkxZ^=xp(QJv5icVD9P>&1UlIJ2A~Ftj+cZ)j!zh39-}l~fCfZ_= z;w0D8z_Z&8_jl@a#`GK0Ey_4(fg&_&jU1owCSy80AWGX6yVq{E--Sc?s_zbN$aZw_ zyP-BtV&cVQ4T4?K4pNVrA0W@BVM6Pa91NKZ-68Xa)`u{mII9wq;+;YTr;TBOci?qD z!`JMgx}Jmf%UryI8R89O%M_Z1ji)gsb>ar4As>ioziZboA9hjUj2!TN3w89-|4b0} z_ydlod!9>}u=La5$HgxG@AO`>A2XF4WIWb$pK&x$-<0#y;Opw-=c6l-qXRv_s)Bb~ z37&OwAR`jV)d^-FZcsHe+dOTnR*jv&sIE+xhI5u>I&bPQaZQgF@)vhm?}4hQwoQRl zD6uCvC!uPzg0?vV?aCy2f^$CH&EBJ4s~;>s<{4Hay7~#E(IpBqq1GrDo(JWi0%t83 zN(?DK59+ge-l(uwd<4@9bx9_1o%pAGe5WM7*DJb4z1ncuj6^i3U^aPnQ)(E6yuHCYLpQJZB;tAGXEDEz*Cl+s z)|Bi{`qP>fapAH5755qT=a<5JLVF}Pf~xUG5S&(|3Kq~5m`{soTFN{shtK@c$R@+i z2~)}%+C`{?Pf|Wd7to4q%OZR>sbB!Vp9C!E+;QxOCgcgu07$>2`zlPnS4<2nUzX_y93({ne>4eM%HGC(-(!-J` zL4??VT#8Dbn3Y59qfGj?f(?80Kerjud(Djt=?mM;_0X)_j6=||8cZdYCrf&0kj~m8 z9iO@Odl!5OzE5znnmty$PCLMFT956(E$4>KyFzTbZHkrWbTG8=so}IaIrMwgr_g{!SmyTN0z`(KLh8>hxesWZ_^($)|t9Y zXK|l_ON(5sv$j)qSc=j6a+l|w2R+x_+ni>og`Cb)o;yfAzvNY7zPFEBgH3BHyAiw8 zc68k1!a}fwnuOH?13w!%JdOO*n5TiS#E%eGi4%gk*u&AJ<`A`v8+Iq`2Goq3!(?)% z>>f^CR@g&%EfRdL2nqfm_OpM7V`z_)W<6_uWX#l;Xxe8RK;5sIdH`L$0(_u;)u*bV zIg2JucMQ|B98CjuPa;%djoKWj(bKU%tVgvStIO9u(~jy4dM>EQP1alXqpo>yWJMsj zKMFo5gVOD#bD*}V2mMFGKE#a1 zRtY{4>LIY6lQ0Q&_|FiTAk4KvgMI)xkQ11~AN3&1b1~g4Hq3jC55WrIpF zSB%YhqqYi!i4G%lGjL>`v*+7;;9@uhrvVeJoioTH4}&^a%IswoLxIo>omV0z+UG@6 zNckhhX~AQXDd{#ioU)`D8%fu6_W!M66_+l0i9ziGlO>6_ob`Z5xjg4RQ`^IQA{oi z@Y6qr-+~LLtRFkh6il6(OcADI(DIVzzQvu^aby->vBp5<8N7TH4j88E+_Kkp8T1|8 z%Dw^63btX#`BSQkZetFTa(Lp~*u{J<(%A!h^qu$$s`$tGBT)RG1`C|z7Yo(mam;=4 z!G*sEI*TxDSC}`fIgA!2lyBvC0(kkkNFRco64>M; z5ofWF_$+k9Ti8dPLN7X%KH^i@BaXONdn@FIPG>{W^2hm|NrmzeO-J-^OXQ)@X4hnqr-3reElI9N4~MykOfk7 znW-$F+b-Rd!Tf2pOz=ugxa&54*o+91M)k`jaZ?FtW|M+j@~ChaUbrrn`8=1l3X#sXWUQj91fjWKP|bV>JxFzbmWf28s!CK%AJ(f zN8AyP!9GuKEYq=C zRU7_|Q~pqJsya9*|Hdx?h2RM4`URSs#Sz!>TqV#N)kFr=p6IM-M>Jk&d40@NcLP8`dUP^GTLap`|?v=d42EP`2!V%^hE<#fRcq$-=|gjOVG? zK0$_m?s}*F7z&!NneJ{o&%e9dp74@~`a^?=OW-e|S)hpzNs_r5F%p#?(?G7^QL?mW z$UiHJ5iqGu^4CUq%uY0n5|vdoL3YxB=7+?)oU+4PP9z32b@7?;4eA3MASzBU5J3O^+*^I)Do!(qYuE0Fn#wzbm{=bG)hrgDP;u>d7m`+Mcb*F!r@qfMl zZ(0BH!9QmGKfifDV-BDdVdf$7znaRr| z>?cpnT2RWa^>R+xfFqb;dCPmk{~4&;I78QS+%aC`?PGmgrtpOTndl)y%*8eGwpigy|OXQJ=@~D zv|$QdgxTA}_KiLqjgKQHoqzH&Sy@aaSZrrZGZ`E8Rv@9`N-zTrzCLP-pugTI3C$XP?=v{vMpYmH_mOH zGyZy6_i*7NuG8xasN7*Jk8o=kj3?oU9~$e?IVRA-mV>%h%6v9pYKGY?Gv}_BSx>Kf z9C$f>qD`S7S8|5AGbzr|35STzpT})|9D@yJ!IS4t)IQ8eD6Z)#FoDJ z4yLp7__hk7KSrbaD;(YpX7oE~6aI?F|KDK%55gh)gvs<9`El()mu&}s zzlt_Ohfdi*cHe1`>mo%%Y;#a)rjn=wWU-n_!L5i+mmHxxx{ekSrH={6vzHsC#V%PN zwVBcW6x-71i4a?Jt<7-f8kpTEXP0vqKCs*P=XT*;%OmN22fFPiXq|e%(dv9JHj?o< z-9#h)5eRW0vL7CxA?9ApLtdx>`ZrXvVm{|r;8jhMl_)j1#zXO^VV{Sb@T2>tTe;7! zBQZt-=O+iXJBwoDs_7_bdHPB#zPb_A4TY9_mU-(i%vZTxW?OLtl|ds2b2 ziW|QtZVIn?RpNBwt}QPTI}P)cSjMbM=|*a!Di9E zl>*By+QO!tZ65tbV#t?aj)=*a^Qu-&UQ%E3bn1U>joH5b{{Nl%W9EPQ%|C7XZ|^|GEa^8>jn5rn!?Wrcap}t!hLp!EUqIs!M2A#C*%AoZEZc97V;bR zgZ>oR1-AVp8J3ymmTnk6TAbrH8(F-_yWR}X8d&l!kDK!?L!k0~G{4M}ZrkN@dk@pC z{Ri?a{({o19lkW!`*jkv*E!Tvo#+i(6%K_~X;2O6aM; zywC5!ckVX0uF*?+cD-}$k-Y41%}=!7 zc-Fesy4K4aZ@ABT+?lRoXQs>UIfkzGhrk8@r47A5CDezKeLFqU?Gf9f98xna9Ib4d zPwCF?BITx6HY(GR(j=B;DJGN?BmiBDZ`ABg@Fz-=@{)EZy}$=-h!^l~qH2sERaeAT z$aL@e#2sNDlRtRZC10Pi^sPKx4lv~y-!9Dk&tG4mYTT}$!Nt9kF29p}YD1u~wFaU7 zxc;fpVe}e~8Y*e2J#Bcol#3>%7amv4Ej?vZIyzj(ykq|VBP20g5!E7_RyAtgY*8kE zlA5%&CnNU#|Nj0Wc*eKy#c%!BbW8dVsWnMEG^MHzT50>`?J<`n=@H+A|4sM>oKSaI zG#uhHy5CRIr1%k2+s6Xfn$0JHq<{vEL_(9($`}FPXd+&c6smGvR#iVKsz-79#lquLVfIbY^&0* zEP!WfR_38-oQrOyAEq8Z=|{X-J>E9Y1S*F%+6eRPqiZGnDqkB}8z;Y}(iX7ItnIf? zIFmSq7hF+pyZbKlnJm!aCtKU=_MYH<+O;v}OM_4Um%x$GUxh1ZdasHWP+AU2b7T7G z87ZO`~ zqE9aft-`0#H)qhXI%-wH=4Y=>*h<$2$;Cn+c}!+@&2)lji($)r(t{*PQbyvX zcvGAzemYJ@mY#zawtW16_ff;|*mQf#(bosPS&pTfv?@IOwRZ0QuMg*+F8py(y!dqC z)j}?gfcwmu+l{mzqk4}(k&td^qCxa3uT(z?2m@f?8TbV&>8(5OG`YX`y$w7Im5MSX za>cw_lQ5ceDY*{^%HQ#A{^M_c*#1x3PHcUfzMS5ZUY9bz<%FhL)j}HOpu!;QmEMU^ zh#X;$gGu?}HsMvlA8>3R4FkCiKNt4D;0Qj#74{-%ga02Nu*%Mr_EC!v|V@cl0eR3}Kl&XU*SE|T-gZ|2TxBt(KOq2b&$V?VNR~oW*F* zX6R=y2U9aaYlUVu0F!{(OkfFm&k9zv!O+UB86+XzUJKY%YbEp+Op{R&%qcF1iyFm& z)rDqc)Hmu|_J0t1U6_RiE`k)tt5E?M-duS$U0-rGl3K7Hv0Tdbn}z1FDt25oqdpaH z#|36$I-93UGB4@WmNv~GT!2|Up}woCkL`~ck?KeepCRx4mOqGT_|a)t(I-~u$!q!b zHSXGf*4Y-1li?nq1^1P)V7Zn3(r75u=P%yFkG{|N)Nq8n-64GwzSMNGK_|@naiTND3N;tB2Hl&{kczZbIo``_FGG5dettK0g?*8hELX}T%- z))s5LRy|8zg`bqCT-wIznF(tTXM+!aDEt7%d>^SGf1!2ePr{cZ<$SU^2D1K{ub5s? z{5ugW zaOOE5IjZbE>nb`U3&>5)=EP-_)>u#SR2%sQr(EUCW{vJluiN*=e+%U7wy({bZBZwGB~W_;NJb3QZ6EAv5K%>fY1YIDX)#_H^f7*|l)N*6iE4R{nPY$H|! z>1%!V5$9oefB{DhK32a+>62}|@ZF~OS@Z4{m!c~$ik?PfNVdn!a?Z0zxG#*(jtRC? zpC+gEEE?_x#h5Y=2D2(54fpTvEg4A`?cL1g%HFcwUn9=?5ZU5UlxXxeeAL_5`tMPKTGwF{nS1(l?y1_Z5xYXpv z3d0`#vqc+ed)21%X8B6riWQCWDERDMJPxQ|NsK!anLuao-uTkQ-P*&+?$nK~A7)rG zR=559-p`z{y!0LECsIST?u18it;&4T!o`a0n5$7nJUt(URfN+y8Ab{y{; ztGsV~@i0{38^bulOMSODZgYR^hfOyHjthdR=|^!iU5v`5(WZXyMo1Bu~<|lpj*owHZk=_JHho z@^0`p%_tIL3Z*%q2f|Xu=IK{U&-I059v{~g(%gRCpf$|to!n+{>!w0e z`jU-C$NI%P`f9_tVMzaQ@i`r$g-b@0mTyh#YBp2AynWX7JFk1=@0$_A$K(Sw$CN7H zpb?)>dZV3A-TmI`w&iVqfA2q-%-&DGo3=mAo_v1Gux47-z^++DLv#V{Eek;`Hmoq5 z_7|Zo^napQe=4{iCZ%iUCA(%&=kPE15BGa|9HpRV^(#-!o~8SStMC@>i#PQK;{d80 z>GHkhS#%ov={6{}zp$TR7TNE*ZcVTcUol}l&c8xs~CsCS10KUQOvtDP< z1N$6xb-5n~E z9ZR#OD*lt%mGkJ(%y7`-E9n-M)oO(mun26$YcMsun9cS8_pX}>XTSF=UzbDX(nmZ#=d%Qua+VCq9;oo%PFMQo_o zSI~IeX)I#8>|bg&{E?0BgGH&{%zr0j5513H;4;Zmlibra7O_psY&PaD_dQ{A>1LuF zk1CJ}=o+j_{4wc=Qx(ve)})Zi_e38d zmG<*+XZWVbiH>=va635K9eOFHyrO>){wDM`KK(0fn|gPlW0vk&&&pNvt0kS`oc@n= z8PU?nmf86Z%WE*p`N;;;zX~10II<0{7dQisp_%*#&e_#4oj4GAM3RNtEI+zB`e{s- zd=}S9eeBuT(a}y<}5@dJV?G|k3+{RL_UzC!*Ptp z#tJsNJoxHzRCjhT4ZCx~wZqfso%dba==1&LP4%Lw_0{358>4S0i5=h}37*2pS2z)_ zO0&U1JLyb4%gZz&dqt~fC!g{ho9-bgVhl-tl#uC6F8n;?0_hx8?ri4^`y^At$(4gE z87t|l3Faa?(TkQ3nVabak*p3fv*}==OXvzzSu?E}7RRa;<%ZUN*U?8$;Vhex%x(94 zh1=<{mxM{bGz8aT_)S>!XAyn)iHvvHFswntIl56Ej{WAg2wYtp|;k4fK=_9n%X zBuKQ!P2jsVEBL398BvdXiE`qI_&Uz(J7H?!8Nn;^x{^iDXw|(bc!ZK$3F3Lex69SG zK1Qau7EglL=%oj|mfvx`v31!79)8lKV(T*F`=4HK;Z^H_-P53hk3t3i)#g^11~rbB zos!FFLx-eiqF*uR23y!llxfU~L8P`Kc3AF+ZYKw7kN72dg()O?&~Zb?!Z>H_wDX$t zlB2;ca&*zjR^qJWemK1zSij&XMgdXf?s45l>z3#7lR#64@8pqhj90S;tZK~v3C`RT zG?f1@QvUKIE~3n?kld3FL{E^&Ge|njb9x4)@&+0q*nimq9+H31Nj}wm5~&^w&V+pL zAM?Hdr5#*vL$RFzYhSq1xzcaKp~`eK!s$xq)vPtu(ve^B$*lHVLsa<1D%!@$K>+6k#M6~ z<=Ki76|R!_;Y8Dx;5oBzIiamdevooES(&sqVFJHSUfif^H1-}jBF$ z8r?m7Uc)$MdY6J8^f}#5Ivcz7Ifg>RL;X{|JgBUJr#-aPsc#}hOv+srFxYU;Z8z*Q zicyX3LbW+T_G++0V25Wfx7l&ja?J7(f|!ih>A1YaHhwP`Qoc%GesA}CJGLH3|F3CD zX&tFQr5x7&oT%04RQa*bdHJPsTl5}Dh4>59SU1@8ABMdW#tB}AelDyHdmgrvSMyxR z-M}&b??|XG@MzePX^(>2Bg@Yq*WXx-(B~RP(6P!*C191w=9!fO(!Y9OTDt7n&MH?Z zjw8SSD{eC>4Q%7$VbVqpp$#R&3{Ov|yacW(L)C=N>@0jhLu`iJDixEge?XC>KYlZVwdxjW0#|pKL*jY-L~gC24Qy2J2mJD1#XF})n!5DN&Xdx*)4>xnNRhZ;70fjaRj&7xWvfI?cto2D=O$1JcQ??g6!{o!43}T5wx}S zQD-9S>9G1ss3p+j``)wPH4o?1X*sx}ra!9~M}YU;ZN@IZi#Y;AaCtevH?H4w6;!i( zHGx)|nU&L)ESq)h#kz*?M3Mc4;O~`mI)|ayaEcN^$aj?Y1oj%>j0RS@N9CWbICpt0)v7r#ovCF7s8`rtovY zOR~6LE)15W8@h$1`5=SpgSvxy@JL7jXmpR)vIj@eSY zHnCm|Zr12L?@IHuu;Ja>RO2#QAPM*$KBD&M9CUmEyw?oNAl2}#j z3~8b>=>2j;JHz$~eh&QXqsiPk=;&c{Eq4w(u5nLVN#i;1oN(mY+wgrG>;^|E=x2*_ zud~%f`W0=#yGcmt@Fjt{$?&v&%bD~04sR^`KLV?|Ba-5Iu9ftnwz(JMU_Q)N`9)t;O00}s_QidXK=Zc5zJJ|Wwuzq*lgUYwVEhD*9=4 zJnN#psZyCnD#T4mZv+i5f;XE={}`W_1FSD#s$B{g0*gbs=emy<67lRe=s$gGprlC@!bpG!$2+E1HD1TVve#O)HB zOr@HRcPH%MvR`{Q$u?Wn|n7ro*vg>M~z*>bau>n*s;rA!<19zsB-#g2JJ)< zan>=xDf6(8?st{ApSV-W(!1;X*7pXFWRGW<&-6oFbz_{eH=FRZ;n&F!$U-ygjUJAv zkmI9L=8{D^AlHGz%N1p`ndP94I1Wy$i@HiOayKdc>!kbicpKe!$*!rkYAuFU!)iX; zP_8KlHISIwZRd)dbjKNV1Ri+jCKAsj<{RLf=_~VSF9W!rn%BzKgL*W(tHX1Fxe-)@ z#Ly4u{|x5Wye7R&Elmf%Ye11y5Zy>?a30^hB1NyFD0WmCAk*gXmSbAI_L%k&``v$} z^rili@*=4#p;41Z-(MeyhFD%nMm5uCcty~awz1zmYoC0V({^a7(zsLq@xmPQ*Jb@h zgVZ=efBQr9{twYT20nZ*p_RBZ3$oV`HLSI+D3fOPA>fm+RpF4H`76GX!*eHCqF2N1`W zEB7mP>;Svu!G3(N5Z|G9YsY5NMxSfSu4T^gm~JrX&*={? z+ISa743CZDFiVxj@uhLpOs)7&@(qf`p~WM50ZC%*aOnp*iLJ{GE2mdyEE@Q|Jd!jt zp3sdQq|R&#{uW*l*(uFXG^%9rd774lTU$D`_T*E^*?a>!$n`ju>l*BixO|b!9 z&1~B2b7H#53f)PEQMdS_xSyoJePNGb(;t%DdIZ<-Pn&6|zDPk~N{T`e)L@xe8OF($ z&q;F_Z=g;Rn)7jD8_b2Y0S#Er+Db^8lsPLw7;f>NC&8abkcjgu>3Yo(GHF*Ql6#(<0x7Ecf!8I-$zFdJHV`?1&kz(6LpvOv&vz2Wb!_C zkvd<+UNFdC4>J1?x-Yr#$9N8qj&he@%RV&Lo?G6~jj@dje(T0H|NA7MeiS}Vg2QlB z1vf_yX#ph)2g+2Vyi%Tz=Bt&KPc6t5cbOzNY7aV%d{G=JYa4-KdMD@IZf6@gV7-}4VxIbTd{@GSEj7ufQf8A! zlP@Lzn5s{UO52;Viy26wITb&pE>%`54}+<;YU7CYW*;&b+ZoiKCn)mU}d=| zQU$m{JdWEin!j+qp1AM22H802AoXgPe5_u(cf#|6MhZI#dneFF&--tLJQlW#yb;Ez z$C9S#0=yhX`6%AUv+!Iq@?@0KVtyLnR#Lp^oHH+~!;_q|1ty2vOsA#jE2r0c;kiy* z1}t5cd|XynxwmTHd7UPgcP<4p-X9x}lXyO~WL)a!r!#=g*|R!h9p$sCWI8(r%lVV{ zg6}78+<5;O)7kd0*Wu4akHya;J0)rw#`ee>QPBoBr1Q*W-LxgtD3x(t@yf(+lU^iy zlE;#Jk}b)zj3T+C8>r(waw> z=U|d*_1)ZN3;O#w(vBLlxOdOMMprKnk%04badP1$d#ICB_*DOeep8>j)MP>#X({E! zm)T#?CNqqasGgpx`{6sOSvVpaWj7foHQ-vpwJn~cYg!NIYxld`?8%n>=q~MXr`5U2 z?AWr{c}0dITUH_UMh3e;Mo9eqSbRhj5grk?l~nrI!W(35pwlMX=0~5>^TesL_gXt) zR?3)gd(mFqU#eeD<|Y$^S(-^?=v@u)Y6jV+R>wA6PNAMhus?dv*>`YQL!yiHeXAv9 zco7Rp9;}rQC~`s0tco14gr_kMvXA9tFMJbzT=)^6?XCa~e{O?wzda8uuFF}**(!Bh zfMe)$O}I;Os@1qiw08U5NnX9j>aKFPd4BdX|C9OD;`;$Mv(Ed#7m6467gEMd0jc01 z$=WC1c_ED`v+v1j6hY>`Hg=X%o)dGG6!rS(A?Y|Qz#ZPqoQNB^u}A?YhfB6G@2PW- zJ4)6@tu3fA3N3RmIV#l6>FBkq@oyY4+Vu6L6_pq&481TnI&KZGabo!h>GB2KXA`hb zQ+AJIzx%hmnU{QapU6MWbmjv+dnppr%}44Ks-%3`)tD?0+8mg;7Ug+WgR%%lQv{p& zMEg_9*D06yd1*Mjc=h@ULY@_zjV8pZ$?OX;1}Pti|K5w%%WbI zLGfTf9h=T;SWEU~6S^A5QZG}aEw~>qT}4Isie$udyqibCMCWFh-3EMeil`#!5 z#qvpo6(%5;`>ar51Bu7WhC=%gEo?WzQho`{Zw$g}4mt+tW;_qGAam6@%N$wUV#RKD zAAXJCTBEVWUFw;4Z*#+HdIn(*TR=cxcn*7Zc}~IZ)WJA$bATWOdkci1cV@Z?@tOxY zCdFwS9RYbWF12ClB)s)L=_H(HJ_%_1g8V0m3>O8zY+fT(N`vbXWO!|tZOWQ&J!&G9DY zlL)jAcS$n}jU2R@L+qMc>8DCq*b0Ambny%R3C`#{=@1=QE?DWc6xs^#T(>yS(_~{} zHakKRzA=0#(kv~K)yK{%@2VQ!z3C?sFD16(QrMq3hQ_xm;Sx=$*_^Xp#UaJ0LaxYy zu@*?@P+2LW5;$#t5+5XER~mkkPIxc5I=`pi{eiD|Bg5C|o^lr8Xl_FHavFAX&OC`K zDR*URwU|Wtvz7`=xpmSeC%chmIPg+~Yd1TfKT!$s8v(+&FD!P z88>j}4n?w}58w56xSm2rP-YmCV2lH96x5|^@x!~3_Z_C&Gn~N^w#mb?N{3dBK=E%8-W|9)0hqnvAAuY|laSC5TF*mvcJyNSJ zZEe!h35r<8R(XhIDm(&)eQ0SWQJ~&1G#f6G&3l|p{H%Fkcsd2|AYjI9!iP>E;S|3ii-ry_+=j;q_r#r{y7&^!QRcTAvET zkfDh^C2MJtxq67yu#9B~8^Kk)k%bF3njX+=fU7+xw_;#v(p+Mxx2bUT@OgrJhtqNJ z5p(;^a0iXrf#`AhA!Q-=nS>2RgBwP+XYGS8o z2Ck)>wo-DH7UZ*$`6Nqk(t7$l%qiR>5Qdn@>fPZJ`T9Hsu0eY)I;Nv5_sGW_F{iV& zn_=}PQ1+FqYIsMQNtUQtv!m8-bb1`U==x+{zwd(oO5oo^z9C1uTAULpj(Qr^EX9cf zzfy@mtc<&?SgwmnkI9NQlZrVcdQ88KXmbb3c@vE+d!1eDBi!6%C^|cwX^uiLS~m#- zc2}|c5j>`bJ+l!8^MvP(8x)z@47?2;#BT3?Ftk&6iQK4oBSH>_-W1M><|5N!?v3<* zPQf)<75hoN%4JfmmCx{2p~s0~_m<|60(&{~2-zP(@lf~?;in-dNKVl3P3v$JuXnAb zt<|97Z3}i*!#L^4KCE7@G#crwd%Qr?kpB5%@8ZM7eV_*qm*@j!#+gOMUAIz5CqGw&uB#ADjYE?2)aZtyND_&ry`Y(bVpTBHyU<;B;e(%#cavDxr8%LI#p791RVs^O zJGswF!4h;JMFH5xZqm(?X`NgUJp@0?5Bnq9#%Cd?{Asj#?DckVUuc|_>($nyV6;Ki zXc<#lx48gUtjwBV74wdqwfe17IKijjG<#g9z#WRcw@E752>gNU{B|(-L%dnfVXhlv z&azdB(VJ<=Hk)7^(SjB2X}?&*R<=_qrV zcqLD{NBMjF&OPNW_nzn7>~g<=o&MR|=j-wIdCELLGOO*_NF_bpg#RTB9dEbjIT~%F zG>2K+Kpu~W3|xm|Sbl{4t{IJV89#|V=o+6!9_0J>jW`kC>SmZPG!M_}fsMOf{)y#}KWWuOK9U}a@|f-6hc4ZVvWET*Gec}$x9R+Or0kf;LFN!Wl9^l_!=3WB;N zizkz<%8hg3YjmFrs6M};kNr>LIa~^j(tFVfq@L+wJiMEd*i2;`s>Xr1{V<40+J8%N zGyDINdLgYeJ@{(orA{ZgnS)N_43jH6<(EmXYa&nJkDrC^VxVKhn%dm{&@$Fvj zCgbL*vB9V_4&XPHgSp6I|H%%6Vfq8#k24GR7J7q9 zYyaVJr0AiK8lMTu#TkQwZt} zZw{j2hr64U!9j7G2?}nYNNU%KP6{dCzgX+KNegL`H)2R+hi@=cFG-7_)4nQ zRJMLn1oBP5^LS{DOFQvNoL)>s2XPM-1a1$sTkWVga$(cuwqABNkNpy9Hv8FAsy3`3 zdJh7ZL;fm|veyT*MgnAFnI#!G=nA53pc)QxK+ltDW>;T||A99%D)~jq-Be3zQtIBc zC^R=4sV9IP65E4NxZxs=aA zCU?>d&>7iH8gRQPQIv>3;4)oHZ6cg!Bl96&SSJm5a&{n4i<=lA1~kA&yoiH#A$s3WjFGoX+*bS?t^{-;sm z2S~(uD$$WP{5kAz&t@H(v0a{dbU-_Bem^1=v6Ma)KQ~#OJq@NwO*cf3_XHaJH>8VghTIgK4C`jo^x{RR1)I%Nw1KhbDht?l z51}S53VK9i9MN;qe(6KD&6|-&m?2JcrwxS*!FFOuN=27QF2)4;(`oBETP6;H0q(PE zG(we+Iq#s}Kzo1q5 zAL#7(j*PDFQ1;p)`lD>ToV`+WbT7MjF+Fu=vKccIy0+}qmV^GkNexY#PW=%cQJMZ@ zsxsM>=+RWhmyo5@q8N@5Oa3H267~_z_O}8xzE(OFJZovTEKBlg&PpruBm3jhE4Jf$ zkeHLE8}QNhmLD1uj28_KIO>OsWNqt%dF@YWL&-pIB4IXbz;XQxZif9E7Xq(CehtN! zh4S}$M6OgD(<>`PNz$f3H%J=R1ZqdvH1eNGFbb+r?$SYpew zk)vT71|h1akD?rf@)X!9IEeSrhB7d{nT(Dtf*kFAQPn6Uq@)y1pwyOg&gT_Hob%e4 zajA|3xfRho)&4|pTG$mFnlb5RA&+WXcg)*5Z~mJ0Gk z0!#bwqE#CYqIGzxYu7!aBcpe*d9i!p^F=x{mI^=yN~~qp3fr}{8ZtIdc`|+5Hh$jt zglx$=8bFTI&GD4^%z+=~Vq`mSI`2`;QB>rw6lYb#oH7}2=GWT&DaX=&NsC9L`c=9; zeec$PgGKDomM2ze`nWfml% zuQMpL3-OxXTP{Fze;lQw4Zf*&@#JFuVzT}mY<0UaA&6Ch`JG=2emCavcxP^W68Lk- zD{=~Ma?Wl<)d3 zqJv~YFNv7jMIXSXe$A^n%inQ7^H>dy5LIh_-s2OTvs}lReXku?nk&o$0>fX6u9{IgB%TF2o5_vX2SKNOMqsOc7b>xFh98`Bhv2 zWie-9nP#NpyqP$X@o>u{N5B$a@$=XU>-18vg36Qh8dPu{Ak{^7!&(9TfH^CLXjbUj z!d<&h|7xLrVMV9rX7JKrU|bkl+-j^`KDi>{zBe;ls7Qct(`Gi!IlHxS!e1Qt0gvH7 zgI-?_>kf~=QMVPf#&Pb`c5aF)`O(<@>W;YScnMmFb?wh7AHI7ncclGe+WYC2wBM%| zC--cb1qoHDYm`~BM`dQoQ}MpAPeZ@od;&sS>>jn(f%Hi%4K%dO@NO2uEe-LlXkr&= z2GQtVngQXP1I0Lwp0R!L;No^RmnL@PLol9#l}dP>D!$!=&ht2~xA{NZ{7vX)=*NPK z^vNHL$dY8^@|%mEgCEwQO|`0;)n&|N^ZdCy&J&jrpBHB*ncl zFddadwX(}!|kI+~FE^X;CI0_62+IKMt+HN%sm*Ktj|}Nf{Y9Ph&>qC5moEp`r{V zp)&d&4J0pdC+rbMpug$x>6x}>!O2SP``22~q&DC&8R3L6nXQ+?+?bqNx7$7K+C?79 zZC3^_<`o=k#qMEu75G^ix7!YnoqVDWaK-4*m+<$WhyO*qJ*rcx1G|8E;MXA(Hv_Q) zOlOt&zB_T$P2k?7If~6Yht%%?zuW%Eo2c>j2~UPv=*g+`+1)2xXB}x^pZE9#$IUxU zL!7cR#(b2hE4uqMKkr$1hLg2vA$g&BF~V58Jiao5zfNV#T${#So5me0qiLp?lhy)y zc7(LveJB=$;TOZ7iVj9hky_NxH_;%wN2}o>bz@wfriNCNN7}KJH>tm*E~kd4{YzR> zdL4S`Z0)5)KN%z$s+m|7S)9Y9@Ti6Voj0@2-{;f1`|N#dMPwLT$^37|U)squmctvL zgSx(-1cPzd*m~m)L%pFIEao*n&L(}gLCh%}U+(1=F0j2|))u(Fc3;B-_yf0@6PIN& z9&&UAbPP7|^D;-vaWWjmxjf6=n?Z`$J+&ZioYzwlUkcwkqDoM=s)paSi?pLWZzO5= z9POdU={GtqIv9RKun>~B`2%TmJzkl&2REC^bFu9!v$>T$;X+p%b3j%qtVxRf5rpLx0Wp+U9^+D%MtVlbKEDp6~M}y94IB|O{>8@IPX@1WYl@|++xK{ z{z`tAzvYiB-e4=$F5g}}PMMst4?^zqv-wr%2&<;;Fo9EMjT$# z;Ey4d`3o|-BEk~G&xx8M#zCqEA3wzi|(XhKffc9Yta z4s%hBOYStPhqL71Cm4e-=Qy~s85Cy5_zI4|2(ny_F4JKz2kpyXo_wGDh-ajU{j=a1 z?rJr8GiBr!T_t1nRWwcxcFqDa1V+>nHI7KNGOjP~Y+R+fQPl^^Y^Gi0o+3k0f?in+ z`&xoNxmx@XjP`@D`=Qr1GXqyZ7BZR6Zi7sXx$>MP_BI-zC;8N?*4x%^^PbUY%!wNh zs;YPy2AS5ij-Wg7Haue;xP1A>7|nxc=+oIJmXo^r6fI7Dj08u~Jrt~@wBzb=kW_L8 z{Qa`{&tVF|zK{q0dGEB_imR#IQH-XrfcsO9%Ba90VmjMJ?oKZLwBW6^%YA`&v(0UC z-*%s1K6?Zk(1w!sHh-ocn>O?(n|GL9{|e&sO7t_)bdB&^MxvtdF2UFd`!IINHxm;R5-V7x-!HK(Eor)YGyyY2CkCM*49x{BwmN zM?bl6Q%CkDsff>YSquIAnp{Z7|5Zqr*=d^upRkOMTfaLK<%k=6_9y=vzbWu($WZ8t zP#yj({3^XLFC)reDS~^ZUr|nW-d-AqdJ=oK98NZ+e3n+jRCXo(v-J13UfF6%m!*ZL zTuag<*0F0IP&r~p@M{)C%EOIhiMi2pzVOsITh;@%=VZg3W(y!i7yRYoQZ8?0J6lVT zf9*Er-~sS5{eQlgqhH~z51`lf;yWoLF??ionrvOGz0&C+KcbGTrLXXK^R1w<_<8t6 zu{qL90(b@UWDz-WlL`kfXSRA;Rn86e2;Ws%+!NJawINzw~@;85tHu4Fd@9k0Kc=d5-+bpCDTV5ctXFt``rdmPr}#{^z5&> zH;Uge=Q3A|qtZULo=bv8hArDBTgzQiLb3L4Y{}F?m=cXI<~>n zg7aBWp`iyws{`-(bI2W%kGkRG5>TAg)3sO|lMGvb24+*Nm|!}sh2yDb+YNp<^Q9yF zVI=Q5ho9ka6lfG_sn5f%3vPs1g6Rp~-JU8u!Y$~}a#s5}XUEwyo%)xHdlruCj?Ax- zkp5D4ap58R=IOO@4K==Ru&BgSIM+F8n%aY z`Ri~cX35d$nHUMG;$5ooxDriI!eJDICz3Cv#G}plJ^SXru?u{a_9P`#t7iw8QjaM| zW99fMS|zM4Ld)jw`92)*4ZFLTaSF(T$hPcde$Apt$( z85napdb3)X&$CON%V%*n6_XgAix*ag%IYvFC0E$x_JrP~>ue`)!#=S$!Vxuq3RL{g z$3Kg*N}?W94>SAMsl_CJG{%>bVJhad?TAZLi&e9+v&<*yI8tAciZc?iUEE7c?KyIW zlQxgR`*fhJIpE!eTJV&+i%g43G-Yk}F*26s?ON2K8SBMt0254Stu}+Ll+==Wdj~3h z5ug2eQY9a4Yy|$u1VU3LIm6y}+QNGzcsa;A%fK&L5@VHiN}Q3#h#R7M;a|x+{|Sd= z6iFus90n2tTF7b9@IF_rU0QEl&*NOT@c(b+W;o$F;HhHr&vu)@V>MnrLD1wL5Q9*X zwXcD-UkF6dFcOWr=`yLLc*=P-d+7{0gwm{4o&ysxN?TMG3TbUja`eOKBheMGJ)L+q zZ*a!&!SJv35#NcDY26&#tPA{Z1J$Hk>A1b-w|1_cCo3rjhtM-x82_p(oHx?Lb>>&& z+rP~3(|rlL;pIMSU6P>r9JNdawP)*n4!f(|n})|Jlq8uRJTl<{|E7`IF(3cGSd>81 z>^T_Ld$O~NPUUI3V8%6FiISwf$)BbCnEG|v2{wQSX;0E0q+dyUkW#GeN>pmb)KjoJ zWt=nkI#IA+q%p9=U*j|JzIUxZ0tq;3%|-h=U~V?$ni5P6Y?(7K0c04!L?xp@8-iu3 zFb)|n8~cswrL5(AQ@%N{(oE8m$dTzf58G0Oi>{8G=F3dFdqQhr{TIULXpIp^IV11j zu^z{B;!9ZG@9pqx>*sAO^X)Gf5Wm8#CGCX|h_ZHhG5YZB-=WkxVsho~B#@HfIU z+`i->`s^?_KYBVmNuD;8_obY(W~MI#=gf+RwGKa92`}jQ`Z0Ukdh^;S*uxZCa2{ys z34V9Sd_R-ce*?c}1-YwRdC?old>SYB{<*w?+pJVE342`@Gl`yX9Pjk@u*)djcKdhv zLOrD}0VhpmuOvI8i%r8r8cI1{Q1b2Y0`qy1y+4RPqXSIi3McFh{>-nvN&MAUOg^W` zZk)oE{4u!VzYBJfK6RZP;E-ev#aRF~#PgWb{J9ff#5f6K>CrSL@oKh`uXh!9(9@lC6=@1vgPedBfOZe7NpE(LvMG!{q9%k+sAK* z<}d30s;ef4v&fihs$Cr+ovj4~JDA7-Z^7?w2YBQqdg|zjqSIJTn#rrMXW>WQH8zo~ zjTcy;^r-UUvlBwM^dxTZdWX5Hw^D=yT?_F-R8uCZ7$!2VJ)}S}EqHFcr1-zem%u$v0OL!<7n9*tNLJRae@07QE z;|KrW=^jW7dmgT)19}c5-VAm&N#;@olTkWv7I}8kdJ^%si?@ayB&p@XMv}MA{lb}n zb56h%OE#zFo|Pte=D^!)ZRfcj4Qn#@Oe3XNu-((7{6dFpYM2luxmwnT9h{pxd^Jvxxd=kNd@;RpCs5XYSKJ~!Ga zUnz~1Z@eZD_AytItIt)%Otc$4PP22?QRYa)-3I#au;LES=FU0gxCP>H-ZSCJ!zuZL zKP2Q2sQ7l0t9>YPDA=D0desVB-3)$Zjv42aon{8QD8|_=_+j(+Yycf@0bG;DJ_eUn zg7>k?qO(j{yKK|8+jtH}VL>kNEqer~$^R0)*(rV!zeQ(1hPQhMo7`U1W~cluMS2Q^?BPa94tp;5C+91hloSzP=hC}i^=g4-&FcDK$Rrv$Q&XC8d9hqv+?9H#mt7Xy(=3@YLCgKnz*j;)t+kAyJ#3 z;o{g!JHZ8q0sWE{UUkTly{a=8n7i3F%V;>LHJ`;Xe})|foXmKLcBG1>v#1);qk~AL>vxQIQ@^>D{vl=-jwu5x5zZHTID^u zD?7QFhu?ao#?@K7n&G*qvR0V{8l+EiE|CE4*c34$L2!fyW_o;gPVF z@J~sgZD$kijIM&g_TcJop!>U$H!EH8aimi$6}=QZ*__&NbDN1=^Wn=lsr_gp#=o2Pv!6FVpquu1 zm@Vu$h)M-_=~;HU0vS#@a9SByU7uVXExJw&VER_qSb5}i!W0TDu-gt^8Oe{)E&_|;Z5H6ZSc`` z9s^8p#+m`wWr4NCB43>&pM4PAtZ8|Oz5@p*?jp*dYP7#IC~GTW00zL-p5pHqF(2ie z)zL;hOJ3Vv_Ns4v!yDhz_EklzT-xTRAumI}M6YpBd_(*grpy{8CX;Of7egP)ja=oR zvYEYegu5;HVg_PoVvFE>+T|G_v|f5<>O2y>z{4)qO^px4O%S3TXLX?Wg8R;#%AXRR5w zcH7|EX}T<@a5<@Hv6*(4zyT=n+Rl2cOxNeV8W5OEI8d&EwTA|NL|=SgU=%(N3r?-U z7P@oNr_pBg{}OT<%{XyqIB&sMvzNZNTCnYhbO#KO-&hFqbWoZr9gf-_xmA3XT#XMm z=lvb19O;U(=d4MrwRj=Vk`avSc2TR77#(}Pj_Ulbch{59NVUBR8PiF!8Vyl z&sa~X;k7)Rb}MaL+5s@ye@pu?wI%slQVkt8Whw#bUCFqJ`Xdg8Z-!plT=u(tZcw3Z zP8|ETv({lan^xQ$lkiRhqy-q33rJ-+iu>;n3X@h897D!h(svv#y@)Vf6 zy~=qB#7M)onMAH{X5b&``*O2&eguxyY?AvCN7(}epna;?q1aN`9cD4*6xj?# z>JrtIG6&?W2t|Gw*xA0AT4}OmD6*CPR&!hsU|FA=@}iGAN2qvN+!a_Jo`S=h+vwJ$yveE^g;` zZ

Pr7`HhwaKc;1wR}s1xpO&T$!0Jzn_bo2n1A_p=?X!8WZu`Q}Lc4yPG^fs$ zd#m-9^HwqUSqt315?rqjbPw$>s=+6;DZB*x+{#I-6&;EkmmI>ee3l&O2DL0cpvg-- zmGo6|D7r>Xsy_8v>WI3+)f*wE>tpFLT5)wfQ z*0wPNOF%$tJimDM`*!=20&i&A{%`dC_Ol1vAXnodeG<*!i?t{T8)LG;Y0H(PcyM>A z+Q4c3Yy!io8D+V$7zRBZCz}JE0zX%_%^~vfPvSdK3%?}ST@QD$mx;0i4Z9ztAk8Q8 z@E+3ZC*qvtx%NA&*!`yL87N#|IBtVC)U4N_MBK(*^T2b=yPr>d+W#$jpo^sQqb`h? z!Gqq-9Nq~^yN^>=8C{POsUNrKas2NJ!JEJ%pPz;k5m=m#SGZtp7_Y|^*_#u5N{+P} z7=a78^rl^scY0Zwcb>n`F<#0laL*(%w2SErc5iU=fPYqm2Is<$!(YMX)PkHfNUU^+ z7m=PJM(Gwn8$H0b*#;9Z#Cutf+G{H2DREbLWi&*m-iTUvNE zGwq|cDN8UVX~1~M@P&R4xxCTy!54EoT)-dR-UmPXQkSpW)E#2me6`$&0K-A4*vsEqG@F3yIKkp zcx-Kk32h3j_A1#@CFFtRtPZTylGk<`hgL6VZ6_Ro)zoBeTqz>4ybM=X4IJP8ckZ4J z_ij>3@^Dw|3jC{o8pQ9~rWD=wV|>leMK{DZL8@w_M`YtTW=0eqvQhfDk4sheKr>z` zUg7(mV#1A@Vk8%xFe!Ae&${)XF}Tr27isW z>HxDknbXc~cmRswx=q||d+l}*+Ab1XOl*8;8`w1ua>_vUP$Ed_9EaCoJ{n{)Gsg7B zG{ExoOVlWVUbErOgzRQ{M1u1qgI=4$n0B(ZN0=5{Nuv$G!4{(W96|AUFl-a$*@u2TI`C^?8U^r$6)U~u zQjdUe&Y`J)%?@z+?O)$U%%7b9+kC(72zsEI#eRcydD1+*I)Z9y6gIABeZb*&wRxt| z#-*b4&GdZ&@B0*Q&ga5@ZZk(jun|}%%fJn6Kr^V*%)_3~Cx@n5(41w0okgYfr0!0w zNu9@o`zYy2!gyS{Di@VTmMkFcCK)9euM9n&{(0Xi?_JcGBk1F9qj)H??n19|-dth| zb~NRi`_1FH=4)|jRpU3!SUwACJNRw`*h$Yv#%jJ*1WVnAD=UL{tIfTQN&6IDhB1GQ z|F-{In7li*6&(_-h`yks{UE7cMKKyOKqfh3M^R?%S9XxbDMr1TMS7zchqF*Iimyh; zCST3|K8{Y~<8TrI1jhwOaOdi|A-X`J($LoL_NQ)K0i)9ROlUVQd5b+VcMV#JHYRd5 zJ`xrmIVK!Qt`{y1r|dS|$}j%B&F^TtKf=lX7)B*a(kyL-!ER>1n~YI$i|nLr`DWw| z@vHE-u>Va^x>>xT0i7BoF*tj@bZyu+Ywcfs&OdiMxemL?JD#x3uQkJ*UVsU(qW`(; z9>d!tK~MgG8x5t6FUa`s;wSQT;MV4suub>bKpux}!gxt(LVgX-BSy#4g~qH4o;pju zhjV5i!~JG3`2b)1y_iGMbCP;V0qj#UnvJ=LA<=cP<5TRKnLbqEI0J^jJsa@S&EW^` zHIRq6a8maT`8!AEo%2G_*@cDV#Xl}y)ju{~wY;6~gjl?t2#@Yf!in|ndcXxMpclYA%?hXZtyX(QNP14v*l9`F?_q^Z5#gB7- zm1bw}N7lL*xawXd3c;K6zg!qTqz}y#{x5%G1E_mp=A=CW-X^*68jzlrW?anpoLLn# zjVCkHz)fEW{c$AAc^)MwnlItx|L>$YTz+d!TY`lW5e|IEO%U zzfmw5If7os&fqgd>&0H6RJZ>Ly6+$8)&59J``epM&+Zq<6_eu7J6D#|Fz zE85E2!z_N0SSy-{@5Y9r3k6yHM!a9#O57=&J&@JgG~P6RF1|j#BmONmFS;bMBSJ@- zMi#>cYD+K!rnEl;eS)<^y@Q$HL23cSyI^2qsA1$&v|Vg}{BUvv=PuVEc!0>U<9HS% z+3Tb|WlH%l`6X}_&6i#Q=Br286h8`g=pz0PaCx+e^$jzD)&4^E2HSvb$~^b|1g&n7 zcMUz7$zou*bRM(|F5KA(zoP-~PU6oK0mNX(?G)s-Q)| zm4a4$B;y$vDit5VI>ZsO~Hc3C($Tgoxw`6R|T%WnU*rQ@;#SUcF&U~D4Fr!k& zEVI>64XU$i>O$pt1uD;wq~SG@_n@(Fnd+VF6JHTI87%2v$_%B;(!J>yz5(9K;2i1) zjLGxVJ1S0XqBfEF&=bri=eaAoxD!cvhC zbeu2Z{&>we0q1NK_XB?&l84H$e9S8RB|atK+*`bv`~|QHD1~jteZt?cpPni!C*LW*4P?+KiWkI4qBrybYh-O@dO4!FDDNpB zA=@DNCC(Oi7S9x~0OG+obQga#cS{OOwu-fkP(dgAk-q4y=^0K=a}9HDb%ctPMa^wh ztUD~MrLpy}wNjB2c$*EtPiJVylTGSXF0pE|$v1p<_ zCzDr>Us^B(8HkM&Zjt25n-D`(tuzO;pL7EKLtuhdHm9ZUN`IX`HN%ndF1>O33$w!f z(!@2*H7wIP)q_+Qh;&6)SzJ6&xD{E%OXIXl{)7$An#lYh&90!`UV&Ezgg=^+1E((N zngO%3TkbOCCwDVKiJK zs+;mWXmXkoxPp<*kgki*{bs*tSHHhe8oB9<4+O@vcTxhVg>U@TON z39O&+9n?oINqgyT$u4m#ajvMh(1YcpgCQ9>hqr*ENiK}NgpJ%qb}7?^c~4iNKX}V} zhEb=e0?!nm*OyM8q#x6D*q^`;-Wom>9s|mZCgBm0>M^TX2Vk307{G>8DF8L(2 zfU}4@i+@!RLAcmt=s-q`UrYR;`rN2!LQE$b5EqGK%GoNbavZT&kq>jchKiqx@305E zF8c}H<_z&_v03~DI-?#)U7j;FExA12Et(9~4%B8!`pljwWTC5`)8sJQUA7Lk%2u6a zRbjir6NPu6Hd|=hWUCH(+GjxAUrfHC;EnRq-pcemM(@`Ldxw%CPPkUMb+~ghKi(r% zmOG8NnJ-2{=nbJw(pcV*Xrua|F0DPG>#i?t+-vfiAEwvOIG!QR3}ZK3;3_D#N5XApqCdh6GM{20 z8+axq@yzs$r^*9AYmB=jxt?rJP5>?lLEa*t!mD}Nqw>ClRQVjXmcL}c8yFvyfcIh* z{FZmeJ0(uT{Ch((gZqj98MXoaaFcMc=!N8ltczltGEH?+nXMv~^HeBoo)GmE;7CrSX5!XN(}FBhL4ts1!# zt{ENy8^zh7eAs4wVq3vh`($8gpf|jA3jYN-|Mf$CBKlZ<>}tF)*@b(ZH(SsT9f?iH zWx@lZh`2z)NT0!7Ru??D>EaK<_4r^cf}9pK;@9MIQqAIfBic~Z|B5YQtFWDzzVtuu zQZMck(+3$NC_=K>8SFuSTIh24bEFfz-dbR`w2C@oTjSLdp7`nbhIoy{&4eSdG&wI- zo^y>mkpD~Y3UQ#>_!r?D(PGJ1Szoxzyz*>Cdxcfu2d&0OVjS?!hRbZSuJSwb`|>oI zNIC&JqGqBpqLJV~sfGUGjpX!CUWqRPl}4FhS4gJ#+D1?js%sj(@;keSHn4d<+Qy5GFa z%s0(3M0E_PRR1Zrzy|HCw5{j_^eb1na@dZ^633!gXqi6=zQ1bpHQ!I~OV3W~4*AyI zmfT17pn6jG$-d+Ox7fYRUErP#cUv>}VfR{cF15zf#5>*V@O5U=*!q4jnEI~PPlQQ?cB5llbv?QF17^wcst%1uKS+#_x&JVeaiz7?e+x)0AtK7nLJmN?StRK_yW> zQAk16*i3GhZIz+Kjaf0>8!I&+35izlZ8DRfWbUi@5i zPGnMq3_BtFfdiVIkN*As5rM|R6_7)h5!@H(8`v2<6lxo37TXIm21hcFQ=cCb96%As zJp3bkCAunpE4e6r4Si-8=_N@H)Mo8OGx3ht1>}U_5AOpfFL^sAj$96&@i*~bWuGvE z8G_#LUFap?E-L|A-E+(}<^=QFzcW}7_L|{PK`1-4HYAN~k4}sYjwxbWqfevvV|C(> z;)@gGl3P5Mki#a~o7y9e=B{mIYf#ai^^Wolq&>`R|C-<)n8#d-fWbdH zH+C{HhvVZl5cEbmqjKCM)Jf%vcf=N;Xq?r4)hYC!4dqN}X1_UU&P~6QUMWM6(J9?w z?q{}`TA3OdU1?3UyVS>&kBP0&1x%29#)l!zd51ZPWUr(?ekNQj@SB-PulH$uHN1m8 z0a8o$17A})H%6`^4P-U^qrClnr|9yaVV&SV z?=OPxzHg{WF8#p39?S`gYNFOYWmHTD>?{$_J_ z@+JvZL3L)vPvJ#EnfRLcJXC1YAglMSsH5nxP>Vl6s~{}@01xGUO!kb&fTY(sFp;g# z?qmKzpLEsR5Yo7g!)sqM${?`g_bc7RBZXOU zK+&JjLQi0pkCnERoD~li=YwiHA7ce1w|eSyVs6|Hy4@>*n#>g68&5lm0!7Vx$8Y<# zq9-=1Eo4ntW?AN2T3eUcCKf5|J&J_jeY7~&xSLZ=JR3cvhwnQ|z)Azr!B+ zGbnGGh3N3~=)G8%#N@=)MCsI7?tg-(;EntxydXLzvB?esA#sMfjz*?A0cwqtn#USi zvr_X-MJS6A3lxVTaW6~uM=}z&nnOfd;Wj*g)xhqeRnZy9Z$Ul5dcK@zi;>W=7cq)wHt1uU`N}%9RfNp$7a%XILL;_5ONo=oTEJnWBY@TASo!&qFO8SiSP38>q zVN*rZaAQZqPTfmQcQvB=NPJiPmJO18!v*L?{sL|uCnvQfz9#$~6r#hSmugM-_TBb0 zr%ph3nM2-(N`DfSM{-Egy%{uSgUMg+7w#so>-9 znzFm17wF;FLuEEsA{GloO@Y075BrQw!Iq<=P#HQ4IU->AUHPrJ=>3|N&nZtfZ+B()4*_`#`X{03L8Q9wJPy4Y35wuRYX>! zeX*l>N8x@^4T(eYQqo2C9n{h7#jI$oXr3?!pNBF6oq)>^aSkR0u-UYPj)Gq;$Nv)) zMkJ%6^L_Q{t8^Yiu&>wW5_4=ME;Aeh+U5@j~$G?gT%1! z$r7o$DI-*8H~3Q!0v!j*Z%NqSXMx^xAGoWsh);?EK(3pj{0X~VyJDAOqkM||ojgbJ zP`*^=m-H527o~~z38&%N*cHJw?#-ko?uw3ym_x7pQ<(ek234o>-9tei_{Ltb=%lST z{K>OgETt{yEH|xD8(YK&>XY9-$H}+?+jeO7#)tS7N>5w5(8k3g@BA`QN!)ICqmhfrh@){}kvS|M}|p&U>un4L9X_ z1v}@V&7ud<8@ME-crPj3xq#}2Jr&vNBIH51NWI!b3?sc{ZIW{!)mr^5-LBj zU-3ZR8D>STWC2N`_^areNGVz?yey343Y>$pz;kShI+0xhC+{WaT55SpkuoL8crw~G z@(-LInc?4|)1mC(8vi8!v_LgTpQs!v9{d^*!Y5xP{4J6fdlxq*(^5}45BX7qMeW#G zoPcwdDY1&XNQBae_!+QX|KU6EQJ4tL5xnFjxN2^R)T+2A(m8xTAo9yuEAtF?OnvD= zkRgHt`>7)Rkh>i!f_PFRknAw$s{`gX| zGUqinpMMhBiax{oKy8*Mejz;!DP?lyA)*PxDW@&{6js2)BDfvpoam7){lqceay-;Zell$pl9<(|{jcyc4; z%ylAvk(H@Oz+_H?$;MarIZ{SGbL-&c%%aA6HhT~G7WyvxH1N%?&MstIvQOC!{zd^y za1MB2Z^h2VyCohb?VLUWEnY!%L9{|VNP1F!n%JxAuSsfz+FZ>@%^grTs)4FETQ!#` zkhhl`<=bSFq&TF&*+6ys8vluZ1)X6Y8byOB7pA8B1hx5txLs4i+JQn?-jkh-4v(}Qb(p;{x{G!l!JN_0&{ zxyM1rvKe`Z{SsY}R*=W#R^q7Yulj{%yRMV|ilMKuk*S60pUG$LZLVhSXqsYNZ5(X8 zV)(7Uo%TiB7c#jjsaiwYLRtA*Ngv?>bcO(OQi0xbIm!u5^W*GTh5+BI*jwGxka|h> zCI!?n>KAatzLVR??c`UoDo|cak~XqG#ZUyKFz*2_L(tcV&SvsiA^0+O!91Z?z#l9P zo2P5BJ#lj)2F`+U*MPj^Bvfij9W7&zDpuo>y=L_&1&KZ}=VI zQgL%hcga{uQ%IiMC|rmCz(!+N(T>P|=xZ17+HlROgD^Sx95#fafw6)5foJ~Z{vu{O z-HMsZv;#VI0UKsdu~*od{=P7?y9I3b@`3JfpXWr{M90U{ftmFswk^IoK_`!;GC6ZO zQgAnUkh55Md_1^CpNaQNkI9=7=anT?GG$5Fw*63!Q{4b}Fb22#VbH=AQ`A!Ik;Nn; z@jBrqVJ|$2?nXxPuW`i5X|cVLO5yC_OLi?ycz;t-^1EB>+Ut03|7x>a=UT^F$6GsF z?}PSC3b_wEig0^*dk)a`?mAz)lT;b+X>T)cOP`3T>AxIo5ZZ|rH@PRmc9sPW+|h`c*5As z_)R|~jnL(2?x<$Kot&q5C(9S_#WT>mFo!J5DVt~z$qj5}7SqM)@xBV)GE@#JfgPsb zHO1|6?}kmf*!{q@)s=F|;a{Qy{=s0F&<=!@gWI48ss%ssVP6?K?sJ2FS;UyxqX7YM z1*%8;$4}!SSIkpmf|H{2Lq!n-mXBXUC-;am2iKf>&s7$WGcU%oddv zZwCc*H_33&X3Pf;X9{bB?L=pwa|PFUt9VDb*Ek)M8)EY#f^ga3h`?t5Mt@y@DRwgb z-S^yQg1&POyqk5IFs)~{G7)Akdz7uiE@K|DBLWp6k#0z&VRU!2PSh417dzg{k0Z6Q2KWHsSP@_HPHIy8Bfcpy=+L$kkCa`TdNX~~eA{RPa|Spm9hkd}I&cb< z+s&eQJQ-h5P3kNq3QCyI zvOB=k^N2a(A)-=12U-QL>r!|cHXm*u6kmkBLF0lp{9N#%+=SWsk9aWJKJpg4xWz)Z zV1{wqAB6iX#yn-$!CbadFb*@82EaSs8R`ZTT3_4(`ojI(xBTAdGE9VD#FyX#@bO1Q zd7|c`tHORb2kVVqKzbnS1tNZc`<>f?TaEJr6omT7oY05Bv_Mr*veogIWna_H>E-kv zTEMhqdNCWB0w$mFGTqoOpb{UzZeUmV*9EoV_K~*H^w@#eg;*9S3bIms&QeZ4F2-Mk ztbq>XgwQUsif>52%3BfH;P^TSyWKX*8LBSe_Uf*hqpob$PS>aIN1>1n0>z(YGOC52)bN=TLIz|-Dv1QqwTI<{D!<^O! z*>9_h`q{VITiW;AV~#$q6=b%D@>KWE^OpB{=rR7o!7CwWr~){%N&xlmGRMPn3XUU# z(DisLaV^;r`Ef-I)Y^!K1I|aQuAKfCY|%Rz7a3=nDw_M6N*gyDat(_O2Mw3>xoMkW zMmt>nLgiBOmE$0v=&a};=%9~*m$_f+Mhu4p-nLN9#(_qc=P5zeCt0@;Sd%zR&Z6$2 zZij22`=NU#>34T>|8#SxkJKm6S??U*GEjKS={xii<|1Q(intfE1ggq*pP7FM!%+e9o7a(hG}Rzk6Y`W3QF)|m=y#w1c1&~q)7d!! zBDtuZ@DG-b4MI1eZ;-}ClQ$`_Sbe7Tn`s%XWc^>=JvD zUCF+Hx|Cucv$w$|Tmkl5C)jGBcN_uELTjV|^fl4gns_WeKRF|%;P^RTxeNFSFt<44dPw>lGjO1CqKFex#zn}xm&phxc|Bi zxkkH+TnpWY+?npHu7++tq}MoM{-X2VfbYc&Zw2TDF8B_4KY4$9uhZ51M*~wsdtpnq zE8aBuijyrkjnxy)5)%@k?5D!0%2aPt&(j>!?9=SfY}6dl7&LX^HoGMs2Fj~Jphu=e zZ-hNzHhT~MfKS84coBLT?TNj_PJ*-G4Dy5DhZo??OT9?mNR)~PBgKIeqzmN+_XbWv zGTa2##I|GGLH@+0;DX?`K;@tryhqtl1?X5$0Y@Xq+bh_EPQnvV9X%G#673h)k{l49 z5k-X)gh%iy*k&{j$w!_F_VMGqSG*BW2?i6_V>2VG!!1M4g0q4pf^Yn7*(OXwW(Ok! zj)fQ8$5Fa66Qb|Yc{GP9%{&BeH{yR1s1U+|dsYvE%$mdm@%xDz$?7Q~rzW=pKaAYN zYT!ME1)}j1u574$9C1~79Mr?rh*iWvm@lWY9*R1 zJb~pR?f5e8o#e!L$!N!L*I+5g+Pnv!14C6Hhr0yMMvhwcy5K<9+EUgL)+?4AtH|al zdI!u`y@Tr*3o7UtpnvY~9pT##e!w7I+`lW>AZ&~*j98hlNbfB`c=@K`LsQC#!zgt5nBb= zE$8XWjE&vwKNeUJ2!mg9YH&?>b)-(LPGTb5ts8hB`PY$CFrOWTFThp8rJ{V86zs-p z;x6nX`WY#M+!o9fIQVOM)p&O9a_+fQA!OOqisXeVhW3Cj^jx5m{}p4WH$fke&m5!I z!j^6a+~@1)+Vo1O(nir-rVM+-zc+Y3G(5~iddHYp@A%$CDj7@t=B(v5=dVC+VBLgw zgr&u2B$H$=c^Rm&RqE%epfX44P^PJ8tM95F!=85laTk1b9SA9*R^-Brwwm|@%o8U9 z=kFWO!I_z?82=LK9~ulZ&kWk>`AD51v)%oG6wt{&x@ep2x3!ivVA*b|XgO~AU~N@2 z#C{kU0w?XbljF)DXHc~~4ZT0S!+neCORP3{Fw{6~j@*wn1R6>;?r#1jWEiT)Ug8>< zxeS(HQZyj4lu6Z2%~0)L?F8Mev~mWIp}(=A$!Qv98U<|OD~3;oFNTSR&T!5WFg@r1 z&d3$&lyaD&jI^qV#C{>m`JXuz63-(@ki!n5QQtss3(t3Ql)Eu_gP*%rz{knDmcf>( zyX%MRn7gimxYBRwu#Dc?ivQn%BV)a}xFb^Npf-Ai4T?xwat(?(qt$nw>JTwWWrS2UYHW9{VRcg zJdLq2hk$QAiy6s$WtuPob}w|gcOk{86WkCz;^h*n5|5JqQa3ppxSx0(1uM{7&;k4w zn#3cdxw5K?Wke&OJ?ASg6Ay?5%9W~7s-ZyF-l3?XSg80TmnuHWH_F~hih!jtO866e zq%B}_Ts8GLK01no9|qpDFX=|!8dQP%v+I^~k9}X!Pume2$9C0Xv~Vr8;ngf-X>a`o z*$??eWsCaSuQ__VZ0;kZ!Slg0$=k_ymR14D{Y0o$xL0Ik>{~+1S`SV;&T(w;- zUGJUGo!^|(!4WXiRl&8-)zf{GoI(wQJhboBW2%d%ymu{dC!2Y%db~h7eE`Y9HG_Gf z=8-kAen|^QDwv1O6P1t*0QGwrBBtu7nXP@Ny{)aRYp%PeZK%DaIi(IO!;13qAu>Wb zO}s`_U)Tfxg6+aqW1F!j7z%%;fM(5(Pr~n`g@QW#7Qs(JPp;D`^*)#IS_ji0bihcum?;ZUqv6sdM8$-`hj0E$X|(Yuxj{QTmdBEl_Es^ zQM5$35N5M8@Y$fH{DWLVP9ZTtNx?4uWd2#`#qEjXu`5w+v|;2?xBzl=-ueap7tBz$ zGE`#Q;Qn;b1#}mhORMP?bQ&;S>M;WU-atO|9yh~1qZ?vm{8R!(+CcZ{7FI1H6{ zooFK7jkBAl25ot5sLcAxs}avt$F%aaK-$-|KWV`LJA`WR zA4~97*lnyFHW>Q>My~WoBoD>_p^)#J@yY`^>jzid(!TH0qK=ffTz&_wwN7kM(eA>sfEwsoHe!P z+Vu7pMURRQdk5$>z3z|jZf^73^mg~1frN)`ftDdf_-FWPv}s~tY7BQ0e-ttt^Wk3L zehXypfyDfh=&HP<+N`0qb#)oK>S_P!d%~MJ*7VzyXPRd^VSHu$Xe* zDwGQw!kcNuUcgOu9s7lK#%cU8ehr*QQT|e1L+*X(Hyg)g(Q2XokVY6`JAsN~Gd+#Q z>6@U_&jVM*0e^*n%|9?OBy>3P4LItf6T?zlx$k*Pzz;ASore`+2XMaduCR;fJ)V#C z#IC}G?lpQD$v`$ByO3Lg=7Q<`2(J#WI%ip8c>Ff_s=h`?fi7Z1(B-f1Z^dT#C-}Ln zk|_%gl>NRAP%&ir+WE%&QocD%GylIpBDfM5(AwCK_=e=7RjSQbWZqoAjMXPz2;ErE6koqXLCpYqItGUw&Aw9))E$D;irPyg|@<@mI=1H zMJT9*?i9UrTyWJU7n3HyZx;YW_7T&5UWisf4L9+-4S2N7E*wM_)|8Ax)Hq zrybX=(ri}}@=lVGq9~Syu$)SXlF`S(5IdGW=M7Wm$xN7>9durDYF%%f-<|c~bS-gZ zyTq=}uH&vs?ndq$_k6MdPS_fXqH-yjXTRqgsJ2%f{9i2VDWJAs3Liq z>lY-jG;vAUM|mdUQa;hRbr;h%r2W!~)25{DgAK)TjY<7WIg0o!?Zz>-~}5=SEh&4 z)tEtSYd;p~1xc89A(4<5aYX;bq>whUhSQTL;lJlkK-yqNxGRmq!=O>>E&2mfxgl5~ zWR+D#mQ-@*PL(M|KK+JfmXs_<2k|; zqIHr+vX*i_kTlLH>JmYPkbt4S@{@AEav{|Jt%x1OUqznco??NVkxrJ}5m8XF<|BXM zl!f7kzZ0(#1 zuV^bsYkc4mlGn)}WQ_E{HzS=oLA`=+#~ScLx{22kMupu)lYnXvM$2I5u)0_W zG$hy~IEu_cJ_$Mt2Jm0;7I7!0?j*3pocNwt+h}LVwv!J>ChihDiE6|{c}E2cpvmq}~>0Joade8YUvEHNK8rW<*|~77pGm< z?NZ-X{FCNLJ_{$HZ+VrI?V?pfHT@i>hj#`w#qEW8vcX|;^mWv6T(FxR_Z$bERh_3C zR~%wzALmbJ+<6dou$#$7|E8~?=jqq#FX_kXuj)E#`a(w6DdLZOvh1m3jCeNa&F~F6S?~<}lDFWb-N9QzC-4q$3Ny2(Xb-F& zmV&uiY2-EX7|BEy3GVSNyjR>&slSO+31?gt9}%q)-Wm*nBK!-pm$|{zXBgi)?>4x{ zuXv_<`g=Zm4tQ>OFZ)7FT3}f4M(AmHQS?##O7dFjcgn)4%Gpx7d!@Uf;{FG z&JKpi%CT#K{OR)Xy(s7JDaC$K21HYUewL2;4F=9Q7TiVdpv;6tulv zUz`m=MVs#a=T?$W$!y97w1d~=7LrE|qE=8tsM6GAsy%pxeau%#1=$r6MTz7UUJ9Y` zu98mj&O}1FP(4@oNPouA#;`hI0wGL@M6bOU+}y_net z`o=$?**+U!fef@3k|4^(1M$4XYN*Z-{v&=@K?u2pKEMLlYFsXq2<7;1bRcR)w_?q) z)~E)_MOLA8VU9K)=^(hm{~!MfPYp_HlMmxPqbq<&bkScD=4Ty2rG1N@?kn^Js0yA% zo>6e4Sv-F|AH5M@LpB#&>90dnSwE6~d(au6&@ntSV5}Q)-m&iCM}#sf{J@;Mm zzj%pgM#$tx8IJEVH4g|>#ho^&^mki_TE1F7SqRISg8YB0{xvLEQ+Ul%Xw}*%D@;0U zV4XYGod3{$(nbE=eB*Cu~W&JmY)%dh>BpGh-FQSVJ#EU%gD% zLN#2Wl|2#X2qz;uIo0BmVdKArZB38#sL9W8b3KBb=gJN&l!$|_rc^V}h=3M#`)5s4SU1fv*U z0W)F-sGFJ#9|?=_rjW~KLG}oS^4D@7r|u?}#%e}dho*sA*2;cjD#PTgE>Lf#foh^! z;ALP_pmXqiuq*TcD`SCpoka6wXU(~KS1Y&gv-tTpVL z+98t!yO8Q=WmJqdMY;*r@SF2cPT$nUMUg1g zj_Lq=IFknf-C)euoLv`q5Ihqq895XimJp^ioJ>v__yGxi8afcu;p^~&I4Qg**25-X zFT9$26r%~XQmL}3YN*So=d0SPb}C~^y;`KkRd1Cyh{}prvJB`0jtI?Ib3tt`k}8$Z zNB;}z{CcK>&*b5`OSnEbD>)t%^|HRV41k1yniiz6e!E+opFoF3!281>A3lWnUlUF{aE_%ba6%lv)#xwE;Kxb`|Ms?P3>Ie z2Dw>UTig(*`R3H)=#bEMe+%ZZS5A3c7aeEplkG+DN9#Cg-)0v%8D|~X2ApvYg5&_7 zbE)gRYZf?!j{>9WD!5T%?rD%bcn(yzh3<{yV9!zbHrEU64x*7R@seCK(g-gj9w6JT zuql_SD{2wF%dpxYHqdD$^rH+X3{+YzZ8h~?V3$V}4diX5PsRI0>xCWQ``#L_ft^8X zq21Ai*aQ4Cq<(b6%A=PB_xYP3HQ-%xNBnrSO1NY2ub*N+Fci4jD$;G}EM^j0%5U+X z4{(BM!Q!Bo7#<~Kvc%oQ0?^P3czxj8szSP;DRd%q7PaA&4F|8D0nXK9v?{QtG{|5i z3w?m@L?@sxkUYVBzME?YRmi3!k(e4C5f%qW0!_Ij(~@598v>30F6s?Ak*rJAr20@{ zs-kCsC*MoacUdVYEPI9@MJmTDB`>6gb0%|MazFF73LXPjqzOI--;Y~`eI$CBQ`Str z0m$CliLuINDvzpKl4P;)BZ_skx@NkxOuFpI_w5|8ygyWrnSy1a#yzFy#1fO9MonD9J%&e_Nas7;)6oN20Xp1 zFySD88`{qO+1sxw9j>UhD^|p*EL+$Z#J|r#tqgqrBd9 zufnh6V(?@*0s4G0UXVBrsfWvX4F4?<<5(mggiU-w?|aWc>MMB=+^fYsr97KF z4?ShPD}1+@OTb5W16|=yv{zz!>ISDj_b9g~&%^(URKvbw8}V)UO1y*U1nitf%Dc+Z z|2guN-BqR4^);+|mAa8i036Hq>W69#IBosPRm270^X7w!oyE5xG2SSSF*ztUCA`vq zgc<6yQzzXwAknRZ<8je&ThLP7(%!P8kXM*p5dJr};6@?W8n@YC3 z$HSCku#0gjU9%z6vA%nvd#w8@xQIuSZmP9!7t<1aGqT8&*s9bY{v(VQeU=IoUzIa7 zIl4vq@5cJ3-^PoE`+A!JHF1q6(z3KW)m326A|>?lcG7lYR`?7r#0KNraXWelm7{jF z6K)oYguk)(=uBjaz{9V~E5&)7*bg%e8z@)v{h!(6pv{>`8=wo=3%alV{(yg5;7Z_h zFc`{<*kiwd^yg1}g}uZ+-Y))E{wP6nn)s~Nvmw+4N zE_m_BsBfu{sEY$LI7^kQ>aOmn83v@0FUs;np?s9gD%mEQi&sY~@=_^NqE~c%Xe9fB zZtwL#s<_g5*3sB5Dq3a5Equ$vLQ!E(!TNtA|8*-6STb&OvWk&+!^~jSroFU77AjF35U?$?XZ(Ki4r( zrL~7##O|PY{0Q%-6u6>oNH=r^KRxs4E^N)uavWZ6%Fn!$g;86j&} z5jzkk<7*OmiFe5YPIulo{xV=k8FXMJ|Afi?*SZ?6lWZF8P~&I3thlZbOY39@35OVF1{#PFZ0SS%09@)5S5g5 zRD0A4O-J zIpNa)?Z5%tof4t>kz0xUl%CgIAVY4VYlH{HPVmts{8~TjDKbnd;%YgD~;~7g_=wyEi*}Lh}vL>`O4IT z=;~k*+m?RhSpz29h4!UIlOd()q2sG#rz2*+?fBz-3aZ97u9wc=aOTFkZn-MD+Pad? zway~v3)ceo68CEN5jT%qPKu~X-e>eD*r8VpABf&d;Jk_GKH(WjO-O#uRzK2O(~27d zru(KQ#%$1#+YQ5wdc9S#k|pAf$nnsfK*>OWy~$jmpZIqAjC7D*#+rcVP6on(t-<`z(1T~Zz?Dh)I^G-PtZYdhlOFHQ5GgAgP_y6fX%|D;yv-Qkj*t4 z3m|I+6L_;ZS*ec6G4Uyp++bh7i`hU^;3KT)rKoMxHtJbx?m(YNucsehd6+{4^Y+=jeaFgq)YeZYQTGPt4RqQd{<=q#h#*0MHik|mjB zwqwUh>?CDoW@ct)W@hH5%*@Qp%(qRMn;2qdlFa`0eCw`Rcg?TqG>UZ2-tYT7yaT|X z!ihR?qhm-K0?G2GObC|BB1s)-dD&xGk!-v4D)4s70oQ)2U?>mr z(kHSh@`-Y$qCy5&{Wx=vW>;4E>^IpBwR5zET2wbuTa+^=`vo-XR%dp}Y_G1b94oCV zit&ZK(zpj{%FrfVk%sV_(ZQ#8*K%&RZLFkcLp1-#LSRff31e?vv zkfVJ`j*i@q&W+CioBWDI#pJ!zUfKit0Y-o3TjmLt7D3qS(Tyk#n}ZsF`7MD> zWF*9JI?I#xlra=Cc~@D6Y`?SslI*wTDVa|yke(N}6FnhH3I5|A#rh)wnCP@k_Kag?FI0uofCJ zPh=J3RppHpdo%W_uV&uT+04WAGwHJ_UQ!u5LPmmX0vr5s*qY0Ht3Bi0MJ~$8b3J!Wc3+15 zThPM?K6>20ADZ}$s7}$UvAv1+sS7|6ZqFFZ=mGS0GixIIDC$MI*k7zS=OXW&fF`Ud zydb;+{u;6DrhJUzv%IogE}tM1L7K3yB3mw(?U1^~9Fd!-B$&&cjcJj8;H?ZKsAzj? zQgFIY>KX0i*biE(S)ZDZ!A*0$v92*1p#q3U53Qga=h7o#E zU8zl;qvB^?(zrFdGOw!}s4J`4nI~1vlyTWI$X(4AVWN@5eqK55Fnk%d7wdty!&#UH z<+59_e?a@}Jk}5G!#;(ygr?IS#w1$32A#i0TxX9@Qn<1I@cUy%&8heF#09!D2OrUPl4!YpxseQagveJm~6E?Dw`{#$yl-tvNiGrVC&#Zdr7tf%cUi8h5wP$7IiV-&@xjO^alN8<46-1Tqu7{ElX~gms?%- zHqL9#=`bUh>bdIs5m+4l8_odwWUbiH|PGx!1?=OnbkTv=|DTvjeecQfZtmO)clb0>3~dR@j-St+rM z7|MHwzh-}A{7B`*PLl(I-+VPaE8(kI#gZ&JWF7#SONMQ^^|z(EwUw=&y@9>RHq15+ zvP&Aje;y0@ll)=|yy%$4eL>L%*l>Qbs}iovqZk_O`KU;wO0Ea7$J>;zuRG%Nu> z>jPk=qmbon9CBLKFeAG^(unnv*_`R6zfZ3PIuH?2lLy1ULTiI?hw)GF|MeFIx`u9q zwua)+5PlNwMmZv^ST*nuGT=X>5>rxjXszfxhK-TUTFSB^qv0v*i9Up{7zR_}UwCg0 zif_lBV_V^Wdju9kCG3l=DnLBQOm|I|fTMSFXltO@_uaeFYxi{XEXkqIx-`=KGr*VIE}zJGev(+U&h!B#_ksE@4&zsf-c4WaH2dm zB+I%BM~lt_OJJ-lS8-IaPCi=BmOEg-GehxSeo^*WI#6<1l!TP=ME)+$FSY}c&x2Bm z`1VMX@EQMG&u|y&Xa-)*9hOYX(hKxt@nwC)-~(saX;TkV*i_D( zXEoaULmv8|qn|U^Gux*R2*NwUkAOeD#r_0VYu0!tXzB++Dq3+bds39?rW@54MCA@1u z{&*?rEzgIu*%VEytnnHw^MrbgI-!1{zMip6VUcnqDbZxnO5s*P1s=v34s+9|*ckjf zrp9)%%`jzXi(SKdqLbJ>@tUWg&T} zf9P4LB=kN!kt&L`k3ElhVppLFJ~eSaC8XU1^6)(71l9tiA$vI*M3+Dks68r$#L-VY zhf@!i;4NUPvH6oQq0=$RKKr5g}F%EYX ze<4_X4hS2H`${#kDsqN$mExOx8JvNR%27pu;tIUa`bj^DH^SeIh?vjog#SW1F?Od5 zz$dth+7|5Xv$#`^r0uDdVV!3-m~6&v#uLVGusiQ-IH-T6U#4$lm}D$vGMdb$J*K@S zvn{=0=k(F}4DM_*+%hV*Ezcf42?&CDZ>JD6W_W8STipyY+EPTsK2rP zuskyx%_FT}!L%s=Zo@WnYoLN}gydsOa9I~SJGg$iC;CnV^1>EMADxn%Pyd4$a0al9 zMnP`rsQi1zIQVM*(r`4F)J$~;^&#~{RYzs9tT)`-M*~$tE==%Ga}VRdoP(|#j{ib~ z?59X8M8meA6)_c>%kIM3&#cUJF}l&8rf|r`-=XS}N5h{(V}oJ;KmTI?Oknpv4DAga z2pxwud{2@a=@(rF%;<&j0${$&Qc7AM`YFKu4`WqE97rp43w8nWZS}EESPi@r=Q-y$ zry?hT-@%&z!9{@AMQ4IHSPv%Xhe;~#kMs@?fzIJWuiTsMg>(cY@jXrn(3GyZ3gNc7 z$34&U$9oK%C=Ws`aw|!T)P&6XmxLz0ie{!2(oWK<0Zll}G_YzQbCJI6VOTMz5fGR4 zFgIu=c1o(r`pfq#$|^p|56V}_C(8#ZCMl-LEmEa)5d7^d5&jT_x&N>($QN*P2!QQ+ zlJo}td8fNwjz0FDwmM*_tO#__rlzQIp{dZQF}U^J_3iYX3?@ShQ{40u^0b(Fw*|F} zonGf-C)>5%o$V_OFv43%l&T6w?Iwwt>6(m{OdpGd{)c_#HWO?hl){U`8N#}vZjyD< zDY9ztCo2z0jeOPe%;}m{*}rp=Id`-#bTf0C=9SCqmpe}TChMVwfVYT99aHX+4Hx5t zinj~@51Gw4m3kQ!ker~(x7GdJp|EW>o8W7nZ(eQrVrc9kEvg&u|Yg?y#VvwbTDMID!mB8pXe)WXuz2NA7IF22mfF zywH?aRTh})ebC&`Y_9ID{;vL}W~wrjsQiHBw>S-%gtx*CfSJO?j>|HW(KE8va7 zW{+oI0B(~5V`1CaLZlZk_O`>6R-1mGcou6H34_bY5FQX(9cTrX)n>sQ=vLaH4bTi` zv~-FAj!RX1b-W+gu#P3hr2eGm(mOHEGR85VLcVM$dJr29@623m6L4Jq;CDGQ!Jwz$ zEP^-bAgnPET)s1}z}xJ4>H_Sy&QsYT((i?H**s50&vN%ySFY~mTaQ5u4IDPCmcfL@E&1P5ia9E+8JLO4Uv*y zWnZ!TqjQmCn!T=Vtz}ZlMpJ=lsj0ImV(ep38usaz=+(x>#=RyS(%V@j8Z!=V)KW0* z+~@Q-UG5#eInWK(hZ~Xs(i@Q^#Oc0t2V)%TD{>V>xa)ueH;L#4^in#|{<5VLrA=hB zVSct+u^{8Lx{Kyx7M4>zhtST|p}Bi^-Slc%9jglAUp%%I=-}cTr)>_f}6TGg=VXs*hwgeq5CJO?3 z(Z$v|mV4&DmY3FZ_PGw9qa(b_8UktWtglpXNw`_$c+8QwN_)Y&i_PJkBM5Oo@?O?W z8C4yFjPLc#XYe*FRt<$7mL_AMqMEFtWT?2WsFg@49M7k7zrhpr9#i47V7J|ftw0VV zI`kd-1lVrVQ)-U{mdc5j~df~TuzvS*)L0BIG<^}w9~lI2rR6R*@) z%O44x3t7kt)LW`Wv`4H`qHn4{?KbTljX@97?$Mt>o9``HDfWSXaRlcck3%ekOtwJ$ zLDE24BAcO*D$6QrC>kq{%WEhOD_SV_%Y2g8;vJ%i!YV{(zLGPF-H2J4=1=a9xhO20 z;lJr=;_B|GXdh*(ZRuZPgp=7L<4|LPah{=}K2iLp7}J+GP=+j1W798VtCHsCAy%`! z9enAFoJ(A@J#GB5pgY7Pi@}d%iP{rYXei@3Gs+r^e!=hat_f}se~CN9MBye;wq&%l z56m=5!FQ8VR#C6lJj!~Ob5FZdyGqwA7t7z5zb3C{?)03US${Q^G&j^4s`5&q)Ff&s z_{eFA-C}K`UrjuT%nvUOIK3O)&EOU=+49=_#X?$N+m_pE*d|+ZYkw?AFLUNKxNseqvmpl}}xbUjar`4Qf@mcj!T2g#W$2Ti{QyZManU zZDCt=ua$)UB}+RNesoU_y*1a zu9*9a)17k*?}UGWTnK_(Wo)HUsobO^W{=bgpAOsvg3@A--JRii;Fh{)yGFaNL(}#? z@WZM?t9zR7F5Gt~hHWGZD2{`olflqoN!Ch7()RT8^dQ<3*gCalrC5CA8nOgk#Cgv9 z1zVES!t3H1Qb@AH=C``CCwwh=3YWZ&!l+m%&y`)2bbzmB8?amHdE4;0>}+Npt$orL zwNuMOJ$;x*7xDKDE`r=t88S>wh}}-Y4wmtbxdR!1BAf@j;et}c0Wh*Kg{4Ie zB)y~wnCJD8y_735e5$gVgIPJS1E{W*>VD}~yws zyEMmg&f#nE>$UyO*$5|()LWS8_$+G_eG`dJ3fG9f&)1g(bk#aaO~6p{(}Q_dRhU(Pm8 zF zz194+1Mh;n!rg)Lr-}TIWCBfNcWM;$&rw$RWXIKR0t0rwp>yqwpMjLDBWoQF?r)P#O#`>m}CI360Wy8;IlWo6a zB#_db&MEF^-i!Vr!3E($@X_vyxMMX_ne=9i7tq=KgFfNBg1zesqCJ5SYGEFnd%}{+ zQm*W*bSiAni&cX()v|Bph{1lcS*OXJo_8Y8sGFO!H0xJp-OTRlZW+B5NojqNf*1i` z&92Bs#_LoBJm?pLr+q6tqg=b}%dCejC*ZF63(UYhZKdE`R@;8UG2U^=-p_W`vdY}b zG8}kDdDa}b5$rYRTc_C@I5n+Yc78PwX_}Rsb56n+UHd+b$0;$%s;F)R;n}Bzm!Q5^fJsySU zZUy9c+aoyRZ+cYfZh{w|7i~>u2O$msG@d+<3HF%vJbzv5VV2R+bJSbQJITA%d&4^d z%$s#XdE|al2DG|)(foJ_I0vuNJJXBPzrhWUG1f8XvW~z6je*|B@9=69L19vKS==8o z8kOZ@<#A;~AyiCs$4PHu;pMp`snv=Q|nEG;e9sKP|v_Nlr~;BzBkn>xoY}oI&Lm&9blgcuD5}% zv}>#<>Z=e$!ZN$i4h}Y(+1Y3x2L_Sdq+zuZ^EhPtl6v~Dq z$u|Wuz=%a0N8ePSf?w?VOjD_fY#Qdx@rd=1lbxRSRV$`8&xwqA|Y!U&(&Q z4AR8O4-qsxBrxAw+Fi}L##Y~|```P@+TM1~Hpq4mc1(NiEgjqJ&FyckUCic^3ue@^ z-*VdW$vnkOnG3CD99x}rp&@Y8-NNgKjqI(^4k`nZs|!;Z^qZ_cSU=t#pv_N^OqBT) z$5k7kBi~TnQZ+<1NF|2^c^`#e_FA$^B!M$ZKcQcM^L6kwPomACfnFczV{GUh^#&5Q z90Lvoin32Jiy2QDa~MvVDOo12jn<=%k)f~&r~|wGHG!wnCzuY^3s(kf@N@9dL3JR~ zH^z)VinWSwjxUAV=1=%;O6d(4QTFNL!h&|)wdhCTnZo|?*pFw z*07meOI3}`1{TVj_;oPh%}&ov&rBDm+rg=@6YDUm4e|$?=uJ5u-XP)+w1clgs!J!c z$hRxUD7z^4DAp-Vas-~TIr0v&TxompZ_#?8fLOwx%o&U>VX5e$@{Cso~ocSOqzaP`F!UR?L(bNCWo=Z27;i(^v_21OJ7f5|JSoF9;A)aUID;pl$w? zu9WRkPE?)DteHI~C!7heCrcnfv>TcEq|>e ztv4;_Eeotu?OU80Hw(_h1H9W|+cYZFmpm9DV@qK+Ii7KuU4z3Be1eR2(o1-|dAh?4?YL*EFW+zV5`M&g#y=6r zEfvXwC-W66&9Tn8$2r<{ zAB@jk1M<*p(n~(4Zbk+0W*85&+zrsqS%n7iHoUfiR>U{3sGTIr11n1}nGIX9ezG0% zju}JLYc-W~mT3=Zk86kN;<=CW+T{~@$F-xf_iDaEuJBYw4aGh9xo#5dhBxp`! z*$WUls~qD9{SN&CtxU=mpBya@)>aNF3ZDdu|GB{1z z;*q#3UNM1#S*ZeT2y~66Fqo_{$W>H|Yk@>s0oan$IiET6x%GK_dD%QIcM!1Fp5d+W z4QvMM18sMDSJE8U#OhE}Lo4BQruN?Uy!F)ba=a~|J?I6_w%g|evXTb8DD{DmG$vFG zPK@8=*2sdWG`>1ZQ5sH&H$ZwP?)&$Pe@8Fg)n^li3z-n;xyfyGM;Snrl5)@c(3&U%I^ZiS_8$EtcRd3p>^G@?vJ=0*4F%sNFqkUWaWr3%07v^?v zNeg+B%7`+7d^|i62DbKU;508uacMi~V&+Tc9cEi3%3g*y;YuJ+D;WKW(Y(ADW?=P1VaY?D7-R7oxX<&aiK42K2Cow3Z2T zgdctYzo*09HC+uHoo$$vWy@02@tI?T30xB=9&zJr8Dwr!9# zV%cLIY477a>7sxL_|ALHw-S=tezHz zP~d$Ji*$}Y0CI~u$xR(h?M%s1#?*S~zf@zkWm3Qi+K5(w(^*uoPUsN*gv{(-871GT zj3{?0+A7M(cLNKKl(mv&O9c|E=%uhMv5xrcKn{sY+ zmUsB9cg)SqL(JuXch=sNVcf3Q>FelM>U$b=#zNyw<45SCSArR>&vw=E&3Vg}bpP=R z1D!))SfG;Bny4xq${LZ^5M!a zs`r^EvNvn9bTr*y-LBl~`N#8K<~G#c26u2keOmQic~V{|DJRMm2)M7|?9rXkAYC?I zJ90ZzDzMi(0!%tH9dvtD$eB%dWINf;97l5p-FX^%CM@SX$27>KsjXG4!>zZi8)0^< zgAUXqJKIHvc2B( z+AWYbmqQLgR#z&Ld=#O=tAZ(l&VpC)|7iwyHB1vmVsC*KI{<5dzGaU=6l^B?m+e7{ zSc91-=-X*6=zVF;Qymj~qAjUhvK)ClJUY|^y1_ZYPeClq11i<$@GUr-EvCjtrpErp zddKtO4lp~h1vpDXVEfaOF@z~b8nC%oIXnTW#!Gl>&IL|A?sZ-XuQcy5*N#W9eEc)E zk39?C_glfPTsvMbDx!`A=LC%2F_11>krn;{M423-pqAf}5Zb zu=lG{Pa?5sn|Rq|pCl*sJ#{SAFVz(K!7Uk+nbVldS(Vv)v1MEb7{-4HcZr8f*Mm1< zjl!iYR!&e*z#Lp66UgcEebVtj*vJ&!BqjrM{2QkUwi;Q^(5Lf~{P=^2BK*<6%X`aR z)_vAh$MM|S*)qT^v1~NoE!k%JY7iN^>4o|n!$3pGu-`ZaX15~f)8$xu*{3;_(AYfe zx$c_=ZLGRraryzS&6Npdx+9nru7jcdD7pe~&i%*J3Y`23e3qa!w3pwDYe_pw1;7cK zoUv0qO|vuS8CcMR+K<|=xtH_sysx^U*`+jXGgqmvWVBIKk=aF4h&sHfco#4Ny$758 zx9Cz*9sKUIdid^_&PVnt_IdVa_Lb1iG(vi|tK%BveO%Dm+-4tV{cNskkyu+;>sxzU z&st7eV%D@BI(6>SUfSLr)Y)`Cm zyiI(3{73=?qiSzjHhl==JM$hQLpNh(fd>` z9fM9YChcG)+YmKfD z=4$|FGb1D$KSZ_z^LTf%JeUuqtV&2Pc5RHrS3`^l5s>^Zd;%sfGI2vmLi$PiSGH7{ zt2&XXg11=>U47kFoi?{a-p{;eAQ??(b<#`%ieuA^tMXyeVWQarE2kIsA8RFjcG?x+ z5z&$l1J8XgJ=a`|oaG#y?e8InJrVL3-+@Cl0@%lOo#mZp93h**Qpa-961U`A%UIV# z+IE&D*XFT5hUYBeL46AUuwYTRU8G5DYvNsM7R|^|u+5m5w@k2(xGJn57D9WWk~}V( zCEG81ExQZ!#=X*Faa-5|+=sK-SRi5+3wA(W`wU(ie~op-vthG|W4GB%wj7$9IJ*Hd zjd`4Yl~$3~jMg)?KCwI&q|SuLgg=FwgbRa9gG0gHIw;JCr|fR{Ww;CZ1m0&Qk(V)g zJQAw{`}2>9WhpIfE!+bvj5zZ+@(FmWo3JC;SzM35@6< z7PLlRJO4t*>6Ur7xyU@qT)l*4T&<^ymln^}kJZ1>4>I&N@QuaBJ0*23Q*3J;C!I3) zEzbkrAZT$r$$yj+Xz0B7hh!pcrr%(uSiRY;uvd6f?hM{j{$&12ux~vQ7Kye>ywcs$ z0{L0x7}dqh3fVKY>$HBj2PAZN^WNu8&OMM*q8SYdoVlv!iZQZ$$s*`Rmf>Wh&%sxE zJtc_KB5%Tn0?mCaFWp_@oDYV>IY7}{0bK3l@ZEpt?Bm)2DZaCg^|t%KZT?|NTN=PO zbBLvnWrDSVeWFtiPgx(|RsYCfjqp-R8)GImB##2WViKzWwc#hBF}9dU5_3gYBpswF zsRbGqb!B4NQQ0NwG08OX2hj|nLof@hk~N5Gz$=$=FXD^wO)$^O#cyM7VE0dFZ)0zQ zx7rj|8^(3opL9`rQ~GMs8-E+s0#)xgq}rc``h`{iO{HIGK1|^)VGHa~3dxRC=ZGE1 zjrZe&u~Z*Y9#F@+ySgiD{CX$i#maNwjCb{JEn!)p}ZiFp`yG@o`!oH{{f_` zt?UveMz^QdCbq^NL=t3;(4@dbV88D1eDXf_Q9uQ{;C<#j?)w+m6j%}X7Dxm_!6a}{ zXuz%Q1bp#I(MGXf@eK)KLY z5Av^y`wGZtzzsZKQX)x6x&!MVTXc$;B2e&4@kVm0VNa0=qXX?Ov&z=NvCFx^rS{DC zEeoWBe@R1xh%S$D;)@f}l!f+;p=9kswCEY^Ag2(F^@aSt{IC4;L_`=A-INqbi=_%o64J#w?a2D`=I8Z`jKi@Mk|F)niMS-n7R4*d*lk^e!59= zVsr+%Ids-P%A4cyy1dR&&|TW-XynX+Byz6nkF&%%+?DUV0}Y3}u#I|YUIh6Kn}ue5 zV7Y854Xpq<^v+LsyZ8qLN{2p>MUm0*Qpr_7AP3B(cF`+HBV3XG zmEM-RrCnr=WGkgbVoW?xBokg2ToTkH{({4l$L|An&FP!~J{O;iZ^ka5{~)QMVwYjZ zSw#;^t=d)SLHA!h=29B(4;B(D_zpJ0u!h3JB0sPrCe z0&c+U?5rj)XQ;NRc9XV}u57M8w|?%2oP=gyre3up16OXAQDRK^hgX2#UU)l=rB+vul&H$WiDx>W~2qbS-p#_c=eoJFOcy0-k|k`wrL|r&x|!+E|6w zdzMyK$~Mn=(Y?cK^gV=43zuX@md5TTTBfd~w*ZZ28M`cybMN!l2+k07g$KlP>0@cW z%qD#)tuCu2YbqNh)k(@gLgR?=2N5G?3on7oX)k{0m`W{;7)9}l1I{U`m&a1^wgRMA^`wahueMASd z>#}-6zHMouRjh4Yq%lZ61jvQiC=;Ve3aLQ`x>Xvfk;ipzx1=@m$)Qq3n!opvku6a zr#*L_dG>AAOO{iX29_S?HYUu_LSIr$(`yW8^w0H(;hupoRWPr!*0OUTE2ni=^fvL= z3K~NXsOON6^Tw{khbP&PE_lbN#X=D$+XBw|d~OD>9q$Y80{;+@H7bd_N^@jgT2eNtPWbCE(AG^p}C9lu0ZO{n3I#W1bRh#GY%+tvRmRBM3UPKugpeR+4OYsTkHTe zm~0msu>anUoa<2rYFoHLgUsiBOpy%J<9p+90526<9&7Q;> zO+T3$lXwxW5ZOasfz(kG|6k8zkK6mt_t)RY{}b*4$9%3x8XX=Fj1c=dS1E;ET|HEoJ@%xd(;bpwZfw%T7o(JThAJ!}T-tl??k04-Gm)4Z{fILDM43A=@2CRo6Y&LeB*s z30U0j5D+@>94U836xcpGdz*E}X(=M=AgTzeU^_ zM-qqO>vb;aPCZK(&9ZLr=2PT8wgD2+QKFKFCRrC)y<#xpz z)yvF%SqpN`YOCwMYLmLVxtnrJbff+^kmbn_c7CYyGfubzjV4gbr1CQm7lkKQupKI%AT?d)8@|HEw2l!z9 zVr^?xhb_Jy_)um{*1+diT+&fB6wF+5-cT_i>ip1 ziyMINoF}O$=?h+|J>r_;_M(%*F+jy%2weP`1hmTp1Nnz}x3~>CkMS@N{1>1SbRE(f z@@5qnm(v$gJ5rrf%fNr07he*2LXIXIK=y`4Ed`GMZeYN6qt;P%DH&CfN{}O{ia_%{ z1!wVRu~xAz@rQ}^$;qiX=|8k==0jE(Qj-G6WJ@^_PG#;E-edk}et&)oo|IdGGa07C zzmVR{HE;*01iO->6rH>c)(gGg0*!#Wz(23(m%+QO*mvAF%zq(x4~R&ba4<9?T#0;4 zU5vDhI3u$nL!;Zl>Ut?IjpH!AizTjtwYV-r$ZX3>Am7nnoZ)-}kq@*AvGkI(r3?YX zAO+K&I*`LV03@nCVy@UCoGDyJj1?gK_B=hefD^~UY$nT3-<38dKgD}T-i7A{n+G2H z^1WT$ZpUe0w_LHjGFLJ8Hf0*(#bb(J7C+M;)tmIa^>)3_*wB2+TGnBQFV#YK$g|YH zCD=JUlDZN(9-SKd8gHM((vN6ydXnK~RzME1kE8AJGn`zmkz0vhPN0Er5l2dg^vH~i zp!%lfRdzcqrR}WiuIrpzGw(_66kSG6Z%u((p&}LSAU98U-LYsl>NcI|RqaJ_M<-C6GT;6wak&xS4?*J_5GcEbF`^2GYs zI@-3)-p^Upy&b-p4+33apLd`77A=e?lF8JuG>6_FZUMWonw(=?4eu*&G~X+@E37SY ziFS)M;#uJOxFRkV&jA;MUN}&=8jM91g)M|?Vv*o1U&3F=OL7i@kMJFwl1Mg-J($I2 zmS-HMjY|DZJ^=^%%fyX%qnMAHL}tPsvo!UFY)q!Y=OA}n0AI_^loIl2({TaQWN!JkVi&ez)IE}c!xjlG6evx3eU@l+Eo5StIxrp6F zYaB+Tx68UE#dz3-*(f$zPa4=scv;Wc4C*sycp zyM2jbM5aZo6do1E^5dUlWn+_K7h@yAlQfxj61vGpa;8X1^3>AJ!T?VA9&1>D$afECh!f4!F+h1)f@5| zN!p?`H#HM%#19iy6ZPXgz(=uwd>tM|PNQZ~yUBgz7_vDvDAGH!Fw#2mojL&Kn66-t z>>B$BH_nOB4UQz0=}|NT{T*`_5@v4&PI(i~BJL^PXWlPds1@YuYT;M+-q zZG0JHKkOXxEr;fLbGQYp)K$n0>kevvW{=glR5de3D{jihOV5i&3D$DTpbc5$Az8mHRVQwt zPKUF=<8;hl-S@?F$t`m)a!qjAAQ#JbJ$I^H6P+6z>+MTyWo>_93!t|Q22S%=i^F;x z5*+8i-Tt5FoNpHJ1y+WIR5|b(luI(wPt#enrSwZoJ<=3oa4N%$Vm$XTkeVM8Y2hMK zL-8w-9^9o%#T>~%FtMBhhIxCjS9BJ>p90}n!YrsGIKyks)#Gz;8U7c|L9NJVW-G=8 zx-q>fRWD^sPE0mSw2BV}&twKP!J1K}BdsDUC?!RQSwgLdpURDlqKqV$It}~9-00e9 zEqI^pj(viY?nt-?&!*=xL##>c!;m_vi~Bj#xlu3?vIUg{5BM%_BW?q*DC|P_f@7pI zO_HjS$cfG*n}xdtcLgr`pL_E?lYrA$#&-%leg^P!Kk@wy^bKtWBI#qW6c2$$a)K(P zLe#j(+DNHrCcMX%K$DM%hvRip1++Gd(ac$_j&M6F;8=MBiJd~VxPl}N8}zTRGb{zE z*#XlzhR!FyV9)G`iCj_Y{v5wS8 zuz73>ob@;N**$vqFKChvcLjhYS<2PcH5G0Jq+>Ir&imPhS@&5dS+`j}THaa>w!Zcu z;M(DPYWwB}GQc_>gxma)*p9@|)YbGr8iziIag(J(|6yG@M>$tG54a-!2Vk2jL#z`^(Z~;dk(_A;1HQ0CqynsRz;)H zoY?DFR$^lEXsQ=*VYRGTNDk0oO2ahc5w|UGJDeWp2(}8Y^JTojTp{NcwhEn!tYBDy z59)#G0ZOe2mj{OTqJYzP+gk~4m{a{<0v7^Z17`nga0-|gxw5%gqP*@74U>w(KvBO$qV>y9+6&`F=RI+ zT_qy%Az?!xS!m#W_5{)^rTJBO6n8MV>8j%l)Xi$lXi00C8l5;C?M%H5bq!pAdDIfu z3`Y&yZp$L`(31Y9vj#*TEE-&Nwdj0tsJMZCq+Y2nZ@f~{$ci~CK!Wy-dxUqtzjUY? zkWX$#l+mK-!Pxcq{e(7^f}YbUhM3urwE;Pde!{i+h&1)o!25>*BkUF28FtJZEDaDqsUuwVj1*?Pyyk>mBP8+X?$f*cJ?Q zAN4wcyHqxOn=GQXMK{IgChe(~w1+f;evQFEV(kC0LYN!4@DS%9j}UYqOfajhE#5C$ z4>y8a;?9y9k^$nEA{;hPN8x5A5}qbF#B|uC_lHSoJ^X(S@nP(2(8`sfGiY~H_mV#n zKNGtl;h&Cuj&_TDBL9)Ssju+%s2G_?wWG4BHq>)!a->3JG5Ll30hG{-kk~hb*q2-fTc0k>o2)@V(;#pIUc#xwTg{uuza!Wz7$`Ww=kYw8U-&2>lrCdkqc=~l zNGjtaB2&q=p>Dzd0$=?qpWci3R{9S5`v}2e4>`APDyi=kk>__G>UNT>^MzL$+v*C2zMUV;IlVu{C$Si6qQA*28 z`iXCfjKcav2VxEp0(Q_=nDZpK)wpH3Z#a`UOR=`>JFJ0>cC>w|&WUT#Kv^8_5y1WB zy~ka99Tn`yU>;D%>@{sLc84czYH>^b48vW+VBLu_kc=ER73jhZ$F3zdsI{1#f#f=OMQ$|CZpfu$lO*q_(`K@OUoh$lQg z)Gn~Xf7LhC`^o*nmEqd!Y~i#xMmU>0o!|#xIM&+wSo5t5EEAv)@W|2zTzVa>t!-oe zxBY~k;wZl~*aEnmm8e=k#JP|Vr|9V;X$jnqn*w*q0X@?xaHm_&dCy(OXAvndx}xAM z8UQru6Ou=g0pMlcDJ~ReND9Q?z%$=l=plTtN&f~Dthsm;y@?WN86<<*n%;}1PFG0H zP0Ev1lhYIbVD7p-@`?IIy^GMJWuwyQyU042WmJx=0{hiUuvSl}gw#XGOB{#}g%3hkn z#E9r)IKLhTp5AkRKVNxob8jPW!prxU245J%F96@>?ckB{1M(!PAd6tvH6GrsWh0X4 z5$q-TMwG=l&m(T4MIaHeNpjn{SHGj=+?Q6OHBV)+IdGeb!}<8 z{qOuP+rG`(9 zX-Q|3Vw1KTA`%+L-;BwPnhvAvdtISs6hSImNK?fJkjtTb;6&6wcJP#Mn0FEIRa0H% zj-T!IA@8&S2xc+%=JrdrqqYs!F`x(DYaMBG*xk-{o+-Zh zf%;TE;4Y51{ZU0ir;zWSvwgzpR*`Y^f|%reI}weNqf--a_3sI#M@4=hKNJ z#zhQ{n5Q41Kd-;6_v&z6w05MXr#eKq@XiB01IdNKW$ng}DSEp;bYKy?cC4&(z`jRQ%r=Qv0yVdicL(YtDTDZ~keu-U8+JW}`a zxlpUoF0>{bDZVP1f_9a~%L|mda2>fpJwQ7a{KER_D|GAiw?G;2wZ2G4=>Da-uO6*= zuD+{&tv*W@5~J`w_-A+{+<-k&9FRq!&qPbWy?!fK#f+qCe4l#Ox{^VOUS-)``?5Nr zN@cd1)>K4PynXlbU5g4w#RgNUX>jE{^S)}iWs2>Nqp5Q&xbQNb75*o|HVn@cGHP}j z8)hrG;X(#@IUNTbdPp)_I$mC`EW$!~IeAp`RChaKTV%`V#Mmuyck0(Bd}2s9j(}Qi z+Ney{B!`UY21UZRaSdb8D9G|e6zPU&9^*?Cmr<2?I)a3I3D?*Fbs+G@7wb#&VxEKG zKbz?=*-OC3wLQ2^+yf4?#D3ZuvRtuz0jz`97DZi|<(Z{PoxvJoi?DBZ$lX1`>7yUG z8Z@P+FweQZLR6?(_-fb}ZX!A*&V{Pv{ScuNDR#r`xej}T_ftQHeNL%=M7kf{n*N*~M~5klBB>O(kKLkN!3xkKZU~;B4%3U6UThlYgsA)@Aw6sp zJ(46zw*Y&zg{lE|35z5w#2whRhe!`O1{l^-e3MGAER@d#h10_Dc_D}Uj7g*R1v30! z_pk7ma6Y)lC^HlJJS*>2e!o1wVu-0BRGqan*H+1E%j-mrZmt!sEO)A>t?z2!A~-2*VtcT2+57BH z?i_zA)CWn2eF}l1(oM1p3QF|???$fBL_&_gUu6I2F0o~C7wb<+EHs=o<|Oq=N=_xUY_9ga3b{Y85TI(`^4s@gTa_wvjZFy-CEOB+?tgCF4t<0fx=Xy8$n*<%; zY2Sfq&e{08!VHMk{}kSVup*PBfo!0Bj{KB7S#er<6x&AJfGYSR*h%%#-qJqT4b?vd zk~F2O)Sc9g0PpW2jY~a1?IE_}yy~>l3lxnevOH-Tx>I4W%Qa zfu;VXG!Ga=*Odxv3$_Mt1FNKekX9m&oJ>5%#;T43k#sarAWj2UjNoq4ZK#cbDyW}4 z>zU~GJMTIty0*EFxF5o3L!o}Q0u(m|fhp8WYBW@6te|$$4VVT@Pqr=lmN~>Eupzjk zWeFpps-O+>Ry0Gh2z?{f%Wo-vz{U~J$SU<4%@Xi!O44?KJY1RXTkTa1r_KPU?tW^e zI!yjTZX}lDpW_elctVe_R*95*U{5qgv>CEancP_>Etug;^Q?6)w&z%H0%>PP%@0*G z%&C<*rfgFoaP>^4)0NZAHuLtX?lr$yzOm^+#}MT{3Uwx{ea{0SY9V|R1g1T^9IEqV z{A%H0c&n%ob^*Q666sRKLX{7zA?iUjP@ev6Jwx=_m@aWC;1?`QoMV`8C^h_K$TD_L zDlooDd{n<)yf6Auq`013PwHlCQi(>&fl|LH4jCMx_<`IJdO$D$PHW$JfK=xGCphIy zw+A81)ZM0qJ!fb8a~o&fU6)+;g(ZRt@rux@kR^E#$3)wa)m%CX-i_F92&_AND! z?hLg{DMEVac&H$pi0na5i?Ssr(7iGZC?azeHx!4!yXZW*R8y^;4g8F5;6st6*GG&1 zHhu^FXMonqg{p*0nr52M)H{gQc$I3Wa*E?i&^ehR;e z|B8FWWHSyJUn`iy%vz>CvyfKMuc&P52t=>)gWkaU!0zBOYAt<}akB>QQ$9w>3@t!* zijz>Wv{-grVN$+>HDf;Bn@~gca6gelB;glTL4`q{D4UFCiiQH^tP~=2Lg2Pv?)%kq z19H_Zz~khu^N?$Vdx85SPiOCRpx-tP=z{sw0cs@`No@x1elzAc#8FnT8SKaGS@vtL zr|>|KhSDGhaZ20>?GMBwiK1BL!uylg!1rV`Wa2JpS?wL*(QeQU(|!t-E4|gn)n)1` z^%-@LJW4=}79T^*Cu~>;RY%23sa@Oxc^CQyNL*jixq(l;TK7~(*veS80b55}J=Q$C zawRBFn2HS*BP*_&)>NjMmxDUkQgh$3#+K+bIce7=_Xr?2b_m{~H?bJxNs`!6z`8Ht z-wD9|5D$~ILY=5w-b_^tJC(KSwc4-s!|H8{S`_1ob;fr~XqVW4PfIYenQTo%V5 za1A5vz3o-@c8+*Qp`EcRt=D0sowkgwE2=wOmufAxwgy(?r;Z-Zf$j(1iT?4y66z!1 zd(GkWf-JNrWC%|LZNFI5Py8*KE9)-Lm)qn8if$?cUP)wXdTM`%Xj!Rtq;8b{kiJ~M zUjKvcE1eHwMQUxDW{kQanMCZwB2@L2qvfZiA=E1oOS*s-_=vELpUd^*HgG+-{Xj36 z#6F-W(>Lk)^Z|MXMBa;GuFMSf4^9a>Af*b{|Yp*T?| zpbearzK|bMK2)86`iudDlKg>eOV%SzL=}D#yQy@_i)Fu|2SmF_7K!mzU8iR9k?XOoj>Bc z3NyjYsU@;Yv_wLod!-G*`#BwJN=zd2)tQ?k8Q9Q$e*kPc%NfL0HDEX3W7Az6PGguKsr1I@A)VnO5_2)tSnz zmAR$})9s3B73V6Jn|`kxXdY)CUOlO{SKUN=vNOju*KK#(JemG)gY_Abt$>J69NUk* z!AZeIJB*waACoLaJIS^xim{J~J!A{*Zr!|y-y-)#9|GcLU)TXuCnCmU#y7@lqs6#6 z>0Z(?N4rxPIL+0?OqHB_c(&@5Gvd1z`ZdcY~4ahuD^>l=p^Nuz{m#8m= z_*qbQSJzeNhkgB*P@8O3ZzVerC$YgQx8i4cIdG^(p_vjwoC4pA<-%0{Uwnw~E_^N^ z!YR(kc3@Ytt=SfAOSYI9$jqQ`!|XK+_5>7EYt#h#!5-i`oeFWG^?Z9_U`PQf@Y&)R zG!4|RTNRnA&#+JM6$CvAOiM-`Iur&1+#%rldHLtmlRQ`^7|8}6^Ho&B6mV4DLo<~jGA zzbs4(5r_}eRV&bZ=^@!k#THfOA0D@F)w?t);G)wFR;{hH8Jcpn9LVR{S+cRX=4*`C2qVlnfq-S=@Q%YOut2+B4r(WgllXTaJMTb;Igh^ZCjq zm0@tDD>SV(O{<)5j;PA1nqITba?|>wV=m;&D0h};jkn6*l3LG%ATPF#odY_sw){fj zT6m1;98fiOqKjm4$`Y&*(MUa4i|YLm8Brg{Y>%B0|9!%uM5*DrL2Ar1h77HYdyIFD zeGTUmev7{wQygiD*r9){y{2AHOi{+ks>B7z+mM~F0LD%vRpCG9UE(?IIpE26|L8pG zNVl&C%J(^&-`>pOu{X0nwSHk$f$v#N-MzX7)--Di+jP*Du6E=)M}w=R7<`)sQX{|- zRLwr-M#BuzJaj6wH?$=D5J?d41b6!{fWuiPpQ#jK6y93>N;6xl0`E0UcMG0t_NRJjhHvAo-5{*LetRja3|0u z_C@7IMSIR}QIcRI~{g9rY$D~kS2Zs6!ebd3o zbE$i!bF4GVl>{oyUY={722i0fArJ`8pbkMbs+-P(_=*;4QVFh>%i@DvHg})v$ZNqj zSRI~+oDkiV>_XQ_n*q5YNtKE>hq}SDYKJCYdsCC8X`u~iXKK^6r!~W1uDl4&)G?X_ z%{=up*ax&C))SA2mAD>Tp*$xuOEN^U;aH)JeNG1gQNF#Nht7KT7U1)+r1mSIQAL`Q zfJ4>MGy!O#6G4H#zB_2b05m$s{mmnFRPY zJ_&t?NW>Zqj=ymn(>gVYOYEsA(Se<+?`cMRWd%?lSaU4B3*u~$}8xJ(sL7=2Q=v?Ml zX)m=!*?+Xpb8K)7b(q0N_?b0meeX+g!iw37Y>9Rb>V0`9;!g2O{HK8_(4PK?*$cdM zfiDxbhx&#)g0_DgVuWw+S#bN9C-=xF!*25hc9Y1}90LXCTirWdj{buF80_O;NBH!~ z`tuN*e4^c}Wi^HB05KI0tL}rMZG&v7bPbvzDH693eH^Y4uJIl&mEX$m=Zkr`OE8m} zub4!Jr*F}zbT4W~unW{`w}ITit>BS>#h>dR5EvMI6Z|LLmwnEjxM0ugXToL_w@U(xFe=$`4zwiul zcZn&x9!PI{Fs#1iXb8z}*4minzu+BDnb_e0M&R7kInyLufd% zRJ2(<1f2<*EseZXF+f#~4I_5JcguTIyDfQ)80O^$jD`4MS^OluxQYCD2k zUl=oC8Mq4Xg8f5zMgAN5T(lST%6*|Csxig;zV+;Ny|m|A3oNmqKS8TUnxC2an#>i| z6$dL)P4i71Dih5@U|%zJjN8e1n8jc6V--P<76Xg3;^$JqH(KXe`0(> zq|L z@jP&a7zKQijbtCvNDRe4!G2Yg$u>!6OLilJgvn5=8xtJo|I$C-cMPhvDOWzsnQh$z zJTkA-lLI?|et{Bj(|JhWVJewROew@qO1Mn^3x1c-L@4Hy1uvf`3<K7Zs2e;S}LHo5Rcx7Wk4q$6OyfR{xO+P}N?oX3Uo=-(br-%@mJwa_9$_uL2DdnWDVO5sc=`TNc<(?di-xOWswCD zgY~ntnd&&=hH{mxH@HsKhI$GlzXLK(ivwP7rspjXbIiTA?lY>_Y&vgl`78JEsQa(mzJ>6mWx z0jiF=O`W8-(K{)evH+2!C*_4$iMWwL(q zdde-JM&3kd$(!VHQcRwJan=bK+EHpB(UB-2ej!8TKVgM7l&Hp*VZE@Ym;qasoY1n{NX z2r-Riw%v{uF696H_@r-kPP_y`rcanXZu%D!W_|)^%+8xVZYE;*`{RFjonBdk{@c+o z5=O-Tc69pqag*^skBATqev2W~r{zzW2gkNTJ3sjTms=49vhdCO)(#{U$#~xYdZ7ZD zXMf+?foKo|yhmuEY9LI!Z*4~#A+NN5w2&lZOtbf`ZKx9XVDDQ5B10aA-?!eMWi0S* z-?u+J#K`o?6Tf=@o>GxL;gmm*=6yA5c5`U|aqWMQ}f{{`K%@3be zMsmBqKCwSKtolrZbT^8ShZKLBxk BO(y^V literal 0 HcmV?d00001 diff --git a/Examples/Tutorial10Fluidlite/src/standalone/LICENSE b/Examples/Tutorial10Fluidlite/src/standalone/LICENSE new file mode 100644 index 0000000..10cc81d --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/LICENSE @@ -0,0 +1,15 @@ +FluidLite (c) 2016 Robin Lobel + +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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/Examples/Tutorial10Fluidlite/src/standalone/Makefile.os2 b/Examples/Tutorial10Fluidlite/src/standalone/Makefile.os2 new file mode 100644 index 0000000..2534abe --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/Makefile.os2 @@ -0,0 +1,53 @@ +# Makefile for OS/2 using Open Watcom + +INCLUDES = -I"../include" -I. -I"../stb" +CPPFLAGS = -DHAVE_CONFIG_H=1 -DHAVE_ALLOCA_H=1 -DNDEBUG +CPPFLAGS+= -DSF3_SUPPORT=SF3_STB_VORBIS + +CFLAGS = -zq -bt=os2 -bm -fp5 -fpi87 -mf -oeatxh -wx -wcd=303 -ei -j -zp8 +# -5s : Pentium stack calling conventions +CFLAGS+= -5s +DLLFLAGS=-bd + +DLLNAME=fluidlit.dll +EXPNAME=fluidlit.exp +LIBNAME=fluidlit.lib +LIBSTATIC=fluidlite_static.lib + +!ifeq target static +BLD_TARGET=$(LIBSTATIC) +!else +CPPFLAGS+= -DFLUIDSYNTH_DLL_EXPORTS +CFLAGS+= $(DLLFLAGS) +BLD_TARGET=$(DLLNAME) +!endif + +COMPILE=wcc386 $(CFLAGS) $(CPPFLAGS) $(INCLUDES) + +OBJ=fluid_sys.obj fluid_conv.obj fluid_hash.obj fluid_list.obj fluid_settings.obj & + stb_vorbis.obj & + fluid_defsfont.obj fluid_ramsfont.obj & + fluid_chorus.obj fluid_dsp_float.obj fluid_rev.obj & + fluid_chan.obj fluid_gen.obj fluid_mod.obj fluid_synth.obj fluid_tuning.obj fluid_voice.obj + +all: $(BLD_TARGET) + +.SUFFIXES: +.SUFFIXES: .obj .c .asm + +.c: ../stb + +$(DLLNAME): $(OBJ) + wlink NAM $@ OP q SYSTEM os2v2_dll INITINSTANCE TERMINSTANCE FIL {$(OBJ)} OPTION IMPF=$(EXPNAME) + wlib -q -b -n -c -pa -s -t -zld -ii -io -inn $(LIBNAME) +$(DLLNAME) + +$(LIBSTATIC): $(OBJ) + wlib -q -b -n -c -pa -s -t -zld -ii -io $@ $(OBJ) + +.c.obj: + $(COMPILE) -fo=$^@ $< + +distclean: clean .symbolic + rm -f *.err $(LIBSTATIC) $(DLLNAME) $(LIBNAME) $(EXPNAME) +clean: .symbolic + rm -f *.obj diff --git a/Examples/Tutorial10Fluidlite/src/standalone/README.md b/Examples/Tutorial10Fluidlite/src/standalone/README.md new file mode 100644 index 0000000..cd35bc1 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/README.md @@ -0,0 +1,65 @@ +# FluidLite + +[![License: LGPL-2.1](https://img.shields.io/badge/License-LGPL--2.1-brightgreen.svg)](https://opensource.org/licenses/LGPL-2.1) +[![Travis-CI Status](https://travis-ci.com/katyo/fluidlite.svg?branch=master)](https://travis-ci.com/katyo/fluidlite) + +FluidLite (c) 2016 Robin Lobel + +FluidLite is a very light version of FluidSynth +designed to be hardware, platform and external dependency independant. +It only uses standard C libraries. + +It also adds support for SF3 files (SF2 files compressed with ogg vorbis) +and an additional setting to remove the constraint of channel 9 (drums): +fluid_settings_setstr(settings, "synth.drums-channel.active", "no"); +you can still select bank 128 on any channel to use drum kits. + +FluidLite keeps very minimal functionnalities (settings and synth), +therefore MIDI file reading, realtime MIDI events and audio output must be +implemented externally. + +## Config + +By default SF3 support is disabled. To enable it use `-DENABLE_SF3=YES` with cmake. + +Alternatively it can be configured to use [stb_vorbis](https://github.com/nothings/stb) to decompress SF3 instead of Xiph's [libogg](https://github.com/xiph/ogg)/[libvorbis](https://github.com/xiph/vorbis). You can pass `-DSTB_VORBIS=YES` to cmake to do it. + +## Usage + +```c +#include +#include + +#include "fluidlite.h" + +#define SAMPLE_RATE 44100 +#define SAMPLE_SIZE sizeof(float) +#define NUM_FRAMES SAMPLE_RATE +#define NUM_CHANNELS 2 +#define NUM_SAMPLES (NUM_FRAMES * NUM_CHANNELS) + +int main() { + fluid_settings_t* settings = new_fluid_settings(); + fluid_synth_t* synth = new_fluid_synth(settings); + fluid_synth_sfload(synth, "soundfont.sf2", 1); + + float* buffer = calloc(SAMPLE_SIZE, NUM_SAMPLES); + + FILE* file = fopen("float32output.pcm", "wb"); + + fluid_synth_noteon(synth, 0, 60, 127); + fluid_synth_write_float(synth, NUM_FRAMES, buffer, 0, NUM_CHANNELS, buffer, 1, NUM_CHANNELS); + fwrite(buffer, SAMPLE_SIZE, NUM_SAMPLES, file); + + fluid_synth_noteoff(synth, 0, 60); + fluid_synth_write_float(synth, NUM_FRAMES, buffer, 0, NUM_CHANNELS, buffer, 1, NUM_CHANNELS); + fwrite(buffer, SAMPLE_SIZE, NUM_SAMPLES, file); + + fclose(file); + + free(buffer); + + delete_fluid_synth(synth); + delete_fluid_settings(settings); +} +``` diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.c new file mode 100644 index 0000000..4e73f6c --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.c @@ -0,0 +1,455 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.h new file mode 100644 index 0000000..f458a7a --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.h @@ -0,0 +1,114 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.c new file mode 100644 index 0000000..0f19995 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.c @@ -0,0 +1,606 @@ +/* + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.h new file mode 100644 index 0000000..e82b2ff --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.h @@ -0,0 +1,56 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_config.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_config.h new file mode 100644 index 0000000..faaf29d --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_config.h @@ -0,0 +1,38 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + + +/* Define to activate debugging message */ +#undef DEBUG + +/* Version number of package */ +#define VERSION "1.0.9" + +/* 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.c new file mode 100644 index 0000000..cf83550 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.c @@ -0,0 +1,320 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.h new file mode 100644 index 0000000..83bb21e --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.h @@ -0,0 +1,63 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.c new file mode 100644 index 0000000..0ba89f4 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.c @@ -0,0 +1,3419 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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, *z; + 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; + } + + + z = inst_zone; + + /* 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; + + p = sf->sample; + while (p) + { + sam = (SFSample *) (p->data); + + /* 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 (sam->loopend > sam->end || sam->loopstart >= sam->loopend + || sam->loopstart <= sam->start) + { /* loop is fowled?? (cluck cluck :) */ + /* can pad loop by 8 samples and ensure at least 4 for loop (2*8+4) */ + if ((sam->end - sam->start) >= 20) + { + sam->loopstart = sam->start + 8; + sam->loopend = sam->end - 8; + } + else + { /* loop is fowled, sample is tiny (can't pad 8 samples) */ + sam->loopstart = sam->start + 1; + 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.h new file mode 100644 index 0000000..b626935 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.h @@ -0,0 +1,603 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + + +/* 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_float.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_float.c new file mode 100644 index 0000000..38d9a60 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_float.c @@ -0,0 +1,685 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_simple.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_simple.c new file mode 100644 index 0000000..663f5a6 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_simple.c @@ -0,0 +1,120 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +/* Purpose: + * Low-level voice processing: + * + * - interpolates (obtains values between the samples of the original waveform data) + * - 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 + * + * + * This file does -not- generate an object file. + * Instead, it is #included in several places in fluid_voice.c. + * The motivation for this is + * - Calling it as a subroutine may be time consuming, especially with optimization off + * - The previous implementation as a macro was clumsy to handle + * + * + * Fluid_voice.c sets a couple of variables before #including this: + * - 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_start: Start processing at this output buffer index + * - dsp_end: End processing just before this output buffer index + * - dsp_a1: Coefficient for the filter + * - dsp_a2: same + * - dsp_b0: same + * - dsp_b1: same + * - dsp_b2: same + * - dsp_filter_flag: Set, the filter is needed (many sound fonts don't use + * the filter at all. If it is left at its default setting + * of roughly 20 kHz, there is no need to apply filterling.) + * - dsp_interp_method: Which interpolation method to use. + * - voice holds the voice structure + * + * Some variables are set and modified: + * - 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_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 + * + */ + + +/* Nonoptimized DSP loop */ +#warning "This code is meant for experiments only."; + +/* wave table interpolation */ +for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + + dsp_coeff = &interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_sample = (dsp_amp * + (dsp_coeff->a0 * dsp_data[dsp_phase_index] + + dsp_coeff->a1 * dsp_data[dsp_phase_index+1] + + dsp_coeff->a2 * dsp_data[dsp_phase_index+2] + + dsp_coeff->a3 * dsp_data[dsp_phase_index+3])); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_amp += dsp_amp_incr; + + /* filter */ + /* The filter is implemented in Direct-II form. */ + dsp_centernode = dsp_sample - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + dsp_sample = dsp_b0 * dsp_centernode + dsp_b1 * dsp_hist1 + dsp_b2 * dsp_hist2; + dsp_hist2 = dsp_hist1; + dsp_hist1 = dsp_centernode; + + /* pan */ + dsp_left_buf[dsp_i] += voice->amp_left * dsp_sample; + dsp_right_buf[dsp_i] += voice->amp_right * dsp_sample; + + /* reverb */ + if (dsp_reverb_buf){ + dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_sample; + } + + /* chorus */ + if (dsp_chorus_buf){ + dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_sample; + } +} + diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.c new file mode 100644 index 0000000..4f9bb02 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.c @@ -0,0 +1,149 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.h new file mode 100644 index 0000000..c35193a --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.h @@ -0,0 +1,44 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.c new file mode 100644 index 0000000..9094908 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.c @@ -0,0 +1,388 @@ +/* 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.h new file mode 100644 index 0000000..af0b5db --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.h @@ -0,0 +1,64 @@ +/* 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_init.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_init.c new file mode 100644 index 0000000..d145c7d --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_init.c @@ -0,0 +1,5 @@ +#ifdef __ANDROID__ +// To make upx work for Android +void _init(void) __attribute__((constructor)); +void _init(void) {} +#endif diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.c new file mode 100644 index 0000000..4d1e64f --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.c @@ -0,0 +1,257 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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-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/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.h new file mode 100644 index 0000000..6d5779b --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.h @@ -0,0 +1,61 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +#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/Examples/Tutorial10Fluidlite/src/standalone/fluid_midi.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_midi.h new file mode 100644 index 0000000..c31be78 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_midi.h @@ -0,0 +1,247 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.c new file mode 100644 index 0000000..21862f3 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.c @@ -0,0 +1,434 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.h new file mode 100644 index 0000000..31fb124 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.h @@ -0,0 +1,40 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_phase.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_phase.h new file mode 100644 index 0000000..5d808ca --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_phase.h @@ -0,0 +1,115 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.c new file mode 100644 index 0000000..5df20eb --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.c @@ -0,0 +1,1117 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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_ramsfont.h" +#include "fluid_sys.h" +#include "fluid_synth.h" + +/* thenumber of samples before the start and after the end */ +#define SAMPLE_LOOP_MARGIN 8 + +/* Prototypes */ +int fluid_rampreset_add_sample(fluid_rampreset_t* preset, fluid_sample_t* sample, int lokey, int hikey); +int fluid_rampreset_izone_set_gen(fluid_rampreset_t* preset, fluid_sample_t* sample, int gen_type, float value); +int fluid_rampreset_izone_set_loop(fluid_rampreset_t* preset, fluid_sample_t* sample, int on, float loopstart, float loopend); +int fluid_rampreset_remove_izone(fluid_rampreset_t* preset, fluid_sample_t* sample); +void fluid_rampreset_updatevoices(fluid_rampreset_t* preset, int gen_type, float val); + +/* + * fluid_ramsfont_create_sfont + */ +fluid_sfont_t* +fluid_ramsfont_create_sfont() +{ + fluid_sfont_t* sfont; + fluid_ramsfont_t* ramsfont; + + ramsfont = new_fluid_ramsfont(); + if (ramsfont == NULL) { + return NULL; + } + + sfont = FLUID_NEW(fluid_sfont_t); + if (sfont == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + sfont->data = ramsfont; + sfont->free = fluid_ramsfont_sfont_delete; + sfont->get_name = fluid_ramsfont_sfont_get_name; + sfont->get_preset = fluid_ramsfont_sfont_get_preset; + sfont->iteration_start = fluid_ramsfont_sfont_iteration_start; + sfont->iteration_next = fluid_ramsfont_sfont_iteration_next; + + return sfont; + +} + +/*************************************************************** + * + * PUBLIC INTERFACE + */ + +int fluid_ramsfont_sfont_delete(fluid_sfont_t* sfont) +{ + if (delete_fluid_ramsfont(sfont->data) != 0) + return -1; + FLUID_FREE(sfont); + return 0; +} + +char* fluid_ramsfont_sfont_get_name(fluid_sfont_t* sfont) +{ + return fluid_ramsfont_get_name((fluid_ramsfont_t*) sfont->data); +} + +fluid_preset_t* fluid_ramsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum) +{ + fluid_preset_t* preset; + fluid_rampreset_t* rampreset; + + rampreset = fluid_ramsfont_get_preset((fluid_ramsfont_t*) sfont->data, bank, prenum); + + if (rampreset == 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 = rampreset; + preset->free = fluid_rampreset_preset_delete; + preset->get_name = fluid_rampreset_preset_get_name; + preset->get_banknum = fluid_rampreset_preset_get_banknum; + preset->get_num = fluid_rampreset_preset_get_num; + preset->noteon = fluid_rampreset_preset_noteon; + preset->notify = NULL; + + return preset; +} + +void fluid_ramsfont_sfont_iteration_start(fluid_sfont_t* sfont) +{ + fluid_ramsfont_iteration_start((fluid_ramsfont_t*) sfont->data); +} + +int fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset) +{ + preset->free = fluid_rampreset_preset_delete; + preset->get_name = fluid_rampreset_preset_get_name; + preset->get_banknum = fluid_rampreset_preset_get_banknum; + preset->get_num = fluid_rampreset_preset_get_num; + preset->noteon = fluid_rampreset_preset_noteon; + preset->notify = NULL; + + return fluid_ramsfont_iteration_next((fluid_ramsfont_t*) sfont->data, preset); +} + +int fluid_rampreset_preset_delete(fluid_preset_t* preset) +{ + FLUID_FREE(preset); + +/* TODO: free modulators */ + + return 0; +} + +char* fluid_rampreset_preset_get_name(fluid_preset_t* preset) +{ + return fluid_rampreset_get_name((fluid_rampreset_t*) preset->data); +} + +int fluid_rampreset_preset_get_banknum(fluid_preset_t* preset) +{ + return fluid_rampreset_get_banknum((fluid_rampreset_t*) preset->data); +} + +int fluid_rampreset_preset_get_num(fluid_preset_t* preset) +{ + return fluid_rampreset_get_num((fluid_rampreset_t*) preset->data); +} + +int fluid_rampreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) +{ + return fluid_rampreset_noteon((fluid_rampreset_t*) preset->data, synth, chan, key, vel); +} + + + + +/*************************************************************** + * + * SFONT + */ + +/* + * new_fluid_ramsfont + */ +fluid_ramsfont_t* new_fluid_ramsfont() +{ + fluid_ramsfont_t* sfont; + + sfont = FLUID_NEW(fluid_ramsfont_t); + if (sfont == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + sfont->name[0] = 0; + sfont->sample = NULL; + sfont->preset = NULL; + + return sfont; +} + +/* + * delete_fluid_ramsfont + */ +int delete_fluid_ramsfont(fluid_ramsfont_t* sfont) +{ + fluid_list_t *list; + fluid_rampreset_t* preset; + + /* Check that no samples are currently used */ + for (list = sfont->sample; list; list = fluid_list_next(list)) { + fluid_sample_t* sam = (fluid_sample_t*) fluid_list_get(list); + if (fluid_sample_refcount(sam) != 0) { + return -1; + } + } + + for (list = sfont->sample; list; list = fluid_list_next(list)) { + /* in ram soundfonts, the samples hold their data : so we should free it ourselves */ + fluid_sample_t* sam = (fluid_sample_t*)fluid_list_get(list); + delete_fluid_ramsample(sam); + } + + if (sfont->sample) { + delete_fluid_list(sfont->sample); + } + + preset = sfont->preset; + while (preset != NULL) { + sfont->preset = preset->next; + delete_fluid_rampreset(preset); + preset = sfont->preset; + } + + FLUID_FREE(sfont); + return FLUID_OK; +} + +/* + * fluid_ramsfont_get_name + */ +char* fluid_ramsfont_get_name(fluid_ramsfont_t* sfont) +{ + return sfont->name; +} + +/* + * fluid_ramsfont_set_name + */ +int +fluid_ramsfont_set_name(fluid_ramsfont_t* sfont, char * name) +{ + FLUID_MEMCPY(sfont->name, name, 20); + return FLUID_OK; +} + + +/* fluid_ramsfont_add_preset + * + * Add a preset to the SoundFont + */ +int fluid_ramsfont_add_preset(fluid_ramsfont_t* sfont, fluid_rampreset_t* preset) +{ + fluid_rampreset_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_ramsfont_add_ramsample + */ +int fluid_ramsfont_add_izone(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int lokey, int hikey) +{ + /*- find or create a preset + - add it the sample using the fluid_rampreset_add_sample fucntion + - add the sample to the list of samples + */ + int err; + + fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); + if (preset == NULL) { + // Create it + preset = new_fluid_rampreset(sfont); + if (preset == NULL) { + return FLUID_FAILED; + } + + preset->bank = bank; + preset->num = num; + + err = fluid_rampreset_add_sample(preset, sample, lokey, hikey); + if (err != FLUID_OK) { + return FLUID_FAILED; + } + + // sort the preset + fluid_ramsfont_add_preset(sfont, preset); + + } else { + + // just add it + err = fluid_rampreset_add_sample(preset, sample, lokey, hikey); + if (err != FLUID_OK) { + return FLUID_FAILED; + } + } + + sfont->sample = fluid_list_append(sfont->sample, sample); + return FLUID_OK; +} + +int fluid_ramsfont_remove_izone(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample) { + int err; + fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); + if (preset == NULL) { + return FLUID_FAILED; + } + + // Fixed a crash bug : remove the sample from the sfont list after + // removing the izone (aschmitt august 2005) + err = fluid_rampreset_remove_izone(preset, sample); + if (err != FLUID_OK) + return err; + + // now we must remove the sample from sfont->sample + sfont->sample = fluid_list_remove(sfont->sample, sample); + + return FLUID_OK; +} + + +/* Note for version 2.0 : missing API fluid_ramsfont_izone_get_gen - Antoine Schmitt May 2003 */ +int fluid_ramsfont_izone_set_gen(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int gen_type, float value) { + + fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); + if (preset == NULL) { + return FLUID_FAILED; + } + + return fluid_rampreset_izone_set_gen(preset, sample, gen_type, value); +} + +/* Note for version 2.0 : missing API fluid_ramsfont_izone_get_loop - Antoine Schmitt May 2003 */ +int fluid_ramsfont_izone_set_loop(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int on, float loopstart, float loopend) { + fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); + if (preset == NULL) { + return FLUID_FAILED; + } + + return fluid_rampreset_izone_set_loop(preset, sample, on, loopstart, loopend); +} + +/* + * fluid_ramsfont_get_preset + */ +fluid_rampreset_t* fluid_ramsfont_get_preset(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num) +{ + fluid_rampreset_t* preset = sfont->preset; + while (preset != NULL) { + if ((preset->bank == bank) && ((preset->num == num))) { + return preset; + } + preset = preset->next; + } + return NULL; +} + +/* + * fluid_ramsfont_iteration_start + */ +void fluid_ramsfont_iteration_start(fluid_ramsfont_t* sfont) +{ + sfont->iter_cur = sfont->preset; +} + +/* + * fluid_ramsfont_iteration_next + */ +int fluid_ramsfont_iteration_next(fluid_ramsfont_t* sfont, fluid_preset_t* preset) +{ + if (sfont->iter_cur == NULL) { + return 0; + } + + preset->data = (void*) sfont->iter_cur; + sfont->iter_cur = fluid_rampreset_next(sfont->iter_cur); + return 1; +} + +/*************************************************************** + * + * PRESET + */ + +typedef struct _fluid_rampreset_voice_t fluid_rampreset_voice_t; +struct _fluid_rampreset_voice_t { + fluid_voice_t *voice; + unsigned int voiceID; +}; + +/* + * new_fluid_rampreset + */ +fluid_rampreset_t* +new_fluid_rampreset(fluid_ramsfont_t* sfont) +{ + fluid_rampreset_t* preset = FLUID_NEW(fluid_rampreset_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; + preset->presetvoices = NULL; + return preset; +} + +/* + * delete_fluid_rampreset + */ +int +delete_fluid_rampreset(fluid_rampreset_t* preset) +{ + int err = FLUID_OK; + fluid_preset_zone_t* zone; + fluid_rampreset_voice_t *data; + + 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; + } + + if (preset->presetvoices != NULL) { + fluid_list_t *tmp = preset->presetvoices, *next; + while (tmp) { + data = (fluid_rampreset_voice_t *)(tmp->data); + FLUID_FREE(data); + + next = tmp->next; + FLUID_FREE(tmp); + tmp = next; + } + } + preset->presetvoices = NULL; + + FLUID_FREE(preset); + return err; +} + +int +fluid_rampreset_get_banknum(fluid_rampreset_t* preset) +{ + return preset->bank; +} + +int +fluid_rampreset_get_num(fluid_rampreset_t* preset) +{ + return preset->num; +} + +char* +fluid_rampreset_get_name(fluid_rampreset_t* preset) +{ + return preset->name; +} + +/* + * fluid_rampreset_next + */ +fluid_rampreset_t* +fluid_rampreset_next(fluid_rampreset_t* preset) +{ + return preset->next; +} + + +/* + * fluid_rampreset_add_zone + */ +int +fluid_rampreset_add_zone(fluid_rampreset_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_rampreset_add_sample + */ +int fluid_rampreset_add_sample(fluid_rampreset_t* preset, fluid_sample_t* sample, int lokey, int hikey) +{ + /* create a new instrument zone, with the given sample */ + + /* one preset zone */ + if (preset->zone == NULL) { + fluid_preset_zone_t* zone; + zone = new_fluid_preset_zone(""); + if (zone == NULL) { + return FLUID_FAILED; + } + + /* its instrument */ + zone->inst = (fluid_inst_t*) new_fluid_inst(); + if (zone->inst == NULL) { + delete_fluid_preset_zone(zone); + return FLUID_FAILED; + } + + fluid_rampreset_add_zone(preset, zone); + } + + /* add an instrument zone for each sample */ + { + fluid_inst_t* inst = fluid_preset_zone_get_inst(preset->zone); + fluid_inst_zone_t* izone = new_fluid_inst_zone(""); + if (izone == NULL) { + return FLUID_FAILED; + } + + if (fluid_inst_add_zone(inst, izone) != FLUID_OK) { + delete_fluid_inst_zone(izone); + return FLUID_FAILED; + } + + izone->sample = sample; + izone->keylo = lokey; + izone->keyhi = hikey; + + // give the preset the name of the sample + FLUID_MEMCPY(preset->name, sample->name, 20); + } + + return FLUID_OK; +} + +fluid_inst_zone_t* fluid_rampreset_izoneforsample(fluid_rampreset_t* preset, fluid_sample_t* sample) +{ + fluid_inst_t* inst; + fluid_inst_zone_t* izone; + + if (preset->zone == NULL) return NULL; + + inst = fluid_preset_zone_get_inst(preset->zone); + izone = inst->zone; + while (izone) { + if (izone->sample == sample) + return izone; + izone = izone->next; + } + return NULL; +} + +int fluid_rampreset_izone_set_loop(fluid_rampreset_t* preset, fluid_sample_t* sample, + int on, float loopstart, float loopend) { + fluid_inst_zone_t* izone = fluid_rampreset_izoneforsample(preset, sample); + short coarse, fine; + + if (izone == NULL) + return FLUID_FAILED; + + if (!on) { + izone->gen[GEN_SAMPLEMODE].flags = GEN_SET; + izone->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED; + fluid_rampreset_updatevoices(preset, GEN_SAMPLEMODE, FLUID_UNLOOPED); + return FLUID_OK; +} + + /* NOTE : We should check that (sample->startloop + loopStart <= sample->endloop - loopend - 32) */ + + /* loopstart */ + if (loopstart > 32767. || loopstart < -32767.) { + coarse = (short)(loopstart/32768.); + fine = (short)(loopstart - (float)(coarse)*32768.); + } else { + coarse = 0; + fine = (short)loopstart; + } + izone->gen[GEN_STARTLOOPADDROFS].flags = GEN_SET; + izone->gen[GEN_STARTLOOPADDROFS].val = fine; + fluid_rampreset_updatevoices(preset, GEN_STARTLOOPADDROFS, fine); + if (coarse) { + izone->gen[GEN_STARTLOOPADDRCOARSEOFS].flags = GEN_SET; + izone->gen[GEN_STARTLOOPADDRCOARSEOFS].val = coarse; + } else { + izone->gen[GEN_STARTLOOPADDRCOARSEOFS].flags = GEN_UNUSED; + } + fluid_rampreset_updatevoices(preset, GEN_STARTLOOPADDRCOARSEOFS, coarse); + + /* loopend */ + if (loopend > 32767. || loopend < -32767.) { + coarse = (short)(loopend/32768.); + fine = (short)(loopend - (float)(coarse)*32768.); + } else { + coarse = 0; + fine = (short)loopend; + } + izone->gen[GEN_ENDLOOPADDROFS].flags = GEN_SET; + izone->gen[GEN_ENDLOOPADDROFS].val = fine; + fluid_rampreset_updatevoices(preset, GEN_ENDLOOPADDROFS, fine); + if (coarse) { + izone->gen[GEN_ENDLOOPADDRCOARSEOFS].flags = GEN_SET; + izone->gen[GEN_ENDLOOPADDRCOARSEOFS].val = coarse; + } else { + izone->gen[GEN_ENDLOOPADDRCOARSEOFS].flags = GEN_UNUSED; + } + fluid_rampreset_updatevoices(preset, GEN_ENDLOOPADDRCOARSEOFS, coarse); + + izone->gen[GEN_SAMPLEMODE].flags = GEN_SET; + izone->gen[GEN_SAMPLEMODE].val = FLUID_LOOP_DURING_RELEASE; + fluid_rampreset_updatevoices(preset, GEN_SAMPLEMODE, FLUID_LOOP_DURING_RELEASE); + + /* If the loop points are the whole samples, we are supposed to + copy the frames around in the margins (the start to the end margin and + the end to the start margin), but it works fine without this. Maybe some time + it will be needed (see SAMPLE_LOOP_MARGIN) -- Antoie Schmitt May 2003 */ + + return FLUID_OK; +} + +int fluid_rampreset_izone_set_gen(fluid_rampreset_t* preset, fluid_sample_t* sample, + int gen_type, float value) { + fluid_inst_zone_t* izone = fluid_rampreset_izoneforsample(preset, sample); + if (izone == NULL) + return FLUID_FAILED; + + izone->gen[gen_type].flags = GEN_SET; + izone->gen[gen_type].val = value; + + fluid_rampreset_updatevoices(preset, gen_type, value); + + return FLUID_OK; +} + +int fluid_rampreset_remove_izone(fluid_rampreset_t* preset, fluid_sample_t* sample) { + fluid_inst_t* inst; + fluid_inst_zone_t* izone, * prev; + int found = 0; + + if (preset->zone == NULL) return FLUID_FAILED; + inst = fluid_preset_zone_get_inst(preset->zone); + izone = inst->zone; + prev = NULL; + while (izone && !found) { + if (izone->sample == sample) { + if (prev == NULL) { + inst->zone = izone->next; + } else { + prev->next = izone->next; + } + izone->next = NULL; + delete_fluid_inst_zone(izone); + found = 1; + } else { + prev = izone; + izone = izone->next; + } + } + if (!found) + return FLUID_FAILED; + + // stop all the voices that use this sample, so that + // the sample can be cleared up + { + fluid_list_t *tmp = preset->presetvoices; + while (tmp) { + fluid_rampreset_voice_t *presetvoice = (fluid_rampreset_voice_t *)(tmp->data); + fluid_voice_t *voice = presetvoice->voice; + if (fluid_voice_is_playing(voice) && (fluid_voice_get_id(voice) == presetvoice->voiceID)) { + // still belongs to the preset + if (voice->sample == sample) { + // uses this sample : turn it off. + // our presetvoices struct will be cleaneup at the next update + fluid_voice_off(voice); + } + } + tmp = tmp->next; + } + } + return FLUID_OK; +} + +/* + * fluid_rampreset_remembervoice + */ +int +fluid_rampreset_remembervoice(fluid_rampreset_t* preset, fluid_voice_t* voice) { + /* stores the voice and the its ID in the preset for later update on gen_set */ + fluid_rampreset_voice_t *presetvoice = FLUID_NEW(fluid_rampreset_voice_t); + if (presetvoice == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + presetvoice->voice = voice; + presetvoice->voiceID = fluid_voice_get_id(voice); + + preset->presetvoices = fluid_list_append(preset->presetvoices, (void *)presetvoice); + if (preset->presetvoices == NULL) { + FLUID_FREE(presetvoice); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + return FLUID_OK; +} + +/* + * fluid_rampreset_updatevoice + */ +void +fluid_rampreset_updatevoices(fluid_rampreset_t* preset, int gen_type, float val) { + fluid_list_t *tmp = preset->presetvoices, *prev = NULL, *next; + + /* walk the presetvoice to update them if they are still active and ours. + If their ID has changed or their state is not playing, they are not ours, so we forget them + */ + while (tmp) { + fluid_rampreset_voice_t *presetvoice = (fluid_rampreset_voice_t *)(tmp->data); + fluid_voice_t *voice = presetvoice->voice; + if (!fluid_voice_is_playing(voice) || (fluid_voice_get_id(voice) != presetvoice->voiceID)) { + /* forget it */ + FLUID_FREE(presetvoice); + + /* unlink it */ + next = tmp->next; + FLUID_FREE(tmp); + if (prev) { + prev->next = next; + } else { + preset->presetvoices = next; + } + tmp = next; + + } else { + + /* update */ + fluid_voice_gen_set(voice, gen_type, val); + fluid_voice_update_param(voice, gen_type); + + /* next */ + prev = tmp; + tmp = tmp->next; + } + } +} + + +/* + * fluid_rampreset_noteon + */ +int +fluid_rampreset_noteon(fluid_rampreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) +{ + fluid_preset_zone_t *preset_zone; + fluid_inst_t* inst; + fluid_inst_zone_t *inst_zone, *global_inst_zone, *z; + 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; + + /* run thru all the zones of this preset */ + preset_zone = preset->zone; + 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; + } + + if (fluid_rampreset_remembervoice(preset, voice) != FLUID_OK) { + return FLUID_FAILED; + } + + z = inst_zone; + + /* 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 (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 { + /* 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; + + /* 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 (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; +} + + + +/*************************************************************** + * + * SAMPLE + */ + + + +/* + * fluid_sample_set_name + */ +int +fluid_sample_set_name(fluid_sample_t* sample, char * name) +{ + FLUID_MEMCPY(sample->name, name, 20); + return FLUID_OK; +} + +/* + * fluid_sample_set_sound_data + */ +int +fluid_sample_set_sound_data(fluid_sample_t* sample, short *data, unsigned int nbframes, short copy_data, int rootkey) +{ + /* 16 bit mono 44.1KHz data in */ + /* in all cases, the sample has ownership of the data : it will release it in the end */ + unsigned int storedNbFrames; + + /* in case we already have some data */ + if (sample->data != NULL) { + FLUID_FREE(sample->data); + } + + if (copy_data) { + + /* nbframes should be >= 48 (SoundFont specs) */ + storedNbFrames = nbframes; + if (storedNbFrames < 48) storedNbFrames = 48; + + sample->data = FLUID_MALLOC(storedNbFrames*2 + 4*SAMPLE_LOOP_MARGIN); + if (sample->data == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + FLUID_MEMSET(sample->data, 0, storedNbFrames*2 + 4*SAMPLE_LOOP_MARGIN); + FLUID_MEMCPY((char*)(sample->data) + 2*SAMPLE_LOOP_MARGIN, data, nbframes*2); + +#if 0 + /* this would do the fill of the margins */ + FLUID_MEMCPY((char*)(sample->data) + 2*SAMPLE_LOOP_MARGIN + storedNbFrames*2, (char*)data, 2*SAMPLE_LOOP_MARGIN); + FLUID_MEMCPY((char*)(sample->data), (char*)data + nbframes*2 - 2*SAMPLE_LOOP_MARGIN, 2*SAMPLE_LOOP_MARGIN); +#endif + + /* pointers */ + /* all from the start of data */ + sample->start = SAMPLE_LOOP_MARGIN; + sample->end = SAMPLE_LOOP_MARGIN + storedNbFrames; + } else { + /* we cannot assure the SAMPLE_LOOP_MARGIN */ + sample->data = data; + sample->start = 0; + sample->end = nbframes; + } + + /* only used as markers for the LOOP generators : set them on the first real frame */ + sample->loopstart = sample->start; + sample->loopend = sample->end; + + sample->samplerate = 44100; + sample->origpitch = rootkey; + sample->pitchadj = 0; + sample->sampletype = FLUID_SAMPLETYPE_MONO; + sample->valid = 1; + + return FLUID_OK; +} + +/* + * new_fluid_ramsample + */ +fluid_sample_t* +new_fluid_ramsample() +{ + /* same as new_fluid_sample. Only here so that it is exported */ + 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)); + + return sample; +} + +/* + * delete_fluid_ramsample + */ +int +delete_fluid_ramsample(fluid_sample_t* sample) +{ + /* same as delete_fluid_sample, plus frees the data */ + if (sample->data != NULL) { + FLUID_FREE(sample->data); + } + sample->data = NULL; + FLUID_FREE(sample); + return FLUID_OK; +} diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.h new file mode 100644 index 0000000..3c3cd1a --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.h @@ -0,0 +1,114 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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_RAMSFONT_H +#define _FLUID_RAMSFONT_H + + +#include "fluidlite.h" +#include "fluidsynth_priv.h" + +#include "fluid_defsfont.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* + + Public interface + + */ + +int fluid_ramsfont_sfont_delete(fluid_sfont_t* sfont); +char* fluid_ramsfont_sfont_get_name(fluid_sfont_t* sfont); +fluid_preset_t* fluid_ramsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); +void fluid_ramsfont_sfont_iteration_start(fluid_sfont_t* sfont); +int fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset); + + +int fluid_rampreset_preset_delete(fluid_preset_t* preset); +char* fluid_rampreset_preset_get_name(fluid_preset_t* preset); +int fluid_rampreset_preset_get_banknum(fluid_preset_t* preset); +int fluid_rampreset_preset_get_num(fluid_preset_t* preset); +int fluid_rampreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); + + +/* + * fluid_ramsfont_t + */ +struct _fluid_ramsfont_t +{ + char name[21]; /* the name of the soundfont */ + fluid_list_t* sample; /* the samples in this soundfont */ + fluid_rampreset_t* preset; /* the presets of this soundfont */ + + fluid_preset_t iter_preset; /* preset interface used in the iteration */ + fluid_rampreset_t* iter_cur; /* the current preset in the iteration */ +}; + +/* interface */ +fluid_ramsfont_t* new_fluid_ramsfont(void); +int delete_fluid_ramsfont(fluid_ramsfont_t* sfont); +char* fluid_ramsfont_get_name(fluid_ramsfont_t* sfont); +fluid_rampreset_t* fluid_ramsfont_get_preset(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int prenum); +void fluid_ramsfont_iteration_start(fluid_ramsfont_t* sfont); +int fluid_ramsfont_iteration_next(fluid_ramsfont_t* sfont, fluid_preset_t* preset); +/* specific */ + + + +/* + * fluid_preset_t + */ +struct _fluid_rampreset_t +{ + fluid_rampreset_t* next; + fluid_ramsfont_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_list_t *presetvoices; /* chained list of used voices */ +}; + +/* interface */ +fluid_rampreset_t* new_fluid_rampreset(fluid_ramsfont_t* sfont); +int delete_fluid_rampreset(fluid_rampreset_t* preset); +fluid_rampreset_t* fluid_rampreset_next(fluid_rampreset_t* preset); +char* fluid_rampreset_get_name(fluid_rampreset_t* preset); +int fluid_rampreset_get_banknum(fluid_rampreset_t* preset); +int fluid_rampreset_get_num(fluid_rampreset_t* preset); +int fluid_rampreset_noteon(fluid_rampreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); + + + + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUID_SFONT_H */ diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.c new file mode 100644 index 0000000..3f09b6d --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.c @@ -0,0 +1,561 @@ +/* + + 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.h new file mode 100644 index 0000000..1d0fd2e --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.h @@ -0,0 +1,67 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.c new file mode 100644 index 0000000..574f451 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.c @@ -0,0 +1,822 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.h new file mode 100644 index 0000000..1108948 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.h @@ -0,0 +1,55 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_sfont.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sfont.h new file mode 100644 index 0000000..74f33e2 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sfont.h @@ -0,0 +1,77 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.c new file mode 100644 index 0000000..06b3aaa --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.c @@ -0,0 +1,3550 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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); +} + +/* + * fluid_version + */ +void fluid_version(int *major, int *minor, int *micro) +{ + *major = FLUIDSYNTH_VERSION_MAJOR; + *minor = FLUIDSYNTH_VERSION_MINOR; + *micro = FLUIDSYNTH_VERSION_MICRO; +} + +/* + * fluid_version_str + */ +char* fluid_version_str(void) +{ + return FLUIDSYNTH_VERSION; +} + + +/* + * 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-save, 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_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/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.h new file mode 100644 index 0000000..ee1f08d --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.h @@ -0,0 +1,204 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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_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/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.c new file mode 100644 index 0000000..f5345a7 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.c @@ -0,0 +1,364 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.h new file mode 100644 index 0000000..d57a411 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.h @@ -0,0 +1,141 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.c new file mode 100644 index 0000000..ec1fc6e --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.c @@ -0,0 +1,144 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.h new file mode 100644 index 0000000..f1b90b3 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.h @@ -0,0 +1,65 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.c new file mode 100644 index 0000000..43f2cde --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.c @@ -0,0 +1,1996 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.h new file mode 100644 index 0000000..40f490c --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.h @@ -0,0 +1,291 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth.c b/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth.c new file mode 100644 index 0000000..a2623d7 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth.c @@ -0,0 +1,710 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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_config.h" + +#include +#include +#include + +#include "fluidsynth_priv.h" + +#if !defined(WIN32) && !defined(MACINTOSH) +#define _GNU_SOURCE +#include +#endif + +#if defined(WIN32) +#include +#endif + +#include "fluidsynth.h" + +#if defined(WIN32) && !defined(MINGW32) +#include "config_win32.h" +#endif + +#ifdef HAVE_SIGNAL_H +#include "signal.h" +#endif + +#include "fluid_lash.h" + +#ifndef WITH_MIDI +#define WITH_MIDI 1 +#endif + +/* default audio fragment count (if none specified) */ +#ifdef WIN32 +#define DEFAULT_FRAG_COUNT 32 +#else +#define DEFAULT_FRAG_COUNT 16 +#endif + +void print_usage(void); +void print_help(void); +void print_welcome(void); + +static fluid_cmd_handler_t* newclient(void* data, char* addr); + +/* + * the globals + */ +fluid_cmd_handler_t* cmd_handler = NULL; +int option_help = 0; /* set to 1 if "-o help" is specified */ + +/* + * support for the getopt function + */ +#if !defined(WIN32) && !defined(MACINTOSH) +#define GETOPT_SUPPORT 1 +int getopt(int argc, char * const argv[], const char *optstring); +extern char *optarg; +extern int optind, opterr, optopt; +#endif + + +/* process_o_cmd_line_option + * + * Purpose: + * Process a command line option -o setting=value, + * for example: -o synth.polyhony=16 + */ +void process_o_cmd_line_option(fluid_settings_t* settings, char* optarg){ + char* val; + for (val = optarg; *val != '\0'; val++) { + if (*val == '=') { + *val++ = 0; + break; + } + } + + /* did user request list of settings */ + if (strcmp (optarg, "help") == 0) + { + option_help = 1; + return; + } + + /* At this point: + * optarg => "synth.polyphony" + * val => "16" + */ + switch(fluid_settings_get_type(settings, optarg)){ + case FLUID_NUM_TYPE: + if (fluid_settings_setnum(settings, optarg, atof(val))){ + break; + }; + case FLUID_INT_TYPE: + if (fluid_settings_setint(settings, optarg, atoi(val))){ + break; + }; + case FLUID_STR_TYPE: + if (fluid_settings_setstr(settings, optarg, val)){ + break; + }; + default: + fprintf (stderr, "Settings argument on command line: Failed to set \"%s\" to \"%s\".\n" + "Most likely the parameter \"%s\" does not exist.\n", optarg, val, optarg); + } +} + +static void +print_pretty_int (int i) +{ + if (i == INT_MAX) printf ("MAXINT"); + else if (i == INT_MIN) printf ("MININT"); + else printf ("%d", i); +} + +/* fluid_settings_foreach function for displaying option help "-o help" */ +static void +settings_foreach_func (void *data, char *name, int type) +{ + fluid_settings_t *settings = (fluid_settings_t *)data; + double dmin, dmax, ddef; + int imin, imax, idef; + char *defstr; + + switch (type) + { + case FLUID_NUM_TYPE: + fluid_settings_getnum_range (settings, name, &dmin, &dmax); + ddef = fluid_settings_getnum_default (settings, name); + printf ("%-24s FLOAT [min=%0.3f, max=%0.3f, def=%0.3f]\n", + name, dmin, dmax, ddef); + break; + case FLUID_INT_TYPE: + fluid_settings_getint_range (settings, name, &imin, &imax); + idef = fluid_settings_getint_default (settings, name); + printf ("%-24s INT [min=", name); + print_pretty_int (imin); + printf (", max="); + print_pretty_int (imax); + printf (", def="); + print_pretty_int (idef); + printf ("]\n"); + break; + case FLUID_STR_TYPE: + defstr = fluid_settings_getstr_default (settings, name); + printf ("%-24s STR", name); + if (defstr) printf (" [def='%s']\n", defstr); + else printf ("\n"); + break; + case FLUID_SET_TYPE: + printf ("%-24s SET\n", name); + break; + } +} + + +#ifdef HAVE_SIGNAL_H +/* + * handle_signal + */ +void handle_signal(int sig_num) +{ +} +#endif + + +/* + * main + */ +int main(int argc, char** argv) +{ + fluid_settings_t* settings; + int arg1 = 1; + char buf[512]; + int c, i, fragcount = DEFAULT_FRAG_COUNT; + int interactive = 1; + int midi_in = 1; + fluid_player_t* player = NULL; + fluid_midi_router_t* router = NULL; + fluid_midi_driver_t* mdriver = NULL; + fluid_audio_driver_t* adriver = NULL; + fluid_synth_t* synth = NULL; + fluid_server_t* server = NULL; + char* midi_id = NULL; + char* midi_driver = NULL; + char* midi_device = NULL; + char* config_file = NULL; + int audio_groups = 0; + int audio_channels = 0; + int with_server = 0; + int dump = 0; + int connect_lash = 1; + char *optchars = "a:C:c:df:G:g:hijK:L:lm:no:p:R:r:sVvz:"; +#ifdef LASH_ENABLED + int enabled_lash = 0; /* set to TRUE if lash gets enabled */ + fluid_lash_args_t *lash_args; + + lash_args = fluid_lash_extract_args (&argc, &argv); +#endif + + settings = new_fluid_settings(); + +#ifdef GETOPT_SUPPORT /* pre section of GETOPT supported argument handling */ + opterr = 0; + + while (1) { + int option_index = 0; + + static struct option long_options[] = { + {"audio-bufcount", 1, 0, 'c'}, + {"audio-bufsize", 1, 0, 'z'}, + {"audio-channels", 1, 0, 'L'}, + {"audio-driver", 1, 0, 'a'}, + {"audio-groups", 1, 0, 'G'}, + {"chorus", 1, 0, 'C'}, + {"connect-jack-outputs", 0, 0, 'j'}, + {"disable-lash", 0, 0, 'l'}, + {"dump", 0, 0, 'd'}, + {"gain", 1, 0, 'g'}, + {"help", 0, 0, 'h'}, + {"load-config", 1, 0, 'f'}, + {"midi-channels", 1, 0, 'K'}, + {"midi-driver", 1, 0, 'm'}, + {"no-midi-in", 0, 0, 'n'}, + {"no-shell", 0, 0, 'i'}, + {"option", 1, 0, 'o'}, + {"portname", 1, 0, 'p'}, + {"reverb", 1, 0, 'R'}, + {"sample-rate", 1, 0, 'r'}, + {"server", 0, 0, 's'}, + {"verbose", 0, 0, 'v'}, + {"version", 0, 0, 'V'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, optchars, long_options, &option_index); + if (c == -1) { + break; + } +#else /* "pre" section to non getopt argument handling */ + for (i = 1; i < argc; i++) { + char *optarg; + + /* Skip non switch arguments (assume they are file names) */ + if ((argv[i][0] != '-') || (argv[i][1] == '\0')) break; + + c = argv[i][1]; + + optarg = strchr (optchars, c); /* find the option character in optchars */ + if (optarg && optarg[1] == ':') /* colon follows if switch argument expected */ + { + if (++i >= argc) + { + printf ("Option -%c requires an argument\n", c); + print_usage(); + exit(0); + } + else + { + optarg = argv[i]; + if (optarg[0] == '-') + { + printf ("Expected argument to option -%c found switch instead\n", c); + print_usage(); + exit(0); + } + } + } + else optarg = ""; +#endif + + switch (c) { +#ifdef GETOPT_SUPPORT + case 0: /* shouldn't normally happen, a long option's flag is set to NULL */ + printf ("option %s", long_options[option_index].name); + if (optarg) { + printf (" with arg %s", optarg); + } + printf ("\n"); + break; +#endif + case 'a': + fluid_settings_setstr(settings, "audio.driver", optarg); + break; + case 'C': + if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) { + fluid_settings_setstr(settings, "synth.chorus.active", "no"); + } else { + fluid_settings_setstr(settings, "synth.chorus.active", "yes"); + } + break; + case 'c': + fluid_settings_setint(settings, "audio.periods", atoi(optarg)); + break; + case 'd': + fluid_settings_setstr(settings, "synth.dump", "yes"); + dump = 1; + break; + case 'f': + config_file = optarg; + break; + case 'G': + audio_groups = atoi(optarg); + break; + case 'g': + fluid_settings_setnum(settings, "synth.gain", atof(optarg)); + break; + case 'h': + print_help(); + break; + case 'i': + interactive = 0; + break; + case 'j': + fluid_settings_setint(settings, "audio.jack.autoconnect", 1); + break; + case 'K': + fluid_settings_setint(settings, "synth.midi-channels", atoi(optarg)); + break; + case 'L': + audio_channels = atoi(optarg); + fluid_settings_setint(settings, "synth.audio-channels", audio_channels); + break; + case 'l': /* disable LASH */ + connect_lash = 0; + break; + case 'm': + fluid_settings_setstr(settings, "midi.driver", optarg); + break; + case 'n': + midi_in = 0; + break; + case 'o': + process_o_cmd_line_option(settings, optarg); + break; + case 'p' : + fluid_settings_setstr(settings, "midi.portname", optarg); + break; + case 'R': + if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) { + fluid_settings_setstr(settings, "synth.reverb.active", "no"); + } else { + fluid_settings_setstr(settings, "synth.reverb.active", "yes"); + } + break; + case 'r': + fluid_settings_setnum(settings, "synth.sample-rate", atof(optarg)); + break; + case 's': + with_server = 1; + break; + case 'V': + printf("FluidSynth %s\n", VERSION); + exit (0); + break; + case 'v': + fluid_settings_setstr(settings, "synth.verbose", "yes"); + break; + case 'z': + fluid_settings_setint(settings, "audio.period-size", atoi(optarg)); + break; +#ifdef GETOPT_SUPPORT + case '?': + printf ("Unknown option %c\n", optopt); + print_usage(); + exit(0); + break; + default: + printf ("?? getopt returned character code 0%o ??\n", c); + break; +#else /* Non getopt default case */ + default: + printf ("Unknown switch '%c'\n", c); + print_usage(); + exit(0); + break; +#endif + } /* end of switch statement */ + } /* end of loop */ + +#ifdef GETOPT_SUPPORT + arg1 = optind; +#else + arg1 = i; +#endif + + /* option help requested? "-o help" */ + if (option_help) + { + print_welcome (); + printf ("FluidSynth settings:\n"); + fluid_settings_foreach (settings, settings, settings_foreach_func); + exit (0); + } + +#ifdef WIN32 + SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); +#endif + +#ifdef LASH_ENABLED + /* connect to the lash server */ + if (connect_lash) + { + enabled_lash = fluid_lash_connect (lash_args); + fluid_settings_setint (settings, "lash.enable", enabled_lash ? 1 : 0); + } +#endif + + /* The 'groups' setting is only relevant for LADSPA operation + * If not given, set number groups to number of audio channels, because + * they are the same (there is nothing between synth output and 'sound card') + */ + if ((audio_groups == 0) && (audio_channels != 0)) { + audio_groups = audio_channels; + } + fluid_settings_setint(settings, "synth.audio-groups", audio_groups); + + /* create the synthesizer */ + synth = new_fluid_synth(settings); + if (synth == NULL) { + fprintf(stderr, "Failed to create the synthesizer\n"); + exit(-1); + } + + cmd_handler = new_fluid_cmd_handler(synth); + if (cmd_handler == NULL) { + fprintf(stderr, "Failed to create the command handler\n"); + goto cleanup; + } + + /* try to load the user or system configuration */ + if (config_file != NULL) { + fluid_source(cmd_handler, config_file); + } else if (fluid_get_userconf(buf, 512) != NULL) { + fluid_source(cmd_handler, buf); + } else if (fluid_get_sysconf(buf, 512) != NULL) { + fluid_source(cmd_handler, buf); + } + + /* load the soundfonts (check that all non options are SoundFont or MIDI files) */ + for (i = arg1; i < argc; i++) { + if (fluid_is_soundfont(argv[i])) + { + if (fluid_synth_sfload(synth, argv[i], 1) == -1) + fprintf(stderr, "Failed to load the SoundFont %s\n", argv[i]); + } + else if (!fluid_is_midifile(argv[i])) + fprintf (stderr, "Parameter '%s' not a SoundFont or MIDI file or error occurred identifying it.\n", + argv[i]); + } + +#ifdef HAVE_SIGNAL_H +/* signal(SIGINT, handle_signal); */ +#endif + + /* start the synthesis thread */ + adriver = new_fluid_audio_driver(settings, synth); + if (adriver == NULL) { + fprintf(stderr, "Failed to create the audio driver\n"); + goto cleanup; + } + + + /* start the midi router and link it to the synth */ +#if WITH_MIDI + if (midi_in) { + /* In dump mode, text output is generated for events going into and out of the router. + * The example dump functions are put into the chain before and after the router.. + */ + + router = new_fluid_midi_router( + settings, + dump ? fluid_midi_dump_postrouter : fluid_synth_handle_midi_event, + (void*)synth); + + if (router == NULL) { + fprintf(stderr, "Failed to create the MIDI input router; no MIDI input\n" + "will be available. You can access the synthesizer \n" + "through the console.\n"); + } else { + fluid_synth_set_midi_router(synth, router); /* Fixme, needed for command handler */ + mdriver = new_fluid_midi_driver( + settings, + dump ? fluid_midi_dump_prerouter : fluid_midi_router_handle_midi_event, + (void*) router); + if (mdriver == NULL) { + fprintf(stderr, "Failed to create the MIDI thread; no MIDI input\n" + "will be available. You can access the synthesizer \n" + "through the console.\n"); + } + } + } +#endif + + /* play the midi files, if any */ + for (i = arg1; i < argc; i++) { + if ((argv[i][0] != '-') && fluid_is_midifile(argv[i])) { + + if (player == NULL) { + player = new_fluid_player(synth); + if (player == NULL) { + fprintf(stderr, "Failed to create the midifile player.\n" + "Continuing without a player.\n"); + break; + } + } + + fluid_player_add(player, argv[i]); + } + } + + if (player != NULL) { + fluid_player_play(player); + } + + /* run the server, if requested */ +#if !defined(MACINTOSH) && !defined(WIN32) + if (with_server) { + server = new_fluid_server(settings, newclient, synth); + if (server == NULL) { + fprintf(stderr, "Failed to create the server.\n" + "Continuing without it.\n"); + } + } +#endif + + +#ifdef LASH_ENABLED + if (enabled_lash) + fluid_lash_create_thread (synth); +#endif + + /* run the shell */ + if (interactive) { + print_welcome(); + + printf ("Type 'help' for information on commands and 'help help' for help topics.\n\n"); + + /* In dump mode we set the prompt to "". The UI cannot easily + * handle lines, which don't end with CR. Changing the prompt + * cannot be done through a command, because the current shell + * does not handle empty arguments. The ordinary case is dump == + * 0. + */ + fluid_settings_setstr(settings, "shell.prompt", dump ? "" : "> "); + fluid_usershell(settings, cmd_handler); + } + + cleanup: + +#if !defined(MACINTOSH) && !defined(WIN32) + if (server != NULL) { + /* if the user typed 'quit' in the shell, kill the server */ + if (!interactive) { + fluid_server_join(server); + } + delete_fluid_server(server); + } +#endif + + if (cmd_handler != NULL) { + delete_fluid_cmd_handler(cmd_handler); + } + + if (player != NULL) { + /* if the user typed 'quit' in the shell, stop the player */ + if (interactive) { + fluid_player_stop(player); + } + fluid_player_join(player); + delete_fluid_player(player); + } + + if (router) { +#if WITH_MIDI + if (mdriver) { + delete_fluid_midi_driver(mdriver); + } + delete_fluid_midi_router(router); +#endif + } + + if (adriver) { + delete_fluid_audio_driver(adriver); + } + + if (synth) { + delete_fluid_synth(synth); + } + + if (settings) { + delete_fluid_settings(settings); + } + + return 0; +} + +static fluid_cmd_handler_t* newclient(void* data, char* addr) +{ + fluid_synth_t* synth = (fluid_synth_t*) data; + return new_fluid_cmd_handler(synth); +} + + +/* + * print_usage + */ +void +print_usage() +{ + print_welcome (); + fprintf(stderr, "Usage: fluidsynth [options] [soundfonts]\n"); + fprintf(stderr, "Try -h for help.\n"); + exit(0); +} + +/* + * print_welcome + */ +void +print_welcome() +{ + printf("FluidSynth version %s\n" + "Copyright (C) 2000-2006 Peter Hanappe and others.\n" + "Distributed under the LGPL license.\n" + "SoundFont(R) is a registered trademark of E-mu Systems, Inc.\n\n", + FLUIDSYNTH_VERSION); +} + +/* + * print_help + */ +void +print_help() +{ + print_welcome (); + printf("Usage: \n"); + printf(" fluidsynth [options] [soundfonts] [midifiles]\n"); + printf("Possible options:\n"); + printf(" -a, --audio-driver=[label]\n" + " The audio driver [alsa,jack,oss,dsound,...]\n"); + printf(" -C, --chorus\n" + " Turn the chorus on or off [0|1|yes|no, default = on]\n"); + printf(" -c, --audio-bufcount=[count]\n" + " Number of audio buffers\n"); + printf(" -d, --dump\n" + " Dump incoming and outgoing MIDI events to stdout\n"); + printf(" -f, --load-config\n" + " Load command configuration file (shell commands)\n"); + printf(" -G, --audio-groups\n" + " Defines the number of LADSPA audio nodes\n"); + printf(" -g, --gain\n" + " Set the master gain [0 < gain < 10, default = 0.2]\n"); + printf(" -h, --help\n" + " Print out this help summary\n"); + printf(" -i, --no-shell\n" + " Don't read commands from the shell [default = yes]\n"); + printf(" -j, --connect-jack-outputs\n" + " Attempt to connect the jack outputs to the physical ports\n"); + printf(" -K, --midi-channels=[num]\n" + " The number of midi channels [default = 16]\n"); + printf(" -L, --audio-channels=[num]\n" + " The number of stereo audio channels [default = 1]\n"); +#ifdef LASH_ENABLED + printf(" -l, --disable-lash\n" + " Don't connect to LASH server\n"); +#endif + printf(" -m, --midi-driver=[label]\n" + " The name of the midi driver to use [oss,alsa,alsa_seq,...]\n"); + printf(" -n, --no-midi-in\n" + " Don't create a midi driver to read MIDI input events [default = yes]\n"); + printf(" -p, --portname=[label]\n" + " Set MIDI port name (alsa_seq, coremidi drivers)\n"); + printf(" -o\n" + " Define a setting, -o name=value (\"-o help\" to dump current values)\n"); + printf(" -R, --reverb\n" + " Turn the reverb on or off [0|1|yes|no, default = on]\n"); + printf(" -r, --sample-rate\n" + " Set the sample rate\n"); + printf(" -s, --server\n" + " Start FluidSynth as a server process\n"); + printf(" -V, --version\n" + " Show version of program\n"); + printf(" -v, --verbose\n" + " Print out verbose messages about midi events\n"); + printf(" -z, --audio-bufsize=[size]\n" + " Size of each audio buffer\n"); + exit(0); +} diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth_priv.h b/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth_priv.h new file mode 100644 index 0000000..2604326 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth_priv.h @@ -0,0 +1,228 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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_PRIV_H +#define _FLUIDSYNTH_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 /* _FLUIDSYNTH_PRIV_H */ diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidlite.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidlite.h new file mode 100644 index 0000000..efe8633 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidlite.h @@ -0,0 +1,102 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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_H +#define _FLUIDSYNTH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* +#if defined(WIN32) +#if defined(FLUIDSYNTH_DLL_EXPORTS) +#define FLUIDSYNTH_API __declspec(dllexport) +#elif defined(FLUIDSYNTH_NOT_A_DLL) +#define FLUIDSYNTH_API +#else +#define FLUIDSYNTH_API __declspec(dllimport) +#endif + +#elif defined(MACOS9) +#define FLUIDSYNTH_API __declspec(export) + +#else +#define FLUIDSYNTH_API +#endif +*/ +#if defined(__WATCOMC__) && defined(FLUIDSYNTH_DLL_EXPORTS) +#define FLUIDSYNTH_API __declspec(dllexport) /* watcom needs the dllexport */ +#else +#define FLUIDSYNTH_API +#endif + +/** + * @file fluidsynth.h + * @brief FluidSynth is a real-time synthesizer designed for SoundFont(R) files. + * + * This is the header of the fluidsynth 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 "fluidsynth/types.h" +#include "fluidsynth/settings.h" +#include "fluidsynth/synth.h" +#include "fluidsynth/sfont.h" +#include "fluidsynth/ramsfont.h" +#include "fluidsynth/log.h" +#include "fluidsynth/misc.h" +#include "fluidsynth/mod.h" +#include "fluidsynth/gen.h" +#include "fluidsynth/voice.h" +#include "fluidsynth/version.h" + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_H */ diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/gen.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/gen.h new file mode 100644 index 0000000..055ea75 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/gen.h @@ -0,0 +1,135 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/log.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/log.h new file mode 100644 index 0000000..51fdebe --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/log.h @@ -0,0 +1,83 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/misc.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/misc.h new file mode 100644 index 0000000..b7d4fdf --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/misc.h @@ -0,0 +1,65 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/mod.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/mod.h new file mode 100644 index 0000000..5fe86cd --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/mod.h @@ -0,0 +1,112 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/ramsfont.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/ramsfont.h new file mode 100644 index 0000000..75a8d67 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/ramsfont.h @@ -0,0 +1,113 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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_RAMSFONT_H +#define _FLUIDSYNTH_RAMSFONT_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************************************************************************/ +/********************************************************************************/ +/* ram soundfonts: + October 2002 - Antoine Schmitt + + ram soundfonts live in ram. The samples are loaded from files + or from RAM. A minimal API manages a soundFont structure, + with presets, each preset having only one preset-zone, which + instrument has potentially many instrument-zones. No global + zones, and nor generator nor modulator other than the default + ones are permitted. This may be extensible in the future. +*/ +/********************************************************************************/ +/********************************************************************************/ + +/* + We are not using the sfloader protocol, as we need more arguments + than what it provides. +*/ + +/** Creates a fluid_sfont_t wrapping an fluid_ramsfont_t */ +FLUIDSYNTH_API fluid_sfont_t* fluid_ramsfont_create_sfont(void); + +/*********************** + * ramsfont specific API + ***********************/ +FLUIDSYNTH_API int fluid_ramsfont_set_name(fluid_ramsfont_t* sfont, char * name); + +/* Creates one instrument zone for the sample inside the preset defined + * by bank/num + * \returns 0 if success + */ +FLUIDSYNTH_API +int fluid_ramsfont_add_izone(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int lokey, int hikey); + +/* Removes the instrument zone corresponding to bank/num and to the sample + * \returns 0 if success + */ +FLUIDSYNTH_API +int fluid_ramsfont_remove_izone(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample); + +/* Sets a generator on an instrument zone + * \returns 0 if success + */ +FLUIDSYNTH_API +int fluid_ramsfont_izone_set_gen(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int gen_type, float value); + +/* Utility : sets the loop start/end values + * \on = 0 or 1; if 0, loopstart and loopend are not used + * \loopstart and loopend are floats, in frames + * \loopstart is counted from frame 0 + * \loopend is counted from the last frame, thus is < 0 + * \returns 0 if success + */ +FLUIDSYNTH_API +int fluid_ramsfont_izone_set_loop(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int on, float loopstart, float loopend); + +/*************************************** + * sample_t specific API for ramsfont + ***************************************/ +FLUIDSYNTH_API fluid_sample_t* new_fluid_ramsample(void); +FLUIDSYNTH_API int delete_fluid_ramsample(fluid_sample_t* sample); +FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t* sample, char * name); + +/* Sets the sound data of the sample + * Warning : if copy_data is FALSE, data should have 8 unused frames at start + * and 8 unused frames at the end. + */ +FLUIDSYNTH_API +int fluid_sample_set_sound_data(fluid_sample_t* sample, short *data, + unsigned int nbframes, short copy_data, int rootkey); + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_RAMSFONT_H */ diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/settings.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/settings.h new file mode 100644 index 0000000..6ffaab2 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/settings.h @@ -0,0 +1,202 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/sfont.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/sfont.h new file mode 100644 index 0000000..1d5120b --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/sfont.h @@ -0,0 +1,252 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/synth.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/synth.h new file mode 100644 index 0000000..9462725 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/synth.h @@ -0,0 +1,713 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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); + + + /* + * + * 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 +// /** 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); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SYNTH_H */ diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/types.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/types.h new file mode 100644 index 0000000..34f3ec7 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/types.h @@ -0,0 +1,67 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/version.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/version.h new file mode 100644 index 0000000..69d621b --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/version.h @@ -0,0 +1,44 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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_VERSION_H +#define _FLUIDSYNTH_VERSION_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define FLUIDSYNTH_VERSION "1.2.1" +#define FLUIDSYNTH_VERSION_MAJOR 1 +#define FLUIDSYNTH_VERSION_MINOR 2 +#define FLUIDSYNTH_VERSION_MICRO 1 + + +FLUIDSYNTH_API void fluid_version(int *major, int *minor, int *micro); + +FLUIDSYNTH_API char* fluid_version_str(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_VERSION_H */ diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/voice.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/voice.h new file mode 100644 index 0000000..13b6b80 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/voice.h @@ -0,0 +1,97 @@ +/* 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 Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * 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/Examples/Tutorial10Fluidlite/src/standalone/main.cbp b/Examples/Tutorial10Fluidlite/src/standalone/main.cbp new file mode 100644 index 0000000..7b4481a --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/main.cbp @@ -0,0 +1,135 @@ + + + + + + diff --git a/Examples/Tutorial10Fluidlite/src/standalone/stb/stb_vorbis.c b/Examples/Tutorial10Fluidlite/src/standalone/stb/stb_vorbis.c new file mode 100644 index 0000000..8bd7e90 --- /dev/null +++ b/Examples/Tutorial10Fluidlite/src/standalone/stb/stb_vorbis.c @@ -0,0 +1,5480 @@ +// Ogg Vorbis audio decoder - v1.20 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking implementation +// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, +// Elias Software, Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// See end of file for license information. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster github:alxprd +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// github:manxorist saga musix github:infatum +// Timur Gagiev Maxwell Koo Peter Waller +// github:audinowho Dougall Johnson David Reid +// github:Clownacy Pedro J. Estebanez Remi Verschelde +// +// Partial history: +// 1.20 - 2020-07-11 - several small fixes +// 1.19 - 2020-02-05 - warnings +// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. +// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) +// 1.16 - 2019-03-04 - fix warnings +// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found +// 1.14 - 2018-02-11 - delete bogus dealloca usage +// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) +// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files +// 1.11 - 2017-07-23 - fix MinGW compilation +// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory +// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant +// 1.04 - 2014-08-27 - fix missing const-correct case in API +// 1.03 - 2014-08-07 - warning fixes +// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char * datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Moreover, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern int stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, + VORBIS_ogg_skeleton_not_supported +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT + #include + #include + #include + #include +#else // STB_VORBIS_NO_CRT + #define NULL 0 + #define malloc(s) 0 + #define free(s) ((void) 0) + #define realloc(s) 0 +#endif // STB_VORBIS_NO_CRT + +/* we need alloca() regardless of STB_VORBIS_NO_CRT, + * because there is not a corresponding 'dealloca' */ +#if !defined(alloca) +# if defined(HAVE_ALLOCA_H) +# include +# elif defined(__GNUC__) +# define alloca __builtin_alloca +# elif defined(_MSC_VER) +# include +# define alloca _alloca +# elif defined(__WATCOMC__) +# include +# endif +#endif + +#include + +#ifndef STB_FORCEINLINE + #if defined(_MSC_VER) + #define STB_FORCEINLINE __forceinline + #elif defined(__GNUC__) || defined(__clang__) + #define STB_FORCEINLINE static __inline __attribute__((always_inline)) + #else + #define STB_FORCEINLINE static __inline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define CHECK(f) ((void) 0) +#endif + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float codetype; + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + // the page to seek to when seeking to start, may be zero + uint32 first_audio_page_offset; + + // p_first is the page on which the first audio packet ends + // (but not necessarily the page on which it starts) + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#define temp_free(f,p) (void)0 +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void setup_temp_free(vorb *f, void *p, int sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+7)&~7; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +STB_FORCEINLINE uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + if (n < 0) return 0; // signed n returns 0 + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + assert(z >= 0 && z < 32); + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propagate availability up the tree + if (z != len[i]) { + assert(/*len[i] >= 0 &&*/ len[i] < 32); + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + if (pow((float) r+1, dim) <= entries) + return -1; + if ((int) floor(pow((float) r, dim)) > entries) + return -1; + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,id; +} stbv__floor_ordering; + +static int STBV_CDECL point_compare(const void *p, const void *q) +{ + stbv__floor_ordering *a = (stbv__floor_ordering *) p; + stbv__floor_ordering *b = (stbv__floor_ordering *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n; + if (f->first_decode && !IS_PUSH_MODE(f)) { + f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; + } + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + f->p_first.page_end = f->p_first.page_start + len; + f->p_first.last_decoded_sample = loc0; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + + assert(f->valid_bits >= n); + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +STB_FORCEINLINE void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_BASE(c) (0) + +static int codebook_decode_start(vorb *f, Codebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +STB_FORCEINLINE void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + LINE_OP(output[x], inverse_db_table[y&255]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y&255]); + } + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +// n is 1/2 of the blocksize -- +// specification: "Correct per-vector decode length is [n]/2" +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + unsigned int actual_size = rtype == 2 ? n*2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch > 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + temp_free(f,part_classdata); + #else + temp_free(f,classifications); + #endif + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +STB_FORCEINLINE void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11 ; + + k00 = z[ -2] - z[-10]; + k11 = z[ -3] - z[-11]; + z[ -2] = z[ -2] + z[-10]; + z[ -3] = z[ -3] + z[-11]; + z[-10] = (k00+k11) * A2; + z[-11] = (k11-k00) * A2; + + k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k11 = z[ -5] - z[-13]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[-12] = k11; + z[-13] = k00; + + k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation + k11 = z[ -7] - z[-15]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-14] = (k00+k11) * A2; + z[-15] = (k00-k11) * A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propagates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_free(f,buf2); + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx,ly, hx,hy, n2); + CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + CHECK(f); +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + CHECK(f); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet; + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; // this doesn't seem right, but has no ill effect on my test files + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + CHECK(f); + + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + if (w == NULL) return 0; + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static int vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left, res; + res = vorbis_decode_packet(f, &len, &left, &right); + if (res) + vorbis_finish_frame(f, len, left, right); + return res; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + f->first_decode = TRUE; + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) { + // check for the Ogg skeleton fishead identifying header to refine our error + if (f->segments[0] == 64 && + getn(f, header, 6) && + header[0] == 'f' && + header[1] == 'i' && + header[2] == 's' && + header[3] == 'h' && + header[4] == 'e' && + header[5] == 'a' && + get8(f) == 'd' && + get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); + else + return error(f, VORBIS_invalid_first_page); + } + + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + CHECK(f); + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); + + if (c->sparse) + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_length >= 32) return error(f, VORBIS_invalid_setup); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) + return error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + CHECK(f); + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + int values = lookup1_values(c->entries, c->dimensions); + if (values < 0) return error(f, VORBIS_invalid_setup); + c->lookup_values = (uint32) values; + } else { + c->lookup_values = c->entries * c->dimensions; + } + if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]; + val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + return error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + CHECK(f); + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); + + CHECK(f); + } + CHECK(f); + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + stbv__floor_ordering p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].id = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values-1; ++j) + if (p[j].x == p[j+1].x) + return error(f, VORBIS_invalid_setup); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].id; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low = 0,hi = 0; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup); + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + // maximum reasonable partition size is f->blocksize_1 + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } + + // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page + // without PAGEFLAG_continued_packet, so this either points to the first page, or + // the page after the end of the headers. It might be cleaner to point to a page + // in the middle of the headers, when that's the page where the first audio packet + // starts, but we'd have to also correctly skip the end of any continued packet in + // stb_vorbis_seek_start. + if (f->next_seg == -1) { + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + } else { + f->first_audio_page_offset = 0; + } + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes &= ~7; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); + } + + f->stream = (uint8 *) data; + f->stream_end = (uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + + +#define SAMPLE_unknown 0xffffffff + +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceding page while finding the +// start of a packet +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe, end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) +{ + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding, last_sample_limit; + double offset = 0.0, bytes_per_sample = 0.0; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + last_sample_limit = 0; + else + last_sample_limit = sample_number - padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (last_sample_limit <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) { + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + } + return 0; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough (if it wasn't an interpolation probe). + if (mid.page_start == right.page_start) { + if (probe >= 2 || delta <= 65536) + break; + } else { + if (last_sample_limit < mid.last_decoded_sample) + right = mid; + else + left = mid; + } + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + if (!vorbis_pump_first_frame(f)) + return 0; + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); +} + +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + uint32 max_frame_samples; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame should start with the sample + if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); + return 1; +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +int stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + return vorbis_pump_first_frame(f); +} + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc+1; + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = (uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f; +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + if (0 != fopen_s(&f, filename, "rb")) + f = NULL; +#else + f = fopen(filename, "rb"); +#endif + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (data == NULL) return NULL; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + if (error) *error = VORBIS__no_error; + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } +} + +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output = NULL; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 + found with Mayhem by ForAllSecure + 1.16 - 2019-03-04 - fix warnings + 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found + 1.14 - 2018-02-11 - delete bogus dealloca usage + 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) + 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files + 1.11 - 2017-07-23 - fix MinGW compilation + 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory + 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015-04-19 - don't define __forceinline if it's redundant + 1.04 - 2014-08-27 - fix missing const-correct case in API + 1.03 - 2014-08-07 - Warning fixes + 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float + 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_HEADER_ONLY + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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. +------------------------------------------------------------------------------ +*/ diff --git a/Examples/Tutorial1_2DSound/src/Tutorial1_2DSound.cbp b/Examples/Tutorial1_2DSound/src/Tutorial1_2DSound.cbp new file mode 100644 index 0000000..f1bbc93 --- /dev/null +++ b/Examples/Tutorial1_2DSound/src/Tutorial1_2DSound.cbp @@ -0,0 +1,91 @@ + + + + + + diff --git a/Examples/Tutorial2_3DSound/src/Tutorial2_3DSound.cbp b/Examples/Tutorial2_3DSound/src/Tutorial2_3DSound.cbp new file mode 100644 index 0000000..273d39c --- /dev/null +++ b/Examples/Tutorial2_3DSound/src/Tutorial2_3DSound.cbp @@ -0,0 +1,91 @@ + + + + + + diff --git a/cAudio/cAudio.cbp b/cAudio/cAudio.cbp new file mode 100644 index 0000000..076bf42 --- /dev/null +++ b/cAudio/cAudio.cbp @@ -0,0 +1,306 @@ + + + + + + diff --git a/cAudioWorkSpace.workspace b/cAudioWorkSpace.workspace new file mode 100755 index 0000000..d9126ef --- /dev/null +++ b/cAudioWorkSpace.workspace @@ -0,0 +1,9 @@ + + + + + + + + + From 9ebfd3962d1f8e08ff96f8878fdc728a7d60b611 Mon Sep 17 00:00:00 2001 From: netpipe Date: Mon, 24 Jan 2022 12:34:15 -0700 Subject: [PATCH 2/2] buildsetting --- cAudio/Headers/cAudioBuildSettings.h | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 cAudio/Headers/cAudioBuildSettings.h diff --git a/cAudio/Headers/cAudioBuildSettings.h b/cAudio/Headers/cAudioBuildSettings.h new file mode 100644 index 0000000..77bf979 --- /dev/null +++ b/cAudio/Headers/cAudioBuildSettings.h @@ -0,0 +1,41 @@ +#pragma once + +// CMake auto-generated configuration options + +#define CAUDIO_STATIC_LIB 0 + +//! This define controls whether the Ogg/Vorbis decoder is compiled into the library. +#define CAUDIO_COMPILE_WITH_OGG_DECODER 1 + +//! This define controls whether the RIFF/Wav decoder is compiled into the library. +#define CAUDIO_COMPILE_WITH_WAV_DECODER 1 + +//! This define controls whether the default filesystem data source is compiled into the library +#define CAUDIO_COMPILE_WITH_FILE_SOURCE 1 + +//! This define controls whether the default file logger (html) is compiled into the library +#define CAUDIO_COMPILE_WITH_FILE_LOG_RECEIVER 1 + +//! This define controls whether the default console logger is compiled into the library +#define CAUDIO_COMPILE_WITH_CONSOLE_LOG_RECEIVER 1 + +//! Define for making the entire library Thread Safe, comment out to disable. Will also disable internal threading by the library. +#define CAUDIO_MAKE_THREAD_SAFE 1 + +//! EFX support +#define CAUDIO_EFX_ENABLED 1 + +//! Tells cAudio to use the C standard memory functions for allocations (memalloc and free) +#define CAUDIO_MEMORY_USE_STD 1 + +//! Tells cAudio to reroute memory allocations from stl containers into the defined memory provider for cAudio, otherwise the standard std::allocator is used. +#define CAUDIO_REROUTE_STL_ALLOCATIONS 1 + +//! Activates the internal memory tracker, which can be used to detect and locate memory leaks. +#define CAUDIO_USE_MEMORYTRACKER 0 + +//! Tells the memory tracker to generate statistics on memory usage by cAudio +#define CAUDIO_MEMORYTRACKER_GENERATE_STATISTICS 0 + +//! Tells the memory tracker to log each and every allocation done by cAudio. This can be very slow, use only if you are debugging an issue. +#define CAUDIO_MEMORYTRACKER_LOG_ALL_ALLOCATIONS 1 \ No newline at end of file

0.000182
+Audio Decoder for extension .wav registered.
+
0.00021
+Audio Decoder for extension .raw registered.
+
0.000222
+Data Source named FileSystem registered (Priority 0).
+
0.022694
+OpenAL Version: 1.1 ALSOFT 1.17.2
+
0.022856
+Vendor: OpenAL Community
+
0.022917
+Renderer: OpenAL Soft
+
0.023275
+EFX Version: 1.0
+
0.023342
+EFX supported and enabled.
+
0.02339
+Supported Extensions: AL_EXT_ALAW AL_EXT_BFORMAT AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_MULAW AL_EXT_MULAW_BFORMAT AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET AL_EXT_source_distance_model AL_LOKI_quadriphonic AL_SOFT_block_alignment AL_SOFT_buffer_samples AL_SOFT_buffer_sub_data AL_SOFT_deferred_updates AL_SOFT_direct_channels AL_SOFT_loop_points AL_SOFT_MSADPCM AL_SOFT_source_latency AL_SOFT_source_length
+
0.044849
+Audio Source (bling) created from Data Source cMemorySource.
+
0.09862
+Audio Source (bling) created from Data Source cMemorySource.
+
0.144129
+Audio Source (bling) created from Data Source cMemorySource.
+
0.206226
+Audio Source (bling) created from Data Source cMemorySource.
+
0.257775
+Audio Source (bling) created from Data Source cMemorySource.
+
0.33304
+Audio Source (bling) created from Data Source cMemorySource.
+
0.398748
+Audio Source (bling) created from Data Source cMemorySource.
+
0.474654
+Audio Source (bling) created from Data Source cMemorySource.
+