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 0000000..bf926be Binary files /dev/null and b/Examples/Tutorial10Fluidlite/src/sitar.sf2 differ 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/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 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 @@ + + + + + + + + +
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.
+