├── .gitignore ├── premake-mingw.bat ├── sfotool ├── Makefile ├── .gitignore ├── sfotool.sln ├── README.md ├── sfotool.vcxproj └── main.c ├── premake-VisualStudio.bat ├── premake5 ├── premake5.exe ├── premake5.osx ├── INSTALL.txt ├── main.cpp ├── LICENSE ├── README.md ├── synth.h ├── premake5.lua ├── synth.cpp ├── tml.h └── tsf.h /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | tml.h.bak 3 | -------------------------------------------------------------------------------- /premake-mingw.bat: -------------------------------------------------------------------------------- 1 | premake5.exe gmake2 || pause 2 | -------------------------------------------------------------------------------- /sfotool/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc main.c -o sfotool 3 | -------------------------------------------------------------------------------- /premake-VisualStudio.bat: -------------------------------------------------------------------------------- 1 | premake5.exe vs2022 || pause 2 | -------------------------------------------------------------------------------- /premake5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nehochupechatat/TinySoundFont/HEAD/premake5 -------------------------------------------------------------------------------- /premake5.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nehochupechatat/TinySoundFont/HEAD/premake5.exe -------------------------------------------------------------------------------- /premake5.osx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nehochupechatat/TinySoundFont/HEAD/premake5.osx -------------------------------------------------------------------------------- /sfotool/.gitignore: -------------------------------------------------------------------------------- 1 | *.ncb 2 | *.opt 3 | *.plg 4 | *.aps 5 | *.ipch 6 | *.suo 7 | *.user 8 | *.sdf 9 | *.opensdf 10 | *.dsw 11 | *-i686 12 | *-x86_64 13 | Debug 14 | Release 15 | .vs 16 | -------------------------------------------------------------------------------- /INSTALL.txt: -------------------------------------------------------------------------------- 1 | For Windows users: 2 | 3 | First, run premake-mingw.bat 4 | Then, get w64devkit, go into your project folder and type 'make'. 5 | Enjoy :) 6 | 7 | If the example program quits then you didn't point to a valid soundfont and MIDI file. -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "synth.h" 2 | #include 3 | #include 4 | #define MINIAUDIO_IMPLEMENTATION 5 | #include "miniaudio.h" 6 | void SongDelay(int ms); 7 | 8 | void SongDelay(int ms) 9 | { 10 | std::this_thread::sleep_for(std::chrono::milliseconds(ms)); 11 | } 12 | 13 | int main(int argc, char **argv) 14 | { 15 | if(argc < 3){ 16 | fprintf(stderr, "usage: %s SF2BANK MIDIFILE\n", argv[0]); 17 | return 1; 18 | } 19 | 20 | if(argc >= 3) 21 | { 22 | Synth synth(argv[1]); 23 | synth.PlaySong(argv[2],0); 24 | while (!synth.HasFileEnded()) SongDelay(250); 25 | } 26 | return 0; 27 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2017-2023 Bernhard Schelling (Based on SFZero, Copyright (C) 2012 Steve Folta, https://github.com/stevefolta/SFZero) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /sfotool/sfotool.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.40629.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sfotool", "sfotool.vcxproj", "{FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Debug|Win32.Build.0 = Debug|Win32 18 | {FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Debug|x64.ActiveCfg = Debug|x64 19 | {FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Debug|x64.Build.0 = Debug|x64 20 | {FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Release|Win32.ActiveCfg = Release|Win32 21 | {FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Release|Win32.Build.0 = Release|Win32 22 | {FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Release|x64.ActiveCfg = Release|x64 23 | {FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinySoundFont 2 | SoundFont2 synthesizer library in a single C/C++ file 3 | 4 | ## Overview 5 | 6 | TinySoundFont is a software synthesizer using SoundFont2 sound bank files. 7 | 8 | The library is a single C header file so it is extremely simple to integrate in your C/C++ projects. 9 | 10 | ```c++ 11 | #define TSF_IMPLEMENTATION 12 | #include "tsf.h" 13 | 14 | ... 15 | 16 | tsf* TinySoundFont = tsf_load_filename("soundfont.sf2"); 17 | tsf_set_output(TinySoundFont, TSF_MONO, 44100, 0); //sample rate 18 | tsf_note_on(TinySoundFont, 0, 60, 1.0f); //preset 0, middle C 19 | short HalfSecond[22050]; //synthesize 0.5 seconds 20 | tsf_render_short(TinySoundFont, HalfSecond, 22050, 0); 21 | ``` 22 | 23 | The library code is based on [SFZero by Steve Folta](https://github.com/stevefolta/SFZero). 24 | 25 | ## Documentation 26 | 27 | The API documentation can be found on [top of the library source code](https://github.com/schellingb/TinySoundFont/blob/master/tsf.h). 28 | 29 | There are also [examples available](https://github.com/schellingb/TinySoundFont/tree/master/examples) which come with a sample SoundFont file and build and play sound on Win32, Win64, Linux and MacOSX with no further dependencies. 30 | 31 | ## Dependencies 32 | 33 | C standard libraries for fopen, math and malloc (can be removed by providing custom functions with #defines). 34 | 35 | ## License 36 | 37 | TinySoundFont is available under the [MIT license](https://choosealicense.com/licenses/mit/). 38 | -------------------------------------------------------------------------------- /sfotool/README.md: -------------------------------------------------------------------------------- 1 | # SFOTool for TinySoundFont 2 | A tool to create heavily compressed .SFO files from .SF2 SoundFont files. 3 | 4 | ## Purpose 5 | SFO files are just regular SoundFont v2 files with the entire block of raw PCM samples replaced 6 | with a single Ogg Vorbis compressed stream. Unlike .sf3 files, which can have every separate font 7 | sample compressed individually, this will compress the entire sound data as if it were a single 8 | sample. This results in much higher compression than processing samples individually but also 9 | higher loss of quality. 10 | 11 | ## Usage Help 12 | ```sh 13 | sfotool : Show type of sample stream contained (PCM or OGG) 14 | sfotool : Dump PCM sample stream to .WAV file 15 | sfotool : Dump OGG sample stream to .OGG file 16 | sfotool : Write new .SF2 soundfont file using PCM sample stream from .WAV file 17 | sfotool : Write new .SFO soundfont file using OGG sample stream from .OGG file 18 | ``` 19 | 20 | ## Making a .SFO file from a .SF2 file 21 | 1. Dump the PCM data of a .SF2 file to a .WAV file 22 | `sfotool ` 23 | 2. Compress the .WAV file to .OGG (i.e. with [Audacity](https://www.audacityteam.org/download/legacy-windows/)) 24 | Make sure to choose the desired compression quality level 25 | 3. Build the .SFO file from the .SF2 file and the new .OGG 26 | `sfotool ` 27 | 28 | # License 29 | SFOTool is available under the [Unlicense](http://unlicense.org/) (public domain). 30 | -------------------------------------------------------------------------------- /synth.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | struct tml_message; 3 | struct tsf; 4 | #include 5 | #include "stdio.h" 6 | #include "miniaudio.h" 7 | 8 | int CaseStrCmp(const char* str1, const char* str2); 9 | 10 | class Synth 11 | { 12 | public: 13 | Synth(const char *filename); 14 | ~Synth(); 15 | static void AudioCallbackStatic(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); 16 | void AudioCallback(ma_device* pDevice, void* pOutput, ma_uint32 frameCount); 17 | void PlaySong(const char *filename, short loops); 18 | void MuteChannel(unsigned char channel); 19 | void SetChannelVolume(unsigned char channel, unsigned char volume); 20 | void UnmuteChannel(unsigned char channel); 21 | void ResetChannelVolume(unsigned char channel); 22 | void SetTempo(double bpm); 23 | void ResetTempo(); 24 | void MuteTrack(unsigned short track); 25 | void UnmuteTrack(unsigned short track); 26 | void ChangeSoundfont(const char* filename); 27 | void ChangeInstrument(char instrument, char channel, char msbBank); 28 | void PauseSongToggle(); 29 | void PauseSong(); 30 | void MuteChannelToggle(unsigned char channel); 31 | void MuteTrackToggle(unsigned short track); 32 | void SetMasterVolume(unsigned char volume); 33 | void SetMasterPitch(unsigned short pitch, char pitchRange); 34 | bool HasFileEnded(); 35 | void SetChannelPan(char pan, char channel); 36 | void SetMasterPan(char pan); 37 | private: 38 | ma_device_config deviceConfig; 39 | ma_device device; 40 | const char *filename; 41 | short loopCount; 42 | short loops; 43 | float oldVolume; 44 | float masterVolume = 1.0f; 45 | bool midiPlaybackEnded; 46 | unsigned short pitchWheel = 8192; 47 | char pitchRangeCustom = 2; 48 | tml_message* TinyMidiLoader; 49 | tml_message* g_MidiMessage; 50 | double g_Msec; 51 | double loopStartPosition; 52 | tml_message* loopStartPositionMessage; 53 | tsf* g_TinySoundFont; 54 | std::set mutedTracks; 55 | double customTempo = 0; 56 | double currentTempo; 57 | char oldInstruments[16]; 58 | char oldMSB[16]; 59 | bool paused = false; 60 | bool mutedChannels[16]={false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false}; 61 | }; -------------------------------------------------------------------------------- /premake5.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2020-2024 Jeffery Myers 2 | -- 3 | --This software is provided "as-is", without any express or implied warranty. In no event 4 | --will the authors be held liable for any damages arising from the use of this software. 5 | 6 | --Permission is granted to anyone to use this software for any purpose, including commercial 7 | --applications, and to alter it and redistribute it freely, subject to the following restrictions: 8 | 9 | -- 1. The origin of this software must not be misrepresented; you must not claim that you 10 | -- wrote the original software. If you use this software in a product, an acknowledgment 11 | -- in the product documentation would be appreciated but is not required. 12 | -- 13 | -- 2. Altered source versions must be plainly marked as such, and must not be misrepresented 14 | -- as being the original software. 15 | -- 16 | -- 3. This notice may not be removed or altered from any source distribution. 17 | 18 | baseName = path.getbasename(os.getcwd()); 19 | 20 | workspace (path.getbasename(os.getcwd())) 21 | location "../" 22 | configurations { "Debug", "Release" } 23 | platforms { "x64", "x86", "ARM64"} 24 | 25 | defaultplatform ("x64") 26 | 27 | filter "configurations:Debug" 28 | defines { "DEBUG" } 29 | symbols "On" 30 | 31 | filter "configurations:Release" 32 | defines { "NDEBUG" } 33 | optimize "On" 34 | 35 | filter { "platforms:x64" } 36 | architecture "x86_64" 37 | 38 | filter { "platforms:Arm64" } 39 | architecture "ARM64" 40 | 41 | filter {} 42 | 43 | targetdir "./%{cfg.buildcfg}/" 44 | 45 | startproject(path.getbasename(os.getcwd())) 46 | 47 | project (path.getbasename(os.getcwd())) 48 | kind "ConsoleApp" 49 | location "./" 50 | targetdir "./%{cfg.buildcfg}" 51 | 52 | 53 | filter "action:vs*" 54 | debugdir "$(SolutionDir)" 55 | 56 | flags { "ShadowedVariables"} 57 | filter{} 58 | 59 | filter "system:windows" 60 | defines{"_WIN32"} 61 | links {"winmm", "gdi32"} 62 | libdirs {"../bin/%{cfg.buildcfg}"} 63 | 64 | filter "system:linux" 65 | links {"pthread", "m", "dl", "rt"} 66 | 67 | filter "system:macosx" 68 | links {"Cocoa.framework", "IOKit.framework", "CoreFoundation.framework", "CoreAudio.framework", "CoreVideo.framework", "AudioToolbox.framework"} 69 | 70 | 71 | 72 | project (path.getbasename(os.getcwd())) 73 | kind "ConsoleApp" 74 | location "./" 75 | targetdir "./bin/%{cfg.buildcfg}" 76 | 77 | vpaths 78 | { 79 | ["Header Files/*"] = { "include/**.h", "include/**.hpp", "**.h", "**.hpp"}, 80 | ["Source Files/*"] = { "src/**.cpp", "src/**.c", "**.cpp","**.c"}, 81 | } 82 | files {"**.hpp", "**.h", "**.cpp","**.c"} 83 | removefiles 84 | { 85 | "./sfotool/**" 86 | } 87 | -------------------------------------------------------------------------------- /sfotool/sfotool.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF} 23 | Win32Proj 24 | sfotool 25 | sfotool 26 | 27 | 28 | 29 | Application 30 | v110_xp 31 | v120 32 | v140 33 | v141 34 | v142 35 | v143 36 | $(DefaultPlatformToolset) 37 | false 38 | MultiByte 39 | $(SolutionDir)$(Configuration)\$(ProjectName)_$(Platform)\ 40 | $(SolutionDir)$(Configuration)\$(ProjectName)_$(Platform)\ 41 | 42 | 43 | true 44 | 45 | 46 | false 47 | true 48 | 49 | 50 | 51 | 52 | 53 | 54 | true 55 | 56 | 57 | false 58 | 59 | 60 | 61 | Level3 62 | true 63 | false 64 | false 65 | false 66 | 67 | 68 | Console 69 | true 70 | 71 | 72 | 73 | 74 | Disabled 75 | WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) 76 | MultiThreadedDebug 77 | 78 | 79 | true 80 | 81 | 82 | 83 | 84 | MaxSpeed 85 | true 86 | WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) 87 | MultiThreaded 88 | /Gw %(AdditionalOptions) 89 | 90 | 91 | true 92 | true 93 | false 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /synth.cpp: -------------------------------------------------------------------------------- 1 | #include "synth.h" 2 | #define TSF_IMPLEMENTATION 3 | #include "tsf.h" 4 | 5 | #define TML_IMPLEMENTATION 6 | #include "tml.h" 7 | 8 | #include 9 | #include 10 | 11 | #include "miniaudio.h" 12 | 13 | int CaseStrCmp(const char* str1, const char* str2) 14 | { 15 | while (*str1 && *str2) 16 | { 17 | if (tolower((unsigned char)*str1) != tolower((unsigned char)*str2)) 18 | { 19 | return *str1 - *str2; 20 | } 21 | str1++; 22 | str2++; 23 | } 24 | return *str1 - *str2; 25 | } 26 | 27 | void Synth::AudioCallbackStatic(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) 28 | { 29 | // Access the Synth instance from user data 30 | Synth* synth = static_cast(pDevice->pUserData); 31 | synth->AudioCallback(pDevice, pOutput, frameCount); 32 | } 33 | 34 | // Callback function called by the audio thread 35 | void Synth::AudioCallback(ma_device* pDevice, void* pOutput, ma_uint32 frameCount) 36 | { 37 | ma_format format = pDevice->playback.format; 38 | ma_uint32 channels = pDevice->playback.channels; 39 | ma_uint32 bytesPerSample = ma_get_bytes_per_sample(format); 40 | ma_uint32 len = frameCount * channels * bytesPerSample; 41 | unsigned char* stream = static_cast(pOutput); 42 | 43 | //Number of samples to process 44 | int SampleBlock, SampleCount = (len / (2 * sizeof(float))); //2 output channels 45 | for (SampleBlock = TSF_RENDER_EFFECTSAMPLEBLOCK; SampleCount; SampleCount -= SampleBlock, stream += (SampleBlock * (2 * sizeof(float)))) 46 | { 47 | //We progress the MIDI playback and then process TSF_RENDER_EFFECTSAMPLEBLOCK samples at once 48 | if (SampleBlock > SampleCount) SampleBlock = SampleCount; 49 | double tempo_us = customTempo > 0 ? customTempo : currentTempo; // Use custom or MIDI-defined tempo 50 | double timeAdvance = SampleBlock * (tempo_us / 44100.0 / 1000.0); 51 | if (!paused) 52 | {//Loop through all MIDI messages which need to be played up until the current playback time 53 | for (g_Msec += timeAdvance; g_MidiMessage && g_Msec >= g_MidiMessage->time; g_MidiMessage = g_MidiMessage->next) 54 | { 55 | 56 | if (mutedTracks.find(g_MidiMessage->trackNum) != mutedTracks.end()) 57 | { 58 | // Track is muted, skip this event 59 | continue; 60 | } 61 | 62 | switch (g_MidiMessage->type) 63 | { 64 | 65 | case TML_PROGRAM_CHANGE: //channel program (preset) change (special handling for 10th MIDI channel with drums) 66 | tsf_channel_set_presetnumber(g_TinySoundFont, g_MidiMessage->channel, g_MidiMessage->program, (g_MidiMessage->channel == 9)); 67 | break; 68 | case TML_NOTE_ON: //play a note 69 | tsf_channel_note_on(g_TinySoundFont, g_MidiMessage->channel, g_MidiMessage->key, g_MidiMessage->velocity / 127.0f); 70 | break; 71 | case TML_NOTE_OFF: //stop a note 72 | tsf_channel_note_off(g_TinySoundFont, g_MidiMessage->channel, g_MidiMessage->key); 73 | break; 74 | case TML_PITCH_BEND: //pitch wheel modification 75 | tsf_channel_set_pitchwheel(g_TinySoundFont, g_MidiMessage->channel, g_MidiMessage->pitch_bend); 76 | break; 77 | case TML_CONTROL_CHANGE: //MIDI controller messages 78 | tsf_channel_midi_control(g_TinySoundFont, g_MidiMessage->channel, g_MidiMessage->control, g_MidiMessage->control_value); 79 | break; 80 | 81 | } 82 | if(g_MidiMessage->type == TML_MARKER && CaseStrCmp(g_MidiMessage->markerName, "loopstart") == 0) 83 | { 84 | //printf("Found Loop Start marker!\n"); 85 | loopStartPosition = g_Msec; 86 | loopStartPositionMessage = g_MidiMessage; 87 | } 88 | 89 | }} 90 | 91 | // Render the block of audio samples in float format 92 | tsf_render_float(g_TinySoundFont, (float*)stream, SampleBlock, 0); 93 | //Looping code 94 | if (!g_MidiMessage || g_MidiMessage->type == TML_MARKER && CaseStrCmp(g_MidiMessage->markerName, "loopend") == 0) 95 | { 96 | if(loops <= -1 || loopCount < loops) 97 | { 98 | g_Msec = (double)loopStartPosition; 99 | g_MidiMessage = loopStartPositionMessage; 100 | if (loops > -1) 101 | {loopCount++;} 102 | } 103 | } 104 | //Muting channel control 105 | for(unsigned char mc = 0; mc < 16; mc++) 106 | { 107 | oldVolume = tsf_channel_get_volume(g_TinySoundFont,mc); 108 | if(mutedChannels[mc] == true) 109 | { 110 | 111 | tsf_channel_set_volume(g_TinySoundFont, mc, 0.0f); 112 | } 113 | else 114 | {tsf_channel_set_volume(g_TinySoundFont,mc,oldVolume);} 115 | } 116 | 117 | //Master pitch control 118 | if(pitchWheel != 8192) 119 | { 120 | for(char c=0;c<16;c++) 121 | {tsf_channel_set_pitchwheel(g_TinySoundFont, c, pitchWheel);} 122 | if(pitchRangeCustom !=2) 123 | { 124 | for(char c=0;c<16;c++) 125 | {tsf_channel_set_pitchrange(g_TinySoundFont, c, pitchRangeCustom);} 126 | } 127 | } 128 | 129 | } 130 | } 131 | 132 | Synth::Synth(const char *fName):filename(fName),loops(0),loopCount(0), g_MidiMessage(nullptr), g_Msec(0.0), g_TinySoundFont(nullptr) 133 | { 134 | // Define the desired audio output format we request 135 | deviceConfig = ma_device_config_init(ma_device_type_playback); 136 | deviceConfig.sampleRate = 44100; 137 | deviceConfig.playback.format = ma_format_f32; 138 | deviceConfig.playback.channels = 2; 139 | deviceConfig.dataCallback = AudioCallbackStatic; 140 | deviceConfig.pUserData = this; 141 | currentTempo = 1000000; 142 | 143 | 144 | loopStartPosition = 0.0f; 145 | g_TinySoundFont = tsf_load_filename(fName); 146 | //Initialize preset on special 10th MIDI channel to use percussion sound bank (128) if available 147 | tsf_channel_set_bank_preset(g_TinySoundFont, 9, 128, 0); 148 | 149 | // Set the SoundFont rendering output mode 150 | tsf_set_output(g_TinySoundFont, TSF_STEREO_INTERLEAVED, deviceConfig.sampleRate, 0.0f); 151 | 152 | // Request the desired audio output format 153 | ma_device_init(TSF_NULL, &deviceConfig, &device); 154 | 155 | } 156 | 157 | Synth::~Synth() 158 | { 159 | tsf_close(g_TinySoundFont); 160 | tml_free(TinyMidiLoader); 161 | } 162 | 163 | void Synth::PlaySong(const char *filename, short loops) 164 | 165 | { 166 | this->loops = loops; 167 | TinyMidiLoader = tml_load_filename(filename); 168 | g_MidiMessage = TinyMidiLoader; 169 | loopStartPositionMessage = g_MidiMessage; 170 | ma_device_start(&device); 171 | 172 | } 173 | 174 | void Synth::MuteChannel(unsigned char channel) 175 | { 176 | mutedChannels[channel]=true; 177 | } 178 | 179 | void Synth::SetChannelVolume(unsigned char channel, unsigned char volume) 180 | { 181 | float channelVolume = volume / 100.0f; 182 | tsf_channel_set_volume(g_TinySoundFont, channel, channelVolume); 183 | } 184 | 185 | void Synth::UnmuteChannel(unsigned char channel) 186 | { 187 | mutedChannels[channel]=false; 188 | } 189 | 190 | void Synth::ResetChannelVolume(unsigned char channel) 191 | { 192 | tsf_channel_set_volume(g_TinySoundFont, channel, oldVolume); 193 | } 194 | 195 | void Synth::SetTempo(double bpm) 196 | { 197 | customTempo = (25000/3) * bpm; 198 | } 199 | 200 | void Synth::ResetTempo() 201 | { 202 | customTempo = 0; 203 | } 204 | 205 | void Synth::ChangeInstrument(char instrument, char channel, char msbBank) 206 | { 207 | if (channel == 9) 208 | {tsf_channel_set_bank_preset(g_TinySoundFont, channel, 128, instrument);} 209 | else 210 | {tsf_channel_set_bank_preset(g_TinySoundFont, channel, msbBank, instrument);} 211 | } 212 | 213 | void Synth::ChangeSoundfont(const char* filename) 214 | { 215 | for(char oi = 0; oi < 16; oi++) 216 | {oldInstruments[oi] = tsf_channel_get_preset_number(g_TinySoundFont, oi); 217 | oldMSB[oi] = tsf_channel_get_preset_bank(g_TinySoundFont, oi); 218 | std::cout << "instrument on channel " << oi << "is " << tsf_channel_get_preset_number(g_TinySoundFont, oi) << "\n"; 219 | } 220 | 221 | g_TinySoundFont = tsf_load_filename(filename); 222 | tsf_channel_set_bank_preset(g_TinySoundFont, 9, 128, 0); 223 | for(char oi = 0; oi < 16; oi++) 224 | {ChangeInstrument(oldInstruments[oi],oi,oldMSB[oi]);} 225 | } 226 | 227 | void Synth::MuteTrack(unsigned short track) 228 | { 229 | mutedTracks.insert(track); 230 | } 231 | 232 | void Synth::UnmuteTrack(unsigned short track) 233 | { 234 | if (mutedTracks.find(track) != mutedTracks.end()) 235 | { 236 | mutedTracks.erase(track); 237 | } 238 | } 239 | 240 | void Synth::PauseSongToggle() 241 | { 242 | paused = !paused; 243 | if (paused) 244 | {for(char i = 0; i<16; i++) 245 | {tsf_channel_note_off_all(g_TinySoundFont, i);}} 246 | } 247 | 248 | void Synth::PauseSong() 249 | { 250 | paused = true; 251 | for(char i = 0; i<16; i++) 252 | {tsf_channel_note_off_all(g_TinySoundFont, i);} 253 | } 254 | 255 | void Synth::MuteChannelToggle(unsigned char channel) 256 | { 257 | mutedChannels[channel]=!mutedChannels[channel]; 258 | } 259 | 260 | void Synth::MuteTrackToggle(unsigned short track) 261 | { 262 | if (mutedTracks.find(track) != mutedTracks.end()) 263 | { 264 | UnmuteTrack(track); 265 | } 266 | else 267 | { 268 | MuteTrack(track); 269 | } 270 | } 271 | 272 | void Synth::SetMasterVolume(unsigned char volume) 273 | { 274 | masterVolume = volume / 100.0f; 275 | tsf_set_volume(g_TinySoundFont, masterVolume); 276 | } 277 | 278 | void Synth::SetMasterPitch(unsigned short pitch, char pitchRange) 279 | { 280 | pitchWheel = pitch; 281 | pitchRangeCustom = pitchRange; 282 | } 283 | 284 | bool Synth::HasFileEnded() 285 | { 286 | if(!g_MidiMessage && loops == 0) 287 | {return true;} 288 | else 289 | {return false;} 290 | } 291 | 292 | void Synth::SetChannelPan(char pan, char channel) 293 | { 294 | float customChannelPan = pan / 128.0f; 295 | tsf_channel_set_pan(g_TinySoundFont, channel, customChannelPan); 296 | } 297 | 298 | void Synth::SetMasterPan(char pan) 299 | { 300 | float customMasterPan = pan / 128.0f; 301 | for(char c=0;c<16;c++) 302 | tsf_channel_set_pan(g_TinySoundFont, c, customMasterPan); 303 | } -------------------------------------------------------------------------------- /sfotool/main.c: -------------------------------------------------------------------------------- 1 | //--------------------------------------------// 2 | // SFOTool // 3 | // License: Public Domain (www.unlicense.org) // 4 | //--------------------------------------------// 5 | 6 | #include 7 | #include 8 | 9 | typedef char sfo_fourcc[4]; 10 | #define SFO_FourCCEquals(a, b) (a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3]) 11 | struct sfo_riffchunk { sfo_fourcc id; unsigned int size; }; 12 | struct sfo_wavheader 13 | { 14 | char RIFF[4]; unsigned int ChunkSize; char WAVE[4], fmt[4]; unsigned int Subchunk1Size; 15 | unsigned short AudioFormat,NumOfChan; unsigned int SamplesPerSec, bytesPerSec; 16 | unsigned short blockAlign, bitsPerSample; char Subchunk2ID[4]; unsigned int Subchunk2Size; 17 | }; 18 | 19 | static void sfo_copy(FILE* src, FILE* trg, unsigned int size) 20 | { 21 | unsigned int block; 22 | unsigned char buf[512]; 23 | for (; size; size -= block) 24 | { 25 | block = (size > sizeof(buf) ? sizeof(buf) : size); 26 | fread(buf, 1, block, src); 27 | fwrite(buf, 1, block, trg); 28 | } 29 | } 30 | 31 | static int sfo_riffchunk_read(struct sfo_riffchunk* parent, struct sfo_riffchunk* chunk, FILE* f) 32 | { 33 | int is_riff, is_list; 34 | if (parent && sizeof(sfo_fourcc) + sizeof(unsigned int) > parent->size) return 0; 35 | if (!fread(&chunk->id, sizeof(sfo_fourcc), 1, f) || *chunk->id <= ' ' || *chunk->id >= 'z') return 0; 36 | if (!fread(&chunk->size, sizeof(unsigned int), 1, f)) return 0; 37 | if (parent && sizeof(sfo_fourcc) + sizeof(unsigned int) + chunk->size > parent->size) return 0; 38 | if (parent) parent->size -= sizeof(sfo_fourcc) + sizeof(unsigned int) + chunk->size; 39 | is_riff = SFO_FourCCEquals(chunk->id, "RIFF"), is_list = SFO_FourCCEquals(chunk->id, "LIST"); 40 | if (is_riff && parent) return 0; /* not allowed */ 41 | if (!is_riff && !is_list) return 1; /* custom type without sub type */ 42 | if (!fread(&chunk->id, sizeof(sfo_fourcc), 1, f) || *chunk->id <= ' ' || *chunk->id >= 'z') return 0; 43 | chunk->size -= sizeof(sfo_fourcc); 44 | return 1; 45 | } 46 | 47 | int main(int argc, const char** argv) 48 | { 49 | const char* arg_sf_in = (argc > 1 ? argv[1] : NULL); 50 | const char* arg_smpl = (argc > 2 ? argv[2] : NULL); 51 | const char* arg_sf_out = (argc > 3 ? argv[3] : NULL); 52 | char ext_sf_in = (arg_sf_in ? arg_sf_in [strlen(arg_sf_in)-1] | 0x20 : '\0'); 53 | char ext_smpl = (arg_smpl ? arg_smpl [strlen(arg_smpl)-1] | 0x20 : '\0'); 54 | char ext_sf_out = (arg_sf_out ? arg_sf_out[strlen(arg_sf_out)-1] | 0x20 : '\0'); 55 | struct sfo_riffchunk chunkHead, chunkList, chunk; 56 | FILE* f_sf_in = NULL, *f_smpl = NULL, *f_sf_out = NULL; 57 | 58 | if (argc < 2 || argc > 4) 59 | { 60 | print_usage: 61 | fprintf(stderr, "Usage Help:\n"); 62 | fprintf(stderr, "%s : Show type of sample stream contained (PCM or OGG)\n", argv[0]); 63 | fprintf(stderr, "%s : Dump PCM sample stream to .WAV file\n", argv[0]); 64 | fprintf(stderr, "%s : Dump OGG sample stream to .OGG file\n", argv[0]); 65 | fprintf(stderr, "%s : Write new .SF2 soundfont file using PCM sample stream from .WAV file\n", argv[0]); 66 | fprintf(stderr, "%s : Write new .SFO soundfont file using OGG sample stream from .OGG file\n", argv[0]); 67 | if (f_sf_in) fclose(f_sf_in); 68 | if (f_smpl) fclose(f_smpl); 69 | if (f_sf_out) fclose(f_sf_out); 70 | return 1; 71 | } 72 | 73 | f_sf_in = fopen(arg_sf_in, "rb"); 74 | if (!f_sf_in) { fprintf(stderr, "Error: Passed input file '%s' does not exist\n\n", arg_sf_in); goto print_usage; } 75 | 76 | if (!sfo_riffchunk_read(NULL, &chunkHead, f_sf_in) || !SFO_FourCCEquals(chunkHead.id, "sfbk")) 77 | { 78 | fprintf(stderr, "Error: Passed input file '%s' is not a valid soundfont file\n\n", arg_sf_in); 79 | goto print_usage; 80 | } 81 | while (sfo_riffchunk_read(&chunkHead, &chunkList, f_sf_in)) 82 | { 83 | unsigned int pos_listsize = (unsigned int)ftell(f_sf_in) - 8; 84 | if (!SFO_FourCCEquals(chunkList.id, "sdta")) 85 | { 86 | fseek(f_sf_in, chunkList.size, SEEK_CUR); 87 | continue; 88 | } 89 | for (; sfo_riffchunk_read(&chunkList, &chunk, f_sf_in); fseek(f_sf_in, chunkList.size, SEEK_CUR)) 90 | { 91 | int is_pcm = SFO_FourCCEquals(chunk.id, "smpl"); 92 | if (!is_pcm && !SFO_FourCCEquals(chunk.id, "smpo")) 93 | continue; 94 | 95 | printf("Soundfont file '%s' contains a %s sample stream\n", arg_sf_in, (is_pcm ? "PCM" : "OGG")); 96 | if (ext_sf_in != '2' && ext_sf_in != 'o') printf(" Warning: Soundfont file has unknown file extension (should be .SF2 or .SFO)\n"); 97 | if (ext_sf_in == '2' && !is_pcm) printf(" Warning: Soundfont file has .SF%c extension but sample stream is %s\n", '2', "OGG (should be .SFO)"); 98 | if (ext_sf_in == 'o' && is_pcm) printf(" Warning: Soundfont file has .SF%c extension but sample stream is %s\n", 'O', "PCM (should be .SF2)"); 99 | if (arg_sf_out) 100 | { 101 | unsigned int pos_smpchunk, end_smpchunk, len_smpl, end_sf, len_list_in, len_list_out; 102 | 103 | printf("Writing file '%s' with samples from '%s'\n", arg_sf_out, arg_smpl); 104 | if (ext_sf_out != '2' && ext_sf_out != 'o') printf(" Warning: Soundfont file has unknown file extension (should be .SF2 or .SFO)\n"); 105 | if (ext_smpl != 'v' && ext_smpl != 'g') printf(" Warning: Sample file has unknown file extension (should be .WAV or .OGG)\n"); 106 | if (ext_sf_out == '2' && ext_smpl != 'v') printf(" Warning: Soundfont file has .SF%c extension but sample file is .%s\n", '2', "OGG"); 107 | if (ext_sf_out == 'o' && ext_smpl == 'v') printf(" Warning: Soundfont file has .SF%c extension but sample file is .%s\n", 'O', "WAV"); 108 | 109 | f_smpl = fopen(arg_smpl, "rb"); 110 | if (!f_smpl) { fprintf(stderr, "Error: Unable to open input file '%s'\n\n", arg_smpl); goto print_usage; } 111 | 112 | if (ext_smpl == 'v') 113 | { 114 | struct sfo_wavheader wav_hdr; 115 | fread(&wav_hdr, sizeof(wav_hdr), 1, f_smpl); 116 | if (!SFO_FourCCEquals(wav_hdr.Subchunk2ID, "data") || !SFO_FourCCEquals(wav_hdr.RIFF, "RIFF") 117 | || !SFO_FourCCEquals(wav_hdr.WAVE, "WAVE") || !SFO_FourCCEquals(wav_hdr.fmt, "fmt ") 118 | || wav_hdr.Subchunk1Size != 16 || wav_hdr.AudioFormat != 1 || wav_hdr.NumOfChan != 1 119 | || wav_hdr.bytesPerSec != wav_hdr.SamplesPerSec * sizeof(short) || wav_hdr.bitsPerSample != sizeof(short) * 8) 120 | { fprintf(stderr, "Input .WAV file is not a valid raw PCM encoded wave file\n\n"); goto print_usage; } 121 | 122 | len_smpl = wav_hdr.Subchunk2Size; 123 | } 124 | else 125 | { 126 | fseek(f_smpl, 0, SEEK_END); 127 | len_smpl = (unsigned int)ftell(f_smpl); 128 | fseek(f_smpl, 0, SEEK_SET); 129 | } 130 | 131 | f_sf_out = fopen(arg_sf_out, "wb"); 132 | if (!f_sf_out) { fprintf(stderr, "Error: Unable to open output file '%s'\n\n", arg_sf_out); goto print_usage; } 133 | 134 | pos_smpchunk = (unsigned int)(ftell(f_sf_in) - sizeof(struct sfo_riffchunk)); 135 | end_smpchunk = pos_smpchunk + (unsigned int)sizeof(struct sfo_riffchunk) + chunk.size; 136 | fseek(f_sf_in, 0, SEEK_END); 137 | end_sf = (unsigned int)ftell(f_sf_in); 138 | 139 | /* Write data before list chunk size */ 140 | fseek(f_sf_in, 0, SEEK_SET); 141 | sfo_copy(f_sf_in, f_sf_out, pos_listsize); 142 | 143 | /* Write new list chunk size */ 144 | fread(&len_list_in, 4, 1, f_sf_in); 145 | len_list_out = len_list_in - chunk.size + len_smpl; 146 | fwrite(&len_list_out, 4, 1, f_sf_out); 147 | 148 | /* Write data until sample chunk */ 149 | sfo_copy(f_sf_in, f_sf_out, pos_smpchunk - pos_listsize - 4); 150 | 151 | /* Write sample chunk */ 152 | fwrite((ext_smpl == 'v' ? "smpl" : "smpo"), 4, 1, f_sf_out); 153 | fwrite(&len_smpl, 4, 1, f_sf_out); 154 | sfo_copy(f_smpl, f_sf_out, len_smpl); 155 | fclose(f_smpl); 156 | 157 | /* Write data after sample chunk */ 158 | fseek(f_sf_in, end_smpchunk, SEEK_SET); 159 | sfo_copy(f_sf_in, f_sf_out, end_sf - end_smpchunk); 160 | fclose(f_sf_out); 161 | } 162 | else if (arg_smpl) 163 | { 164 | f_smpl = fopen(arg_smpl, "wb"); 165 | printf("Writing file '%s' with %s sample stream\n", arg_smpl, (is_pcm ? "PCM" : "OGG")); 166 | if (ext_smpl != 'v' && ext_smpl != 'g') printf(" Warning: Sample file has unknown file extension (should be .WAV or .OGG)\n"); 167 | if (ext_smpl == 'v' && !is_pcm) printf(" Warning: Sample file has .%s extension but sample stream is %s\n", "WAV", "OGG (should be .OGG)"); 168 | if (ext_smpl == 'g' && is_pcm) printf(" Warning: Sample file has .%s extension but sample stream is %s\n", "OGG", "PCM (should be .WAV)"); 169 | if (!f_smpl) { fprintf(stderr, "Unable to open output file '%s'\n\n", arg_smpl); goto print_usage; } 170 | 171 | if (is_pcm) 172 | { 173 | struct sfo_wavheader wav_hdr; 174 | memcpy(wav_hdr.Subchunk2ID, "data", 4); 175 | memcpy(wav_hdr.RIFF, "RIFF", 4); 176 | memcpy(wav_hdr.WAVE, "WAVE", 4); 177 | memcpy(wav_hdr.fmt, "fmt ", 4); 178 | wav_hdr.Subchunk1Size = 16; 179 | wav_hdr.AudioFormat = 1; 180 | wav_hdr.NumOfChan = 1; 181 | wav_hdr.Subchunk2Size = (unsigned int)chunk.size; 182 | wav_hdr.ChunkSize = sizeof(wav_hdr) - 4 - 4 + wav_hdr.Subchunk2Size; 183 | wav_hdr.SamplesPerSec = 22050; 184 | wav_hdr.bytesPerSec = 22050 * sizeof(short); 185 | wav_hdr.blockAlign = 1 * sizeof(short); 186 | wav_hdr.bitsPerSample = sizeof(short) * 8; 187 | fwrite(&wav_hdr, sizeof(wav_hdr), 1, f_smpl); 188 | } 189 | sfo_copy(f_sf_in, f_smpl, chunk.size); 190 | fclose(f_smpl); 191 | printf("DONE\n"); 192 | } 193 | fclose(f_sf_in); 194 | return 0; 195 | } 196 | } 197 | 198 | fprintf(stderr, "Passed input file is not a valid soundfont file\n\n"); 199 | goto print_usage; 200 | } 201 | -------------------------------------------------------------------------------- /tml.h: -------------------------------------------------------------------------------- 1 | /* TinyMidiLoader - v0.7 - Minimalistic midi parsing library - https://github.com/schellingb/TinySoundFont 2 | no warranty implied; use at your own risk 3 | Do this: 4 | #define TML_IMPLEMENTATION 5 | before you include this file in *one* C or C++ file to create the implementation. 6 | // i.e. it should look like this: 7 | #include ... 8 | #include ... 9 | #define TML_IMPLEMENTATION 10 | #include "tml.h" 11 | 12 | [OPTIONAL] #define TML_NO_STDIO to remove stdio dependency 13 | [OPTIONAL] #define TML_MALLOC, TML_REALLOC, and TML_FREE to avoid stdlib.h 14 | [OPTIONAL] #define TML_MEMCPY to avoid string.h 15 | 16 | LICENSE (ZLIB) 17 | 18 | Copyright (C) 2017, 2018 Bernhard Schelling 19 | 20 | This software is provided 'as-is', without any express or implied 21 | warranty. In no event will the authors be held liable for any damages 22 | arising from the use of this software. 23 | 24 | Permission is granted to anyone to use this software for any purpose, 25 | including commercial applications, and to alter it and redistribute it 26 | freely, subject to the following restrictions: 27 | 28 | 1. The origin of this software must not be misrepresented; you must not 29 | claim that you wrote the original software. If you use this software 30 | in a product, an acknowledgment in the product documentation would be 31 | appreciated but is not required. 32 | 2. Altered source versions must be plainly marked as such, and must not be 33 | misrepresented as being the original software. 34 | 3. This notice may not be removed or altered from any source distribution. 35 | 36 | */ 37 | 38 | #ifndef TML_INCLUDE_TML_INL 39 | #define TML_INCLUDE_TML_INL 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | // Define this if you want the API functions to be static 46 | #ifdef TML_STATIC 47 | #define TMLDEF static 48 | #else 49 | #define TMLDEF extern 50 | #endif 51 | 52 | // Channel message type 53 | enum TMLMessageType 54 | { 55 | TML_NOTE_OFF = 0x80, TML_NOTE_ON = 0x90, TML_KEY_PRESSURE = 0xA0, TML_CONTROL_CHANGE = 0xB0, TML_PROGRAM_CHANGE = 0xC0, TML_CHANNEL_PRESSURE = 0xD0, TML_PITCH_BEND = 0xE0, TML_SET_TEMPO = 0x51 56 | }; 57 | 58 | // Midi controller numbers 59 | enum TMLController 60 | { 61 | TML_BANK_SELECT_MSB, TML_MODULATIONWHEEL_MSB, TML_BREATH_MSB, TML_FOOT_MSB = 4, TML_PORTAMENTO_TIME_MSB, TML_DATA_ENTRY_MSB, TML_VOLUME_MSB, 62 | TML_BALANCE_MSB, TML_PAN_MSB = 10, TML_EXPRESSION_MSB, TML_EFFECTS1_MSB, TML_EFFECTS2_MSB, TML_GPC1_MSB = 16, TML_GPC2_MSB, TML_GPC3_MSB, TML_GPC4_MSB, 63 | TML_BANK_SELECT_LSB = 32, TML_MODULATIONWHEEL_LSB, TML_BREATH_LSB, TML_FOOT_LSB = 36, TML_PORTAMENTO_TIME_LSB, TML_DATA_ENTRY_LSB, TML_VOLUME_LSB, 64 | TML_BALANCE_LSB, TML_PAN_LSB = 42, TML_EXPRESSION_LSB, TML_EFFECTS1_LSB, TML_EFFECTS2_LSB, TML_GPC1_LSB = 48, TML_GPC2_LSB, TML_GPC3_LSB, TML_GPC4_LSB, 65 | TML_SUSTAIN_SWITCH = 64, TML_PORTAMENTO_SWITCH, TML_SOSTENUTO_SWITCH, TML_SOFT_PEDAL_SWITCH, TML_LEGATO_SWITCH, TML_HOLD2_SWITCH, 66 | TML_SOUND_CTRL1, TML_SOUND_CTRL2, TML_SOUND_CTRL3, TML_SOUND_CTRL4, TML_SOUND_CTRL5, TML_SOUND_CTRL6, 67 | TML_SOUND_CTRL7, TML_SOUND_CTRL8, TML_SOUND_CTRL9, TML_SOUND_CTRL10, TML_GPC5, TML_GPC6, TML_GPC7, TML_GPC8, 68 | TML_PORTAMENTO_CTRL, TML_FX_REVERB = 91, TML_FX_TREMOLO, TML_FX_CHORUS, TML_FX_CELESTE_DETUNE, TML_FX_PHASER, 69 | TML_DATA_ENTRY_INCR, TML_DATA_ENTRY_DECR, TML_NRPN_LSB, TML_NRPN_MSB, TML_RPN_LSB, TML_RPN_MSB, 70 | TML_ALL_SOUND_OFF = 120, TML_ALL_CTRL_OFF, TML_LOCAL_CONTROL, TML_ALL_NOTES_OFF, TML_OMNI_OFF, TML_OMNI_ON, TML_POLY_OFF, TML_POLY_ON 71 | }; 72 | 73 | // A single MIDI message linked to the next message in time 74 | typedef struct tml_message 75 | { 76 | // Time of the message in milliseconds 77 | unsigned int time; 78 | 79 | // Type (see TMLMessageType) and channel number 80 | unsigned char type, channel; 81 | 82 | // 2 byte of parameter data based on the type: 83 | // - key, velocity for TML_NOTE_ON and TML_NOTE_OFF messages 84 | // - key, key_pressure for TML_KEY_PRESSURE messages 85 | // - control, control_value for TML_CONTROL_CHANGE messages (see TMLController) 86 | // - program for TML_PROGRAM_CHANGE messages 87 | // - channel_pressure for TML_CHANNEL_PRESSURE messages 88 | // - pitch_bend for TML_PITCH_BEND messages 89 | union 90 | { 91 | struct { union { char key, control, program, channel_pressure; }; union { char velocity, key_pressure, control_value; }; }; 92 | struct { unsigned short pitch_bend; }; 93 | }; 94 | 95 | //extra 96 | int trackNum; 97 | char* markerName; 98 | // The pointer to the next message in time following this event 99 | struct tml_message* next; 100 | } tml_message; 101 | 102 | // The load functions will return a pointer to a struct tml_message. 103 | // Normally the linked list gets traversed by following the next pointers. 104 | // Make sure to keep the pointer to the first message to free the memory. 105 | // On error the tml_load* functions will return NULL most likely due to an 106 | // invalid MIDI stream (or if the file did not exist in tml_load_filename). 107 | 108 | #ifndef TML_NO_STDIO 109 | // Directly load a MIDI file from a .mid file path 110 | TMLDEF tml_message* tml_load_filename(const char* filename); 111 | #endif 112 | 113 | // Load a MIDI file from a block of memory 114 | TMLDEF tml_message* tml_load_memory(const void* buffer, int size); 115 | 116 | // Get infos about this loaded MIDI file, returns the note count 117 | // NULL can be passed for any output value pointer if not needed. 118 | // used_channels: Will be set to how many channels play notes 119 | // (i.e. 1 if channel 15 is used but no other) 120 | // used_programs: Will be set to how many different programs are used 121 | // total_notes: Will be set to the total number of note on messages 122 | // time_first_note: Will be set to the time of the first note on message 123 | // time_length: Will be set to the total time in milliseconds 124 | TMLDEF int tml_get_info(tml_message* first_message, int* used_channels, int* used_programs, int* total_notes, unsigned int* time_first_note, unsigned int* time_length); 125 | 126 | // Read the tempo (microseconds per quarter note) value from a message with the type TML_SET_TEMPO 127 | TMLDEF int tml_get_tempo_value(tml_message* set_tempo_message); 128 | 129 | // Free all the memory of the linked message list (can also call free() manually) 130 | TMLDEF void tml_free(tml_message* f); 131 | 132 | // Stream structure for the generic loading 133 | struct tml_stream 134 | { 135 | // Custom data given to the functions as the first parameter 136 | void* data; 137 | 138 | // Function pointer will be called to read 'size' bytes into ptr (returns number of read bytes) 139 | int (*read)(void* data, void* ptr, unsigned int size); 140 | }; 141 | 142 | // Generic Midi loading method using the stream structure above 143 | TMLDEF tml_message* tml_load(struct tml_stream* stream); 144 | //TMLDEF tml_message* tml_load_tsf_stream(struct tsf_stream* stream); 145 | 146 | #ifdef __cplusplus 147 | } 148 | #endif 149 | 150 | // end header 151 | // --------------------------------------------------------------------------------------------------------- 152 | #endif //TML_INCLUDE_TML_INL 153 | 154 | #ifdef TML_IMPLEMENTATION 155 | 156 | #if !defined(TML_MALLOC) || !defined(TML_FREE) || !defined(TML_REALLOC) 157 | # include 158 | # define TML_MALLOC malloc 159 | # define TML_FREE free 160 | # define TML_REALLOC realloc 161 | #endif 162 | 163 | #if !defined(TML_MEMCPY) 164 | # include 165 | # define TML_MEMCPY memcpy 166 | #endif 167 | 168 | #ifndef TML_NO_STDIO 169 | # include 170 | #endif 171 | 172 | #define TML_NULL 0 173 | 174 | ////crash on errors and warnings to find broken midi files while debugging 175 | //#define TML_ERROR(msg) *(int*)0 = 0xbad; 176 | //#define TML_WARN(msg) *(int*)0 = 0xf00d; 177 | 178 | ////print errors and warnings 179 | //#define TML_ERROR(msg) printf("ERROR: %s\n", msg); 180 | //#define TML_WARN(msg) printf("WARNING: %s\n", msg); 181 | 182 | #ifndef TML_ERROR 183 | #define TML_ERROR(msg) 184 | #endif 185 | 186 | #ifndef TML_WARN 187 | #define TML_WARN(msg) 188 | #endif 189 | 190 | #ifdef __cplusplus 191 | extern "C" { 192 | #endif 193 | 194 | #ifndef TML_NO_STDIO 195 | static int tml_stream_stdio_read(FILE* f, void* ptr, unsigned int size) { return (int)fread(ptr, 1, size, f); } 196 | TMLDEF tml_message* tml_load_filename(const char* filename) 197 | { 198 | struct tml_message* res; 199 | struct tml_stream stream = { TML_NULL, (int(*)(void*,void*,unsigned int))&tml_stream_stdio_read }; 200 | #if __STDC_WANT_SECURE_LIB__ 201 | FILE* f = TML_NULL; fopen_s(&f, filename, "rb"); 202 | #else 203 | FILE* f = fopen(filename, "rb"); 204 | #endif 205 | if (!f) { TML_ERROR("File not found"); return 0; } 206 | stream.data = f; 207 | res = tml_load(&stream); 208 | fclose(f); 209 | return res; 210 | } 211 | #endif 212 | 213 | struct tml_stream_memory { const char* buffer; unsigned int total, pos; }; 214 | static int tml_stream_memory_read(struct tml_stream_memory* m, void* ptr, unsigned int size) { if (size > m->total - m->pos) size = m->total - m->pos; TML_MEMCPY(ptr, m->buffer+m->pos, size); m->pos += size; return size; } 215 | TMLDEF struct tml_message* tml_load_memory(const void* buffer, int size) 216 | { 217 | struct tml_stream stream = { TML_NULL, (int(*)(void*,void*,unsigned int))&tml_stream_memory_read }; 218 | struct tml_stream_memory f = { 0, 0, 0 }; 219 | f.buffer = (const char*)buffer; 220 | f.total = size; 221 | stream.data = &f; 222 | return tml_load(&stream); 223 | } 224 | 225 | struct tml_track 226 | { 227 | unsigned int Idx, End, Ticks; 228 | }; 229 | 230 | struct tml_tempomsg 231 | { 232 | unsigned int time; 233 | unsigned char type, Tempo[3]; 234 | tml_message* next; 235 | }; 236 | 237 | struct tml_parser 238 | { 239 | unsigned char *buf, *buf_end; 240 | int last_status, message_array_size, message_count; 241 | }; 242 | 243 | enum TMLSystemType 244 | { 245 | TML_TEXT = 0x01, TML_COPYRIGHT = 0x02, TML_TRACK_NAME = 0x03, TML_INST_NAME = 0x04, TML_LYRIC = 0x05, TML_MARKER = 0x06, TML_CUE_POINT = 0x07, 246 | TML_EOT = 0x2f, TML_SMPTE_OFFSET = 0x54, TML_TIME_SIGNATURE = 0x58, TML_KEY_SIGNATURE = 0x59, TML_SEQUENCER_EVENT = 0x7f, 247 | TML_SYSEX = 0xf0, TML_TIME_CODE = 0xf1, TML_SONG_POSITION = 0xf2, TML_SONG_SELECT = 0xf3, TML_TUNE_REQUEST = 0xf6, TML_EOX = 0xf7, TML_SYNC = 0xf8, 248 | TML_TICK = 0xf9, TML_START = 0xfa, TML_CONTINUE = 0xfb, TML_STOP = 0xfc, TML_ACTIVE_SENSING = 0xfe, TML_SYSTEM_RESET = 0xff 249 | }; 250 | 251 | static int tml_readbyte(struct tml_parser* p) 252 | { 253 | return (p->buf == p->buf_end ? -1 : *(p->buf++)); 254 | } 255 | 256 | static int tml_readvariablelength(struct tml_parser* p) 257 | { 258 | unsigned int res = 0, i = 0; 259 | unsigned char c; 260 | for (; i != 4; i++) 261 | { 262 | if (p->buf == p->buf_end) { TML_WARN("Unexpected end of file"); return -1; } 263 | c = *(p->buf++); 264 | if (c & 0x80) res = ((res | (c & 0x7F)) << 7); 265 | else return (int)(res | c); 266 | } 267 | TML_WARN("Invalid variable length byte count"); return -1; 268 | } 269 | 270 | static int tml_parsemessage(tml_message** f, struct tml_parser* p, int tNum) 271 | { 272 | int deltatime = tml_readvariablelength(p), status = tml_readbyte(p); 273 | tml_message* evt; 274 | 275 | if (deltatime & 0xFFF00000) deltatime = 0; //throw away delays that are insanely high for malformatted midis 276 | if (status < 0) { TML_WARN("Unexpected end of file"); return -1; } 277 | if ((status & 0x80) == 0) 278 | { 279 | // Invalid, use same status as before 280 | if ((p->last_status & 0x80) == 0) { TML_WARN("Undefined status and invalid running status"); return -1; } 281 | p->buf--; 282 | status = p->last_status; 283 | } 284 | else p->last_status = status; 285 | 286 | if (p->message_array_size == p->message_count) 287 | { 288 | //start allocated memory size of message array at 64, double each time until 8192, then add 1024 entries until done 289 | p->message_array_size += (!p->message_array_size ? 64 : (p->message_array_size > 4096 ? 1024 : p->message_array_size)); 290 | *f = (tml_message*)TML_REALLOC(*f, p->message_array_size * sizeof(tml_message)); 291 | if (!*f) { TML_ERROR("Out of memory"); return -1; } 292 | } 293 | evt = *f + p->message_count; 294 | 295 | //check what message we have 296 | if ((status == TML_SYSEX) || (status == TML_EOX)) //sysex 297 | { 298 | //sysex messages are not handled 299 | p->buf += tml_readvariablelength(p); 300 | if (p->buf > p->buf_end) { TML_WARN("Unexpected end of file"); p->buf = p->buf_end; return -1; } 301 | evt->type = 0; 302 | } 303 | else if (status == 0xFF) //meta events 304 | { 305 | int meta_type = tml_readbyte(p), buflen = tml_readvariablelength(p); 306 | unsigned char* metadata = p->buf; 307 | if (meta_type < 0) { TML_WARN("Unexpected end of file"); return -1; } 308 | if (buflen > 0 && (p->buf += buflen) > p->buf_end) { TML_WARN("Unexpected end of file"); p->buf = p->buf_end; return -1; } 309 | 310 | switch (meta_type) 311 | { 312 | case TML_EOT: 313 | if (buflen != 0) { TML_WARN("Invalid length for EndOfTrack event"); return -1; } 314 | if (!deltatime) return TML_EOT; //no need to store this message 315 | evt->type = TML_EOT; 316 | break; 317 | 318 | case TML_SET_TEMPO: 319 | if (buflen != 3) { TML_WARN("Invalid length for SetTempo meta event"); return -1; } 320 | evt->type = TML_SET_TEMPO; 321 | ((struct tml_tempomsg*)evt)->Tempo[0] = metadata[0]; 322 | ((struct tml_tempomsg*)evt)->Tempo[1] = metadata[1]; 323 | ((struct tml_tempomsg*)evt)->Tempo[2] = metadata[2]; 324 | break; 325 | case TML_MARKER: 326 | if (buflen == 0) { TML_WARN("Invalid length for Marker event"); return -1; } 327 | evt->type = TML_MARKER; 328 | evt->markerName = (char*)malloc(buflen + 1); 329 | memcpy(evt->markerName, metadata, buflen); 330 | evt->markerName[buflen] = '\0'; 331 | break; 332 | default: 333 | evt->type = 0; 334 | } 335 | } 336 | else //channel message 337 | { 338 | int param1=0, param2=0; 339 | if ((param1 = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; } 340 | evt->key = (param1 & 0x7f); 341 | evt->channel = (status & 0x0f); 342 | 343 | switch (evt->type = (status & 0xf0)) 344 | { 345 | case TML_NOTE_OFF: 346 | case TML_NOTE_ON: 347 | case TML_KEY_PRESSURE: 348 | case TML_CONTROL_CHANGE: 349 | if ((param2 = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; } 350 | evt->velocity = (param2 & 0x7f); 351 | break; 352 | 353 | case TML_PITCH_BEND: 354 | if ((param2 = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; } 355 | evt->pitch_bend = ((param2 & 0x7f) << 7) | evt->key; 356 | break; 357 | 358 | case TML_PROGRAM_CHANGE: 359 | case TML_CHANNEL_PRESSURE: 360 | evt->velocity = 0; 361 | break; 362 | 363 | default: //ignore system/manufacture messages 364 | evt->type = 0; 365 | break; 366 | } 367 | 368 | evt->trackNum = tNum; 369 | } 370 | 371 | if (deltatime || evt->type) 372 | { 373 | evt->time = deltatime; 374 | p->message_count++; 375 | } 376 | return evt->type; 377 | } 378 | 379 | TMLDEF tml_message* tml_load(struct tml_stream* stream) 380 | { 381 | int num_tracks, division, trackbufsize = 0; 382 | unsigned char midi_header[14], *trackbuf = TML_NULL; 383 | struct tml_message* messages = TML_NULL; 384 | struct tml_track *tracks, *t, *tracksEnd; 385 | struct tml_parser p = { TML_NULL, TML_NULL, 0, 0, 0 }; 386 | 387 | // Parse MIDI header 388 | if (stream->read(stream->data, midi_header, 14) != 14) { TML_ERROR("Unexpected end of file"); return messages; } 389 | if (midi_header[0] != 'M' || midi_header[1] != 'T' || midi_header[2] != 'h' || midi_header[3] != 'd' || 390 | midi_header[7] != 6 || midi_header[9] > 2) { TML_ERROR("Doesn't look like a MIDI file: invalid MThd header"); return messages; } 391 | if (midi_header[12] & 0x80) { TML_ERROR("File uses unsupported SMPTE timing"); return messages; } 392 | num_tracks = (int)(midi_header[10] << 8) | midi_header[11]; 393 | division = (int)(midi_header[12] << 8) | midi_header[13]; //division is ticks per beat (quarter-note) 394 | if (num_tracks <= 0 && division <= 0) { TML_ERROR("Doesn't look like a MIDI file: invalid track or division values"); return messages; } 395 | 396 | // Allocate temporary tracks array for parsing 397 | tracks = (struct tml_track*)TML_MALLOC(sizeof(struct tml_track) * num_tracks); 398 | tracksEnd = &tracks[num_tracks]; 399 | for (t = tracks; t != tracksEnd; t++) t->Idx = t->End = t->Ticks = 0; 400 | 401 | // Read all messages for all tracks 402 | int tNum = 0; 403 | for (t = tracks; t != tracksEnd; t++) 404 | { 405 | unsigned char track_header[8]; 406 | int track_length; 407 | if (stream->read(stream->data, track_header, 8) != 8) { TML_WARN("Unexpected end of file"); break; } 408 | if (track_header[0] != 'M' || track_header[1] != 'T' || track_header[2] != 'r' || track_header[3] != 'k') 409 | { TML_WARN("Invalid MTrk header"); break; } 410 | 411 | // Get size of track data and read into buffer (allocate bigger buffer if needed) 412 | track_length = track_header[7] | (track_header[6] << 8) | (track_header[5] << 16) | (track_header[4] << 24); 413 | if (track_length < 0) { TML_WARN("Invalid MTrk header"); break; } 414 | if (trackbufsize < track_length) { TML_FREE(trackbuf); trackbuf = (unsigned char*)TML_MALLOC(trackbufsize = track_length); } 415 | if (stream->read(stream->data, trackbuf, track_length) != track_length) { TML_WARN("Unexpected end of file"); break; } 416 | 417 | t->Idx = p.message_count; 418 | for (p.buf_end = (p.buf = trackbuf) + track_length; p.buf != p.buf_end;) 419 | { 420 | int type = tml_parsemessage(&messages, &p, tNum); 421 | if (type == TML_EOT || type < 0) break; //file end or illegal data encountered 422 | } 423 | tNum++; 424 | if (p.buf != p.buf_end) { TML_WARN( "Track length did not match data length"); } 425 | t->End = p.message_count; 426 | } 427 | TML_FREE(trackbuf); 428 | 429 | // Change message time signature from delta ticks to actual msec values and link messages ordered by time 430 | if (p.message_count) 431 | { 432 | tml_message *PrevMessage = TML_NULL, *Msg, *MsgEnd, Swap; 433 | unsigned int ticks = 0, tempo_ticks = 0; //tick counter and value at last tempo change 434 | int step_smallest, msec, tempo_msec = 0; //msec value at last tempo change 435 | double ticks2time = 500000 / (1000.0 * division); //milliseconds per tick 436 | 437 | // Loop through all messages over all tracks ordered by time 438 | for (step_smallest = 0; step_smallest != 0x7fffffff; ticks += step_smallest) 439 | { 440 | step_smallest = 0x7fffffff; 441 | msec = tempo_msec + (int)((ticks - tempo_ticks) * ticks2time); 442 | for (t = tracks; t != tracksEnd; t++) 443 | { 444 | if (t->Idx == t->End) continue; 445 | for (Msg = &messages[t->Idx], MsgEnd = &messages[t->End]; Msg != MsgEnd && t->Ticks + Msg->time == ticks; Msg++, t->Idx++) 446 | { 447 | t->Ticks += Msg->time; 448 | if (Msg->type == TML_SET_TEMPO) 449 | { 450 | unsigned char* Tempo = ((struct tml_tempomsg*)Msg)->Tempo; 451 | ticks2time = ((Tempo[0]<<16)|(Tempo[1]<<8)|Tempo[2])/(1000.0 * division); 452 | tempo_msec = msec; 453 | tempo_ticks = ticks; 454 | } 455 | if (Msg->type) 456 | { 457 | Msg->time = msec; 458 | if (PrevMessage) { PrevMessage->next = Msg; PrevMessage = Msg; } 459 | else { Swap = *Msg; *Msg = *messages; *messages = Swap; PrevMessage = messages; } 460 | } 461 | } 462 | if (Msg != MsgEnd && t->Ticks + Msg->time > ticks) 463 | { 464 | int step = (int)(t->Ticks + Msg->time - ticks); 465 | if (step < step_smallest) step_smallest = step; 466 | } 467 | } 468 | } 469 | if (PrevMessage) PrevMessage->next = TML_NULL; 470 | else p.message_count = 0; 471 | } 472 | TML_FREE(tracks); 473 | 474 | if (p.message_count == 0) 475 | { 476 | TML_FREE(messages); 477 | messages = TML_NULL; 478 | } 479 | 480 | return messages; 481 | } 482 | 483 | /* 484 | TMLDEF tml_message* tml_load_tsf_stream(struct tsf_stream* stream) 485 | { 486 | return tml_load((struct tml_stream*)stream); 487 | } 488 | */ 489 | 490 | TMLDEF int tml_get_info(tml_message* Msg, int* out_used_channels, int* out_used_programs, int* out_total_notes, unsigned int* out_time_first_note, unsigned int* out_time_length) 491 | { 492 | int used_programs = 0, used_channels = 0, total_notes = 0; 493 | unsigned int time_first_note = 0xffffffff, time_length = 0; 494 | unsigned char channels[16] = { 0 }, programs[128] = { 0 }; 495 | for (;Msg; Msg = Msg->next) 496 | { 497 | time_length = Msg->time; 498 | if (Msg->type == TML_PROGRAM_CHANGE && !programs[(int)Msg->program]) { programs[(int)Msg->program] = 1; used_programs++; } 499 | if (Msg->type != TML_NOTE_ON) continue; 500 | if (time_first_note == 0xffffffff) time_first_note = time_length; 501 | if (!channels[Msg->channel]) { channels[Msg->channel] = 1; used_channels++; } 502 | total_notes++; 503 | } 504 | if (time_first_note == 0xffffffff) time_first_note = 0; 505 | if (out_used_channels ) *out_used_channels = used_channels; 506 | if (out_used_programs ) *out_used_programs = used_programs; 507 | if (out_total_notes ) *out_total_notes = total_notes; 508 | if (out_time_first_note) *out_time_first_note = time_first_note; 509 | if (out_time_length ) *out_time_length = time_length; 510 | return total_notes; 511 | } 512 | 513 | TMLDEF int tml_get_tempo_value(tml_message* msg) 514 | { 515 | unsigned char* Tempo; 516 | if (!msg || msg->type != TML_SET_TEMPO) return 0; 517 | Tempo = ((struct tml_tempomsg*)msg)->Tempo; 518 | return ((Tempo[0]<<16)|(Tempo[1]<<8)|Tempo[2]); 519 | } 520 | 521 | TMLDEF void tml_free(tml_message* f) 522 | { 523 | TML_FREE(f); 524 | } 525 | 526 | #ifdef __cplusplus 527 | } 528 | #endif 529 | 530 | #endif //TML_IMPLEMENTATION 531 | -------------------------------------------------------------------------------- /tsf.h: -------------------------------------------------------------------------------- 1 | /* TinySoundFont - v0.9 - SoundFont2 synthesizer - https://github.com/schellingb/TinySoundFont 2 | no warranty implied; use at your own risk 3 | Do this: 4 | #define TSF_IMPLEMENTATION 5 | before you include this file in *one* C or C++ file to create the implementation. 6 | // i.e. it should look like this: 7 | #include ... 8 | #include ... 9 | #define TSF_IMPLEMENTATION 10 | #include "tsf.h" 11 | 12 | [OPTIONAL] #define TSF_NO_STDIO to remove stdio dependency 13 | [OPTIONAL] #define TSF_MALLOC, TSF_REALLOC, and TSF_FREE to avoid stdlib.h 14 | [OPTIONAL] #define TSF_MEMCPY, TSF_MEMSET to avoid string.h 15 | [OPTIONAL] #define TSF_POW, TSF_POWF, TSF_EXPF, TSF_LOG, TSF_TAN, TSF_LOG10, TSF_SQRT to avoid math.h 16 | 17 | NOT YET IMPLEMENTED 18 | - Support for ChorusEffectsSend and ReverbEffectsSend generators 19 | - Better low-pass filter without lowering performance too much 20 | - Support for modulators 21 | 22 | LICENSE (MIT) 23 | 24 | Copyright (C) 2017-2023 Bernhard Schelling 25 | Based on SFZero, Copyright (C) 2012 Steve Folta (https://github.com/stevefolta/SFZero) 26 | 27 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 28 | software and associated documentation files (the "Software"), to deal in the Software 29 | without restriction, including without limitation the rights to use, copy, modify, merge, 30 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 31 | to whom the Software is furnished to do so, subject to the following conditions: 32 | 33 | The above copyright notice and this permission notice shall be included in all 34 | copies or substantial portions of the Software. 35 | 36 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 37 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 38 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 39 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 40 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 41 | USE OR OTHER DEALINGS IN THE SOFTWARE. 42 | 43 | */ 44 | 45 | #ifndef TSF_INCLUDE_TSF_INL 46 | #define TSF_INCLUDE_TSF_INL 47 | 48 | #ifdef __cplusplus 49 | extern "C" { 50 | # define CPP_DEFAULT0 = 0 51 | #else 52 | # define CPP_DEFAULT0 53 | #endif 54 | 55 | //define this if you want the API functions to be static 56 | #ifdef TSF_STATIC 57 | #define TSFDEF static 58 | #else 59 | #define TSFDEF extern 60 | #endif 61 | 62 | // The load functions will return a pointer to a struct tsf which all functions 63 | // thereafter take as the first parameter. 64 | // On error the tsf_load* functions will return NULL most likely due to invalid 65 | // data (or if the file did not exist in tsf_load_filename). 66 | typedef struct tsf tsf; 67 | 68 | #ifndef TSF_NO_STDIO 69 | // Directly load a SoundFont from a .sf2 file path 70 | TSFDEF tsf* tsf_load_filename(const char* filename); 71 | #endif 72 | 73 | TSFDEF int tsf_count_held_sustain(tsf* f); 74 | 75 | // Load a SoundFont from a block of memory 76 | TSFDEF tsf* tsf_load_memory(const void* buffer, int size); 77 | 78 | // Stream structure for the generic loading 79 | struct tsf_stream 80 | { 81 | // Custom data given to the functions as the first parameter 82 | void* data; 83 | 84 | // Function pointer will be called to read 'size' bytes into ptr (returns number of read bytes) 85 | int (*read)(void* data, void* ptr, unsigned int size); 86 | 87 | // Function pointer will be called to skip ahead over 'count' bytes (returns 1 on success, 0 on error) 88 | int (*skip)(void* data, unsigned int count); 89 | }; 90 | 91 | // Generic SoundFont loading method using the stream structure above 92 | TSFDEF tsf* tsf_load(struct tsf_stream* stream); 93 | 94 | // Copy a tsf instance from an existing one, use tsf_close to close it as well. 95 | // All copied tsf instances and their original instance are linked, and share the underlying soundfont. 96 | // This allows loading a soundfont only once, but using it for multiple independent playbacks. 97 | // (This function isn't thread-safe without locking.) 98 | TSFDEF tsf* tsf_copy(tsf* f); 99 | 100 | // Free the memory related to this tsf instance 101 | TSFDEF void tsf_close(tsf* f); 102 | 103 | // Stop all playing notes immediately and reset all channel parameters 104 | TSFDEF void tsf_reset(tsf* f); 105 | 106 | // Returns the preset index from a bank and preset number, or -1 if it does not exist in the loaded SoundFont 107 | TSFDEF int tsf_get_presetindex(const tsf* f, int bank, int preset_number); 108 | 109 | // Returns the number of presets in the loaded SoundFont 110 | TSFDEF int tsf_get_presetcount(const tsf* f); 111 | 112 | // Returns the name of a preset index >= 0 and < tsf_get_presetcount() 113 | TSFDEF const char* tsf_get_presetname(const tsf* f, int preset_index); 114 | 115 | // Returns the name of a preset by bank and preset number 116 | TSFDEF const char* tsf_bank_get_presetname(const tsf* f, int bank, int preset_number); 117 | 118 | // Supported output modes by the render methods 119 | enum TSFOutputMode 120 | { 121 | // Two channels with single left/right samples one after another 122 | TSF_STEREO_INTERLEAVED, 123 | // Two channels with all samples for the left channel first then right 124 | TSF_STEREO_UNWEAVED, 125 | // A single channel (stereo instruments are mixed into center) 126 | TSF_MONO 127 | }; 128 | 129 | // Thread safety: 130 | // 131 | // 1. Rendering / voices: 132 | // 133 | // Your audio output which calls the tsf_render* functions will most likely 134 | // run on a different thread than where the playback tsf_note* functions 135 | // are called. In which case some sort of concurrency control like a 136 | // mutex needs to be used so they are not called at the same time. 137 | // Alternatively, you can pre-allocate a maximum number of voices that can 138 | // play simultaneously by calling tsf_set_max_voices after loading. 139 | // That way memory re-allocation will not happen during tsf_note_on and 140 | // TSF should become mostly thread safe. 141 | // There is a theoretical chance that ending notes would negatively influence 142 | // a voice that is rendering at the time but it is hard to say. 143 | // Also be aware, this has not been tested much. 144 | // 145 | // 2. Channels: 146 | // 147 | // Calls to tsf_channel_set_... functions may allocate new channels 148 | // if no channel with that number was previously used. Make sure to 149 | // create all channels at the beginning as required if you call tsf_render* 150 | // from a different thread. 151 | 152 | // Setup the parameters for the voice render methods 153 | // outputmode: if mono or stereo and how stereo channel data is ordered 154 | // samplerate: the number of samples per second (output frequency) 155 | // global_gain_db: volume gain in decibels (>0 means higher, <0 means lower) 156 | TSFDEF void tsf_set_output(tsf* f, enum TSFOutputMode outputmode, int samplerate, float global_gain_db CPP_DEFAULT0); 157 | 158 | // Set the global gain as a volume factor 159 | // global_gain: the desired volume where 1.0 is 100% 160 | TSFDEF void tsf_set_volume(tsf* f, float global_gain); 161 | 162 | // Set the maximum number of voices to play simultaneously 163 | // Depending on the soundfond, one note can cause many new voices to be started, 164 | // so don't keep this number too low or otherwise sounds may not play. 165 | // max_voices: maximum number to pre-allocate and set the limit to 166 | // (tsf_set_max_voices returns 0 if allocation failed, otherwise 1) 167 | TSFDEF int tsf_set_max_voices(tsf* f, int max_voices); 168 | 169 | // Start playing a note 170 | // preset_index: preset index >= 0 and < tsf_get_presetcount() 171 | // key: note value between 0 and 127 (60 being middle C) 172 | // vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full) 173 | // bank: instrument bank number (alternative to preset_index) 174 | // preset_number: preset number (alternative to preset_index) 175 | // (tsf_note_on returns 0 if the allocation of a new voice failed, otherwise 1) 176 | // (tsf_bank_note_on returns 0 if preset does not exist or allocation failed, otherwise 1) 177 | TSFDEF int tsf_note_on(tsf* f, int preset_index, int key, float vel); 178 | TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel); 179 | 180 | // Stop playing a note 181 | // (bank_note_off returns 0 if preset does not exist, otherwise 1) 182 | TSFDEF void tsf_note_off(tsf* f, int preset_index, int key); 183 | TSFDEF int tsf_bank_note_off(tsf* f, int bank, int preset_number, int key); 184 | 185 | // Stop playing all notes (end with sustain and release) 186 | TSFDEF void tsf_note_off_all(tsf* f); 187 | 188 | // Returns the number of active voices 189 | TSFDEF int tsf_active_voice_count(tsf* f); 190 | 191 | // Render output samples into a buffer 192 | // You can either render as signed 16-bit values (tsf_render_short) or 193 | // as 32-bit float values (tsf_render_float) 194 | // buffer: target buffer of size samples * output_channels * sizeof(type) 195 | // samples: number of samples to render 196 | // flag_mixing: if 0 clear the buffer first, otherwise mix into existing data 197 | TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing CPP_DEFAULT0); 198 | TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing CPP_DEFAULT0); 199 | 200 | // Higher level channel based functions, set up channel parameters 201 | // channel: channel number 202 | // preset_index: preset index >= 0 and < tsf_get_presetcount() 203 | // preset_number: preset number (alternative to preset_index) 204 | // flag_mididrums: 0 for normal channels, otherwise apply MIDI drum channel rules 205 | // bank: instrument bank number (alternative to preset_index) 206 | // pan: stereo panning value from 0.0 (left) to 1.0 (right) (default 0.5 center) 207 | // volume: linear volume scale factor (default 1.0 full) 208 | // pitch_wheel: pitch wheel position 0 to 16383 (default 8192 unpitched) 209 | // pitch_range: range of the pitch wheel in semitones (default 2.0, total +/- 2 semitones) 210 | // tuning: tuning of all playing voices in semitones (default 0.0, standard (A440) tuning) 211 | // (tsf_set_preset_number and set_bank_preset return 0 if preset does not exist, otherwise 1) 212 | // (tsf_channel_set_... return 0 if a new channel needed allocation and that failed, otherwise 1) 213 | TSFDEF int tsf_channel_set_presetindex(tsf* f, int channel, int preset_index); 214 | TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums CPP_DEFAULT0); 215 | TSFDEF int tsf_channel_set_bank(tsf* f, int channel, int bank); 216 | TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number); 217 | TSFDEF int tsf_channel_set_pan(tsf* f, int channel, float pan); 218 | TSFDEF int tsf_channel_set_volume(tsf* f, int channel, float volume); 219 | TSFDEF int tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel); 220 | TSFDEF int tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range); 221 | TSFDEF int tsf_channel_set_tuning(tsf* f, int channel, float tuning); 222 | 223 | // Start or stop playing notes on a channel (needs channel preset to be set) 224 | // channel: channel number 225 | // key: note value between 0 and 127 (60 being middle C) 226 | // vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full) 227 | // (tsf_channel_note_on returns 0 on allocation failure of new voice, otherwise 1) 228 | TSFDEF int tsf_channel_note_on(tsf* f, int channel, int key, float vel); 229 | TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key); 230 | TSFDEF void tsf_channel_note_off_all(tsf* f, int channel); //end with sustain and release 231 | TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel); //end immediately 232 | 233 | // Apply a MIDI control change to the channel (not all controllers are supported!) 234 | // (tsf_channel_midi_control returns 0 on allocation failure of new channel, otherwise 1) 235 | TSFDEF int tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value); 236 | 237 | // Get current values set on the channels 238 | TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel); 239 | TSFDEF int tsf_channel_get_preset_bank(tsf* f, int channel); 240 | TSFDEF int tsf_channel_get_preset_number(tsf* f, int channel); 241 | TSFDEF float tsf_channel_get_pan(tsf* f, int channel); 242 | TSFDEF float tsf_channel_get_volume(tsf* f, int channel); 243 | TSFDEF int tsf_channel_get_pitchwheel(tsf* f, int channel); 244 | TSFDEF float tsf_channel_get_pitchrange(tsf* f, int channel); 245 | TSFDEF float tsf_channel_get_tuning(tsf* f, int channel); 246 | 247 | #ifdef __cplusplus 248 | # undef CPP_DEFAULT0 249 | } 250 | #endif 251 | 252 | // end header 253 | // --------------------------------------------------------------------------------------------------------- 254 | #endif //TSF_INCLUDE_TSF_INL 255 | 256 | #ifdef TSF_IMPLEMENTATION 257 | #undef TSF_IMPLEMENTATION 258 | 259 | // The lower this block size is the more accurate the effects are. 260 | // Increasing the value significantly lowers the CPU usage of the voice rendering. 261 | // If LFO affects the low-pass filter it can be hearable even as low as 8. 262 | #ifndef TSF_RENDER_EFFECTSAMPLEBLOCK 263 | #define TSF_RENDER_EFFECTSAMPLEBLOCK 64 264 | #endif 265 | 266 | // When using tsf_render_short, to do the conversion a buffer of a fixed size is 267 | // allocated on the stack. On low memory platforms this could be made smaller. 268 | // Increasing this above 512 should not have a significant impact on performance. 269 | // The value should be a multiple of TSF_RENDER_EFFECTSAMPLEBLOCK. 270 | #ifndef TSF_RENDER_SHORTBUFFERBLOCK 271 | #define TSF_RENDER_SHORTBUFFERBLOCK 512 272 | #endif 273 | 274 | // Grace release time for quick voice off (avoid clicking noise) 275 | #define TSF_FASTRELEASETIME 0.01f 276 | 277 | #if !defined(TSF_MALLOC) || !defined(TSF_FREE) || !defined(TSF_REALLOC) 278 | # include 279 | # define TSF_MALLOC malloc 280 | # define TSF_FREE free 281 | # define TSF_REALLOC realloc 282 | #endif 283 | 284 | #if !defined(TSF_MEMCPY) || !defined(TSF_MEMSET) 285 | # include 286 | # define TSF_MEMCPY memcpy 287 | # define TSF_MEMSET memset 288 | #endif 289 | 290 | #if !defined(TSF_POW) || !defined(TSF_POWF) || !defined(TSF_EXPF) || !defined(TSF_LOG) || !defined(TSF_TAN) || !defined(TSF_LOG10) || !defined(TSF_SQRT) 291 | # include 292 | # if !defined(__cplusplus) && !defined(NAN) && !defined(powf) && !defined(expf) && !defined(sqrtf) 293 | # define powf (float)pow // deal with old math.h 294 | # define expf (float)exp // files that come without 295 | # define sqrtf (float)sqrt // powf, expf and sqrtf 296 | # endif 297 | # define TSF_POW pow 298 | # define TSF_POWF powf 299 | # define TSF_EXPF expf 300 | # define TSF_LOG log 301 | # define TSF_TAN tan 302 | # define TSF_LOG10 log10 303 | # define TSF_SQRTF sqrtf 304 | #endif 305 | 306 | #ifndef TSF_NO_STDIO 307 | # include 308 | #endif 309 | 310 | #define TSF_TRUE 1 311 | #define TSF_FALSE 0 312 | #define TSF_BOOL char 313 | #define TSF_PI 3.14159265358979323846264338327950288 314 | #define TSF_NULL 0 315 | 316 | #ifdef __cplusplus 317 | extern "C" { 318 | #endif 319 | 320 | typedef char tsf_fourcc[4]; 321 | typedef signed char tsf_s8; 322 | typedef unsigned char tsf_u8; 323 | typedef unsigned short tsf_u16; 324 | typedef signed short tsf_s16; 325 | typedef unsigned int tsf_u32; 326 | typedef char tsf_char20[20]; 327 | 328 | #define TSF_FourCCEquals(value1, value2) (value1[0] == value2[0] && value1[1] == value2[1] && value1[2] == value2[2] && value1[3] == value2[3]) 329 | 330 | struct tsf 331 | { 332 | struct tsf_preset* presets; 333 | float* fontSamples; 334 | struct tsf_voice* voices; 335 | struct tsf_channels* channels; 336 | 337 | int presetNum; 338 | int voiceNum; 339 | int maxVoiceNum; 340 | unsigned int voicePlayIndex; 341 | 342 | enum TSFOutputMode outputmode; 343 | float outSampleRate; 344 | float globalGainDB; 345 | int* refCount; 346 | }; 347 | 348 | #ifndef TSF_NO_STDIO 349 | static int tsf_stream_stdio_read(FILE* f, void* ptr, unsigned int size) { return (int)fread(ptr, 1, size, f); } 350 | static int tsf_stream_stdio_skip(FILE* f, unsigned int count) { return !fseek(f, count, SEEK_CUR); } 351 | TSFDEF tsf* tsf_load_filename(const char* filename) 352 | { 353 | tsf* res; 354 | struct tsf_stream stream = { TSF_NULL, (int(*)(void*,void*,unsigned int))&tsf_stream_stdio_read, (int(*)(void*,unsigned int))&tsf_stream_stdio_skip }; 355 | #if __STDC_WANT_SECURE_LIB__ 356 | FILE* f = TSF_NULL; fopen_s(&f, filename, "rb"); 357 | #else 358 | FILE* f = fopen(filename, "rb"); 359 | #endif 360 | if (!f) 361 | { 362 | //if (e) *e = TSF_FILENOTFOUND; 363 | return TSF_NULL; 364 | } 365 | stream.data = f; 366 | res = tsf_load(&stream); 367 | fclose(f); 368 | return res; 369 | } 370 | #endif 371 | 372 | struct tsf_stream_memory { const char* buffer; unsigned int total, pos; }; 373 | static int tsf_stream_memory_read(struct tsf_stream_memory* m, void* ptr, unsigned int size) { if (size > m->total - m->pos) size = m->total - m->pos; TSF_MEMCPY(ptr, m->buffer+m->pos, size); m->pos += size; return size; } 374 | static int tsf_stream_memory_skip(struct tsf_stream_memory* m, unsigned int count) { if (m->pos + count > m->total) return 0; m->pos += count; return 1; } 375 | TSFDEF tsf* tsf_load_memory(const void* buffer, int size) 376 | { 377 | struct tsf_stream stream = { TSF_NULL, (int(*)(void*,void*,unsigned int))&tsf_stream_memory_read, (int(*)(void*,unsigned int))&tsf_stream_memory_skip }; 378 | struct tsf_stream_memory f = { 0, 0, 0 }; 379 | f.buffer = (const char*)buffer; 380 | f.total = size; 381 | stream.data = &f; 382 | return tsf_load(&stream); 383 | } 384 | 385 | enum { TSF_LOOPMODE_NONE, TSF_LOOPMODE_CONTINUOUS, TSF_LOOPMODE_SUSTAIN }; 386 | 387 | enum { TSF_SEGMENT_NONE, TSF_SEGMENT_DELAY, TSF_SEGMENT_ATTACK, TSF_SEGMENT_HOLD, TSF_SEGMENT_DECAY, TSF_SEGMENT_SUSTAIN, TSF_SEGMENT_RELEASE, TSF_SEGMENT_DONE }; 388 | 389 | struct tsf_hydra 390 | { 391 | struct tsf_hydra_phdr *phdrs; struct tsf_hydra_pbag *pbags; struct tsf_hydra_pmod *pmods; 392 | struct tsf_hydra_pgen *pgens; struct tsf_hydra_inst *insts; struct tsf_hydra_ibag *ibags; 393 | struct tsf_hydra_imod *imods; struct tsf_hydra_igen *igens; struct tsf_hydra_shdr *shdrs; 394 | int phdrNum, pbagNum, pmodNum, pgenNum, instNum, ibagNum, imodNum, igenNum, shdrNum; 395 | }; 396 | 397 | union tsf_hydra_genamount { struct { tsf_u8 lo, hi; } range; tsf_s16 shortAmount; tsf_u16 wordAmount; }; 398 | struct tsf_hydra_phdr { tsf_char20 presetName; tsf_u16 preset, bank, presetBagNdx; tsf_u32 library, genre, morphology; }; 399 | struct tsf_hydra_pbag { tsf_u16 genNdx, modNdx; }; 400 | struct tsf_hydra_pmod { tsf_u16 modSrcOper, modDestOper; tsf_s16 modAmount; tsf_u16 modAmtSrcOper, modTransOper; }; 401 | struct tsf_hydra_pgen { tsf_u16 genOper; union tsf_hydra_genamount genAmount; }; 402 | struct tsf_hydra_inst { tsf_char20 instName; tsf_u16 instBagNdx; }; 403 | struct tsf_hydra_ibag { tsf_u16 instGenNdx, instModNdx; }; 404 | struct tsf_hydra_imod { tsf_u16 modSrcOper, modDestOper; tsf_s16 modAmount; tsf_u16 modAmtSrcOper, modTransOper; }; 405 | struct tsf_hydra_igen { tsf_u16 genOper; union tsf_hydra_genamount genAmount; }; 406 | struct tsf_hydra_shdr { tsf_char20 sampleName; tsf_u32 start, end, startLoop, endLoop, sampleRate; tsf_u8 originalPitch; tsf_s8 pitchCorrection; tsf_u16 sampleLink, sampleType; }; 407 | 408 | #define TSFR(FIELD) stream->read(stream->data, &i->FIELD, sizeof(i->FIELD)); 409 | static void tsf_hydra_read_phdr(struct tsf_hydra_phdr* i, struct tsf_stream* stream) { TSFR(presetName) TSFR(preset) TSFR(bank) TSFR(presetBagNdx) TSFR(library) TSFR(genre) TSFR(morphology) } 410 | static void tsf_hydra_read_pbag(struct tsf_hydra_pbag* i, struct tsf_stream* stream) { TSFR(genNdx) TSFR(modNdx) } 411 | static void tsf_hydra_read_pmod(struct tsf_hydra_pmod* i, struct tsf_stream* stream) { TSFR(modSrcOper) TSFR(modDestOper) TSFR(modAmount) TSFR(modAmtSrcOper) TSFR(modTransOper) } 412 | static void tsf_hydra_read_pgen(struct tsf_hydra_pgen* i, struct tsf_stream* stream) { TSFR(genOper) TSFR(genAmount) } 413 | static void tsf_hydra_read_inst(struct tsf_hydra_inst* i, struct tsf_stream* stream) { TSFR(instName) TSFR(instBagNdx) } 414 | static void tsf_hydra_read_ibag(struct tsf_hydra_ibag* i, struct tsf_stream* stream) { TSFR(instGenNdx) TSFR(instModNdx) } 415 | static void tsf_hydra_read_imod(struct tsf_hydra_imod* i, struct tsf_stream* stream) { TSFR(modSrcOper) TSFR(modDestOper) TSFR(modAmount) TSFR(modAmtSrcOper) TSFR(modTransOper) } 416 | static void tsf_hydra_read_igen(struct tsf_hydra_igen* i, struct tsf_stream* stream) { TSFR(genOper) TSFR(genAmount) } 417 | static void tsf_hydra_read_shdr(struct tsf_hydra_shdr* i, struct tsf_stream* stream) { TSFR(sampleName) TSFR(start) TSFR(end) TSFR(startLoop) TSFR(endLoop) TSFR(sampleRate) TSFR(originalPitch) TSFR(pitchCorrection) TSFR(sampleLink) TSFR(sampleType) } 418 | #undef TSFR 419 | 420 | struct tsf_riffchunk { tsf_fourcc id; tsf_u32 size; }; 421 | struct tsf_envelope { float delay, attack, hold, decay, sustain, release, keynumToHold, keynumToDecay; }; 422 | struct tsf_voice_envelope { float level, slope; int samplesUntilNextSegment; short segment, midiVelocity; struct tsf_envelope parameters; TSF_BOOL segmentIsExponential, isAmpEnv; }; 423 | struct tsf_voice_lowpass { double QInv, a0, a1, b1, b2, z1, z2; TSF_BOOL active; }; 424 | struct tsf_voice_lfo { int samplesUntil; float level, delta; }; 425 | 426 | struct tsf_region 427 | { 428 | int loop_mode; 429 | unsigned int sample_rate; 430 | unsigned char lokey, hikey, lovel, hivel; 431 | unsigned int group, offset, end, loop_start, loop_end; 432 | int transpose, tune, pitch_keycenter, pitch_keytrack; 433 | float attenuation, pan; 434 | struct tsf_envelope ampenv, modenv; 435 | int initialFilterQ, initialFilterFc; 436 | int modEnvToPitch, modEnvToFilterFc, modLfoToFilterFc, modLfoToVolume; 437 | float delayModLFO; 438 | int freqModLFO, modLfoToPitch; 439 | float delayVibLFO; 440 | int freqVibLFO, vibLfoToPitch; 441 | }; 442 | 443 | struct tsf_preset 444 | { 445 | tsf_char20 presetName; 446 | tsf_u16 preset, bank; 447 | struct tsf_region* regions; 448 | int regionNum; 449 | }; 450 | 451 | struct tsf_voice 452 | { 453 | int playingPreset, playingKey, playingChannel, heldSustain; 454 | struct tsf_region* region; 455 | double pitchInputTimecents, pitchOutputFactor; 456 | double sourceSamplePosition; 457 | float noteGainDB, panFactorLeft, panFactorRight; 458 | unsigned int playIndex, loopStart, loopEnd; 459 | struct tsf_voice_envelope ampenv, modenv; 460 | struct tsf_voice_lowpass lowpass; 461 | struct tsf_voice_lfo modlfo, viblfo; 462 | }; 463 | 464 | struct tsf_channel 465 | { 466 | unsigned short presetIndex, bank, pitchWheel, midiPan, midiVolume, midiExpression, midiRPN, midiData, sustain; 467 | float panOffset, gainDB, pitchRange, tuning; 468 | }; 469 | 470 | struct tsf_channels 471 | { 472 | void (*setupVoice)(tsf* f, struct tsf_voice* voice); 473 | int channelNum, activeChannel; 474 | struct tsf_channel channels[1]; 475 | }; 476 | 477 | static double tsf_timecents2Secsd(double timecents) { return TSF_POW(2.0, timecents / 1200.0); } 478 | static float tsf_timecents2Secsf(float timecents) { return TSF_POWF(2.0f, timecents / 1200.0f); } 479 | static float tsf_cents2Hertz(float cents) { return 8.176f * TSF_POWF(2.0f, cents / 1200.0f); } 480 | static float tsf_decibelsToGain(float db) { return (db > -100.f ? TSF_POWF(10.0f, db * 0.05f) : 0); } 481 | static float tsf_gainToDecibels(float gain) { return (gain <= .00001f ? -100.f : (float)(20.0 * TSF_LOG10(gain))); } 482 | 483 | static TSF_BOOL tsf_riffchunk_read(struct tsf_riffchunk* parent, struct tsf_riffchunk* chunk, struct tsf_stream* stream) 484 | { 485 | TSF_BOOL IsRiff, IsList; 486 | if (parent && sizeof(tsf_fourcc) + sizeof(tsf_u32) > parent->size) return TSF_FALSE; 487 | if (!stream->read(stream->data, &chunk->id, sizeof(tsf_fourcc)) || *chunk->id <= ' ' || *chunk->id >= 'z') return TSF_FALSE; 488 | if (!stream->read(stream->data, &chunk->size, sizeof(tsf_u32))) return TSF_FALSE; 489 | if (parent && sizeof(tsf_fourcc) + sizeof(tsf_u32) + chunk->size > parent->size) return TSF_FALSE; 490 | if (parent) parent->size -= sizeof(tsf_fourcc) + sizeof(tsf_u32) + chunk->size; 491 | IsRiff = TSF_FourCCEquals(chunk->id, "RIFF"), IsList = TSF_FourCCEquals(chunk->id, "LIST"); 492 | if (IsRiff && parent) return TSF_FALSE; //not allowed 493 | if (!IsRiff && !IsList) return TSF_TRUE; //custom type without sub type 494 | if (!stream->read(stream->data, &chunk->id, sizeof(tsf_fourcc)) || *chunk->id <= ' ' || *chunk->id >= 'z') return TSF_FALSE; 495 | chunk->size -= sizeof(tsf_fourcc); 496 | return TSF_TRUE; 497 | } 498 | 499 | static void tsf_region_clear(struct tsf_region* i, TSF_BOOL for_relative) 500 | { 501 | TSF_MEMSET(i, 0, sizeof(struct tsf_region)); 502 | i->hikey = i->hivel = 127; 503 | i->pitch_keycenter = 60; // C4 504 | if (for_relative) return; 505 | 506 | i->pitch_keytrack = 100; 507 | 508 | i->pitch_keycenter = -1; 509 | 510 | // SF2 defaults in timecents. 511 | i->ampenv.delay = i->ampenv.attack = i->ampenv.hold = i->ampenv.decay = i->ampenv.release = -12000.0f; 512 | i->modenv.delay = i->modenv.attack = i->modenv.hold = i->modenv.decay = i->modenv.release = -12000.0f; 513 | 514 | i->initialFilterFc = 13500; 515 | 516 | i->delayModLFO = -12000.0f; 517 | i->delayVibLFO = -12000.0f; 518 | } 519 | 520 | static void tsf_region_operator(struct tsf_region* region, tsf_u16 genOper, union tsf_hydra_genamount* amount, struct tsf_region* merge_region) 521 | { 522 | enum 523 | { 524 | _GEN_TYPE_MASK = 0x0F, 525 | GEN_FLOAT = 0x01, 526 | GEN_INT = 0x02, 527 | GEN_UINT_ADD = 0x03, 528 | GEN_UINT_ADD15 = 0x04, 529 | GEN_KEYRANGE = 0x05, 530 | GEN_VELRANGE = 0x06, 531 | GEN_LOOPMODE = 0x07, 532 | GEN_GROUP = 0x08, 533 | GEN_KEYCENTER = 0x09, 534 | 535 | _GEN_LIMIT_MASK = 0xF0, 536 | GEN_INT_LIMIT12K = 0x10, //min -12000, max 12000 537 | GEN_INT_LIMITFC = 0x20, //min 1500, max 13500 538 | GEN_INT_LIMITQ = 0x30, //min 0, max 960 539 | GEN_INT_LIMIT960 = 0x40, //min -960, max 960 540 | GEN_INT_LIMIT16K4500 = 0x50, //min -16000, max 4500 541 | GEN_FLOAT_LIMIT12K5K = 0x60, //min -12000, max 5000 542 | GEN_FLOAT_LIMIT12K8K = 0x70, //min -12000, max 8000 543 | GEN_FLOAT_LIMIT1200 = 0x80, //min -1200, max 1200 544 | GEN_FLOAT_LIMITPAN = 0x90, //* .001f, min -.5f, max .5f, 545 | GEN_FLOAT_LIMITATTN = 0xA0, //* .1f, min 0, max 144.0 546 | GEN_FLOAT_MAX1000 = 0xB0, //min 0, max 1000 547 | GEN_FLOAT_MAX1440 = 0xC0, //min 0, max 1440 548 | 549 | _GEN_MAX = 59 550 | }; 551 | #define _TSFREGIONOFFSET(TYPE, FIELD) (unsigned char)(((TYPE*)&((struct tsf_region*)0)->FIELD) - (TYPE*)0) 552 | #define _TSFREGIONENVOFFSET(TYPE, ENV, FIELD) (unsigned char)(((TYPE*)&((&(((struct tsf_region*)0)->ENV))->FIELD)) - (TYPE*)0) 553 | static const struct { unsigned char mode, offset; } genMetas[_GEN_MAX] = 554 | { 555 | { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, offset ) }, // 0 StartAddrsOffset 556 | { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, end ) }, // 1 EndAddrsOffset 557 | { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, loop_start ) }, // 2 StartloopAddrsOffset 558 | { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, loop_end ) }, // 3 EndloopAddrsOffset 559 | { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, offset ) }, // 4 StartAddrsCoarseOffset 560 | { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modLfoToPitch ) }, // 5 ModLfoToPitch 561 | { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, vibLfoToPitch ) }, // 6 VibLfoToPitch 562 | { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modEnvToPitch ) }, // 7 ModEnvToPitch 563 | { GEN_INT | GEN_INT_LIMITFC , _TSFREGIONOFFSET( int, initialFilterFc ) }, // 8 InitialFilterFc 564 | { GEN_INT | GEN_INT_LIMITQ , _TSFREGIONOFFSET( int, initialFilterQ ) }, // 9 InitialFilterQ 565 | { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modLfoToFilterFc ) }, //10 ModLfoToFilterFc 566 | { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modEnvToFilterFc ) }, //11 ModEnvToFilterFc 567 | { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, end ) }, //12 EndAddrsCoarseOffset 568 | { GEN_INT | GEN_INT_LIMIT960 , _TSFREGIONOFFSET( int, modLfoToVolume ) }, //13 ModLfoToVolume 569 | { 0 , (0 ) }, // Unused 570 | { 0 , (0 ) }, //15 ChorusEffectsSend (unsupported) 571 | { 0 , (0 ) }, //16 ReverbEffectsSend (unsupported) 572 | { GEN_FLOAT | GEN_FLOAT_LIMITPAN , _TSFREGIONOFFSET( float, pan ) }, //17 Pan 573 | { 0 , (0 ) }, // Unused 574 | { 0 , (0 ) }, // Unused 575 | { 0 , (0 ) }, // Unused 576 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONOFFSET( float, delayModLFO ) }, //21 DelayModLFO 577 | { GEN_INT | GEN_INT_LIMIT16K4500 , _TSFREGIONOFFSET( int, freqModLFO ) }, //22 FreqModLFO 578 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONOFFSET( float, delayVibLFO ) }, //23 DelayVibLFO 579 | { GEN_INT | GEN_INT_LIMIT16K4500 , _TSFREGIONOFFSET( int, freqVibLFO ) }, //24 FreqVibLFO 580 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, modenv, delay ) }, //25 DelayModEnv 581 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, modenv, attack ) }, //26 AttackModEnv 582 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, modenv, hold ) }, //27 HoldModEnv 583 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, modenv, decay ) }, //28 DecayModEnv 584 | { GEN_FLOAT | GEN_FLOAT_MAX1000 , _TSFREGIONENVOFFSET( float, modenv, sustain ) }, //29 SustainModEnv 585 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, modenv, release ) }, //30 ReleaseModEnv 586 | { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, modenv, keynumToHold ) }, //31 KeynumToModEnvHold 587 | { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, modenv, keynumToDecay) }, //32 KeynumToModEnvDecay 588 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, ampenv, delay ) }, //33 DelayVolEnv 589 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, ampenv, attack ) }, //34 AttackVolEnv 590 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, ampenv, hold ) }, //35 HoldVolEnv 591 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, ampenv, decay ) }, //36 DecayVolEnv 592 | { GEN_FLOAT | GEN_FLOAT_MAX1440 , _TSFREGIONENVOFFSET( float, ampenv, sustain ) }, //37 SustainVolEnv 593 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, ampenv, release ) }, //38 ReleaseVolEnv 594 | { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, ampenv, keynumToHold ) }, //39 KeynumToVolEnvHold 595 | { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, ampenv, keynumToDecay) }, //40 KeynumToVolEnvDecay 596 | { 0 , (0 ) }, // Instrument (special) 597 | { 0 , (0 ) }, // Reserved 598 | { GEN_KEYRANGE , (0 ) }, //43 KeyRange 599 | { GEN_VELRANGE , (0 ) }, //44 VelRange 600 | { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, loop_start ) }, //45 StartloopAddrsCoarseOffset 601 | { 0 , (0 ) }, //46 Keynum (special) 602 | { 0 , (0 ) }, //47 Velocity (special) 603 | { GEN_FLOAT | GEN_FLOAT_LIMITATTN , _TSFREGIONOFFSET( float, attenuation ) }, //48 InitialAttenuation 604 | { 0 , (0 ) }, // Reserved 605 | { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, loop_end ) }, //50 EndloopAddrsCoarseOffset 606 | { GEN_INT , _TSFREGIONOFFSET( int, transpose ) }, //51 CoarseTune 607 | { GEN_INT , _TSFREGIONOFFSET( int, tune ) }, //52 FineTune 608 | { 0 , (0 ) }, // SampleID (special) 609 | { GEN_LOOPMODE , _TSFREGIONOFFSET( int, loop_mode ) }, //54 SampleModes 610 | { 0 , (0 ) }, // Reserved 611 | { GEN_INT , _TSFREGIONOFFSET( int, pitch_keytrack ) }, //56 ScaleTuning 612 | { GEN_GROUP , _TSFREGIONOFFSET(unsigned int, group ) }, //57 ExclusiveClass 613 | { GEN_KEYCENTER , _TSFREGIONOFFSET( int, pitch_keycenter ) }, //58 OverridingRootKey 614 | }; 615 | #undef _TSFREGIONOFFSET 616 | #undef _TSFREGIONENVOFFSET 617 | if (amount) 618 | { 619 | int offset; 620 | if (genOper >= _GEN_MAX) return; 621 | offset = genMetas[genOper].offset; 622 | switch (genMetas[genOper].mode & _GEN_TYPE_MASK) 623 | { 624 | case GEN_FLOAT: (( float*)region)[offset] = amount->shortAmount; return; 625 | case GEN_INT: (( int*)region)[offset] = amount->shortAmount; return; 626 | case GEN_UINT_ADD: ((unsigned int*)region)[offset] += amount->shortAmount; return; 627 | case GEN_UINT_ADD15: ((unsigned int*)region)[offset] += amount->shortAmount<<15; return; 628 | case GEN_KEYRANGE: region->lokey = amount->range.lo; region->hikey = amount->range.hi; return; 629 | case GEN_VELRANGE: region->lovel = amount->range.lo; region->hivel = amount->range.hi; return; 630 | case GEN_LOOPMODE: region->loop_mode = ((amount->wordAmount&3) == 3 ? TSF_LOOPMODE_SUSTAIN : ((amount->wordAmount&3) == 1 ? TSF_LOOPMODE_CONTINUOUS : TSF_LOOPMODE_NONE)); return; 631 | case GEN_GROUP: region->group = amount->wordAmount; return; 632 | case GEN_KEYCENTER: region->pitch_keycenter = amount->shortAmount; return; 633 | } 634 | } 635 | else //merge regions and clamp values 636 | { 637 | for (genOper = 0; genOper != _GEN_MAX; genOper++) 638 | { 639 | int offset = genMetas[genOper].offset; 640 | switch (genMetas[genOper].mode & _GEN_TYPE_MASK) 641 | { 642 | case GEN_FLOAT: 643 | { 644 | float *val = &((float*)region)[offset], vfactor, vmin, vmax; 645 | *val += ((float*)merge_region)[offset]; 646 | switch (genMetas[genOper].mode & _GEN_LIMIT_MASK) 647 | { 648 | case GEN_FLOAT_LIMIT12K5K: vfactor = 1.0f; vmin = -12000.0f; vmax = 5000.0f; break; 649 | case GEN_FLOAT_LIMIT12K8K: vfactor = 1.0f; vmin = -12000.0f; vmax = 8000.0f; break; 650 | case GEN_FLOAT_LIMIT1200: vfactor = 1.0f; vmin = -1200.0f; vmax = 1200.0f; break; 651 | case GEN_FLOAT_LIMITPAN: vfactor = 0.001f; vmin = -0.5f; vmax = 0.5f; break; 652 | case GEN_FLOAT_LIMITATTN: vfactor = 0.1f; vmin = 0.0f; vmax = 144.0f; break; 653 | case GEN_FLOAT_MAX1000: vfactor = 1.0f; vmin = 0.0f; vmax = 1000.0f; break; 654 | case GEN_FLOAT_MAX1440: vfactor = 1.0f; vmin = 0.0f; vmax = 1440.0f; break; 655 | default: continue; 656 | } 657 | *val *= vfactor; 658 | if (*val < vmin) *val = vmin; 659 | else if (*val > vmax) *val = vmax; 660 | continue; 661 | } 662 | case GEN_INT: 663 | { 664 | int *val = &((int*)region)[offset], vmin, vmax; 665 | *val += ((int*)merge_region)[offset]; 666 | switch (genMetas[genOper].mode & _GEN_LIMIT_MASK) 667 | { 668 | case GEN_INT_LIMIT12K: vmin = -12000; vmax = 12000; break; 669 | case GEN_INT_LIMITFC: vmin = 1500; vmax = 13500; break; 670 | case GEN_INT_LIMITQ: vmin = 0; vmax = 960; break; 671 | case GEN_INT_LIMIT960: vmin = -960; vmax = 960; break; 672 | case GEN_INT_LIMIT16K4500: vmin = -16000; vmax = 4500; break; 673 | default: continue; 674 | } 675 | if (*val < vmin) *val = vmin; 676 | else if (*val > vmax) *val = vmax; 677 | continue; 678 | } 679 | case GEN_UINT_ADD: 680 | { 681 | ((unsigned int*)region)[offset] += ((unsigned int*)merge_region)[offset]; 682 | continue; 683 | } 684 | } 685 | } 686 | } 687 | } 688 | 689 | static void tsf_region_envtosecs(struct tsf_envelope* p, TSF_BOOL sustainIsGain) 690 | { 691 | // EG times need to be converted from timecents to seconds. 692 | // Pin very short EG segments. Timecents don't get to zero, and our EG is 693 | // happier with zero values. 694 | p->delay = (p->delay < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->delay)); 695 | p->attack = (p->attack < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->attack)); 696 | p->release = (p->release < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->release)); 697 | 698 | // If we have dynamic hold or decay times depending on key number we need 699 | // to keep the values in timecents so we can calculate it during startNote 700 | if (!p->keynumToHold) p->hold = (p->hold < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->hold)); 701 | if (!p->keynumToDecay) p->decay = (p->decay < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->decay)); 702 | 703 | if (p->sustain < 0.0f) p->sustain = 0.0f; 704 | else if (sustainIsGain) p->sustain = tsf_decibelsToGain(-p->sustain / 10.0f); 705 | else p->sustain = 1.0f - (p->sustain / 1000.0f); 706 | } 707 | 708 | static int tsf_load_presets(tsf* res, struct tsf_hydra *hydra, unsigned int fontSampleCount) 709 | { 710 | enum { GenInstrument = 41, GenKeyRange = 43, GenVelRange = 44, GenSampleID = 53 }; 711 | // Read each preset. 712 | struct tsf_hydra_phdr *pphdr, *pphdrMax; 713 | res->presetNum = hydra->phdrNum - 1; 714 | res->presets = (struct tsf_preset*)TSF_MALLOC(res->presetNum * sizeof(struct tsf_preset)); 715 | if (!res->presets) return 0; 716 | else { int i; for (i = 0; i != res->presetNum; i++) res->presets[i].regions = TSF_NULL; } 717 | for (pphdr = hydra->phdrs, pphdrMax = pphdr + hydra->phdrNum - 1; pphdr != pphdrMax; pphdr++) 718 | { 719 | int sortedIndex = 0, region_index = 0; 720 | struct tsf_hydra_phdr *otherphdr; 721 | struct tsf_preset* preset; 722 | struct tsf_hydra_pbag *ppbag, *ppbagEnd; 723 | struct tsf_region globalRegion; 724 | for (otherphdr = hydra->phdrs; otherphdr != pphdrMax; otherphdr++) 725 | { 726 | if (otherphdr == pphdr || otherphdr->bank > pphdr->bank) continue; 727 | else if (otherphdr->bank < pphdr->bank) sortedIndex++; 728 | else if (otherphdr->preset > pphdr->preset) continue; 729 | else if (otherphdr->preset < pphdr->preset) sortedIndex++; 730 | else if (otherphdr < pphdr) sortedIndex++; 731 | } 732 | 733 | preset = &res->presets[sortedIndex]; 734 | TSF_MEMCPY(preset->presetName, pphdr->presetName, sizeof(preset->presetName)); 735 | preset->presetName[sizeof(preset->presetName)-1] = '\0'; //should be zero terminated in source file but make sure 736 | preset->bank = pphdr->bank; 737 | preset->preset = pphdr->preset; 738 | preset->regionNum = 0; 739 | 740 | //count regions covered by this preset 741 | for (ppbag = hydra->pbags + pphdr->presetBagNdx, ppbagEnd = hydra->pbags + pphdr[1].presetBagNdx; ppbag != ppbagEnd; ppbag++) 742 | { 743 | unsigned char plokey = 0, phikey = 127, plovel = 0, phivel = 127; 744 | struct tsf_hydra_pgen *ppgen, *ppgenEnd; struct tsf_hydra_inst *pinst; struct tsf_hydra_ibag *pibag, *pibagEnd; struct tsf_hydra_igen *pigen, *pigenEnd; 745 | for (ppgen = hydra->pgens + ppbag->genNdx, ppgenEnd = hydra->pgens + ppbag[1].genNdx; ppgen != ppgenEnd; ppgen++) 746 | { 747 | if (ppgen->genOper == GenKeyRange) { plokey = ppgen->genAmount.range.lo; phikey = ppgen->genAmount.range.hi; continue; } 748 | if (ppgen->genOper == GenVelRange) { plovel = ppgen->genAmount.range.lo; phivel = ppgen->genAmount.range.hi; continue; } 749 | if (ppgen->genOper != GenInstrument) continue; 750 | if (ppgen->genAmount.wordAmount >= hydra->instNum) continue; 751 | pinst = hydra->insts + ppgen->genAmount.wordAmount; 752 | for (pibag = hydra->ibags + pinst->instBagNdx, pibagEnd = hydra->ibags + pinst[1].instBagNdx; pibag != pibagEnd; pibag++) 753 | { 754 | unsigned char ilokey = 0, ihikey = 127, ilovel = 0, ihivel = 127; 755 | for (pigen = hydra->igens + pibag->instGenNdx, pigenEnd = hydra->igens + pibag[1].instGenNdx; pigen != pigenEnd; pigen++) 756 | { 757 | if (pigen->genOper == GenKeyRange) { ilokey = pigen->genAmount.range.lo; ihikey = pigen->genAmount.range.hi; continue; } 758 | if (pigen->genOper == GenVelRange) { ilovel = pigen->genAmount.range.lo; ihivel = pigen->genAmount.range.hi; continue; } 759 | if (pigen->genOper == GenSampleID && ihikey >= plokey && ilokey <= phikey && ihivel >= plovel && ilovel <= phivel) preset->regionNum++; 760 | } 761 | } 762 | } 763 | } 764 | 765 | preset->regions = (struct tsf_region*)TSF_MALLOC(preset->regionNum * sizeof(struct tsf_region)); 766 | if (!preset->regions) 767 | { 768 | int i; for (i = 0; i != res->presetNum; i++) TSF_FREE(res->presets[i].regions); 769 | TSF_FREE(res->presets); 770 | return 0; 771 | } 772 | tsf_region_clear(&globalRegion, TSF_TRUE); 773 | 774 | // Zones. 775 | for (ppbag = hydra->pbags + pphdr->presetBagNdx, ppbagEnd = hydra->pbags + pphdr[1].presetBagNdx; ppbag != ppbagEnd; ppbag++) 776 | { 777 | struct tsf_hydra_pgen *ppgen, *ppgenEnd; struct tsf_hydra_inst *pinst; struct tsf_hydra_ibag *pibag, *pibagEnd; struct tsf_hydra_igen *pigen, *pigenEnd; 778 | struct tsf_region presetRegion = globalRegion; 779 | int hadGenInstrument = 0; 780 | 781 | // Generators. 782 | for (ppgen = hydra->pgens + ppbag->genNdx, ppgenEnd = hydra->pgens + ppbag[1].genNdx; ppgen != ppgenEnd; ppgen++) 783 | { 784 | // Instrument. 785 | if (ppgen->genOper == GenInstrument) 786 | { 787 | struct tsf_region instRegion; 788 | tsf_u16 whichInst = ppgen->genAmount.wordAmount; 789 | if (whichInst >= hydra->instNum) continue; 790 | 791 | tsf_region_clear(&instRegion, TSF_FALSE); 792 | pinst = &hydra->insts[whichInst]; 793 | for (pibag = hydra->ibags + pinst->instBagNdx, pibagEnd = hydra->ibags + pinst[1].instBagNdx; pibag != pibagEnd; pibag++) 794 | { 795 | // Generators. 796 | struct tsf_region zoneRegion = instRegion; 797 | int hadSampleID = 0; 798 | for (pigen = hydra->igens + pibag->instGenNdx, pigenEnd = hydra->igens + pibag[1].instGenNdx; pigen != pigenEnd; pigen++) 799 | { 800 | if (pigen->genOper == GenSampleID) 801 | { 802 | struct tsf_hydra_shdr* pshdr; 803 | 804 | //preset region key and vel ranges are a filter for the zone regions 805 | if (zoneRegion.hikey < presetRegion.lokey || zoneRegion.lokey > presetRegion.hikey) continue; 806 | if (zoneRegion.hivel < presetRegion.lovel || zoneRegion.lovel > presetRegion.hivel) continue; 807 | if (presetRegion.lokey > zoneRegion.lokey) zoneRegion.lokey = presetRegion.lokey; 808 | if (presetRegion.hikey < zoneRegion.hikey) zoneRegion.hikey = presetRegion.hikey; 809 | if (presetRegion.lovel > zoneRegion.lovel) zoneRegion.lovel = presetRegion.lovel; 810 | if (presetRegion.hivel < zoneRegion.hivel) zoneRegion.hivel = presetRegion.hivel; 811 | 812 | //sum regions 813 | tsf_region_operator(&zoneRegion, 0, TSF_NULL, &presetRegion); 814 | 815 | // EG times need to be converted from timecents to seconds. 816 | tsf_region_envtosecs(&zoneRegion.ampenv, TSF_TRUE); 817 | tsf_region_envtosecs(&zoneRegion.modenv, TSF_FALSE); 818 | 819 | // LFO times need to be converted from timecents to seconds. 820 | zoneRegion.delayModLFO = (zoneRegion.delayModLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayModLFO)); 821 | zoneRegion.delayVibLFO = (zoneRegion.delayVibLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayVibLFO)); 822 | 823 | // Fixup sample positions 824 | pshdr = &hydra->shdrs[pigen->genAmount.wordAmount]; 825 | zoneRegion.offset += pshdr->start; 826 | zoneRegion.end += pshdr->end; 827 | zoneRegion.loop_start += pshdr->startLoop; 828 | zoneRegion.loop_end += pshdr->endLoop; 829 | if (pshdr->endLoop > 0) zoneRegion.loop_end -= 1; 830 | if (zoneRegion.loop_end > fontSampleCount) zoneRegion.loop_end = fontSampleCount; 831 | if (zoneRegion.pitch_keycenter == -1) zoneRegion.pitch_keycenter = pshdr->originalPitch; 832 | zoneRegion.tune += pshdr->pitchCorrection; 833 | zoneRegion.sample_rate = pshdr->sampleRate; 834 | if (zoneRegion.end && zoneRegion.end < fontSampleCount) zoneRegion.end++; 835 | else zoneRegion.end = fontSampleCount; 836 | 837 | preset->regions[region_index] = zoneRegion; 838 | region_index++; 839 | hadSampleID = 1; 840 | } 841 | else tsf_region_operator(&zoneRegion, pigen->genOper, &pigen->genAmount, TSF_NULL); 842 | } 843 | 844 | // Handle instrument's global zone. 845 | if (pibag == hydra->ibags + pinst->instBagNdx && !hadSampleID) 846 | instRegion = zoneRegion; 847 | 848 | // Modulators (TODO) 849 | //if (ibag->instModNdx < ibag[1].instModNdx) addUnsupportedOpcode("any modulator"); 850 | } 851 | hadGenInstrument = 1; 852 | } 853 | else tsf_region_operator(&presetRegion, ppgen->genOper, &ppgen->genAmount, TSF_NULL); 854 | } 855 | 856 | // Modulators (TODO) 857 | //if (pbag->modNdx < pbag[1].modNdx) addUnsupportedOpcode("any modulator"); 858 | 859 | // Handle preset's global zone. 860 | if (ppbag == hydra->pbags + pphdr->presetBagNdx && !hadGenInstrument) 861 | globalRegion = presetRegion; 862 | } 863 | } 864 | return 1; 865 | } 866 | 867 | #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H 868 | static int tsf_decode_ogg(const tsf_u8 *pSmpl, const tsf_u8 *pSmplEnd, float** pRes, tsf_u32* pResNum, tsf_u32* pResMax, tsf_u32 resInitial) 869 | { 870 | float *res = *pRes, *oldres; tsf_u32 resNum = *pResNum; tsf_u32 resMax = *pResMax; stb_vorbis *v; 871 | 872 | // Use whatever stb_vorbis API that is available (either pull or push) 873 | #if !defined(STB_VORBIS_NO_PULLDATA_API) && !defined(STB_VORBIS_NO_FROMMEMORY) 874 | v = stb_vorbis_open_memory(pSmpl, (int)(pSmplEnd - pSmpl), TSF_NULL, TSF_NULL); 875 | #else 876 | { int use, err; v = stb_vorbis_open_pushdata(pSmpl, (int)(pSmplEnd - pSmpl), &use, &err, TSF_NULL); pSmpl += use; } 877 | #endif 878 | if (v == TSF_NULL) return 0; 879 | 880 | for (;;) 881 | { 882 | float** outputs; int n_samples; 883 | 884 | // Decode one frame of vorbis samples with whatever stb_vorbis API that is available 885 | #if !defined(STB_VORBIS_NO_PULLDATA_API) && !defined(STB_VORBIS_NO_FROMMEMORY) 886 | n_samples = stb_vorbis_get_frame_float(v, TSF_NULL, &outputs); 887 | if (!n_samples) break; 888 | #else 889 | if (pSmpl >= pSmplEnd) break; 890 | { int use = stb_vorbis_decode_frame_pushdata(v, pSmpl, (int)(pSmplEnd - pSmpl), TSF_NULL, &outputs, &n_samples); pSmpl += use; } 891 | if (!n_samples) continue; 892 | #endif 893 | 894 | // Expand our output buffer if necessary then copy over the decoded frame samples 895 | resNum += n_samples; 896 | if (resNum > resMax) 897 | { 898 | do { resMax += (resMax ? (resMax < 1048576 ? resMax : 1048576) : resInitial); } while (resNum > resMax); 899 | res = (float*)TSF_REALLOC((oldres = res), resMax * sizeof(float)); 900 | if (!res) { TSF_FREE(oldres); stb_vorbis_close(v); return 0; } 901 | } 902 | TSF_MEMCPY(res + resNum - n_samples, outputs[0], n_samples * sizeof(float)); 903 | } 904 | stb_vorbis_close(v); 905 | *pRes = res; *pResNum = resNum; *pResMax = resMax; 906 | return 1; 907 | } 908 | 909 | static int tsf_decode_sf3_samples(const void* rawBuffer, float** pFloatBuffer, unsigned int* pSmplCount, struct tsf_hydra *hydra) 910 | { 911 | const tsf_u8* smplBuffer = (const tsf_u8*)rawBuffer; 912 | tsf_u32 smplLength = *pSmplCount, resNum = 0, resMax = 0, resInitial = (smplLength > 0x100000 ? (smplLength & ~0xFFFFF) : 65536); 913 | float *res = TSF_NULL, *oldres; 914 | int i, shdrLast = hydra->shdrNum - 1, is_sf3 = 0; 915 | for (i = 0; i <= shdrLast; i++) 916 | { 917 | struct tsf_hydra_shdr *shdr = &hydra->shdrs[i]; 918 | if (shdr->sampleType & 0x30) // compression flags (sometimes Vorbis flag) 919 | { 920 | const tsf_u8 *pSmpl = smplBuffer + shdr->start, *pSmplEnd = smplBuffer + shdr->end; 921 | if (pSmpl + 4 > pSmplEnd || !TSF_FourCCEquals(pSmpl, "OggS")) 922 | { 923 | shdr->start = shdr->end = shdr->startLoop = shdr->endLoop = 0; 924 | continue; 925 | } 926 | 927 | // Fix up sample indices in shdr (end index is set after decoding) 928 | shdr->start = resNum; 929 | shdr->startLoop += resNum; 930 | shdr->endLoop += resNum; 931 | if (!tsf_decode_ogg(pSmpl, pSmplEnd, &res, &resNum, &resMax, resInitial)) { TSF_FREE(res); return 0; } 932 | shdr->end = resNum; 933 | is_sf3 = 1; 934 | } 935 | else // raw PCM sample 936 | { 937 | float *out; short *in = (short*)smplBuffer + resNum, *inEnd; tsf_u32 oldResNum = resNum; 938 | if (is_sf3) // Fix up sample indices in shdr 939 | { 940 | tsf_u32 fix_offset = resNum - shdr->start; 941 | in -= fix_offset; 942 | shdr->start = resNum; 943 | shdr->end += fix_offset; 944 | shdr->startLoop += fix_offset; 945 | shdr->endLoop += fix_offset; 946 | } 947 | inEnd = in + ((shdr->end >= shdr->endLoop ? shdr->end : shdr->endLoop) - resNum); 948 | if (i == shdrLast || (tsf_u8*)inEnd > (smplBuffer + smplLength)) inEnd = (short*)(smplBuffer + smplLength); 949 | if (inEnd <= in) continue; 950 | 951 | // expand our output buffer if necessary then convert the PCM data from short to float 952 | resNum += (tsf_u32)(inEnd - in); 953 | if (resNum > resMax) 954 | { 955 | do { resMax += (resMax ? (resMax < 1048576 ? resMax : 1048576) : resInitial); } while (resNum > resMax); 956 | res = (float*)TSF_REALLOC((oldres = res), resMax * sizeof(float)); 957 | if (!res) { TSF_FREE(oldres); return 0; } 958 | } 959 | 960 | // Convert the samples from short to float 961 | for (out = res + oldResNum; in < inEnd;) 962 | *(out++) = (float)(*(in++) / 32767.0); 963 | } 964 | } 965 | 966 | // Trim the sample buffer down then return success (unless out of memory) 967 | if (!(*pFloatBuffer = (float*)TSF_REALLOC(res, resNum * sizeof(float)))) *pFloatBuffer = res; 968 | *pSmplCount = resNum; 969 | return (res ? 1 : 0); 970 | } 971 | #endif 972 | 973 | static int tsf_load_samples(void** pRawBuffer, float** pFloatBuffer, unsigned int* pSmplCount, struct tsf_riffchunk *chunkSmpl, struct tsf_stream* stream) 974 | { 975 | #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H 976 | // With OGG Vorbis support we cannot pre-allocate the memory for tsf_decode_sf3_samples 977 | tsf_u32 resNum, resMax; float* oldres; 978 | *pSmplCount = chunkSmpl->size; 979 | *pRawBuffer = (void*)TSF_MALLOC(*pSmplCount); 980 | if (!*pRawBuffer || !stream->read(stream->data, *pRawBuffer, chunkSmpl->size)) return 0; 981 | if (chunkSmpl->id[3] != 'o') return 1; 982 | 983 | // Decode custom .sfo 'smpo' format where all samples are in a single ogg stream 984 | resNum = resMax = 0; 985 | if (!tsf_decode_ogg((tsf_u8*)*pRawBuffer, (tsf_u8*)*pRawBuffer + chunkSmpl->size, pFloatBuffer, &resNum, &resMax, 65536)) return 0; 986 | if (!(*pFloatBuffer = (float*)TSF_REALLOC((oldres = *pFloatBuffer), resNum * sizeof(float)))) *pFloatBuffer = oldres; 987 | *pSmplCount = resNum; 988 | return (*pFloatBuffer ? 1 : 0); 989 | #else 990 | // Inline convert the samples from short to float 991 | float *res, *out; const short *in; 992 | *pSmplCount = chunkSmpl->size / (unsigned int)sizeof(short); 993 | *pFloatBuffer = (float*)TSF_MALLOC(*pSmplCount * sizeof(float)); 994 | if (!*pFloatBuffer || !stream->read(stream->data, *pFloatBuffer, chunkSmpl->size)) return 0; 995 | for (res = *pFloatBuffer, out = res + *pSmplCount, in = (short*)res + *pSmplCount; out != res;) 996 | *(--out) = (float)(*(--in) / 32767.0); 997 | return 1; 998 | #endif 999 | } 1000 | 1001 | static int tsf_voice_envelope_release_samples(struct tsf_voice_envelope* e, float outSampleRate) 1002 | { 1003 | return (int)((e->parameters.release <= 0 ? TSF_FASTRELEASETIME : e->parameters.release) * outSampleRate); 1004 | } 1005 | 1006 | static void tsf_voice_envelope_nextsegment(struct tsf_voice_envelope* e, short active_segment, float outSampleRate) 1007 | { 1008 | switch (active_segment) 1009 | { 1010 | case TSF_SEGMENT_NONE: 1011 | e->samplesUntilNextSegment = (int)(e->parameters.delay * outSampleRate); 1012 | if (e->samplesUntilNextSegment > 0) 1013 | { 1014 | e->segment = TSF_SEGMENT_DELAY; 1015 | e->segmentIsExponential = TSF_FALSE; 1016 | e->level = 0.0; 1017 | e->slope = 0.0; 1018 | return; 1019 | } 1020 | /* fall through */ 1021 | case TSF_SEGMENT_DELAY: 1022 | e->samplesUntilNextSegment = (int)(e->parameters.attack * outSampleRate); 1023 | if (e->samplesUntilNextSegment > 0) 1024 | { 1025 | if (!e->isAmpEnv) 1026 | { 1027 | //mod env attack duration scales with velocity (velocity of 1 is full duration, max velocity is 0.125 times duration) 1028 | e->samplesUntilNextSegment = (int)(e->parameters.attack * ((145 - e->midiVelocity) / 144.0f) * outSampleRate); 1029 | } 1030 | e->segment = TSF_SEGMENT_ATTACK; 1031 | e->segmentIsExponential = TSF_FALSE; 1032 | e->level = 0.0f; 1033 | e->slope = 1.0f / e->samplesUntilNextSegment; 1034 | return; 1035 | } 1036 | /* fall through */ 1037 | case TSF_SEGMENT_ATTACK: 1038 | e->samplesUntilNextSegment = (int)(e->parameters.hold * outSampleRate); 1039 | if (e->samplesUntilNextSegment > 0) 1040 | { 1041 | e->segment = TSF_SEGMENT_HOLD; 1042 | e->segmentIsExponential = TSF_FALSE; 1043 | e->level = 1.0f; 1044 | e->slope = 0.0f; 1045 | return; 1046 | } 1047 | /* fall through */ 1048 | case TSF_SEGMENT_HOLD: 1049 | e->samplesUntilNextSegment = (int)(e->parameters.decay * outSampleRate); 1050 | if (e->samplesUntilNextSegment > 0) 1051 | { 1052 | e->segment = TSF_SEGMENT_DECAY; 1053 | e->level = 1.0f; 1054 | if (e->isAmpEnv) 1055 | { 1056 | // I don't truly understand this; just following what LinuxSampler does. 1057 | float mysterySlope = -9.226f / e->samplesUntilNextSegment; 1058 | e->slope = TSF_EXPF(mysterySlope); 1059 | e->segmentIsExponential = TSF_TRUE; 1060 | if (e->parameters.sustain > 0.0f) 1061 | { 1062 | // Again, this is following LinuxSampler's example, which is similar to 1063 | // SF2-style decay, where "decay" specifies the time it would take to 1064 | // get to zero, not to the sustain level. The SFZ spec is not that 1065 | // specific about what "decay" means, so perhaps it's really supposed 1066 | // to specify the time to reach the sustain level. 1067 | e->samplesUntilNextSegment = (int)(TSF_LOG(e->parameters.sustain) / mysterySlope); 1068 | } 1069 | } 1070 | else 1071 | { 1072 | e->slope = -1.0f / e->samplesUntilNextSegment; 1073 | e->samplesUntilNextSegment = (int)(e->parameters.decay * (1.0f - e->parameters.sustain) * outSampleRate); 1074 | e->segmentIsExponential = TSF_FALSE; 1075 | } 1076 | return; 1077 | } 1078 | /* fall through */ 1079 | case TSF_SEGMENT_DECAY: 1080 | e->segment = TSF_SEGMENT_SUSTAIN; 1081 | e->level = e->parameters.sustain; 1082 | e->slope = 0.0f; 1083 | e->samplesUntilNextSegment = 0x7FFFFFFF; 1084 | e->segmentIsExponential = TSF_FALSE; 1085 | return; 1086 | case TSF_SEGMENT_SUSTAIN: 1087 | e->segment = TSF_SEGMENT_RELEASE; 1088 | e->samplesUntilNextSegment = tsf_voice_envelope_release_samples(e, outSampleRate); 1089 | if (e->isAmpEnv) 1090 | { 1091 | // I don't truly understand this; just following what LinuxSampler does. 1092 | float mysterySlope = -9.226f / e->samplesUntilNextSegment; 1093 | e->slope = TSF_EXPF(mysterySlope); 1094 | e->segmentIsExponential = TSF_TRUE; 1095 | } 1096 | else 1097 | { 1098 | e->slope = -e->level / e->samplesUntilNextSegment; 1099 | e->segmentIsExponential = TSF_FALSE; 1100 | } 1101 | return; 1102 | case TSF_SEGMENT_RELEASE: 1103 | default: 1104 | e->segment = TSF_SEGMENT_DONE; 1105 | e->segmentIsExponential = TSF_FALSE; 1106 | e->level = e->slope = 0.0f; 1107 | e->samplesUntilNextSegment = 0x7FFFFFF; 1108 | } 1109 | } 1110 | 1111 | static void tsf_voice_envelope_setup(struct tsf_voice_envelope* e, struct tsf_envelope* new_parameters, int midiNoteNumber, short midiVelocity, TSF_BOOL isAmpEnv, float outSampleRate) 1112 | { 1113 | e->parameters = *new_parameters; 1114 | if (e->parameters.keynumToHold) 1115 | { 1116 | e->parameters.hold += e->parameters.keynumToHold * (60.0f - midiNoteNumber); 1117 | e->parameters.hold = (e->parameters.hold < -10000.0f ? 0.0f : tsf_timecents2Secsf(e->parameters.hold)); 1118 | } 1119 | if (e->parameters.keynumToDecay) 1120 | { 1121 | e->parameters.decay += e->parameters.keynumToDecay * (60.0f - midiNoteNumber); 1122 | e->parameters.decay = (e->parameters.decay < -10000.0f ? 0.0f : tsf_timecents2Secsf(e->parameters.decay)); 1123 | } 1124 | e->midiVelocity = midiVelocity; 1125 | e->isAmpEnv = isAmpEnv; 1126 | tsf_voice_envelope_nextsegment(e, TSF_SEGMENT_NONE, outSampleRate); 1127 | } 1128 | 1129 | static void tsf_voice_envelope_process(struct tsf_voice_envelope* e, int numSamples, float outSampleRate) 1130 | { 1131 | if (e->slope) 1132 | { 1133 | if (e->segmentIsExponential) e->level *= TSF_POWF(e->slope, (float)numSamples); 1134 | else e->level += (e->slope * numSamples); 1135 | } 1136 | if ((e->samplesUntilNextSegment -= numSamples) <= 0) 1137 | tsf_voice_envelope_nextsegment(e, e->segment, outSampleRate); 1138 | } 1139 | 1140 | static void tsf_voice_lowpass_setup(struct tsf_voice_lowpass* e, float Fc) 1141 | { 1142 | // Lowpass filter from http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/ 1143 | double K = TSF_TAN(TSF_PI * Fc), KK = K * K; 1144 | double norm = 1 / (1 + K * e->QInv + KK); 1145 | e->a0 = KK * norm; 1146 | e->a1 = 2 * e->a0; 1147 | e->b1 = 2 * (KK - 1) * norm; 1148 | e->b2 = (1 - K * e->QInv + KK) * norm; 1149 | } 1150 | 1151 | static float tsf_voice_lowpass_process(struct tsf_voice_lowpass* e, double In) 1152 | { 1153 | double Out = In * e->a0 + e->z1; e->z1 = In * e->a1 + e->z2 - e->b1 * Out; e->z2 = In * e->a0 - e->b2 * Out; return (float)Out; 1154 | } 1155 | 1156 | static void tsf_voice_lfo_setup(struct tsf_voice_lfo* e, float delay, int freqCents, float outSampleRate) 1157 | { 1158 | e->samplesUntil = (int)(delay * outSampleRate); 1159 | e->delta = (4.0f * tsf_cents2Hertz((float)freqCents) / outSampleRate); 1160 | e->level = 0; 1161 | } 1162 | 1163 | static void tsf_voice_lfo_process(struct tsf_voice_lfo* e, int blockSamples) 1164 | { 1165 | if (e->samplesUntil > blockSamples) { e->samplesUntil -= blockSamples; return; } 1166 | e->level += e->delta * blockSamples; 1167 | if (e->level > 1.0f) { e->delta = -e->delta; e->level = 2.0f - e->level; } 1168 | else if (e->level < -1.0f) { e->delta = -e->delta; e->level = -2.0f - e->level; } 1169 | } 1170 | 1171 | static void tsf_voice_kill(struct tsf_voice* v) 1172 | { 1173 | v->playingPreset = -1; 1174 | } 1175 | 1176 | static void tsf_voice_end(tsf* f, struct tsf_voice* v) 1177 | { 1178 | // if maxVoiceNum is set, assume that voice rendering and note queuing are on separate threads 1179 | // so to minimize the chance that voice rendering would advance the segment at the same time 1180 | // we just do it twice here and hope that it sticks 1181 | int repeats = (f->maxVoiceNum ? 2 : 1); 1182 | while (repeats--) 1183 | { 1184 | tsf_voice_envelope_nextsegment(&v->ampenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); 1185 | tsf_voice_envelope_nextsegment(&v->modenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); 1186 | if (v->region->loop_mode == TSF_LOOPMODE_SUSTAIN) 1187 | { 1188 | // Continue playing, but stop looping. 1189 | v->loopEnd = v->loopStart; 1190 | } 1191 | } 1192 | } 1193 | 1194 | static void tsf_voice_endquick(tsf* f, struct tsf_voice* v) 1195 | { 1196 | // if maxVoiceNum is set, assume that voice rendering and note queuing are on separate threads 1197 | // so to minimize the chance that voice rendering would advance the segment at the same time 1198 | // we just do it twice here and hope that it sticks 1199 | int repeats = (f->maxVoiceNum ? 2 : 1); 1200 | while (repeats--) 1201 | { 1202 | v->ampenv.parameters.release = 0.0f; tsf_voice_envelope_nextsegment(&v->ampenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); 1203 | v->modenv.parameters.release = 0.0f; tsf_voice_envelope_nextsegment(&v->modenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); 1204 | } 1205 | } 1206 | 1207 | static void tsf_voice_calcpitchratio(struct tsf_voice* v, float pitchShift, float outSampleRate) 1208 | { 1209 | double note = v->playingKey + v->region->transpose + v->region->tune / 100.0; 1210 | double adjustedPitch = v->region->pitch_keycenter + (note - v->region->pitch_keycenter) * (v->region->pitch_keytrack / 100.0); 1211 | if (pitchShift) adjustedPitch += pitchShift; 1212 | v->pitchInputTimecents = adjustedPitch * 100.0; 1213 | v->pitchOutputFactor = v->region->sample_rate / (tsf_timecents2Secsd(v->region->pitch_keycenter * 100.0) * outSampleRate); 1214 | } 1215 | 1216 | static void tsf_voice_render(tsf* f, struct tsf_voice* v, float* outputBuffer, int numSamples) 1217 | { 1218 | struct tsf_region* region = v->region; 1219 | float* input = f->fontSamples; 1220 | float* outL = outputBuffer; 1221 | float* outR = (f->outputmode == TSF_STEREO_UNWEAVED ? outL + numSamples : TSF_NULL); 1222 | 1223 | // Cache some values, to give them at least some chance of ending up in registers. 1224 | TSF_BOOL updateModEnv = (region->modEnvToPitch || region->modEnvToFilterFc); 1225 | TSF_BOOL updateModLFO = (v->modlfo.delta && (region->modLfoToPitch || region->modLfoToFilterFc || region->modLfoToVolume)); 1226 | TSF_BOOL updateVibLFO = (v->viblfo.delta && (region->vibLfoToPitch)); 1227 | TSF_BOOL isLooping = (v->loopStart < v->loopEnd); 1228 | unsigned int tmpLoopStart = v->loopStart, tmpLoopEnd = v->loopEnd; 1229 | double tmpSampleEndDbl = (double)region->end, tmpLoopEndDbl = (double)tmpLoopEnd + 1.0; 1230 | double tmpSourceSamplePosition = v->sourceSamplePosition; 1231 | struct tsf_voice_lowpass tmpLowpass = v->lowpass; 1232 | 1233 | TSF_BOOL dynamicLowpass = (region->modLfoToFilterFc || region->modEnvToFilterFc); 1234 | float tmpSampleRate = f->outSampleRate, tmpInitialFilterFc, tmpModLfoToFilterFc, tmpModEnvToFilterFc; 1235 | 1236 | TSF_BOOL dynamicPitchRatio = (region->modLfoToPitch || region->modEnvToPitch || region->vibLfoToPitch); 1237 | double pitchRatio; 1238 | float tmpModLfoToPitch, tmpVibLfoToPitch, tmpModEnvToPitch; 1239 | 1240 | TSF_BOOL dynamicGain = (region->modLfoToVolume != 0); 1241 | float noteGain = 0, tmpModLfoToVolume; 1242 | 1243 | if (dynamicLowpass) tmpInitialFilterFc = (float)region->initialFilterFc, tmpModLfoToFilterFc = (float)region->modLfoToFilterFc, tmpModEnvToFilterFc = (float)region->modEnvToFilterFc; 1244 | else tmpInitialFilterFc = 0, tmpModLfoToFilterFc = 0, tmpModEnvToFilterFc = 0; 1245 | 1246 | if (dynamicPitchRatio) pitchRatio = 0, tmpModLfoToPitch = (float)region->modLfoToPitch, tmpVibLfoToPitch = (float)region->vibLfoToPitch, tmpModEnvToPitch = (float)region->modEnvToPitch; 1247 | else pitchRatio = tsf_timecents2Secsd(v->pitchInputTimecents) * v->pitchOutputFactor, tmpModLfoToPitch = 0, tmpVibLfoToPitch = 0, tmpModEnvToPitch = 0; 1248 | 1249 | if (dynamicGain) tmpModLfoToVolume = (float)region->modLfoToVolume * 0.1f; 1250 | else noteGain = tsf_decibelsToGain(v->noteGainDB), tmpModLfoToVolume = 0; 1251 | 1252 | while (numSamples) 1253 | { 1254 | float gainMono, gainLeft, gainRight; 1255 | int blockSamples = (numSamples > TSF_RENDER_EFFECTSAMPLEBLOCK ? TSF_RENDER_EFFECTSAMPLEBLOCK : numSamples); 1256 | numSamples -= blockSamples; 1257 | 1258 | if (dynamicLowpass) 1259 | { 1260 | float fres = tmpInitialFilterFc + v->modlfo.level * tmpModLfoToFilterFc + v->modenv.level * tmpModEnvToFilterFc; 1261 | float lowpassFc = (fres <= 13500 ? tsf_cents2Hertz(fres) / tmpSampleRate : 1.0f); 1262 | tmpLowpass.active = (lowpassFc < 0.499f); 1263 | if (tmpLowpass.active) tsf_voice_lowpass_setup(&tmpLowpass, lowpassFc); 1264 | } 1265 | 1266 | if (dynamicPitchRatio) 1267 | pitchRatio = tsf_timecents2Secsd(v->pitchInputTimecents + (v->modlfo.level * tmpModLfoToPitch + v->viblfo.level * tmpVibLfoToPitch + v->modenv.level * tmpModEnvToPitch)) * v->pitchOutputFactor; 1268 | 1269 | if (dynamicGain) 1270 | noteGain = tsf_decibelsToGain(v->noteGainDB + (v->modlfo.level * tmpModLfoToVolume)); 1271 | 1272 | gainMono = noteGain * v->ampenv.level; 1273 | 1274 | // Update EG. 1275 | tsf_voice_envelope_process(&v->ampenv, blockSamples, tmpSampleRate); 1276 | if (updateModEnv) tsf_voice_envelope_process(&v->modenv, blockSamples, tmpSampleRate); 1277 | 1278 | // Update LFOs. 1279 | if (updateModLFO) tsf_voice_lfo_process(&v->modlfo, blockSamples); 1280 | if (updateVibLFO) tsf_voice_lfo_process(&v->viblfo, blockSamples); 1281 | 1282 | switch (f->outputmode) 1283 | { 1284 | case TSF_STEREO_INTERLEAVED: 1285 | gainLeft = gainMono * v->panFactorLeft, gainRight = gainMono * v->panFactorRight; 1286 | while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) 1287 | { 1288 | unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); 1289 | 1290 | // Simple linear interpolation. 1291 | float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); 1292 | 1293 | // Low-pass filter. 1294 | if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); 1295 | 1296 | *outL++ += val * gainLeft; 1297 | *outL++ += val * gainRight; 1298 | 1299 | // Next sample. 1300 | tmpSourceSamplePosition += pitchRatio; 1301 | if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); 1302 | } 1303 | break; 1304 | 1305 | case TSF_STEREO_UNWEAVED: 1306 | gainLeft = gainMono * v->panFactorLeft, gainRight = gainMono * v->panFactorRight; 1307 | while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) 1308 | { 1309 | unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); 1310 | 1311 | // Simple linear interpolation. 1312 | float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); 1313 | 1314 | // Low-pass filter. 1315 | if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); 1316 | 1317 | *outL++ += val * gainLeft; 1318 | *outR++ += val * gainRight; 1319 | 1320 | // Next sample. 1321 | tmpSourceSamplePosition += pitchRatio; 1322 | if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); 1323 | } 1324 | break; 1325 | 1326 | case TSF_MONO: 1327 | while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) 1328 | { 1329 | unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); 1330 | 1331 | // Simple linear interpolation. 1332 | float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); 1333 | 1334 | // Low-pass filter. 1335 | if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); 1336 | 1337 | *outL++ += val * gainMono; 1338 | 1339 | // Next sample. 1340 | tmpSourceSamplePosition += pitchRatio; 1341 | if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); 1342 | } 1343 | break; 1344 | } 1345 | 1346 | if (tmpSourceSamplePosition >= tmpSampleEndDbl || v->ampenv.segment == TSF_SEGMENT_DONE) 1347 | { 1348 | tsf_voice_kill(v); 1349 | return; 1350 | } 1351 | } 1352 | 1353 | v->sourceSamplePosition = tmpSourceSamplePosition; 1354 | if (tmpLowpass.active || dynamicLowpass) v->lowpass = tmpLowpass; 1355 | } 1356 | 1357 | TSFDEF tsf* tsf_load(struct tsf_stream* stream) 1358 | { 1359 | tsf* res = TSF_NULL; 1360 | struct tsf_riffchunk chunkHead; 1361 | struct tsf_riffchunk chunkList; 1362 | struct tsf_hydra hydra; 1363 | void* rawBuffer = TSF_NULL; 1364 | float* floatBuffer = TSF_NULL; 1365 | tsf_u32 smplCount = 0; 1366 | 1367 | if (!tsf_riffchunk_read(TSF_NULL, &chunkHead, stream) || !TSF_FourCCEquals(chunkHead.id, "sfbk")) 1368 | { 1369 | //if (e) *e = TSF_INVALID_NOSF2HEADER; 1370 | return res; 1371 | } 1372 | 1373 | // Read hydra and locate sample data. 1374 | TSF_MEMSET(&hydra, 0, sizeof(hydra)); 1375 | while (tsf_riffchunk_read(&chunkHead, &chunkList, stream)) 1376 | { 1377 | struct tsf_riffchunk chunk; 1378 | if (TSF_FourCCEquals(chunkList.id, "pdta")) 1379 | { 1380 | while (tsf_riffchunk_read(&chunkList, &chunk, stream)) 1381 | { 1382 | #define HandleChunk(chunkName) (TSF_FourCCEquals(chunk.id, #chunkName) && !(chunk.size % chunkName##SizeInFile)) \ 1383 | { \ 1384 | int num = chunk.size / chunkName##SizeInFile, i; \ 1385 | hydra.chunkName##Num = num; \ 1386 | hydra.chunkName##s = (struct tsf_hydra_##chunkName*)TSF_MALLOC(num * sizeof(struct tsf_hydra_##chunkName)); \ 1387 | if (!hydra.chunkName##s) goto out_of_memory; \ 1388 | for (i = 0; i < num; ++i) tsf_hydra_read_##chunkName(&hydra.chunkName##s[i], stream); \ 1389 | } 1390 | enum 1391 | { 1392 | phdrSizeInFile = 38, pbagSizeInFile = 4, pmodSizeInFile = 10, 1393 | pgenSizeInFile = 4, instSizeInFile = 22, ibagSizeInFile = 4, 1394 | imodSizeInFile = 10, igenSizeInFile = 4, shdrSizeInFile = 46 1395 | }; 1396 | if HandleChunk(phdr) else if HandleChunk(pbag) else if HandleChunk(pmod) 1397 | else if HandleChunk(pgen) else if HandleChunk(inst) else if HandleChunk(ibag) 1398 | else if HandleChunk(imod) else if HandleChunk(igen) else if HandleChunk(shdr) 1399 | else stream->skip(stream->data, chunk.size); 1400 | #undef HandleChunk 1401 | } 1402 | } 1403 | else if (TSF_FourCCEquals(chunkList.id, "sdta")) 1404 | { 1405 | while (tsf_riffchunk_read(&chunkList, &chunk, stream)) 1406 | { 1407 | if ((TSF_FourCCEquals(chunk.id, "smpl") 1408 | #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H 1409 | || TSF_FourCCEquals(chunk.id, "smpo") 1410 | #endif 1411 | ) && !rawBuffer && !floatBuffer && chunk.size >= sizeof(short)) 1412 | { 1413 | if (!tsf_load_samples(&rawBuffer, &floatBuffer, &smplCount, &chunk, stream)) goto out_of_memory; 1414 | } 1415 | else stream->skip(stream->data, chunk.size); 1416 | } 1417 | } 1418 | else stream->skip(stream->data, chunkList.size); 1419 | } 1420 | if (!hydra.phdrs || !hydra.pbags || !hydra.pmods || !hydra.pgens || !hydra.insts || !hydra.ibags || !hydra.imods || !hydra.igens || !hydra.shdrs) 1421 | { 1422 | //if (e) *e = TSF_INVALID_INCOMPLETE; 1423 | } 1424 | else if (!rawBuffer && !floatBuffer) 1425 | { 1426 | //if (e) *e = TSF_INVALID_NOSAMPLEDATA; 1427 | } 1428 | else 1429 | { 1430 | #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H 1431 | if (!floatBuffer && !tsf_decode_sf3_samples(rawBuffer, &floatBuffer, &smplCount, &hydra)) goto out_of_memory; 1432 | #endif 1433 | res = (tsf*)TSF_MALLOC(sizeof(tsf)); 1434 | if (res) TSF_MEMSET(res, 0, sizeof(tsf)); 1435 | if (!res || !tsf_load_presets(res, &hydra, smplCount)) goto out_of_memory; 1436 | res->outSampleRate = 44100.0f; 1437 | res->fontSamples = floatBuffer; 1438 | floatBuffer = TSF_NULL; // don't free below 1439 | } 1440 | if (0) 1441 | { 1442 | out_of_memory: 1443 | TSF_FREE(res); 1444 | res = TSF_NULL; 1445 | //if (e) *e = TSF_OUT_OF_MEMORY; 1446 | } 1447 | TSF_FREE(hydra.phdrs); TSF_FREE(hydra.pbags); TSF_FREE(hydra.pmods); 1448 | TSF_FREE(hydra.pgens); TSF_FREE(hydra.insts); TSF_FREE(hydra.ibags); 1449 | TSF_FREE(hydra.imods); TSF_FREE(hydra.igens); TSF_FREE(hydra.shdrs); 1450 | TSF_FREE(rawBuffer); TSF_FREE(floatBuffer); 1451 | return res; 1452 | } 1453 | 1454 | TSFDEF tsf* tsf_copy(tsf* f) 1455 | { 1456 | tsf* res; 1457 | if (!f) return TSF_NULL; 1458 | if (!f->refCount) 1459 | { 1460 | f->refCount = (int*)TSF_MALLOC(sizeof(int)); 1461 | if (!f->refCount) return TSF_NULL; 1462 | *f->refCount = 1; 1463 | } 1464 | res = (tsf*)TSF_MALLOC(sizeof(tsf)); 1465 | if (!res) return TSF_NULL; 1466 | TSF_MEMCPY(res, f, sizeof(tsf)); 1467 | res->voices = TSF_NULL; 1468 | res->voiceNum = 0; 1469 | res->channels = TSF_NULL; 1470 | (*res->refCount)++; 1471 | return res; 1472 | } 1473 | 1474 | TSFDEF void tsf_close(tsf* f) 1475 | { 1476 | if (!f) return; 1477 | if (!f->refCount || !--(*f->refCount)) 1478 | { 1479 | struct tsf_preset *preset = f->presets, *presetEnd = preset + f->presetNum; 1480 | for (; preset != presetEnd; preset++) TSF_FREE(preset->regions); 1481 | TSF_FREE(f->presets); 1482 | TSF_FREE(f->fontSamples); 1483 | TSF_FREE(f->refCount); 1484 | } 1485 | TSF_FREE(f->channels); 1486 | TSF_FREE(f->voices); 1487 | TSF_FREE(f); 1488 | } 1489 | 1490 | TSFDEF void tsf_reset(tsf* f) 1491 | { 1492 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1493 | for (; v != vEnd; v++) 1494 | if (v->playingPreset != -1 && (v->ampenv.segment < TSF_SEGMENT_RELEASE || v->ampenv.parameters.release)) 1495 | tsf_voice_endquick(f, v); 1496 | if (f->channels) { TSF_FREE(f->channels); f->channels = TSF_NULL; } 1497 | } 1498 | 1499 | TSFDEF int tsf_get_presetindex(const tsf* f, int bank, int preset_number) 1500 | { 1501 | const struct tsf_preset *presets; 1502 | int i, iMax; 1503 | for (presets = f->presets, i = 0, iMax = f->presetNum; i < iMax; i++) 1504 | if (presets[i].preset == preset_number && presets[i].bank == bank) 1505 | return i; 1506 | return -1; 1507 | } 1508 | 1509 | TSFDEF int tsf_get_presetcount(const tsf* f) 1510 | { 1511 | return f->presetNum; 1512 | } 1513 | 1514 | TSFDEF const char* tsf_get_presetname(const tsf* f, int preset) 1515 | { 1516 | return (preset < 0 || preset >= f->presetNum ? TSF_NULL : f->presets[preset].presetName); 1517 | } 1518 | 1519 | TSFDEF const char* tsf_bank_get_presetname(const tsf* f, int bank, int preset_number) 1520 | { 1521 | return tsf_get_presetname(f, tsf_get_presetindex(f, bank, preset_number)); 1522 | } 1523 | 1524 | TSFDEF void tsf_set_output(tsf* f, enum TSFOutputMode outputmode, int samplerate, float global_gain_db) 1525 | { 1526 | f->outputmode = outputmode; 1527 | f->outSampleRate = (float)(samplerate >= 1 ? samplerate : 44100.0f); 1528 | f->globalGainDB = global_gain_db; 1529 | } 1530 | 1531 | TSFDEF void tsf_set_volume(tsf* f, float global_volume) 1532 | { 1533 | f->globalGainDB = (global_volume == 1.0f ? 0 : -tsf_gainToDecibels(1.0f / global_volume)); 1534 | } 1535 | 1536 | TSFDEF int tsf_set_max_voices(tsf* f, int max_voices) 1537 | { 1538 | int i = f->voiceNum; 1539 | int newVoiceNum = (f->voiceNum > max_voices ? f->voiceNum : max_voices); 1540 | struct tsf_voice *newVoices = (struct tsf_voice*)TSF_REALLOC(f->voices, newVoiceNum * sizeof(struct tsf_voice)); 1541 | if (!newVoices) return 0; 1542 | f->voices = newVoices; 1543 | f->voiceNum = f->maxVoiceNum = newVoiceNum; 1544 | for (; i < max_voices; i++) 1545 | f->voices[i].playingPreset = -1; 1546 | return 1; 1547 | } 1548 | 1549 | TSFDEF int tsf_note_on(tsf* f, int preset_index, int key, float vel) 1550 | { 1551 | short midiVelocity = (short)(vel * 127); 1552 | int voicePlayIndex; 1553 | struct tsf_region *region, *regionEnd; 1554 | 1555 | if (preset_index < 0 || preset_index >= f->presetNum) return 1; 1556 | if (vel <= 0.0f) { tsf_note_off(f, preset_index, key); return 1; } 1557 | 1558 | // Play all matching regions. 1559 | voicePlayIndex = f->voicePlayIndex++; 1560 | for (region = f->presets[preset_index].regions, regionEnd = region + f->presets[preset_index].regionNum; region != regionEnd; region++) 1561 | { 1562 | struct tsf_voice *voice, *v, *vEnd; TSF_BOOL doLoop; float lowpassFilterQDB, lowpassFc; 1563 | if (key < region->lokey || key > region->hikey || midiVelocity < region->lovel || midiVelocity > region->hivel) continue; 1564 | 1565 | voice = TSF_NULL, v = f->voices, vEnd = v + f->voiceNum; 1566 | if (region->group) 1567 | { 1568 | for (; v != vEnd; v++) 1569 | if (v->playingPreset == preset_index && v->region->group == region->group) tsf_voice_endquick(f, v); 1570 | else if (v->playingPreset == -1 && !voice) voice = v; 1571 | } 1572 | else for (; v != vEnd; v++) if (v->playingPreset == -1) { voice = v; break; } 1573 | 1574 | if (!voice) 1575 | { 1576 | if (f->maxVoiceNum) 1577 | { 1578 | // Voices have been pre-allocated and limited to a maximum, try to kill a voice off in its release envelope 1579 | int bestKillReleaseSamplePos = -999999999; 1580 | for (v = f->voices; v != vEnd; v++) 1581 | { 1582 | if (v->ampenv.segment == TSF_SEGMENT_RELEASE) 1583 | { 1584 | // We're looking for the voice furthest into its release 1585 | int releaseSamplesDone = tsf_voice_envelope_release_samples(&v->ampenv, f->outSampleRate) - v->ampenv.samplesUntilNextSegment; 1586 | if (releaseSamplesDone > bestKillReleaseSamplePos) 1587 | { 1588 | bestKillReleaseSamplePos = releaseSamplesDone; 1589 | voice = v; 1590 | } 1591 | } 1592 | } 1593 | if (!voice) 1594 | continue; 1595 | tsf_voice_kill(voice); 1596 | } 1597 | else 1598 | { 1599 | // Allocate more voices so we don't need to kill one off. 1600 | struct tsf_voice* newVoices; 1601 | f->voiceNum += 4; 1602 | newVoices = (struct tsf_voice*)TSF_REALLOC(f->voices, f->voiceNum * sizeof(struct tsf_voice)); 1603 | if (!newVoices) return 0; 1604 | f->voices = newVoices; 1605 | voice = &f->voices[f->voiceNum - 4]; 1606 | voice[1].playingPreset = voice[2].playingPreset = voice[3].playingPreset = -1; 1607 | } 1608 | } 1609 | 1610 | voice->region = region; 1611 | voice->playingPreset = preset_index; 1612 | voice->playingKey = key; 1613 | voice->playIndex = voicePlayIndex; 1614 | voice->heldSustain = 0; 1615 | voice->noteGainDB = f->globalGainDB - (region->attenuation / 10.0f) - tsf_gainToDecibels(1.0f / vel); 1616 | 1617 | if (f->channels) 1618 | { 1619 | f->channels->setupVoice(f, voice); 1620 | } 1621 | else 1622 | { 1623 | tsf_voice_calcpitchratio(voice, 0, f->outSampleRate); 1624 | // The SFZ spec is silent about the pan curve, but a 3dB pan law seems common. This sqrt() curve matches what Dimension LE does; Alchemy Free seems closer to sin(adjustedPan * pi/2). 1625 | voice->panFactorLeft = TSF_SQRTF(0.5f - region->pan); 1626 | voice->panFactorRight = TSF_SQRTF(0.5f + region->pan); 1627 | } 1628 | 1629 | // Offset/end. 1630 | voice->sourceSamplePosition = region->offset; 1631 | 1632 | // Loop. 1633 | doLoop = (region->loop_mode != TSF_LOOPMODE_NONE && region->loop_start < region->loop_end); 1634 | voice->loopStart = (doLoop ? region->loop_start : 0); 1635 | voice->loopEnd = (doLoop ? region->loop_end : 0); 1636 | 1637 | // Setup envelopes. 1638 | tsf_voice_envelope_setup(&voice->ampenv, ®ion->ampenv, key, midiVelocity, TSF_TRUE, f->outSampleRate); 1639 | tsf_voice_envelope_setup(&voice->modenv, ®ion->modenv, key, midiVelocity, TSF_FALSE, f->outSampleRate); 1640 | 1641 | // Setup lowpass filter. 1642 | lowpassFc = (region->initialFilterFc <= 13500 ? tsf_cents2Hertz((float)region->initialFilterFc) / f->outSampleRate : 1.0f); 1643 | lowpassFilterQDB = region->initialFilterQ / 10.0f; 1644 | voice->lowpass.QInv = 1.0 / TSF_POW(10.0, (lowpassFilterQDB / 20.0)); 1645 | voice->lowpass.z1 = voice->lowpass.z2 = 0; 1646 | voice->lowpass.active = (lowpassFc < 0.499f); 1647 | if (voice->lowpass.active) tsf_voice_lowpass_setup(&voice->lowpass, lowpassFc); 1648 | 1649 | // Setup LFO filters. 1650 | tsf_voice_lfo_setup(&voice->modlfo, region->delayModLFO, region->freqModLFO, f->outSampleRate); 1651 | tsf_voice_lfo_setup(&voice->viblfo, region->delayVibLFO, region->freqVibLFO, f->outSampleRate); 1652 | } 1653 | return 1; 1654 | } 1655 | 1656 | TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel) 1657 | { 1658 | int preset_index = tsf_get_presetindex(f, bank, preset_number); 1659 | if (preset_index == -1) return 0; 1660 | return tsf_note_on(f, preset_index, key, vel); 1661 | } 1662 | 1663 | TSFDEF void tsf_note_off(tsf* f, int preset_index, int key) 1664 | { 1665 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = TSF_NULL, *vMatchLast = TSF_NULL; 1666 | for (; v != vEnd; v++) 1667 | { 1668 | //Find the first and last entry in the voices list with matching preset, key and look up the smallest play index 1669 | if (v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE) continue; 1670 | else if (!vMatchFirst || v->playIndex < vMatchFirst->playIndex) vMatchFirst = vMatchLast = v; 1671 | else if (v->playIndex == vMatchFirst->playIndex) vMatchLast = v; 1672 | } 1673 | if (!vMatchFirst) return; 1674 | for (v = vMatchFirst; v <= vMatchLast; v++) 1675 | { 1676 | //Stop all voices with matching preset, key and the smallest play index which was enumerated above 1677 | if (v != vMatchFirst && v != vMatchLast && 1678 | (v->playIndex != vMatchFirst->playIndex || v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE)) continue; 1679 | tsf_voice_end(f, v); 1680 | } 1681 | } 1682 | 1683 | TSFDEF int tsf_count_held_sustain(tsf* f) 1684 | { 1685 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1686 | int result = 0; 1687 | for (; v != vEnd; v++) 1688 | { 1689 | if (v->playingPreset == -1 || v->ampenv.segment >= TSF_SEGMENT_RELEASE || v->heldSustain == 0) continue; 1690 | result++; 1691 | } 1692 | return result; 1693 | } 1694 | 1695 | 1696 | TSFDEF int tsf_bank_note_off(tsf* f, int bank, int preset_number, int key) 1697 | { 1698 | int preset_index = tsf_get_presetindex(f, bank, preset_number); 1699 | if (preset_index == -1) return 0; 1700 | tsf_note_off(f, preset_index, key); 1701 | return 1; 1702 | } 1703 | 1704 | TSFDEF void tsf_note_off_all(tsf* f) 1705 | { 1706 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1707 | for (; v != vEnd; v++) if (v->playingPreset != -1 && v->ampenv.segment < TSF_SEGMENT_RELEASE) 1708 | tsf_voice_end(f, v); 1709 | } 1710 | 1711 | TSFDEF int tsf_active_voice_count(tsf* f) 1712 | { 1713 | int count = 0; 1714 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1715 | for (; v != vEnd; v++) if (v->playingPreset != -1) count++; 1716 | return count; 1717 | } 1718 | 1719 | TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing) 1720 | { 1721 | float outputSamples[TSF_RENDER_SHORTBUFFERBLOCK]; 1722 | int channels = (f->outputmode == TSF_MONO ? 1 : 2), maxChannelSamples = TSF_RENDER_SHORTBUFFERBLOCK / channels; 1723 | while (samples > 0) 1724 | { 1725 | int channelSamples = (samples > maxChannelSamples ? maxChannelSamples : samples); 1726 | short* bufferEnd = buffer + channelSamples * channels; 1727 | float *floatSamples = outputSamples; 1728 | tsf_render_float(f, floatSamples, channelSamples, TSF_FALSE); 1729 | samples -= channelSamples; 1730 | 1731 | if (flag_mixing) 1732 | while (buffer != bufferEnd) 1733 | { 1734 | float v = *floatSamples++; 1735 | int vi = *buffer + (v < -1.00004566f ? (int)-32768 : (v > 1.00001514f ? (int)32767 : (int)(v * 32767.5f))); 1736 | *buffer++ = (vi < -32768 ? (short)-32768 : (vi > 32767 ? (short)32767 : (short)vi)); 1737 | } 1738 | else 1739 | while (buffer != bufferEnd) 1740 | { 1741 | float v = *floatSamples++; 1742 | *buffer++ = (v < -1.00004566f ? (short)-32768 : (v > 1.00001514f ? (short)32767 : (short)(v * 32767.5f))); 1743 | } 1744 | } 1745 | } 1746 | 1747 | TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing) 1748 | { 1749 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1750 | if (!flag_mixing) TSF_MEMSET(buffer, 0, (f->outputmode == TSF_MONO ? 1 : 2) * sizeof(float) * samples); 1751 | for (; v != vEnd; v++) 1752 | if (v->playingPreset != -1) 1753 | tsf_voice_render(f, v, buffer, samples); 1754 | } 1755 | 1756 | static void tsf_channel_setup_voice(tsf* f, struct tsf_voice* v) 1757 | { 1758 | struct tsf_channel* c = &f->channels->channels[f->channels->activeChannel]; 1759 | float newpan = v->region->pan + c->panOffset; 1760 | v->playingChannel = f->channels->activeChannel; 1761 | v->noteGainDB += c->gainDB; 1762 | tsf_voice_calcpitchratio(v, (c->pitchWheel == 8192 ? c->tuning : ((c->pitchWheel / 16383.0f * c->pitchRange * 2.0f) - c->pitchRange + c->tuning)), f->outSampleRate); 1763 | if (newpan <= -0.5f) { v->panFactorLeft = 1.0f; v->panFactorRight = 0.0f; } 1764 | else if (newpan >= 0.5f) { v->panFactorLeft = 0.0f; v->panFactorRight = 1.0f; } 1765 | else { v->panFactorLeft = TSF_SQRTF(0.5f - newpan); v->panFactorRight = TSF_SQRTF(0.5f + newpan); } 1766 | } 1767 | 1768 | static struct tsf_channel* tsf_channel_init(tsf* f, int channel) 1769 | { 1770 | int i; 1771 | if (f->channels && channel < f->channels->channelNum) return &f->channels->channels[channel]; 1772 | if (!f->channels) 1773 | { 1774 | f->channels = (struct tsf_channels*)TSF_MALLOC(sizeof(struct tsf_channels) + sizeof(struct tsf_channel) * channel); 1775 | if (!f->channels) return TSF_NULL; 1776 | f->channels->setupVoice = &tsf_channel_setup_voice; 1777 | f->channels->channelNum = 0; 1778 | f->channels->activeChannel = 0; 1779 | } 1780 | else 1781 | { 1782 | struct tsf_channels *newChannels = (struct tsf_channels*)TSF_REALLOC(f->channels, sizeof(struct tsf_channels) + sizeof(struct tsf_channel) * channel); 1783 | if (!newChannels) return TSF_NULL; 1784 | f->channels = newChannels; 1785 | } 1786 | i = f->channels->channelNum; 1787 | f->channels->channelNum = channel + 1; 1788 | for (; i <= channel; i++) 1789 | { 1790 | struct tsf_channel* c = &f->channels->channels[i]; 1791 | c->presetIndex = c->bank = c->sustain = 0; 1792 | c->pitchWheel = c->midiPan = 8192; 1793 | c->midiVolume = c->midiExpression = 16383; 1794 | c->midiRPN = 0xFFFF; 1795 | c->midiData = 0; 1796 | c->panOffset = 0.0f; 1797 | c->gainDB = 0.0f; 1798 | c->pitchRange = 2.0f; 1799 | c->tuning = 0.0f; 1800 | } 1801 | return &f->channels->channels[channel]; 1802 | } 1803 | 1804 | static void tsf_channel_applypitch(tsf* f, int channel, struct tsf_channel* c) 1805 | { 1806 | struct tsf_voice *v, *vEnd; 1807 | float pitchShift = (c->pitchWheel == 8192 ? c->tuning : ((c->pitchWheel / 16383.0f * c->pitchRange * 2.0f) - c->pitchRange + c->tuning)); 1808 | for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++) 1809 | if (v->playingPreset != -1 && v->playingChannel == channel) 1810 | tsf_voice_calcpitchratio(v, pitchShift, f->outSampleRate); 1811 | } 1812 | 1813 | TSFDEF int tsf_channel_set_presetindex(tsf* f, int channel, int preset_index) 1814 | { 1815 | struct tsf_channel *c = tsf_channel_init(f, channel); 1816 | if (!c) return 0; 1817 | c->presetIndex = (unsigned short)preset_index; 1818 | return 1; 1819 | } 1820 | 1821 | TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums) 1822 | { 1823 | int preset_index; 1824 | struct tsf_channel *c = tsf_channel_init(f, channel); 1825 | if (!c) return 0; 1826 | if (flag_mididrums) 1827 | { 1828 | preset_index = tsf_get_presetindex(f, 128 | (c->bank & 0x7FFF), preset_number); 1829 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, 128, preset_number); 1830 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, 128, 0); 1831 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, (c->bank & 0x7FFF), preset_number); 1832 | } 1833 | else preset_index = tsf_get_presetindex(f, (c->bank & 0x7FFF), preset_number); 1834 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, 0, preset_number); 1835 | if (preset_index != -1) 1836 | { 1837 | c->presetIndex = (unsigned short)preset_index; 1838 | return 1; 1839 | } 1840 | return 0; 1841 | } 1842 | 1843 | TSFDEF int tsf_channel_set_bank(tsf* f, int channel, int bank) 1844 | { 1845 | struct tsf_channel *c = tsf_channel_init(f, channel); 1846 | if (!c) return 0; 1847 | c->bank = (unsigned short)bank; 1848 | return 1; 1849 | } 1850 | 1851 | TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number) 1852 | { 1853 | int preset_index; 1854 | struct tsf_channel *c = tsf_channel_init(f, channel); 1855 | if (!c) return 0; 1856 | preset_index = tsf_get_presetindex(f, bank, preset_number); 1857 | if (preset_index == -1) return 0; 1858 | c->presetIndex = (unsigned short)preset_index; 1859 | c->bank = (unsigned short)bank; 1860 | return 1; 1861 | } 1862 | 1863 | TSFDEF int tsf_channel_set_pan(tsf* f, int channel, float pan) 1864 | { 1865 | struct tsf_voice *v, *vEnd; 1866 | struct tsf_channel *c = tsf_channel_init(f, channel); 1867 | if (!c) return 0; 1868 | for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++) 1869 | if (v->playingChannel == channel && v->playingPreset != -1) 1870 | { 1871 | float newpan = v->region->pan + pan - 0.5f; 1872 | if (newpan <= -0.5f) { v->panFactorLeft = 1.0f; v->panFactorRight = 0.0f; } 1873 | else if (newpan >= 0.5f) { v->panFactorLeft = 0.0f; v->panFactorRight = 1.0f; } 1874 | else { v->panFactorLeft = TSF_SQRTF(0.5f - newpan); v->panFactorRight = TSF_SQRTF(0.5f + newpan); } 1875 | } 1876 | c->panOffset = pan - 0.5f; 1877 | return 1; 1878 | } 1879 | 1880 | TSFDEF int tsf_channel_set_volume(tsf* f, int channel, float volume) 1881 | { 1882 | float gainDB = tsf_gainToDecibels(volume), gainDBChange; 1883 | struct tsf_voice *v, *vEnd; 1884 | struct tsf_channel *c = tsf_channel_init(f, channel); 1885 | if (!c) return 0; 1886 | if (gainDB == c->gainDB) return 1; 1887 | for (v = f->voices, vEnd = v + f->voiceNum, gainDBChange = gainDB - c->gainDB; v != vEnd; v++) 1888 | if (v->playingPreset != -1 && v->playingChannel == channel) 1889 | v->noteGainDB += gainDBChange; 1890 | c->gainDB = gainDB; 1891 | return 1; 1892 | } 1893 | 1894 | TSFDEF int tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel) 1895 | { 1896 | struct tsf_channel *c = tsf_channel_init(f, channel); 1897 | if (!c) return 0; 1898 | if (c->pitchWheel == pitch_wheel) return 1; 1899 | c->pitchWheel = (unsigned short)pitch_wheel; 1900 | tsf_channel_applypitch(f, channel, c); 1901 | return 1; 1902 | } 1903 | 1904 | TSFDEF int tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range) 1905 | { 1906 | struct tsf_channel *c = tsf_channel_init(f, channel); 1907 | if (!c) return 0; 1908 | if (c->pitchRange == pitch_range) return 1; 1909 | c->pitchRange = pitch_range; 1910 | if (c->pitchWheel != 8192) tsf_channel_applypitch(f, channel, c); 1911 | return 1; 1912 | } 1913 | 1914 | TSFDEF int tsf_channel_set_tuning(tsf* f, int channel, float tuning) 1915 | { 1916 | struct tsf_channel *c = tsf_channel_init(f, channel); 1917 | if (!c) return 0; 1918 | if (c->tuning == tuning) return 1; 1919 | c->tuning = tuning; 1920 | tsf_channel_applypitch(f, channel, c); 1921 | return 1; 1922 | } 1923 | 1924 | TSFDEF int tsf_channel_set_sustain(tsf* f, int channel, int sustain) 1925 | { 1926 | struct tsf_channel *c = tsf_channel_init(f, channel); 1927 | if (!c) return 0; 1928 | if (c->sustain == sustain) return 1; 1929 | c->sustain = sustain; 1930 | // Turning on sustain does no action now, just starts note_off behaving differently 1931 | if (sustain) return 1; 1932 | // Turning off sustain, actually end voices that got a note_off and were set to heldSustain status 1933 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1934 | for (; v != vEnd; v++) 1935 | if (v->playingPreset != -1 && v->playingChannel == channel && v->ampenv.segment < TSF_SEGMENT_RELEASE && v->heldSustain) 1936 | tsf_voice_end(f, v); 1937 | return 1; 1938 | } 1939 | 1940 | TSFDEF int tsf_channel_note_on(tsf* f, int channel, int key, float vel) 1941 | { 1942 | if (!f->channels || channel >= f->channels->channelNum) return 1; 1943 | f->channels->activeChannel = channel; 1944 | if (!vel) { 1945 | tsf_channel_note_off(f, channel, key); 1946 | return 1; 1947 | } 1948 | return tsf_note_on(f, f->channels->channels[channel].presetIndex, key, vel); 1949 | } 1950 | 1951 | TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key) 1952 | { 1953 | int sustain = f->channels->channels[channel].sustain; 1954 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = TSF_NULL, *vMatchLast = TSF_NULL; 1955 | for (; v != vEnd; v++) 1956 | { 1957 | //Find the first and last entry in the voices list with matching channel, key and look up the smallest play index 1958 | if (v->playingPreset == -1 || v->playingChannel != channel || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE || v->heldSustain) continue; 1959 | else if (!vMatchFirst || v->playIndex < vMatchFirst->playIndex) vMatchFirst = vMatchLast = v; 1960 | else if (v->playIndex == vMatchFirst->playIndex) vMatchLast = v; 1961 | } 1962 | if (!vMatchFirst) return; 1963 | for (v = vMatchFirst; v <= vMatchLast; v++) 1964 | { 1965 | //Stop all voices with matching channel, key and the smallest play index which was enumerated above 1966 | if (v != vMatchFirst && v != vMatchLast && 1967 | (v->playIndex != vMatchFirst->playIndex || v->playingPreset == -1 || v->playingChannel != channel || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE)) continue; 1968 | if (sustain) { 1969 | // Don't turn off if sustain is active, just mark as held by sustain so we don't forget it 1970 | v->heldSustain = 1; 1971 | } else { 1972 | tsf_voice_end(f, v); 1973 | } 1974 | } 1975 | } 1976 | 1977 | TSFDEF void tsf_channel_note_off_all(tsf* f, int channel) 1978 | { 1979 | // Ignore sustain channel settings, note_off_all overrides 1980 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1981 | for (; v != vEnd; v++) 1982 | if (v->playingPreset != -1 && v->playingChannel == channel && v->ampenv.segment < TSF_SEGMENT_RELEASE) 1983 | tsf_voice_end(f, v); 1984 | } 1985 | 1986 | TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel) 1987 | { 1988 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1989 | for (; v != vEnd; v++) 1990 | if (v->playingPreset != -1 && v->playingChannel == channel && (v->ampenv.segment < TSF_SEGMENT_RELEASE || v->ampenv.parameters.release)) 1991 | tsf_voice_endquick(f, v); 1992 | } 1993 | 1994 | TSFDEF int tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value) 1995 | { 1996 | struct tsf_channel* c = tsf_channel_init(f, channel); 1997 | if (!c) return 0; 1998 | switch (controller) 1999 | { 2000 | case 7 /*VOLUME_MSB*/ : c->midiVolume = (unsigned short)((c->midiVolume & 0x7F ) | (control_value << 7)); goto TCMC_SET_VOLUME; 2001 | case 39 /*VOLUME_LSB*/ : c->midiVolume = (unsigned short)((c->midiVolume & 0x3F80) | control_value); goto TCMC_SET_VOLUME; 2002 | case 11 /*EXPRESSION_MSB*/ : c->midiExpression = (unsigned short)((c->midiExpression & 0x7F ) | (control_value << 7)); goto TCMC_SET_VOLUME; 2003 | case 43 /*EXPRESSION_LSB*/ : c->midiExpression = (unsigned short)((c->midiExpression & 0x3F80) | control_value); goto TCMC_SET_VOLUME; 2004 | case 10 /*PAN_MSB*/ : c->midiPan = (unsigned short)((c->midiPan & 0x7F ) | (control_value << 7)); goto TCMC_SET_PAN; 2005 | case 42 /*PAN_LSB*/ : c->midiPan = (unsigned short)((c->midiPan & 0x3F80) | control_value); goto TCMC_SET_PAN; 2006 | case 6 /*DATA_ENTRY_MSB*/ : c->midiData = (unsigned short)((c->midiData & 0x7F) | (control_value << 7)); goto TCMC_SET_DATA; 2007 | case 38 /*DATA_ENTRY_LSB*/ : c->midiData = (unsigned short)((c->midiData & 0x3F80) | control_value); goto TCMC_SET_DATA; 2008 | case 0 /*BANK_SELECT_MSB*/ : c->bank = (unsigned short)(0x8000 | control_value); return 1; //bank select MSB alone acts like LSB 2009 | case 32 /*BANK_SELECT_LSB*/ : c->bank = (unsigned short)((c->bank & 0x8000 ? ((c->bank & 0x7F) << 7) : 0) | control_value); return 1; 2010 | case 101 /*RPN_MSB*/ : c->midiRPN = (unsigned short)(((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x7F ) | (control_value << 7)); return 1; 2011 | case 100 /*RPN_LSB*/ : c->midiRPN = (unsigned short)(((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x3F80) | control_value); return 1; 2012 | case 98 /*NRPN_LSB*/ : c->midiRPN = 0xFFFF; return 1; 2013 | case 99 /*NRPN_MSB*/ : c->midiRPN = 0xFFFF; return 1; 2014 | case 64 /*SUSTAIN*/ : tsf_channel_set_sustain(f, channel, control_value >= 64); return 1; 2015 | case 120 /*ALL_SOUND_OFF*/ : tsf_channel_sounds_off_all(f, channel); return 1; 2016 | case 123 /*ALL_NOTES_OFF*/ : tsf_channel_note_off_all(f, channel); return 1; 2017 | case 121 /*ALL_CTRL_OFF*/ : 2018 | c->midiVolume = c->midiExpression = 16383; 2019 | c->midiPan = 8192; 2020 | c->bank = 0; 2021 | c->midiRPN = 0xFFFF; 2022 | c->midiData = 0; 2023 | tsf_channel_set_volume(f, channel, 1.0f); 2024 | tsf_channel_set_pan(f, channel, 0.5f); 2025 | tsf_channel_set_pitchrange(f, channel, 2.0f); 2026 | tsf_channel_set_tuning(f, channel, 0); 2027 | return 1; 2028 | } 2029 | return 1; 2030 | TCMC_SET_VOLUME: 2031 | //Raising to the power of 3 seems to result in a decent sounding volume curve for MIDI 2032 | tsf_channel_set_volume(f, channel, TSF_POWF((c->midiVolume / 16383.0f) * (c->midiExpression / 16383.0f), 3.0f)); 2033 | return 1; 2034 | TCMC_SET_PAN: 2035 | tsf_channel_set_pan(f, channel, c->midiPan / 16383.0f); 2036 | return 1; 2037 | TCMC_SET_DATA: 2038 | if (c->midiRPN == 0) tsf_channel_set_pitchrange(f, channel, (c->midiData >> 7) + 0.01f * (c->midiData & 0x7F)); 2039 | else if (c->midiRPN == 1) tsf_channel_set_tuning(f, channel, (int)c->tuning + ((float)c->midiData - 8192.0f) / 8192.0f); //fine tune 2040 | else if (c->midiRPN == 2 && controller == 6) tsf_channel_set_tuning(f, channel, ((float)control_value - 64.0f) + (c->tuning - (int)c->tuning)); //coarse tune 2041 | return 1; 2042 | } 2043 | 2044 | TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel) 2045 | { 2046 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].presetIndex : 0); 2047 | } 2048 | 2049 | TSFDEF int tsf_channel_get_preset_bank(tsf* f, int channel) 2050 | { 2051 | return (f->channels && channel < f->channels->channelNum ? (f->channels->channels[channel].bank & 0x7FFF) : 0); 2052 | } 2053 | 2054 | TSFDEF int tsf_channel_get_preset_number(tsf* f, int channel) 2055 | { 2056 | return (f->channels && channel < f->channels->channelNum ? f->presets[f->channels->channels[channel].presetIndex].preset : 0); 2057 | } 2058 | 2059 | TSFDEF float tsf_channel_get_pan(tsf* f, int channel) 2060 | { 2061 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].panOffset - 0.5f : 0.5f); 2062 | } 2063 | 2064 | TSFDEF float tsf_channel_get_volume(tsf* f, int channel) 2065 | { 2066 | return (f->channels && channel < f->channels->channelNum ? tsf_decibelsToGain(f->channels->channels[channel].gainDB) : 1.0f); 2067 | } 2068 | 2069 | TSFDEF int tsf_channel_get_pitchwheel(tsf* f, int channel) 2070 | { 2071 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].pitchWheel : 8192); 2072 | } 2073 | 2074 | TSFDEF float tsf_channel_get_pitchrange(tsf* f, int channel) 2075 | { 2076 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].pitchRange : 2.0f); 2077 | } 2078 | 2079 | TSFDEF float tsf_channel_get_tuning(tsf* f, int channel) 2080 | { 2081 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].tuning : 0.0f); 2082 | } 2083 | 2084 | #ifdef __cplusplus 2085 | } 2086 | #endif 2087 | 2088 | #endif //TSF_IMPLEMENTATION 2089 | --------------------------------------------------------------------------------