-
-
Notifications
You must be signed in to change notification settings - Fork 527
Description
Flutter Dart + Foreign Function Interface + Windows10
Custom CPP:
`#include <stdint.h>
#include <fluidsynth.h>
#include <string.h>
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#ifdef _WIN32
#define DllExport __declspec(dllexport)
#else
#define DllExport
#endif
extern "C" {
fluid_settings_t* g_settings = nullptr;
fluid_synth_t* g_synth = nullptr;
ma_device g_device;
bool g_initialized = false;
char g_last_sf2_path[4096] = {0}; // English: Fixed buffer size for Release stability
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) {
if (g_synth && g_initialized) {
fluid_synth_write_float(g_synth, frameCount, (float*)pOutput, 0, 2, (float*)pOutput, 1, 2);
} else {
memset(pOutput, 0, frameCount * 2 * sizeof(float));
}
(void)pInput;
}
DllExport int8_t load_sf2(const char* path) {
if (g_initialized) {
g_initialized = false;
ma_device_stop(&g_device);
ma_device_uninit(&g_device);
if (g_synth) delete_fluid_synth(g_synth);
if (g_settings) delete_fluid_settings(g_settings);
}
if (path) {
#ifdef _WIN32
strncpy_s(g_last_sf2_path, path, sizeof(g_last_sf2_path) - 1);
#else
strncpy(g_last_sf2_path, path, sizeof(g_last_sf2_path) - 1);
#endif
}
g_settings = new_fluid_settings();
fluid_settings_setint(g_settings, "synth.polyphony", 512);
fluid_settings_setnum(g_settings, "synth.sample-rate", 44100.0);
g_synth = new_fluid_synth(g_settings);
if (fluid_synth_sfload(g_synth, g_last_sf2_path, 1) == -1) return 0;
ma_device_config config = ma_device_config_init(ma_device_type_playback);
config.playback.format = ma_format_f32;
config.playback.channels = 2;
config.sampleRate = 44100;
config.dataCallback = data_callback;
if (ma_device_init(NULL, &config, &g_device) != MA_SUCCESS) return 0;
g_initialized = true;
ma_device_start(&g_device);
return 1;
}
DllExport int8_t reinit_synth() {
if (g_last_sf2_path[0] == '\0') return 0;
return load_sf2(g_last_sf2_path);
}
DllExport void set_volume(float volume) {
if (g_synth) fluid_synth_set_gain(g_synth, volume);
}
DllExport void stop_all() {
if (g_synth) {
for (int i = 0; i < 16; i++) fluid_synth_all_notes_off(g_synth, i);
}
}
DllExport void note_on(int32_t chan, int32_t key, float velocity) {
if (g_synth && g_initialized) {
if (ma_device_get_state(&g_device) != ma_device_state_started) ma_device_start(&g_device);
fluid_synth_noteon(g_synth, chan, key, (int)(velocity * 127));
}
}
DllExport void note_off(int32_t chan, int32_t key) {
if (g_synth && g_initialized) fluid_synth_noteoff(g_synth, chan, key);
}
DllExport void program_change(int32_t chan, int32_t prog) {
if (g_synth && g_initialized) {
if (ma_device_get_state(&g_device) != ma_device_state_started) ma_device_start(&g_device);
fluid_synth_program_change(g_synth, chan, prog);
}
}
DllExport void control_change(int32_t chan, int32_t ctrl, int32_t val) {
if (g_synth && g_initialized) {
if (ma_device_get_state(&g_device) != ma_device_state_started) ma_device_start(&g_device);
fluid_synth_cc(g_synth, chan, ctrl, val);
}
}
DllExport int32_t get_active_voices() {
return g_synth ? fluid_synth_get_active_voice_count(g_synth) : 0;
}
}
Miniaudio.h 44227 fragment:
` /*
We need to check again if the device is in a started state because it's possible for one thread to have started the device
while another was waiting on the mutex.
/
if (ma_device_get_state(pDevice) == ma_device_state_started) {
ma_mutex_unlock(&pDevice->startStopLock);
return MA_SUCCESS; / Already started. */
}
/* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
ma_device__set_state(pDevice, ma_device_state_starting);
/* Asynchronous backends need to be handled differently. */`
Action: Change Headphones to Speaker