Giter VIP home page Giter VIP logo

Comments (15)

schellingb avatar schellingb commented on May 30, 2024 2

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.

schellingb avatar schellingb commented on May 30, 2024

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.

DanielMatarov avatar DanielMatarov commented on May 30, 2024

Hello,

Those have been some really useful answers, I will contact you if i have any further questions

thanks!

from tinysoundfont.

adrianmcroft avatar adrianmcroft commented on May 30, 2024

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.

schellingb avatar schellingb commented on May 30, 2024

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.

adrianmcroft avatar adrianmcroft commented on May 30, 2024

You're too kind, there are some examples loaded into Bela, I'll try and dig something relevant out.

Many thanks

from tinysoundfont.

adrianmcroft avatar adrianmcroft commented on May 30, 2024

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....

BasicBela.txt

from tinysoundfont.

adrianmcroft avatar adrianmcroft commented on May 30, 2024

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.

schellingb avatar schellingb commented on May 30, 2024

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.

adrianmcroft avatar adrianmcroft commented on May 30, 2024

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.

adrianmcroft avatar adrianmcroft commented on May 30, 2024

So, It works !! I'll have to rope a video together.....

https://plus.google.com/u/0/collection/c7x6cB

from tinysoundfont.

adrianmcroft avatar adrianmcroft commented on May 30, 2024

et voila https://www.facebook.com/SoundfontBela/ (finally)...

from tinysoundfont.

adrianmcroft avatar adrianmcroft commented on May 30, 2024

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.

kavika13 avatar kavika13 commented on May 30, 2024

@schellingb

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.

fungos avatar fungos commented on May 30, 2024

@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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.