Comments (15)
I think something like this could work?
This code is basically example1.c which only plays two notes of the default instrument at the start and does nothing else. If you want to play a bunch of notes over time look at example2.c and if you want to play MIDI look at example3.c.
As I don't know how to load a file on the Bela platform you need to figure out which of the 2 loading-methods you can use.
[Advanced] Also because Bela only accepts a single sample with each audioWrite call we need to do a lot of work with our sample buffer and the many for loops it might be nicer to modify tsf_voice_render inside tsf.h and call audioWrite directly instead of rendering into a buffer of floats.
#include <Bela.h>
#define TSF_IMPLEMENTATION
#include "../tsf.h"
static tsf* g_TinySoundFont;
static float* g_SampleBuffer;
bool setup(BelaContext *context, void *userData)
{
---------------------------------------------------------------------------------------------------
//If Bela has a file system and allows loading of files with fopen, use this:
g_TinySoundFont = tsf_load_filename("soundfont.sf2");
-----------------------------------------------------------------------------------------------------
//If Bela has no file system you need to load your soundfont data into a memory buffer
int SF2FileSize = ***; //get the size of the sf2 file
unsigned char* SF2FileBuffer = ***; //get the data of the sf2 file
g_TinySoundFont = tsf_load_memory(SF2FileBuffer, SF2FileSize);
-----------------------------------------------------------------------------------------------------
if (!g_TinySoundFont) return false; //error: Could not load soundfont
// Set the rendering output mode
enum TSFOutputMode OutputMode;
if (context->audioOutChannels == 1) OutputMode = TSF_MONO;
else if (context->audioOutChannels == 2) OutputMode = TSF_STEREO_INTERLEAVED;
else return false; //error: Unsupported number of output channels
tsf_set_output(g_TinySoundFont, TSF_STEREO_INTERLEAVED, context->audioSampleRate, 0);
//allocate a block of memory for rendering samples to
g_SampleBuffer = malloc(TSF_RENDER_EFFECTSAMPLEBLOCK * context->audioOutChannels);
// Start two notes on preset 0 (default instrument)
tsf_note_on(g_TinySoundFont, 0, 48, 1.0f); //C2
tsf_note_on(g_TinySoundFont, 0, 52, 1.0f); //E2
return true;
}
void render(BelaContext *context, void *userData)
{
for(unsigned int i = 0; i < context->audioFrames; i += TSF_RENDER_EFFECTSAMPLEBLOCK)
{
//Render a block of samples to our buffer
unsigned int renderCount = (i + TSF_RENDER_EFFECTSAMPLEBLOCK > context->audioFrames ? context->audioFrames - i : TSF_RENDER_EFFECTSAMPLEBLOCK);
tsf_render_float(g_TinySoundFont, g_SampleBuffer, (int)renderCount, 0);
//Write each rendered sample to the Bela output
float* pBuffer = g_SampleBuffer;
for(unsigned int n = i; n < renderCount; n++)
for(unsigned int channel = 0; channel < context->audioOutChannels; channel++)
audioWrite(context, n, channel, *(pBuffer++));
}
}
void cleanup(BelaContext *context, void *userData)
{
if (g_SampleBuffer) { free(g_SampleBuffer); g_SampleBuffer = NULL; }
if (g_TinySoundFont) { tsf_close(g_TinySoundFont); g_TinySoundFont = NULL; }
}
from tinysoundfont.
Hi Daniel!
Rest assured, TSF works perfectly fine without SDL.
The TSF library itself does just synthesize wave data into a buffer so it alone has no means to output audio via some speaker. That requires plenty of operating system interaction code for each system supported so that is why the examples use SDL2 for that. Maybe I should add a sample that just writes a .WAV file.
If you look at the top page for the library there is a simplified code sample with no dependencies.
To integrate it into something like a game engine you need to perform these steps:
- Perform the tsf_load_* function and store the tsf* pointer during the asset loading phase of a sound font (.sf2) file. Also set the drum channel and the output format like in example3 here.
- Perform the tml_load_* function and store the tml_message* pointer during the asset loading phase of a MIDI file.
- Now your game engine should have some kind of callback form the audio thread where you can feed it audio wave form data. Inside that callback you should scan through the passed tml_message and issue tsf_channel_* functions according to the MIDI data. Then finally call a tsf_render_* function to synthesize the amount of samples requested by the audio thread. This is the biggest step and it should end up looking very close to the
AudioCallback
function from example3. - When the sound font asset is being destroyed, call tsf_close
- When the midi font asset is being destroyed, call tml_free
Very broad overview. Make sure Godot engine allows integration with code under the MIT license (used by TSF) and the zlib license (used by TML).
If you have any other questions, feel free to ask! Good luck! :-)
from tinysoundfont.
Hello,
Those have been some really useful answers, I will contact you if i have any further questions
thanks!
from tinysoundfont.
Thanks Daniel and Bernhard, I'd like to run Tiny on my Bela (low latency cape for BeagleBone Black) and need to disconnect the SDL (mainly because of the Posix calls to dlsym). Being a Windows guy I'm lost on Debian - luckily Bela presents like a VST with one callback to fill the audio buffer, it doing the audio presentation. I can develop on VS windows, test in a Vst Host and then port the code.
Cheers for the help.......
from tinysoundfont.
This library really has no connections to SDL, it's just the examples that use it as a way to output sound on win/mac/linux. See the sample code in the README.md on how to generate audio data with TSF.
I don't know about Bela and the API you're presented with, but if you post some sample audio output code I can fill in how it would look with TSF doing some sound :-)
from tinysoundfont.
You're too kind, there are some examples loaded into Bela, I'll try and dig something relevant out.
Many thanks
from tinysoundfont.
I really don't want to give you work Bernhard, you've done all of the work already.....
I've sent this file up as an example of a Bela file - for reference - but I'll hack away with TFS, If I was in gear with C++ I would be laughing but It won't take long. It's a very interesting project for me anyway....
from tinysoundfont.
So I fiddled around - made it worse, unrolled it a bit. My C++ is really poor, damn you C# learning....
#include
<Bela.h>
#include "SoundFontHelper.h"
#include "SoundFontGlobals.h"
#define TSF_IMPLEMENTATION
#include "tsf.h"
struct SoundFontGlobals // AMC
{
float* sampleBuffer;
bool presetIsValid;
char* soundfontFilename;
int totalNumInputChannels;
int totalNumOutputChannels;
int sampleRate;
};
SoundFontGlobals soundFontGlobals;
static tsf* tinySoundFont;
bool setup(BelaContext *context, void *userData)
{
soundFontGlobals.presetIsValid = false;
soundFontGlobals.totalNumInputChannels = context->audioInChannels;
soundFontGlobals.totalNumOutputChannels = context->audioOutChannels;
soundFontGlobals.sampleRate = context->audioSampleRate;
soundFontGlobals.soundfontFilename = (char*)"HSStrings.sf2";
tinySoundFont = tsf_load_filename(soundFontGlobals.soundfontFilename);
if (!tinySoundFont)
{
// Couldn't load Soundfont file
}
else
{
// Set the rendering output mode
enum TSFOutputMode OutputMode = TSF_STEREO_UNWEAVED;
if (soundFontGlobals.totalNumOutputChannels == 1) OutputMode = TSF_MONO;
else if (soundFontGlobals.totalNumOutputChannels == 2) OutputMode = TSF_STEREO_INTERLEAVED;
if ((OutputMode == TSF_MONO) | (OutputMode == TSF_STEREO_INTERLEAVED))
{
tsf_set_output(tinySoundFont, OutputMode, soundFontGlobals.sampleRate, 0);
//allocate a block of memory for rendering samples to
soundFontGlobals.sampleBuffer = (float*)malloc(TSF_RENDER_EFFECTSAMPLEBLOCK * soundFontGlobals.totalNumOutputChannels);
soundFontGlobals.presetIsValid = true;
}
else
{
// error: Unsupported number of output channels
}
}
if (soundFontGlobals.presetIsValid)
{
// Start two notes on preset 0 (default instrument)
tsf_note_on(tinySoundFont, 0, 36, 1.0f); //C2
tsf_note_on(tinySoundFont, 0, 40, 1.0f); //E2
tsf_note_on(tinySoundFont, 0, 45, 1.0f); //A2
tsf_note_on(tinySoundFont, 0, 48, 1.0f); //C3
tsf_note_on(tinySoundFont, 0, 52, 1.0f); //E3
tsf_note_on(tinySoundFont, 0, 57, 1.0f); //A3
tsf_note_on(tinySoundFont, 0, 48 + 12, 1.0f); //C4
tsf_note_on(tinySoundFont, 0, 52 + 12, 1.0f); //E4
tsf_note_on(tinySoundFont, 0, 57 + 12, 1.0f); //A4
tsf_note_on(tinySoundFont, 0, 48 + 24, 1.0f); //C5
tsf_note_on(tinySoundFont, 0, 52 + 24, 1.0f); //E5
tsf_note_on(tinySoundFont, 0, 57 + 24, 1.0f); //A5
tsf_note_on(tinySoundFont, 0, 48 + 36, 1.0f); //C6
tsf_note_on(tinySoundFont, 0, 52 + 36, 1.0f); //E6
tsf_note_on(tinySoundFont, 0, 57 + 36, 1.0f); //A6
}
return true;
}
void render(BelaContext *context, void *userData)
{
for(unsigned int i = 0; i < context->audioFrames; i += TSF_RENDER_EFFECTSAMPLEBLOCK)
{
//Render a block of samples to our buffer
unsigned int renderCount = (i + TSF_RENDER_EFFECTSAMPLEBLOCK > context->audioFrames ? context->audioFrames - i : TSF_RENDER_EFFECTSAMPLEBLOCK);
tsf_render_float(tinySoundFont, soundFontGlobals.sampleBuffer, (int)renderCount, 0);
//Write each rendered sample to the Bela output
float* pBuffer = soundFontGlobals.sampleBuffer;
for(unsigned int n = i; n < renderCount; n++)
for(unsigned int channel = 0; channel < context->audioOutChannels; channel++)
audioWrite(context, n, channel, *(pBuffer++));
}
}
void cleanup(BelaContext *context, void *userData)
{
if (soundFontGlobals.sampleBuffer) { free(soundFontGlobals.sampleBuffer); soundFontGlobals.sampleBuffer = NULL; }
if (tinySoundFont) { tsf_close(tinySoundFont); tinySoundFont = NULL; }
}
`
So playing 15 notes of a looped string preset, running at 88000hz with a window of 16 samples, Bela runs at 35%.
from tinysoundfont.
So... things are going great? Or is that bad? I can't tell >_<
Also, you might want to check out how to properly post code here with syntax highlighting.
from tinysoundfont.
Ah that's how you do it :)
Yes that's brilliant, I want to replace my hardware synths with something portable - Bela is tiddly and only requires a midi controller plugged into its Midi port, 5v and Audio in and out. I can stop lugging around 200kgs of synth gear.....
Many many thanks for your help getting here, I'll keep you up to date with my adventures.....
I'll probably have to stop hogging Daniels thread though :)
from tinysoundfont.
So, It works !! I'll have to rope a video together.....
https://plus.google.com/u/0/collection/c7x6cB
from tinysoundfont.
et voila https://www.facebook.com/SoundfontBela/ (finally)...
from tinysoundfont.
Lots to do, not least of which is mention your work properly and undo some of the nasty code things that I've done. I got Very busy today - nailing down any potential kites for Storm Callum - but will start doing some appropriate documentation.
It works nicely though and sounds great too....
from tinysoundfont.
Would you be interested in getting examples using other audio toolkits?
I adapted the sample code to work with this lib, and wanted to know if I should send a PR:
https://github.com/dr-soft/mini_al
from tinysoundfont.
@kavika13 can you share your sample somewhere? I'm interested, currently looking to get something able to mix and play mp3/midi/wav together.
from tinysoundfont.
Related Issues (20)
- `TSF_MALLOC(...)` without non-NULL check/error handling HOT 3
- Design issue: having playback handling and soundfont in same structure incentivizes people to write memory hogs(?) HOT 3
- `tsf_set_max_voices()` should actually be enforced HOT 2
- Unicode open HOT 1
- Would be possible to sustain a note? HOT 2
- Is .sfz support something devs consider also thank you for the library HOT 6
- voices active for slightly longer than they should be
- Reading samples directly at runtime (port to STM32) HOT 1
- MIDI latency HOT 1
- Likely memory leak in use of TSF_REALLOC in new sf3 code in out of memory conditions HOT 1
- Player ending before end of music (not playing last notes)
- [Question] Loading multiple SF2 files?
- soundnames inside drumkits
- large soundfonts HOT 2
- Dynamic Voice Allocation and Immediate Cutoff Mechanism Proposal for Memory Optimization in SoundFont Library HOT 1
- Extending sfotool support with optional sample load callback. HOT 2
- [Question] What's the current status of MIDI playback or SoundFont2 support? HOT 5
- Midi soundfont use in dosbox pure crash RetroArch in android 13
- Fixed-point version for FPU-less systems
- sfo file created with sfo_tool fails to load with tsf_load_memory. HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from tinysoundfont.