2015-08-02 12:12:44 +00:00
# ifndef DEPENDENCY_AUDIO_DECODE
//Stubs:
//(none required)
# else
# define DEPENDENCY_AUDIO_DECODE_OGG
# define DEPENDENCY_AUDIO_DECODE_MP3
# define DEPENDENCY_AUDIO_DECODE_WAV
# ifdef QB64_BACKSLASH_FILESYSTEM
2017-08-06 03:37:54 +00:00
# ifdef DEPENDENCY_AUDIO_DECODE_MP3
# include "mp3_mini\\src.c"
# endif
# ifdef DEPENDENCY_AUDIO_DECODE_WAV
# include "wav\\src.c"
# endif
# ifdef DEPENDENCY_AUDIO_DECODE_OGG
# include "ogg\\src.c"
# endif
2015-08-02 12:12:44 +00:00
# else
2017-08-06 03:37:54 +00:00
# ifdef DEPENDENCY_AUDIO_DECODE_MP3
# include "mp3_mini/src.c"
# endif
# ifdef DEPENDENCY_AUDIO_DECODE_WAV
# include "wav/src.c"
# endif
# ifdef DEPENDENCY_AUDIO_DECODE_OGG
# include "ogg/src.c"
# endif
2015-08-02 12:12:44 +00:00
# endif
2017-10-22 13:51:17 +00:00
# include <string.h>
2015-08-02 12:12:44 +00:00
//forward refs:
void sub__sndvol ( int32 handle , float volume ) ;
void sub__sndclose ( int32 handle ) ;
int32 func__sndopen ( qbs * filename , qbs * requirements , int32 passed ) {
2017-08-06 03:37:54 +00:00
sndsetup ( ) ;
if ( new_error ) return 0 ;
static qbs * s1 = NULL ;
if ( ! s1 ) s1 = qbs_new ( 0 , 0 ) ;
qbs_set ( s1 , qbs_add ( filename , qbs_new_txt_len ( " \0 " , 1 ) ) ) ; //s1=filename+CHR$(0)
2015-08-02 12:12:44 +00:00
2017-08-06 03:37:54 +00:00
//load file
if ( s1 - > len = = 1 ) return 0 ; //return invalid handle if null length string
static int32 fh , result ;
static int64 lof ;
fh = gfs_open ( s1 , 1 , 0 , 0 ) ;
if ( fh < 0 ) return 0 ;
lof = gfs_lof ( fh ) ;
static uint8 * content ;
content = ( uint8 * ) malloc ( lof ) ; if ( ! content ) { gfs_close ( fh ) ; return 0 ; }
result = gfs_read ( fh , - 1 , content , lof ) ;
gfs_close ( fh ) ;
if ( result < 0 ) { free ( content ) ; return 0 ; }
//identify file format
static snd_sequence_struct * seq ;
//OGG?
2015-08-02 12:12:44 +00:00
# ifdef DEPENDENCY_AUDIO_DECODE_OGG
2017-08-06 03:37:54 +00:00
if ( lof > = 3 ) {
if ( content [ 0 ] = = 79 ) { if ( content [ 1 ] = = 103 ) { if ( content [ 2 ] = = 103 ) { //"Ogg"
seq = snd_decode_ogg ( content , lof ) ;
goto got_seq ;
} } }
} //3
2015-08-02 12:12:44 +00:00
# endif
2017-08-06 03:37:54 +00:00
//WAV?
2015-08-02 12:12:44 +00:00
# ifdef DEPENDENCY_AUDIO_DECODE_WAV
2017-08-06 03:37:54 +00:00
if ( lof > = 12 ) {
if ( ( * ( uint32 * ) & content [ 8 ] ) = = 0x45564157 ) { //WAVE
seq = snd_decode_wav ( content , lof ) ;
goto got_seq ;
} //WAVE
}
2015-08-02 12:12:44 +00:00
# endif
2017-08-06 03:37:54 +00:00
//assume mp3!
//MP3?
2015-08-02 12:12:44 +00:00
# ifdef DEPENDENCY_AUDIO_DECODE_MP3
2017-08-06 03:37:54 +00:00
seq = snd_decode_mp3 ( content , lof ) ;
2015-08-02 12:12:44 +00:00
# endif
got_seq :
2017-08-06 03:37:54 +00:00
free ( content ) ;
if ( seq = = NULL ) return 0 ;
2015-08-02 12:12:44 +00:00
2017-08-06 03:37:54 +00:00
//convert sequence (includes sample rate conversion etc etc)
//just perform sample_rate fix for now...
//1. 8->16bit conversion and/or edian conversion
static int32 incorrect_format ;
incorrect_format = 0 ;
if ( seq - > bits_per_sample ! = 16 ) incorrect_format = 1 ;
if ( seq - > is_unsigned ) incorrect_format = 1 ;
//todo... if (seq->endian==???)
//this section does not fix the frequency, only the bits per sample
//and signed-ness of the data
if ( incorrect_format ) {
static int32 bps ; bps = seq - > bits_per_sample / 8 ;
static int32 samples ; samples = seq - > data_size / bps ;
static uint8 * new_data ;
if ( bps ! = 2 ) {
new_data = ( uint8 * ) malloc ( samples * 2 ) ;
} else {
2017-08-10 11:50:31 +00:00
new_data = ( uint8 * ) seq - > data ;
2017-08-06 03:37:54 +00:00
}
static int32 i , v ;
for ( i = 0 ; i < samples ; i + + ) {
//read original value
v = 0 ;
if ( bps = = 1 ) {
if ( seq - > is_unsigned ) {
v = * ( uint8 * ) ( seq - > data + i * 1 ) ;
v = ( v - 128 ) * 256 ;
} else {
v = * ( int8 * ) ( seq - > data + i * 1 ) ;
v = v * 128 ;
}
}
if ( bps = = 2 ) {
if ( seq - > is_unsigned ) {
v = * ( uint16 * ) ( seq - > data + i * 2 ) ;
v = v - 32768 ;
} else {
v = * ( int16 * ) ( seq - > data + i * 2 ) ;
}
}
//place new value into array
( ( int16 * ) new_data ) [ i ] = v ;
} //i
2017-08-10 11:50:31 +00:00
if ( bps ! = 2 ) { free ( seq - > data ) ; seq - > data = ( uint16 * ) new_data ; seq - > data_size = samples * 2 ; }
2017-08-06 03:37:54 +00:00
//update seq info
seq - > bits_per_sample = 16 ;
seq - > is_unsigned = 0 ;
} //incorrect format
//2. samplerate conversion
if ( seq - > sample_rate ! = snd_frequency ) { //need to resample seq->data
//create new resampler
SpeexResamplerState * state ;
state = speex_resampler_init ( seq - > channels , seq - > sample_rate , snd_frequency , SPEEX_RESAMPLER_QUALITY_MIN , NULL ) ;
if ( ! state ) { //NULL means failure
free ( seq - > data ) ;
return 0 ;
}
//allocate new memory for output
int32 out_samples_max = ( ( double ) seq - > data_size / seq - > channels / 2 ) * ( ( ( ( double ) snd_frequency ) / ( ( double ) seq - > sample_rate ) ) + 0.1 ) + 100 ; //10%+100 extra samples as a buffer-zone
int16 * resampled = ( int16 * ) malloc ( out_samples_max * seq - > channels * sizeof ( int16 ) ) ;
if ( ! resampled ) {
free ( seq - > data ) ;
return 0 ;
}
2015-08-02 12:12:44 +00:00
2017-08-06 03:37:54 +00:00
//establish data sizes
//in_len will be set by the resampler to number of samples processed
2017-10-22 13:51:17 +00:00
spx_uint32_t in_len = seq - > data_size / seq - > channels / 2 ; // divide by 2 because 2byte samples, divide by #channels because function wants it per-channel
2017-08-06 03:37:54 +00:00
//out_len will be set to the number of samples written
2017-10-22 13:51:17 +00:00
spx_uint32_t out_len = out_samples_max * seq - > channels * sizeof ( int16 ) ;
2017-08-06 03:37:54 +00:00
//resample!
if ( speex_resampler_process_interleaved_int ( state , ( spx_int16_t * ) seq - > data , & in_len , ( spx_int16_t * ) resampled , & out_len ) ! = RESAMPLER_ERR_SUCCESS ) {
//Error
free ( resampled ) ;
free ( seq - > data ) ;
speex_resampler_destroy ( state ) ;
return 0 ;
}
//destroy the resampler anyway
speex_resampler_destroy ( state ) ;
//establish real size of new data and update seq
free ( seq - > data ) ; //That was the old data
seq - > data_size = out_len * seq - > channels * 2 ; //remember out_len is perchannel, and each sample is 2 bytes
2017-08-10 11:50:31 +00:00
seq - > data = ( uint16_t * ) realloc ( resampled , seq - > data_size ) ; //we overestimated the array size before, so make it the correct size now
2017-08-06 03:37:54 +00:00
if ( ! seq - > data ) { //realloc could fail
free ( resampled ) ;
return 0 ;
}
seq - > sample_rate = snd_frequency ;
}
2017-08-10 11:50:31 +00:00
//Unpack stereo data into separate left/right buffers
if ( seq - > channels = = 1 ) {
seq - > channels = 1 ;
seq - > data_left = seq - > data ;
seq - > data_left_size = seq - > data_size ;
seq - > data_right = NULL ;
seq - > data_right_size = 0 ;
2017-08-06 03:37:54 +00:00
}
2017-08-10 11:50:31 +00:00
else if ( seq - > channels = = 2 ) {
seq - > data_left_size = seq - > data_right_size = seq - > data_size / 2 ;
seq - > data_left = ( uint16_t * ) malloc ( seq - > data_size / 2 ) ;
if ( ! seq - > data_left ) {
free ( seq - > data ) ;
return 0 ;
}
seq - > data_right = ( uint16_t * ) malloc ( seq - > data_size / 2 ) ;
if ( ! seq - > data_right ) {
free ( seq - > data_left ) ;
free ( seq - > data ) ;
return 0 ;
}
for ( int sample = 0 ; sample < seq - > data_size / 4 ; sample + + ) {
seq - > data_left [ sample ] = seq - > data [ sample * 2 ] ;
seq - > data_right [ sample ] = seq - > data [ sample * 2 + 1 ] ;
}
free ( seq - > data ) ;
seq - > data = NULL ;
2017-08-06 03:37:54 +00:00
}
2017-08-10 11:50:31 +00:00
else {
free ( seq - > data ) ;
return 0 ;
}
2017-08-06 03:37:54 +00:00
//attach sequence to handle (& inc. refs)
//create snd handle
static int32 handle ; handle = list_add ( snd_handles ) ;
static snd_struct * snd ; snd = ( snd_struct * ) list_get ( snd_handles , handle ) ;
snd - > internal = 0 ;
snd - > type = 2 ;
snd - > seq = seq ;
snd - > volume = 1.0 ;
2017-08-10 11:50:31 +00:00
if ( seq - > channels = = 1 ) {
snd - > bal_left_x = snd - > bal_left_y = snd - > bal_left_z = 0 ;
}
else if ( seq - > channels = = 2 ) {
2017-08-10 12:06:20 +00:00
snd - > bal_left_x = - 0.01 ;
2017-08-10 11:50:31 +00:00
snd - > bal_left_y = snd - > bal_left_z = 0 ;
2017-08-10 12:06:20 +00:00
snd - > bal_right_x = 0.01 ;
2017-08-10 11:50:31 +00:00
snd - > bal_right_y = snd - > bal_right_z = 0 ;
}
snd - > bal_update = 1 ;
sndupdate ( snd ) ;
2017-08-06 03:37:54 +00:00
return handle ;
2015-08-02 12:12:44 +00:00
}
2020-12-31 04:40:54 +00:00
mem_block func__memsound ( int32 i , int32 targetChannel ) {
static mem_block b ;
if ( new_error ) goto error ;
if ( i < = 0 ) goto error ;
sndsetup ( ) ;
static snd_struct * sn ;
sn = ( snd_struct * ) list_get ( snd_handles , i ) ;
if ( ! sn ) {
goto error ;
}
if ( ! snd_allow_internal ) {
if ( sn - > internal ) {
goto error ;
}
}
if ( targetChannel < 1 | | targetChannel > sn - > seq - > channels ) goto error ;
if ( sn - > lock_id ) {
b . lock_offset = ( ptrszint ) sn - > lock_offset ; b . lock_id = sn - > lock_id ; //get existing tag
} else {
new_mem_lock ( ) ;
mem_lock_tmp - > type = 5 ; //sound
b . lock_offset = ( ptrszint ) mem_lock_tmp ; b . lock_id = mem_lock_id ;
sn - > lock_offset = ( void * ) mem_lock_tmp ; sn - > lock_id = mem_lock_id ; //create tag
}
if ( targetChannel = = 1 ) {
b . offset = ( ptrszint ) sn - > seq - > data_left ;
b . size = sn - > seq - > data_left_size ;
}
if ( targetChannel = = 2 ) {
b . offset = ( ptrszint ) sn - > seq - > data_right ;
b . size = sn - > seq - > data_right_size ;
}
b . type = 0 ; //sn->bytes_per_pixel+128+1024+2048;//integer+unsigned+pixeltype
b . elementsize = sn - > seq - > bits_per_sample / 8 ;
b . sound = i ;
return b ;
error :
b . offset = 0 ;
b . size = 0 ;
b . lock_offset = ( ptrszint ) mem_lock_base ; b . lock_id = 1073741821 ; //set invalid lock
b . type = 0 ;
b . elementsize = 0 ;
b . sound = 0 ;
return b ;
}
2015-08-02 12:12:44 +00:00
2017-08-11 11:19:52 +00:00
void sub__sndplayfile ( qbs * filename , int32 sync , double volume , int32 passed ) {
2017-08-06 03:37:54 +00:00
if ( new_error ) return ;
sndsetup ( ) ;
2017-08-11 11:19:52 +00:00
int32 handle ;
handle = func__sndopen ( filename , NULL , 0 ) ;
if ( ! handle ) return ;
if ( passed & 2 ) {
sub__sndvol ( handle , volume ) ;
2017-08-06 03:37:54 +00:00
}
sub__sndplay ( handle ) ;
sub__sndclose ( handle ) ;
2015-08-02 12:12:44 +00:00
}
# endif