2024-10-24 11:33:08 -07:00
# include "fluidsynth_backend.hpp"
# include <algorithm>
# include <ipc/common.pb.h>
# include <exception>
# include <filesystem>
# include "file_backend.hpp"
# include <stddef.h>
# include <string.h>
# include <file_backend.hpp>
# include <util.hpp>
2024-12-11 12:56:17 -08:00
# include <filesystem>
# include <limits.h>
2024-12-29 09:19:33 -08:00
# include <log.hpp>
2024-12-11 12:56:17 -08:00
namespace fs = std : : filesystem ;
2024-12-29 09:19:33 -08:00
using Looper : : Log : : LogStream ;
static void log_fn ( int level , const char * msg , void * data ) {
LogStream * stream ;
switch ( level ) {
case FLUID_ERR :
case FLUID_PANIC : stream = & ERROR ; break ;
case FLUID_WARN : stream = & WARNING ; break ;
default :
case FLUID_INFO : stream = & INFO ; break ;
case FLUID_DBG : stream = & DEBUG ; break ;
}
stream - > writeln ( msg ) ;
}
2024-10-26 09:01:06 -07:00
void FluidSynthBackend : : fluidsynth_get_property_list_wrapper ( void * udata , const char * name , int type ) {
2024-10-24 11:33:08 -07:00
( ( FluidSynthBackend * ) udata ) - > fluidsynth_get_property_list ( name , type ) ;
}
2024-10-26 09:01:06 -07:00
void FluidSynthBackend : : fluidsynth_get_property_list ( const char * name , int type ) {
Property property ;
property . set_path ( fmt : : format ( " fluidsynth/{0} " , name ) ) ;
property . set_name ( name ) ;
switch ( type ) {
2024-12-11 12:56:17 -08:00
case FLUID_NO_TYPE :
2024-10-26 09:01:06 -07:00
case FLUID_NUM_TYPE : {
property . set_type ( PropertyType : : Double ) ;
2024-12-11 12:56:17 -08:00
double min , max ;
if ( fluid_settings_getnum_range ( settings , name , & min , & max ) = = FLUID_OK ) {
auto * range = property . mutable_hint ( ) - > mutable_range ( ) ;
range - > set_min ( min ) ;
range - > set_max ( max ) ;
}
2024-10-26 09:01:06 -07:00
} break ;
case FLUID_INT_TYPE : {
property . set_type ( PropertyType : : Int ) ;
2024-12-11 12:56:17 -08:00
int min , max ;
if ( fluid_settings_getint_range ( settings , name , & min , & max ) = = FLUID_OK ) {
auto * range = property . mutable_hint ( ) - > mutable_range ( ) ;
range - > set_min ( min ) ;
range - > set_max ( max ) ;
}
2024-10-26 09:01:06 -07:00
} break ;
case FLUID_STR_TYPE : {
property . set_type ( PropertyType : : String ) ;
} break ;
2024-12-11 12:56:17 -08:00
default : {
throw std : : exception ( ) ;
} break ;
2024-10-26 09:01:06 -07:00
}
property . set_id ( PropertyId : : BackendSpecific ) ;
fluidsynth_properties . push_back ( property ) ;
}
2024-12-29 09:19:33 -08:00
static bool log_fn_registered = false ;
2024-10-24 11:33:08 -07:00
std : : vector < Property > FluidSynthBackend : : get_property_list ( ) {
2024-12-29 07:46:34 -08:00
return fluidsynth_properties ;
2024-10-24 11:33:08 -07:00
}
void FluidSynthBackend : : load ( const char * filename ) {
2024-12-29 07:46:34 -08:00
memset ( & spec , 0 , sizeof ( spec ) ) ;
2024-12-29 09:19:33 -08:00
if ( ! log_fn_registered ) {
fluid_set_log_function ( FLUID_PANIC , & log_fn , nullptr ) ;
fluid_set_log_function ( FLUID_ERR , & log_fn , nullptr ) ;
fluid_set_log_function ( FLUID_WARN , & log_fn , nullptr ) ;
fluid_set_log_function ( FLUID_INFO , & log_fn , nullptr ) ;
fluid_set_log_function ( FLUID_DBG , & log_fn , nullptr ) ;
log_fn_registered = true ;
}
2024-10-24 11:33:08 -07:00
current_file = filename ;
2024-10-26 09:01:06 -07:00
spec . format = AUDIO_F32SYS ;
2024-10-24 11:33:08 -07:00
spec . samples = 100 ;
spec . channels = 2 ;
2024-12-11 12:56:17 -08:00
spec . freq = 96000 ;
2024-10-24 11:33:08 -07:00
spec . size = 100 * 2 * sizeof ( int16_t ) ;
2024-12-11 12:56:17 -08:00
File * file = open_file ( filename ) ;
settings = new_fluid_settings ( ) ;
fluid_settings_setnum ( settings , " synth.sample-rate " , 96000.0 ) ;
2024-12-12 10:43:42 -08:00
fluid_settings_setnum ( settings , " synth.gain " , 0.5 ) ;
2024-12-29 07:46:34 -08:00
fluid_settings_foreach ( settings , ( void * ) this , & fluidsynth_get_property_list_wrapper ) ;
2024-12-11 12:56:17 -08:00
synth = new_fluid_synth ( settings ) ;
player = new_fluid_player ( synth ) ;
fs : : path fpath ( filename ) ;
fpath = fpath . parent_path ( ) / fpath . stem ( ) ;
std : : string fpath_str = fpath . string ( ) ;
std : : vector < std : : string > try_paths = std : : vector < std : : string > ( {
fpath_str + " .sf2 " , fpath_str + " .dls "
} ) ;
bool found = false ;
for ( auto & path : try_paths ) {
const char * path_c = path . c_str ( ) ;
if ( fluid_is_soundfont ( path_c ) ) {
fluid_synth_sfload ( synth , path_c , 1 ) ;
found = true ;
break ;
}
}
if ( ! found ) {
WARNING . writeln ( " Could not find a valid companion file to use as a sound font. Make sure the extension is all-lowercase, if you're on a case sensitive filesystem (Such as on Linux). " ) ;
throw std : : exception ( ) ;
}
file_data = malloc ( file - > get_len ( ) ) ;
file_len = file - > read ( file_data , 1 , file - > get_len ( ) ) ;
delete file ;
file_data = realloc ( file_data , file_len ) ;
fluid_player_add_mem ( player , file_data , file_len ) ;
2024-12-29 07:46:34 -08:00
fluid_player_set_loop ( player , - 1 ) ;
fluid_player_play ( player ) ;
size_t prev_pos = 0 ;
size_t cur_pos = 0 ;
size_t samples = 0 ;
sample_positions . push_back ( { 0 , 0 } ) ;
float fakebuf ;
fluid_player_seek ( player , 0 ) ;
while ( prev_pos < = cur_pos ) {
size_t samples_to_add = std : : floor ( ( 96000.0 * 60.0 ) / fluid_player_get_bpm ( player ) ) ;
fluid_synth_write_float ( synth , samples_to_add , & fakebuf , 0 , 0 , & fakebuf , 0 , 0 ) ;
samples + = samples_to_add ;
prev_pos = cur_pos ;
while ( prev_pos = = cur_pos ) {
cur_pos = fluid_player_get_current_tick ( player ) ;
if ( prev_pos = = cur_pos ) {
fluid_synth_write_float ( synth , 1 , & fakebuf , 0 , 0 , & fakebuf , 0 , 0 ) ;
samples + + ;
}
}
sample_positions . push_back ( { samples , cur_pos } ) ;
}
this - > length = ( ( double ) samples ) / 96000.0 ;
2024-10-24 11:33:08 -07:00
}
extern SDL_AudioSpec obtained ;
2024-10-26 09:01:06 -07:00
void FluidSynthBackend : : switch_stream ( int idx ) {
2024-12-29 07:46:34 -08:00
fluid_player_seek ( player , 0 ) ;
open = true ;
2024-10-24 11:33:08 -07:00
}
2024-10-26 09:01:06 -07:00
void FluidSynthBackend : : cleanup ( ) {
2024-12-11 12:56:17 -08:00
delete_fluid_player ( player ) ;
player = nullptr ;
delete_fluid_synth ( synth ) ;
synth = nullptr ;
delete_fluid_settings ( settings ) ;
settings = nullptr ;
file_len = 0 ;
free ( file_data ) ;
file_data = nullptr ;
2024-12-29 07:46:34 -08:00
open = false ;
2024-10-24 11:33:08 -07:00
}
2024-10-26 09:01:06 -07:00
size_t FluidSynthBackend : : render ( void * buf , size_t maxlen ) {
2024-12-11 12:56:17 -08:00
size_t sample_type_len = sizeof ( float ) ;
maxlen / = sample_type_len * 2 ;
2024-12-29 07:46:34 -08:00
position + = ( ( double ) maxlen ) / 96000.0 ;
2024-12-11 12:56:17 -08:00
maxlen * = sample_type_len * 2 ;
if ( fluid_synth_write_float ( synth , maxlen / 2 / sample_type_len , buf , 0 , 2 , buf , 1 , 2 ) = = FLUID_FAILED ) {
return 0 ;
}
2024-12-29 07:46:34 -08:00
if ( position > = length ) {
int tick = fluid_player_get_current_tick ( player ) ;
int prev_sample = 0 ;
int cur_sample = 0 ;
for ( auto & pair : sample_positions ) {
prev_sample = cur_sample ;
cur_sample = pair . first ;
if ( pair . second > tick ) {
position = ( ( double ) prev_sample ) / 96000.0 ;
break ;
} else if ( pair . second = = tick ) {
position = ( ( double ) cur_sample ) / 96000.0 ;
break ;
}
}
}
2024-12-11 12:56:17 -08:00
return maxlen ;
}
bool FluidSynthBackend : : is_fluidsynth_setting ( std : : string path ) {
const char * prefix = " fluidsynth/ " ;
if ( path . length ( ) < strlen ( prefix ) ) return false ;
return path . substr ( 0 , strlen ( prefix ) ) = = std : : string ( prefix ) ;
2024-10-24 11:33:08 -07:00
}
2024-10-26 09:01:06 -07:00
void FluidSynthBackend : : seek ( double position ) {
2024-12-29 07:46:34 -08:00
size_t tick = 0 ;
size_t sample = position * 96000 ;
size_t prev_sample = 0 ;
size_t next_sample = 0 ;
for ( auto & pair : sample_positions ) {
prev_sample = next_sample ;
next_sample = pair . first ;
if ( next_sample > sample ) {
tick = pair . second - 1 ;
this - > position = ( ( double ) prev_sample ) / 96000.0 ;
break ;
} else if ( next_sample = = sample ) {
tick = pair . second ;
this - > position = ( ( double ) next_sample ) / 96000.0 ;
prev_sample = next_sample ;
break ;
}
}
fluid_player_seek ( player , tick ) ;
float fakebuf ;
if ( sample > prev_sample ) fluid_synth_write_float ( synth , sample - prev_sample , & fakebuf , 0 , 0 , & fakebuf , 0 , 0 ) ;
this - > position = position ;
2024-10-24 11:33:08 -07:00
}
2024-10-26 09:01:06 -07:00
double FluidSynthBackend : : get_position ( ) {
2024-12-29 07:46:34 -08:00
return position ;
2024-10-24 11:33:08 -07:00
}
2024-10-26 09:01:06 -07:00
int FluidSynthBackend : : get_stream_idx ( ) {
2024-10-24 11:33:08 -07:00
return 0 ;
2024-12-11 12:56:17 -08:00
}
void FluidSynthBackend : : set_fluidsynth_property_str ( std : : string path , std : : string val ) {
fluid_settings_setstr ( settings , path . c_str ( ) , val . c_str ( ) ) ;
}
void FluidSynthBackend : : set_fluidsynth_property_num ( std : : string path , double val ) {
fluid_settings_setnum ( settings , path . c_str ( ) , val ) ;
}
void FluidSynthBackend : : set_fluidsynth_property_int ( std : : string path , int val ) {
fluid_settings_setint ( settings , path . c_str ( ) , val ) ;
}
std : : optional < std : : string > FluidSynthBackend : : get_fluidsynth_property_str ( std : : string path ) {
char * tmp ;
if ( fluid_settings_dupstr ( settings , path . c_str ( ) , & tmp ) = = FLUID_OK ) {
std : : string output = tmp ;
free ( ( void * ) tmp ) ;
return output ;
} else {
return { } ;
}
}
std : : optional < double > FluidSynthBackend : : get_fluidsynth_property_num ( std : : string path ) {
double output = NAN ;
if ( fluid_settings_getnum ( settings , path . c_str ( ) , & output ) = = FLUID_OK ) return output ;
else return { } ;
}
std : : optional < int > FluidSynthBackend : : get_fluidsynth_property_int ( std : : string path ) {
int output = 0 ;
if ( fluid_settings_getint ( settings , path . c_str ( ) , & output ) = = FLUID_OK ) return output ;
else return { } ;
}
void FluidSynthBackend : : reset_fluidsynth_property ( std : : string path ) {
switch ( fluid_settings_get_type ( settings , path . c_str ( ) ) ) {
case FLUID_INT_TYPE : {
int output ;
if ( fluid_settings_getint_default ( settings , path . c_str ( ) , & output ) = = FLUID_OK ) {
fluid_settings_setint ( settings , path . c_str ( ) , output ) ;
}
} break ;
case FLUID_STR_TYPE : {
char * val ;
if ( fluid_settings_getstr_default ( settings , path . c_str ( ) , & val ) = = FLUID_OK ) {
fluid_settings_setstr ( settings , path . c_str ( ) , val ) ;
}
} break ;
case FLUID_NUM_TYPE : {
double val ;
if ( fluid_settings_getnum_default ( settings , path . c_str ( ) , & val ) = = FLUID_OK ) {
fluid_settings_setnum ( settings , path . c_str ( ) , val ) ;
}
} break ;
}
2024-12-12 10:43:42 -08:00
}