2022-02-09 02:29:03 +00:00
/* vim: set et ts=3 sw=3 sts=3 ft=c:
*
* Copyright ( C ) 2021 Christopher Snowhill . All rights reserved .
* https : //github.com/kode54/sflist
* https : //gist.github.com/kode54/a7bb01a0db3f2e996145b77f0ca510d5
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ` ` AS IS ' ' AND
* ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT
* LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE .
*/
# include <ctype.h>
# include <math.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <json-builder.h>
# include "sflist.h"
# ifndef PRId64
# ifdef _MSC_VER
# define PRId64 "I64d"
# else
# define PRId64 "lld"
# endif
# endif
/* Extras needed */
static int json_equal ( const json_value * a , const json_value * b ) ;
static int json_equal_array ( const json_value * a , const json_value * b ) {
unsigned int i , j ;
if ( a - > u . array . length ! = b - > u . array . length ) return 0 ;
for ( i = 0 , j = a - > u . array . length ; i < j ; + + i ) {
if ( ! json_equal ( a - > u . array . values [ i ] , b - > u . array . values [ i ] ) )
return 0 ;
}
return 1 ;
}
static int json_equal_object ( const json_value * a , const json_value * b ) {
unsigned int i , j ;
if ( a - > u . object . length ! = b - > u . object . length ) return 0 ;
for ( i = 0 , j = a - > u . object . length ; i < j ; + + i ) {
if ( strcmp ( a - > u . object . values [ i ] . name , b - > u . object . values [ i ] . name ) )
return 0 ;
if ( ! json_equal ( a - > u . object . values [ i ] . value , b - > u . object . values [ i ] . value ) )
return 0 ;
}
return 1 ;
}
static int json_equal ( const json_value * a , const json_value * b ) {
if ( a - > type ! = b - > type ) return 0 ;
switch ( a - > type ) {
case json_none :
case json_null :
return 1 ;
case json_integer :
return a - > u . integer = = b - > u . integer ;
case json_double :
return a - > u . dbl = = b - > u . dbl ;
case json_boolean :
return ! a - > u . boolean = = ! b - > u . boolean ;
case json_string :
return ! strcmp ( a - > u . string . ptr , b - > u . string . ptr ) ;
case json_array :
return json_equal_array ( a , b ) ;
case json_object :
return json_equal_object ( a , b ) ;
}
return 0 ;
}
static int json_signum ( double val ) {
return ( val > 0.0 ) - ( val < 0.0 ) ;
}
# define json_compare_invalid -1000
static int json_compare ( const json_value * a , const json_value * b ) {
if ( a - > type ! = b - > type ) return json_compare_invalid ;
switch ( a - > type ) {
case json_none :
case json_null :
return 0 ;
case json_integer :
return ( int ) ( a - > u . integer - b - > u . integer ) ;
case json_double :
return json_signum ( a - > u . dbl - b - > u . dbl ) ;
case json_boolean :
return ! ! a - > u . boolean - ! ! b - > u . boolean ;
case json_string :
return strcmp ( a - > u . string . ptr , b - > u . string . ptr ) ;
case json_array :
case json_object :
return json_compare_invalid ;
}
return json_compare_invalid ;
}
static int json_array_contains_value ( const json_value * array , const json_value * value ) {
unsigned int i , j ;
for ( i = 0 , j = array - > u . array . length ; i < j ; + + i ) {
if ( json_equal ( array - > u . array . values [ i ] , value ) )
return 1 ;
}
return 0 ;
}
json_value * json_array_merge ( json_value * arrayA , json_value * arrayB ) {
unsigned int i , j ;
if ( arrayA - > type ! = json_array | | arrayB - > type ! = json_array )
return 0 ;
for ( i = 0 , j = arrayB - > u . array . length ; i < j ; + + i ) {
if ( ! json_array_contains_value ( arrayA , arrayB - > u . array . values [ i ] ) ) {
json_array_push ( arrayA , arrayB - > u . array . values [ i ] ) ;
}
}
json_builder_free ( arrayB ) ;
return arrayA ;
}
static int json_compare_callback ( const void * a , const void * b ) {
const json_value * aa = ( const json_value * ) a ;
const json_value * bb = ( const json_value * ) b ;
return json_compare ( aa , bb ) ;
}
json_value * json_array_sort ( json_value * array ) {
unsigned int i , j ;
json_type type ;
if ( array - > type ! = json_array ) return 0 ;
if ( array - > u . array . length < 2 ) return array ;
type = array - > u . array . values [ 0 ] - > type ;
for ( i = 1 , j = array - > u . array . length ; i < j ; + + i ) {
if ( array - > u . array . values [ i ] - > type ! = type )
return 0 ;
}
qsort ( array - > u . array . values , j , sizeof ( json_value * ) , json_compare_callback ) ;
return array ;
}
/* Processing begins */
static size_t sflist_parse_int ( const char * in , const char * * end ) {
size_t rval = 0 ;
while ( in < * end ) {
if ( isdigit ( * in ) ) {
rval = ( rval * 10 ) + ( * in - ' 0 ' ) ;
} else
break ;
+ + in ;
}
* end = in ;
return rval ;
}
static double sflist_parse_float ( const char * in , const char * * end ) {
size_t whole = 0 ;
size_t decimal = 0 ;
size_t decimal_places = 0 ;
double sign = 1.0 ;
const char * end_orig = * end ;
const char * ptr = in ;
if ( * ptr = = ' - ' ) {
+ + ptr ;
sign = - 1.0 ;
}
whole = sflist_parse_int ( ptr , end ) ;
if ( * end = = ptr | | ( * * end ! = ' . ' & & * end < end_orig ) ) {
* end = in ;
return 0.0 ;
}
if ( * end < end_orig ) {
ptr = * end + 1 ;
* end = end_orig ;
decimal = sflist_parse_int ( ptr , end ) ;
if ( * end = = ptr | | * end < end_orig ) {
* end = in ;
return 0.0 ;
}
decimal_places = * end - ptr ;
}
return ( ( ( double ) whole ) + ( ( ( double ) decimal ) / pow ( 10.0 , ( double ) decimal_places ) ) ) * sign ;
}
static json_value * sflist_load_v1 ( const char * sflist , size_t size , char * error_buf ) {
json_value * rval = 0 ;
json_value * arr = json_array_new ( 0 ) ;
json_value * channels = 0 ;
json_value * patchMappings = 0 ;
double gain = 0.0 ;
const char * ptr = sflist ;
const char * end = sflist + size ;
unsigned int cur_line = 0 ;
while ( ptr < end ) {
const char * line_start = ptr ;
json_value * obj = 0 ;
const char * path = 0 ;
const char * pipe = 0 ;
const char * lend = ptr ;
+ + cur_line ;
while ( lend < end & & * lend & & * lend ! = ' \r ' & & * lend ! = ' \n ' ) {
if ( * lend = = ' | ' )
pipe = lend ;
+ + lend ;
}
if ( pipe )
path = pipe + 1 ;
else
path = ptr ;
if ( pipe ) {
while ( ptr < pipe ) {
char c ;
const char * fend = ptr ;
const char * vend ;
while ( fend < pipe & & * fend ! = ' & ' ) + + fend ;
vend = fend ;
switch ( c = * ptr + + ) {
case ' & ' :
continue ;
case ' c ' : {
json_value * this_channels ;
size_t channel_low = sflist_parse_int ( ptr , & vend ) ;
size_t channel_high = 0 ;
size_t i ;
if ( vend = = ptr | | ( * vend ! = ' - ' & & * vend ! = ' & ' & & * vend ! = ' | ' ) ) {
sprintf ( error_buf , " Invalid channel number (%u:%u) " , cur_line , ( int ) ( vend - line_start + 1 ) ) ;
goto error ;
}
if ( * vend ! = ' - ' )
channel_high = channel_low ;
else {
ptr = vend + 1 ;
vend = fend ;
channel_high = sflist_parse_int ( ptr , & vend ) ;
if ( vend = = ptr | | ( * vend ! = ' & ' & & * vend ! = ' | ' ) ) {
sprintf ( error_buf , " Invalid channel range end value (%u:%u) " , cur_line , ( int ) ( vend - line_start + 1 ) ) ;
goto error ;
}
}
if ( ! channels )
channels = json_array_new ( 0 ) ;
this_channels = json_array_new ( 0 ) ;
for ( i = channel_low ; i < = channel_high ; + + i )
json_array_push ( this_channels , json_integer_new ( i ) ) ;
channels = json_array_merge ( channels , this_channels ) ;
ptr = fend ;
} break ;
case ' p ' : {
json_value * mapping = 0 ;
json_value * mapping_destination = 0 ;
json_value * mapping_source = 0 ;
long source_bank = - 1 ;
long source_program = - 1 ;
long dest_bank = - 1 ;
long dest_program = - 1 ;
size_t val = sflist_parse_int ( ptr , & vend ) ;
if ( vend = = ptr | | ( * vend ! = ' = ' & & * vend ! = ' , ' & & * vend ! = ' | ' ) ) {
sprintf ( error_buf , " Invalid preset number (%u:%u) " , cur_line , ( int ) ( vend - line_start + 1 ) ) ;
goto error ;
}
dest_program = val ;
if ( * vend = = ' , ' ) {
dest_bank = val ;
ptr = vend + 1 ;
vend = fend ;
val = sflist_parse_int ( ptr , & vend ) ;
if ( vend = = ptr | | ( * vend ! = ' = ' & & * vend ! = ' | ' ) ) {
sprintf ( error_buf , " Invalid preset number (%u:%u) " , cur_line , ( int ) ( vend - line_start + 1 ) ) ;
goto error ;
}
dest_program = val ;
}
if ( * vend = = ' = ' ) {
ptr = vend + 1 ;
vend = fend ;
val = sflist_parse_int ( ptr , & vend ) ;
if ( vend = = ptr | | ( * vend ! = ' , ' & & * vend ! = ' | ' ) ) {
sprintf ( error_buf , " Invalid preset number (%u:%u) " , cur_line , ( int ) ( vend - line_start + 1 ) ) ;
goto error ;
}
source_program = val ;
if ( * vend = = ' , ' ) {
source_bank = val ;
ptr = vend + 1 ;
vend = fend ;
val = sflist_parse_int ( ptr , & vend ) ;
if ( vend = = ptr | | ( * vend ! = ' & ' & & * vend ! = ' | ' ) ) {
sprintf ( error_buf , " Invalid preset number (%u:%u) " , cur_line , ( int ) ( vend - line_start + 1 ) ) ;
goto error ;
}
source_program = val ;
}
}
if ( ! patchMappings )
patchMappings = json_array_new ( 0 ) ;
mapping = json_object_new ( 0 ) ;
mapping_destination = json_object_new ( 0 ) ;
if ( dest_bank ! = - 1 ) {
json_object_push ( mapping_destination , " bank " , json_integer_new ( dest_bank ) ) ;
}
json_object_push ( mapping_destination , " program " , json_integer_new ( dest_program ) ) ;
json_object_push ( mapping , " destination " , mapping_destination ) ;
if ( source_program ! = - 1 ) {
mapping_source = json_object_new ( 0 ) ;
if ( source_bank ! = - 1 ) {
json_object_push ( mapping_source , " bank " , json_integer_new ( source_bank ) ) ;
}
json_object_push ( mapping_source , " program " , json_integer_new ( source_program ) ) ;
json_object_push ( mapping , " source " , mapping_source ) ;
}
json_array_push ( patchMappings , mapping ) ;
ptr = fend ;
} break ;
case ' g ' : {
double val = sflist_parse_float ( ptr , & vend ) ;
if ( vend = = ptr | | vend < fend ) {
sprintf ( error_buf , " Invalid gain value (%u:%u) " , cur_line , ( int ) ( vend - line_start + 1 ) ) ;
goto error ;
}
gain = val ;
ptr = fend ;
} break ;
default :
sprintf ( error_buf , " Invalid character in preset '%c' (%u:%u) " , c , cur_line , ( unsigned int ) ( ptr - line_start ) ) ;
goto error ;
}
}
}
obj = json_object_new ( 0 ) ;
json_object_push ( obj , " fileName " , json_string_new_length ( ( unsigned int ) ( lend - path ) , path ) ) ;
if ( gain ! = 0.0 ) {
json_object_push ( obj , " gain " , json_double_new ( gain ) ) ;
gain = 0.0 ;
}
if ( channels ) {
channels = json_array_sort ( channels ) ;
json_object_push ( obj , " channels " , channels ) ;
channels = 0 ;
}
if ( patchMappings ) {
json_object_push ( obj , " patchMappings " , patchMappings ) ;
patchMappings = 0 ;
}
json_array_push ( arr , obj ) ;
ptr = lend ;
while ( ptr < end & & ( * ptr = = ' \n ' | | * ptr = = ' \r ' ) ) + + ptr ;
}
rval = json_object_new ( 1 ) ;
json_object_push ( rval , " soundFonts " , arr ) ;
return rval ;
error :
if ( channels ) json_builder_free ( channels ) ;
if ( patchMappings ) json_builder_free ( patchMappings ) ;
if ( arr ) json_builder_free ( arr ) ;
return 0 ;
}
static json_value * sflist_load_v2 ( const char * sflist , size_t size , char * error ) {
json_value * rval = 0 ;
json_settings settings = { 0 } ;
settings . value_extra = json_builder_extra ;
rval = json_parse_ex ( & settings , sflist , size , error ) ;
return rval ;
}
static const json_value * json_object_item ( const json_value * object , const char * name ) {
unsigned int i , j ;
if ( object - > type ! = json_object ) return & json_value_none ;
for ( i = 0 , j = object - > u . object . length ; i < j ; + + i ) {
if ( ! strcmp ( object - > u . object . values [ i ] . name , name ) )
return object - > u . object . values [ i ] . value ;
}
return & json_value_none ;
}
2022-06-30 06:25:59 +00:00
static void sflist_process_patchmappings ( BASS_MIDI_FONTEX2 * out , BASS_MIDI_FONTEX2 * fontex , const json_value * patchMappings , unsigned int channel , unsigned int channelCount ) {
2022-02-09 02:29:03 +00:00
unsigned int i , j ;
for ( i = 0 , j = patchMappings - > u . array . length ; i < j ; + + i ) {
json_value * preset = patchMappings - > u . array . values [ i ] ;
const json_value * destination = json_object_item ( preset , " destination " ) ;
const json_value * source = json_object_item ( preset , " source " ) ;
const json_value * destination_bank = json_object_item ( destination , " bank " ) ;
const json_value * destination_program = json_object_item ( destination , " program " ) ;
const json_value * source_bank = json_object_item ( source , " bank " ) ;
const json_value * source_program = json_object_item ( source , " program " ) ;
fontex - > spreset = ( source_program - > type = = json_none ) ? - 1 : ( int ) source_program - > u . integer ;
fontex - > sbank = ( source_bank - > type = = json_none ) ? - 1 : ( int ) source_bank - > u . integer ;
fontex - > dpreset = ( destination_program - > type = = json_none ) ? - 1 : ( int ) destination_program - > u . integer ;
fontex - > dbank = ( destination_bank - > type = = json_none ) ? 0 : ( int ) destination_bank - > u . integer ;
2022-06-30 06:25:59 +00:00
fontex - > minchan = channel ;
fontex - > numchan = channelCount ;
2022-02-09 02:29:03 +00:00
* out + + = * fontex ;
}
}
static sflist_presets * sflist_process ( const json_value * sflist , const char * base_path , char * error_buf ) {
# ifdef _WIN32
wchar_t path16 [ 32768 ] ;
# endif
char path_temp [ 32768 ] ;
const char * base_path_end = base_path + strlen ( base_path ) - 1 ;
unsigned int presets_to_allocate = 0 ;
sflist_presets * rval = calloc ( 1 , sizeof ( sflist_presets ) ) ;
json_value * arr ;
unsigned int i , j , k , l , preset_number ;
HSOUNDFONT hfont = 0 ;
2022-06-30 06:25:59 +00:00
BASS_MIDI_FONTEX2 fontex ;
2022-02-09 02:29:03 +00:00
if ( ! rval ) {
strcpy ( error_buf , " Out of memory " ) ;
goto error ;
}
if ( sflist - > type ! = json_object | | sflist - > u . object . length ! = 1 | |
strcmp ( sflist - > u . object . values [ 0 ] . name , " soundFonts " ) ) {
if ( sflist - > type ! = json_object )
strcpy ( error_buf , " Base JSON item is not an object " ) ;
else if ( sflist - > u . object . length ! = 1 )
sprintf ( error_buf , " Base JSON object contains unexpected number of items (wanted 1, got %u) " , sflist - > u . object . length ) ;
else
sprintf ( error_buf , " Base JSON object contains '%s' object instead of 'soundFonts' " , sflist - > u . object . values [ 0 ] . name ) ;
goto error ;
}
arr = sflist - > u . object . values [ 0 ] . value ;
if ( arr - > type ! = json_array ) {
strcpy ( error_buf , " JSON 'soundFonts' object is not an array " ) ;
goto error ;
}
for ( i = 0 , j = arr - > u . array . length ; i < j ; + + i ) {
const json_value * obj = arr - > u . array . values [ i ] ;
const json_value * path = 0 ;
const json_value * gain = 0 ;
const json_value * channels = 0 ;
const json_value * patchMappings = 0 ;
unsigned int patches_needed = 1 ;
if ( obj - > type ! = json_object ) {
sprintf ( error_buf , " soundFont item #%u is not an object " , i + 1 ) ;
goto error ;
}
path = json_object_item ( obj , " fileName " ) ;
gain = json_object_item ( obj , " gain " ) ;
channels = json_object_item ( obj , " channels " ) ;
patchMappings = json_object_item ( obj , " patchMappings " ) ;
if ( path - > type = = json_none ) {
sprintf ( error_buf , " soundFont item #%u has no 'fileName' " , i + 1 ) ;
goto error ;
}
if ( path - > type ! = json_string ) {
sprintf ( error_buf , " soundFont item #%u 'fileName' is not a string " , i + 1 ) ;
goto error ;
}
if ( gain - > type ! = json_none & & gain - > type ! = json_integer & & gain - > type ! = json_double ) {
sprintf ( error_buf , " soundFont item #%u has an invalid gain value " , i + 1 ) ;
goto error ;
}
if ( channels - > type ! = json_none ) {
if ( channels - > type ! = json_array ) {
sprintf ( error_buf , " soundFont item #%u 'channels' is not an array " , i + 1 ) ;
goto error ;
}
2022-06-30 06:25:59 +00:00
int prevchannel = - 1 ;
int contiguouschannelsets = 0 ;
2022-02-09 02:29:03 +00:00
for ( k = 0 , l = channels - > u . array . length ; k < l ; + + k ) {
json_value * channel = channels - > u . array . values [ k ] ;
if ( channel - > type ! = json_integer ) {
sprintf ( error_buf , " soundFont item #%u 'channels' #%u is not an integer " , i + 1 , k + 1 ) ;
goto error ;
}
if ( channel - > u . integer < 1 | | channel - > u . integer > 48 ) {
sprintf ( error_buf , " soundFont item #%u 'channels' #%u is out of range (wanted 1-48, got % " PRId64 " ) " , i + 1 , k + 1 , channel - > u . integer ) ;
goto error ;
}
2022-06-30 06:25:59 +00:00
if ( prevchannel < 0 | | channel - > u . integer > ( prevchannel + 1 ) ) {
+ + contiguouschannelsets ;
}
prevchannel = ( int ) channel - > u . integer ;
2022-02-09 02:29:03 +00:00
}
2022-06-30 06:25:59 +00:00
patches_needed = contiguouschannelsets ;
2022-02-09 02:29:03 +00:00
}
if ( patchMappings - > type ! = json_none ) {
if ( patchMappings - > type ! = json_array ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' is not an array " , i + 1 ) ;
goto error ;
}
for ( k = 0 , l = patchMappings - > u . array . length ; k < l ; + + k ) {
unsigned int m , n ;
unsigned int source_found = 0 ;
unsigned int destination_found = 0 ;
json_value * mapping = patchMappings - > u . array . values [ k ] ;
if ( mapping - > type ! = json_object ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u is not an object " , i + 1 , k + 1 ) ;
goto error ;
}
for ( m = 0 , n = mapping - > u . object . length ; m < n ; + + m ) {
unsigned int o , p ;
json_value * item = mapping - > u . object . values [ m ] . value ;
const char * name = mapping - > u . object . values [ m ] . name ;
unsigned int bank_found = 0 ;
unsigned int program_found = 0 ;
if ( strcmp ( name , " source " ) & & strcmp ( name , " destination " ) ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u contains an invalid '%s' field " , i + 1 , k + 1 , name ) ;
goto error ;
}
if ( item - > type ! = json_object ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u '%s' is not an object " , i + 1 , k + 1 , name ) ;
goto error ;
}
if ( ! strcmp ( name , " source " ) )
+ + source_found ;
else
+ + destination_found ;
for ( o = 0 , p = item - > u . object . length ; o < p ; + + o ) {
int range_min = 0 ;
int range_max = 128 ;
json_value * item2 = item - > u . object . values [ o ] . value ;
const char * name2 = item - > u . object . values [ o ] . name ;
if ( strcmp ( name2 , " bank " ) & & strcmp ( name2 , " program " ) ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u '%s' contains an invalid '%s' field " , i + 1 , k + 1 , name , name2 ) ;
goto error ;
}
if ( item2 - > type ! = json_integer ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u '%s' '%s' is not an integer " , i + 1 , k + 1 , name , name2 ) ;
}
if ( ! strcmp ( name2 , " program " ) ) {
if ( ! strcmp ( name , " destination " ) )
range_max = 65535 ;
else
range_max = 127 ;
}
if ( item2 - > u . integer < range_min | | item2 - > u . integer > range_max ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u '%s' '%s' is out of range (expected %d-%d, got % " PRId64 " ) " , i + 1 , k + 1 , name , name2 , range_min , range_max , item - > u . integer ) ;
goto error ;
}
if ( ! strcmp ( name2 , " bank " ) )
+ + bank_found ;
else
+ + program_found ;
}
if ( ! bank_found & & ! program_found ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u '%s' contains no 'bank' or 'program' " , i + 1 , k + 1 , name ) ;
goto error ;
}
if ( bank_found > 1 ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u '%s' contains more than one 'bank' " , i + 1 , k + 1 , name ) ;
goto error ;
}
if ( program_found > 1 ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u '%s' contains more than one 'program' " , i + 1 , k + 1 , name ) ;
goto error ;
}
}
if ( ! destination_found ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u is missing 'destination' " , i + 1 , k + 1 ) ;
goto error ;
}
if ( destination_found > 1 ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u contains more than one 'destination' " , i + 1 , k + 1 ) ;
goto error ;
}
if ( source_found > 1 ) {
sprintf ( error_buf , " soundFont item #%u 'patchMappings' #%u contains more than one 'source' " , i + 1 , k + 1 ) ;
}
}
patches_needed * = l ;
}
presets_to_allocate + = patches_needed ;
}
rval - > count = presets_to_allocate ;
2022-06-30 06:25:59 +00:00
rval - > presets = calloc ( sizeof ( BASS_MIDI_FONTEX2 ) , rval - > count ) ;
2022-02-09 02:29:03 +00:00
if ( ! rval - > presets ) {
strcpy ( error_buf , " Out of memory " ) ;
goto error ;
}
preset_number = 0 ;
for ( i = arr - > u . array . length , j = 0 ; i - - ; + + j ) {
const json_value * obj = arr - > u . array . values [ i ] ;
const json_value * path = json_object_item ( obj , " fileName " ) ;
const json_value * gain = json_object_item ( obj , " gain " ) ;
const json_value * channels = json_object_item ( obj , " channels " ) ;
const json_value * patchMappings = json_object_item ( obj , " patchMappings " ) ;
const void * bass_path ;
const char * path_ptr = path - > u . string . ptr ;
unsigned int bass_flags = 0 ;
# ifdef _WIN32
if ( ! ( isalpha ( * path_ptr ) & & path_ptr [ 1 ] = = ' : ' ) ) {
if ( strlen ( path_ptr ) + ( base_path_end - base_path + 2 ) > 32767 ) {
strcpy ( error_buf , " Base path plus SoundFont relative path is longer than 32767 characters " ) ;
goto error ;
}
strcpy ( path_temp , base_path ) ;
if ( * base_path_end ! = ' \\ ' & & * base_path_end ! = ' / ' )
strcat ( path_temp , " \\ " ) ;
strcat ( path_temp , path_ptr ) ;
path_ptr = path_temp ;
}
MultiByteToWideChar ( CP_UTF8 , 0 , path_ptr , - 1 , path16 , 32767 ) ;
path16 [ 32767 ] = ' \0 ' ;
bass_path = ( void * ) path16 ;
bass_flags = BASS_UNICODE ;
# else
if ( * path_ptr ! = ' / ' ) {
if ( strlen ( path_ptr ) + ( base_path_end - base_path + 2 ) > 32767 ) {
strcpy ( error_buf , " Base path plus SoundFont relative path is longer than 32767 characters " ) ;
goto error ;
}
strcpy ( path_temp , base_path ) ;
if ( * base_path_end ! = ' / ' )
strcat ( path_temp , " / " ) ;
strcat ( path_temp , path_ptr ) ;
path_ptr = path_temp ;
}
bass_path = ( void * ) path_ptr ;
# endif
hfont = BASS_MIDI_FontInit ( bass_path , bass_flags ) ;
if ( ! hfont ) {
int error_code = BASS_ErrorGetCode ( ) ;
if ( error_code = = BASS_ERROR_FILEOPEN ) {
sprintf ( error_buf , " Could not open SoundFont bank '%s' " , path - > u . string . ptr ) ;
goto error ;
} else if ( error_code = = BASS_ERROR_FILEFORM ) {
sprintf ( error_buf , " SoundFont bank '%s' is not a supported format " , path - > u . string . ptr ) ;
goto error ;
} else {
sprintf ( error_buf , " SoundFont bank '%s' failed to load with error #%u " , path - > u . string . ptr , error_code ) ;
goto error ;
}
}
if ( gain - > type ! = json_none ) {
double gain_value = 0.0 ;
if ( gain - > type = = json_integer ) {
gain_value = ( double ) gain - > u . integer ;
} else if ( gain - > type = = json_double ) {
gain_value = gain - > u . dbl ;
}
gain_value = pow ( 10.0 , gain_value / 20.0 ) ;
BASS_MIDI_FontSetVolume ( hfont , gain_value ) ;
}
fontex . font = hfont ;
fontex . spreset = - 1 ;
fontex . sbank = - 1 ;
fontex . dpreset = - 1 ;
fontex . dbank = 0 ;
fontex . dbanklsb = 0 ;
2022-06-30 06:25:59 +00:00
fontex . minchan = 0 ;
fontex . numchan = 48 ;
2022-02-09 02:29:03 +00:00
/* Simplest case, whole bank loading */
if ( channels - > type = = json_none & & patchMappings - > type = = json_none ) {
rval - > presets [ preset_number + + ] = fontex ;
} else if ( patchMappings - > type = = json_none ) {
2022-06-30 06:25:59 +00:00
int prevchannel = - 1 ;
int firstchannel = - 1 ;
2022-02-09 02:29:03 +00:00
for ( k = 0 , l = channels - > u . array . length ; k < l ; + + k ) {
2022-06-30 06:25:59 +00:00
int channel = ( int ) channels - > u . array . values [ k ] - > u . integer ;
if ( firstchannel < 0 ) {
firstchannel = channel ;
prevchannel = channel ;
}
if ( channel > ( prevchannel + 1 ) ) {
fontex . minchan = firstchannel ;
fontex . numchan = prevchannel - firstchannel + 1 ;
rval - > presets [ preset_number + + ] = fontex ;
firstchannel = channel ;
}
prevchannel = channel ;
2022-02-09 02:29:03 +00:00
}
2022-06-30 06:25:59 +00:00
fontex . minchan = firstchannel ;
fontex . numchan = prevchannel - firstchannel + 1 ;
rval - > presets [ preset_number + + ] = fontex ;
2022-02-09 02:29:03 +00:00
} else if ( channels - > type = = json_none ) {
2022-06-30 06:25:59 +00:00
sflist_process_patchmappings ( rval - > presets + preset_number , & fontex , patchMappings , 0 , 48 ) ;
2022-02-09 02:29:03 +00:00
preset_number + = patchMappings - > u . array . length ;
} else {
2022-06-30 06:25:59 +00:00
int prevchannel = - 1 ;
int firstchannel = - 1 ;
2022-02-09 02:29:03 +00:00
for ( k = 0 , l = channels - > u . array . length ; k < l ; + + k ) {
2022-06-30 06:25:59 +00:00
int channel = ( int ) channels - > u . array . values [ k ] - > u . integer ;
if ( firstchannel < 0 ) {
firstchannel = channel ;
prevchannel = channel ;
}
if ( channel > ( prevchannel + 1 ) ) {
sflist_process_patchmappings ( rval - > presets + preset_number , & fontex , patchMappings , firstchannel , prevchannel - firstchannel + 1 ) ;
preset_number + = patchMappings - > u . array . length ;
}
prevchannel = channel ;
2022-02-09 02:29:03 +00:00
}
2022-06-30 06:25:59 +00:00
sflist_process_patchmappings ( rval - > presets + preset_number , & fontex , patchMappings , firstchannel , prevchannel - firstchannel + 1 ) ;
preset_number + = patchMappings - > u . array . length ;
2022-02-09 02:29:03 +00:00
}
}
return rval ;
error :
if ( hfont ) {
BASS_MIDI_FontFree ( hfont ) ;
}
if ( rval ) {
sflist_free ( rval ) ;
}
return 0 ;
}
static int strpbrkn_all ( const char * str , size_t size , const char * chrs ) {
const char * end = str + size ;
while ( str < end & & * chrs ) {
while ( str < end & & * str ! = * chrs ) + + str ;
+ + str , + + chrs ;
}
return str < end ;
}
sflist_presets * sflist_load ( const char * sflist , size_t size , const char * base_path , char * error ) {
sflist_presets * rval ;
json_value * list = 0 ;
/* Handle Unicode byte order markers */
if ( size > = 2 ) {
if ( ( sflist [ 0 ] = = 0xFF & & sflist [ 1 ] = = 0xFE ) | |
( sflist [ 0 ] = = 0xFE & & sflist [ 1 ] = = 0xFF ) ) {
strcpy ( error , " UTF-16 encoding is not supported at this time " ) ;
return 0 ;
}
if ( size > = 3 & & sflist [ 0 ] = = 0xEF & &
sflist [ 1 ] = = 0xBB & & sflist [ 2 ] = = 0xBF ) {
sflist + = 3 ;
size - = 3 ;
}
}
list = sflist_load_v2 ( sflist , size , error ) ;
if ( ! list ) {
if ( ! strpbrkn_all ( sflist , size , " {[]} " ) )
list = sflist_load_v1 ( sflist , size , error ) ;
}
if ( ! list ) {
return 0 ;
}
rval = sflist_process ( list , base_path , error ) ;
json_builder_free ( list ) ;
return rval ;
}
void sflist_free ( sflist_presets * presetlist ) {
if ( presetlist ) {
if ( presetlist - > presets ) {
unsigned int i , j ;
for ( i = 0 , j = presetlist - > count ; i < j ; + + i ) {
HSOUNDFONT hfont = presetlist - > presets [ i ] . font ;
if ( hfont ) {
BASS_MIDI_FontFree ( hfont ) ;
}
}
free ( presetlist - > presets ) ;
}
free ( presetlist ) ;
}
}
const char * sflist_upgrade ( const char * sflist , size_t size , char * error_buf ) {
char * rval = 0 ;
json_value * list = 0 ;
size_t length = 0 ;
const json_serialize_opts opts = {
json_serialize_mode_multiline ,
0 ,
3 /* indent_size */
} ;
list = sflist_load_v2 ( sflist , size , error_buf ) ;
if ( ! list ) {
if ( ! strpbrkn_all ( sflist , size , " {[]} " ) )
list = sflist_load_v1 ( sflist , size , error_buf ) ;
}
if ( ! list ) {
return 0 ;
}
length = json_measure_ex ( list , opts ) ;
rval = ( char * ) malloc ( length + 1 ) ;
if ( ! rval ) {
strcpy ( error_buf , " Out of memory " ) ;
goto error ;
}
json_serialize_ex ( rval , list , opts ) ;
json_builder_free ( list ) ;
rval [ length ] = ' \0 ' ;
return ( const char * ) rval ;
error :
if ( list ) {
json_builder_free ( list ) ;
}
return 0 ;
}
void sflist_upgrade_free ( const char * ptr ) {
free ( ( void * ) ptr ) ;
}