2017-03-05 15:47:28 +01:00
/**************************************************************************/
2022-07-23 11:34:36 -03:00
/* audio_stream_wav.cpp */
2017-03-05 15:47:28 +01:00
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2022-07-23 11:34:36 -03:00
# include "audio_stream_wav.h"
2020-01-26 11:24:14 +01:00
2024-11-11 12:55:57 -05:00
# include "core/io/file_access_memory.h"
2018-09-11 18:13:45 +02:00
# include "core/io/marshalls.h"
2017-02-02 22:51:26 -03:00
2024-11-11 12:55:57 -05:00
const float TRIM_DB_LIMIT = - 50 ;
const int TRIM_FADE_OUT_FRAMES = 500 ;
2022-09-22 08:54:15 -05:00
void AudioStreamPlaybackWAV : : start ( double p_from_pos ) {
2022-07-23 11:34:36 -03:00
if ( base - > format = = AudioStreamWAV : : FORMAT_IMA_ADPCM ) {
2017-11-12 11:11:45 -03:00
//no seeking in IMA_ADPCM
for ( int i = 0 ; i < 2 ; i + + ) {
ima_adpcm [ i ] . step_index = 0 ;
ima_adpcm [ i ] . predictor = 0 ;
ima_adpcm [ i ] . loop_step_index = 0 ;
ima_adpcm [ i ] . loop_predictor = 0 ;
ima_adpcm [ i ] . last_nibble = - 1 ;
ima_adpcm [ i ] . loop_pos = 0x7FFFFFFF ;
ima_adpcm [ i ] . window_ofs = 0 ;
}
offset = 0 ;
} else {
seek ( p_from_pos ) ;
2017-02-02 22:51:26 -03:00
}
sign = 1 ;
active = true ;
}
2022-07-23 11:34:36 -03:00
void AudioStreamPlaybackWAV : : stop ( ) {
2017-02-02 22:51:26 -03:00
active = false ;
}
2022-07-23 11:34:36 -03:00
bool AudioStreamPlaybackWAV : : is_playing ( ) const {
2017-02-02 22:51:26 -03:00
return active ;
}
2022-07-23 11:34:36 -03:00
int AudioStreamPlaybackWAV : : get_loop_count ( ) const {
2017-02-02 22:51:26 -03:00
return 0 ;
}
2022-09-22 08:54:15 -05:00
double AudioStreamPlaybackWAV : : get_playback_position ( ) const {
2017-02-02 22:51:26 -03:00
return float ( offset > > MIX_FRAC_BITS ) / base - > mix_rate ;
}
2020-05-14 14:29:06 +02:00
2022-09-22 08:54:15 -05:00
void AudioStreamPlaybackWAV : : seek ( double p_time ) {
2022-07-23 11:34:36 -03:00
if ( base - > format = = AudioStreamWAV : : FORMAT_IMA_ADPCM ) {
2017-02-02 22:51:26 -03:00
return ; //no seeking in ima-adpcm
2020-05-14 16:41:43 +02:00
}
2017-02-02 22:51:26 -03:00
2022-09-22 08:54:15 -05:00
double max = base - > get_length ( ) ;
2017-02-02 22:51:26 -03:00
if ( p_time < 0 ) {
p_time = 0 ;
} else if ( p_time > = max ) {
p_time = max - 0.001 ;
}
offset = uint64_t ( p_time * base - > mix_rate ) < < MIX_FRAC_BITS ;
}
2024-04-16 20:33:29 -03:00
template < typename Depth , bool is_stereo , bool is_ima_adpcm , bool is_qoa >
void AudioStreamPlaybackWAV : : do_resample ( const Depth * p_src , AudioFrame * p_dst , int64_t & p_offset , int32_t & p_increment , uint32_t p_amount , IMA_ADPCM_State * p_ima_adpcm , QOA_State * p_qoa ) {
2017-02-02 22:51:26 -03:00
// this function will be compiled branchless by any decent compiler
2024-04-16 20:33:29 -03:00
int32_t final = 0 , final_r = 0 , next = 0 , next_r = 0 ;
2023-05-11 12:32:23 +02:00
while ( p_amount ) {
p_amount - - ;
int64_t pos = p_offset > > MIX_FRAC_BITS ;
2024-04-16 20:33:29 -03:00
if ( is_stereo & & ! is_ima_adpcm & & ! is_qoa ) {
2017-02-02 22:51:26 -03:00
pos < < = 1 ;
2020-05-14 16:41:43 +02:00
}
2017-02-02 22:51:26 -03:00
if ( is_ima_adpcm ) {
2023-05-11 12:32:23 +02:00
int64_t sample_pos = pos + p_ima_adpcm [ 0 ] . window_ofs ;
2017-02-02 22:51:26 -03:00
2023-05-11 12:32:23 +02:00
while ( sample_pos > p_ima_adpcm [ 0 ] . last_nibble ) {
2017-02-02 22:51:26 -03:00
static const int16_t _ima_adpcm_step_table [ 89 ] = {
7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 16 , 17 ,
19 , 21 , 23 , 25 , 28 , 31 , 34 , 37 , 41 , 45 ,
50 , 55 , 60 , 66 , 73 , 80 , 88 , 97 , 107 , 118 ,
130 , 143 , 157 , 173 , 190 , 209 , 230 , 253 , 279 , 307 ,
337 , 371 , 408 , 449 , 494 , 544 , 598 , 658 , 724 , 796 ,
876 , 963 , 1060 , 1166 , 1282 , 1411 , 1552 , 1707 , 1878 , 2066 ,
2272 , 2499 , 2749 , 3024 , 3327 , 3660 , 4026 , 4428 , 4871 , 5358 ,
5894 , 6484 , 7132 , 7845 , 8630 , 9493 , 10442 , 11487 , 12635 , 13899 ,
15289 , 16818 , 18500 , 20350 , 22385 , 24623 , 27086 , 29794 , 32767
} ;
static const int8_t _ima_adpcm_index_table [ 16 ] = {
- 1 , - 1 , - 1 , - 1 , 2 , 4 , 6 , 8 ,
- 1 , - 1 , - 1 , - 1 , 2 , 4 , 6 , 8
} ;
for ( int i = 0 ; i < ( is_stereo ? 2 : 1 ) ; i + + ) {
int16_t nibble , diff , step ;
2023-05-11 12:32:23 +02:00
p_ima_adpcm [ i ] . last_nibble + + ;
2017-02-02 22:51:26 -03:00
2024-08-30 10:00:26 -03:00
uint8_t nbb = p_src [ ( p_ima_adpcm [ i ] . last_nibble > > 1 ) * ( is_stereo ? 2 : 1 ) + i ] ;
2023-05-11 12:32:23 +02:00
nibble = ( p_ima_adpcm [ i ] . last_nibble & 1 ) ? ( nbb > > 4 ) : ( nbb & 0xF ) ;
step = _ima_adpcm_step_table [ p_ima_adpcm [ i ] . step_index ] ;
2017-02-02 22:51:26 -03:00
2023-05-11 12:32:23 +02:00
p_ima_adpcm [ i ] . step_index + = _ima_adpcm_index_table [ nibble ] ;
if ( p_ima_adpcm [ i ] . step_index < 0 ) {
p_ima_adpcm [ i ] . step_index = 0 ;
2020-05-14 16:41:43 +02:00
}
2023-05-11 12:32:23 +02:00
if ( p_ima_adpcm [ i ] . step_index > 88 ) {
p_ima_adpcm [ i ] . step_index = 88 ;
2020-05-14 16:41:43 +02:00
}
2017-02-02 22:51:26 -03:00
diff = step > > 3 ;
2020-05-14 16:41:43 +02:00
if ( nibble & 1 ) {
2017-02-02 22:51:26 -03:00
diff + = step > > 2 ;
2020-05-14 16:41:43 +02:00
}
if ( nibble & 2 ) {
2017-02-02 22:51:26 -03:00
diff + = step > > 1 ;
2020-05-14 16:41:43 +02:00
}
if ( nibble & 4 ) {
2017-02-02 22:51:26 -03:00
diff + = step ;
2020-05-14 16:41:43 +02:00
}
if ( nibble & 8 ) {
2017-02-02 22:51:26 -03:00
diff = - diff ;
2020-05-14 16:41:43 +02:00
}
2017-02-02 22:51:26 -03:00
2023-05-11 12:32:23 +02:00
p_ima_adpcm [ i ] . predictor + = diff ;
if ( p_ima_adpcm [ i ] . predictor < - 0x8000 ) {
p_ima_adpcm [ i ] . predictor = - 0x8000 ;
} else if ( p_ima_adpcm [ i ] . predictor > 0x7FFF ) {
p_ima_adpcm [ i ] . predictor = 0x7FFF ;
2020-05-14 16:41:43 +02:00
}
2017-02-02 22:51:26 -03:00
/* store loop if there */
2023-05-11 12:32:23 +02:00
if ( p_ima_adpcm [ i ] . last_nibble = = p_ima_adpcm [ i ] . loop_pos ) {
p_ima_adpcm [ i ] . loop_step_index = p_ima_adpcm [ i ] . step_index ;
p_ima_adpcm [ i ] . loop_predictor = p_ima_adpcm [ i ] . predictor ;
2017-02-02 22:51:26 -03:00
}
2023-05-11 12:32:23 +02:00
//printf("%i - %i - pred %i\n",int(p_ima_adpcm[i].last_nibble),int(nibble),int(p_ima_adpcm[i].predictor));
2017-02-02 22:51:26 -03:00
}
}
2023-05-11 12:32:23 +02:00
final = p_ima_adpcm [ 0 ] . predictor ;
2017-02-02 22:51:26 -03:00
if ( is_stereo ) {
2023-05-11 12:32:23 +02:00
final_r = p_ima_adpcm [ 1 ] . predictor ;
2017-02-02 22:51:26 -03:00
}
} else {
2024-04-16 20:33:29 -03:00
if ( is_qoa ) {
if ( pos ! = p_qoa - > cache_pos ) { // Prevents triple decoding on lower mix rates.
for ( int i = 0 ; i < 2 ; i + + ) {
// Sign operations prevent triple decoding on backward loops, maxing prevents pop.
2024-08-13 00:34:40 -03:00
uint32_t interp_pos = MIN ( pos + ( i * sign ) + ( sign < 0 ) , p_qoa - > desc . samples - 1 ) ;
2024-04-16 20:33:29 -03:00
uint32_t new_data_ofs = 8 + interp_pos / QOA_FRAME_LEN * p_qoa - > frame_len ;
if ( p_qoa - > data_ofs ! = new_data_ofs ) {
p_qoa - > data_ofs = new_data_ofs ;
2024-08-30 10:00:26 -03:00
const uint8_t * ofs_src = ( uint8_t * ) p_src + p_qoa - > data_ofs ;
qoa_decode_frame ( ofs_src , p_qoa - > frame_len , & p_qoa - > desc , p_qoa - > dec . ptr ( ) , & p_qoa - > dec_len ) ;
2024-04-16 20:33:29 -03:00
}
2017-02-02 22:51:26 -03:00
2024-08-13 00:34:40 -03:00
uint32_t dec_idx = ( interp_pos % QOA_FRAME_LEN ) * p_qoa - > desc . channels ;
2024-04-16 20:33:29 -03:00
if ( ( sign > 0 & & i = = 0 ) | | ( sign < 0 & & i = = 1 ) ) {
final = p_qoa - > dec [ dec_idx ] ;
p_qoa - > cache [ 0 ] = final ;
if ( is_stereo ) {
final_r = p_qoa - > dec [ dec_idx + 1 ] ;
p_qoa - > cache_r [ 0 ] = final_r ;
}
} else {
next = p_qoa - > dec [ dec_idx ] ;
p_qoa - > cache [ 1 ] = next ;
if ( is_stereo ) {
next_r = p_qoa - > dec [ dec_idx + 1 ] ;
p_qoa - > cache_r [ 1 ] = next_r ;
}
}
}
p_qoa - > cache_pos = pos ;
} else {
final = p_qoa - > cache [ 0 ] ;
if ( is_stereo ) {
final_r = p_qoa - > cache_r [ 0 ] ;
}
next = p_qoa - > cache [ 1 ] ;
if ( is_stereo ) {
next_r = p_qoa - > cache_r [ 1 ] ;
}
}
} else {
final = p_src [ pos ] ;
2020-05-14 16:41:43 +02:00
if ( is_stereo ) {
2024-04-16 20:33:29 -03:00
final_r = p_src [ pos + 1 ] ;
2020-05-14 16:41:43 +02:00
}
2017-02-02 22:51:26 -03:00
2024-04-16 20:33:29 -03:00
if constexpr ( sizeof ( Depth ) = = 1 ) { /* conditions will not exist anymore when compiled! */
final < < = 8 ;
if ( is_stereo ) {
final_r < < = 8 ;
}
}
2017-02-02 22:51:26 -03:00
2020-05-14 16:41:43 +02:00
if ( is_stereo ) {
2024-04-16 20:33:29 -03:00
next = p_src [ pos + 2 ] ;
next_r = p_src [ pos + 3 ] ;
} else {
next = p_src [ pos + 1 ] ;
2020-05-14 16:41:43 +02:00
}
2017-02-02 22:51:26 -03:00
2024-04-16 20:33:29 -03:00
if constexpr ( sizeof ( Depth ) = = 1 ) {
next < < = 8 ;
if ( is_stereo ) {
next_r < < = 8 ;
}
}
}
2023-05-11 12:32:23 +02:00
int32_t frac = int64_t ( p_offset & MIX_FRAC_MASK ) ;
2017-02-02 22:51:26 -03:00
final = final + ( ( next - final ) * frac > > MIX_FRAC_BITS ) ;
2020-05-14 16:41:43 +02:00
if ( is_stereo ) {
2017-02-02 22:51:26 -03:00
final_r = final_r + ( ( next_r - final_r ) * frac > > MIX_FRAC_BITS ) ;
2020-05-14 16:41:43 +02:00
}
2017-02-02 22:51:26 -03:00
}
if ( ! is_stereo ) {
final_r = final ; //copy to right channel if stereo
}
2024-01-09 15:01:12 +01:00
p_dst - > left = final / 32767.0 ;
p_dst - > right = final_r / 32767.0 ;
2017-02-02 22:51:26 -03:00
p_dst + + ;
2023-05-11 12:32:23 +02:00
p_offset + = p_increment ;
2017-02-02 22:51:26 -03:00
}
}
2022-07-23 11:34:36 -03:00
int AudioStreamPlaybackWAV : : mix ( AudioFrame * p_buffer , float p_rate_scale , int p_frames ) {
2024-08-30 10:00:26 -03:00
if ( base - > data . is_empty ( ) | | ! active ) {
2017-02-02 22:51:26 -03:00
for ( int i = 0 ; i < p_frames ; i + + ) {
p_buffer [ i ] = AudioFrame ( 0 , 0 ) ;
}
2021-08-27 10:27:58 -07:00
return 0 ;
2017-02-02 22:51:26 -03:00
}
int len = base - > data_bytes ;
switch ( base - > format ) {
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_8_BITS :
2020-05-10 13:00:47 +02:00
len / = 1 ;
break ;
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_16_BITS :
2020-05-10 13:00:47 +02:00
len / = 2 ;
break ;
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_IMA_ADPCM :
2020-05-10 13:00:47 +02:00
len * = 2 ;
break ;
2024-04-16 20:33:29 -03:00
case AudioStreamWAV : : FORMAT_QOA :
2024-08-13 00:34:40 -03:00
len = qoa . desc . samples * qoa . desc . channels ;
2024-04-16 20:33:29 -03:00
break ;
2017-02-02 22:51:26 -03:00
}
if ( base - > stereo ) {
len / = 2 ;
}
/* some 64-bit fixed point precaches */
2017-07-15 01:23:10 -03:00
int64_t loop_begin_fp = ( ( int64_t ) base - > loop_begin < < MIX_FRAC_BITS ) ;
2017-02-02 22:51:26 -03:00
int64_t loop_end_fp = ( ( int64_t ) base - > loop_end < < MIX_FRAC_BITS ) ;
int64_t length_fp = ( ( int64_t ) len < < MIX_FRAC_BITS ) ;
2022-07-23 11:34:36 -03:00
int64_t begin_limit = ( base - > loop_mode ! = AudioStreamWAV : : LOOP_DISABLED ) ? loop_begin_fp : 0 ;
2024-09-09 16:12:22 -03:00
int64_t end_limit = ( base - > loop_mode ! = AudioStreamWAV : : LOOP_DISABLED ) ? loop_end_fp : length_fp - MIX_FRAC_LEN ;
2017-02-02 22:51:26 -03:00
bool is_stereo = base - > stereo ;
int32_t todo = p_frames ;
2022-07-23 11:34:36 -03:00
if ( base - > loop_mode = = AudioStreamWAV : : LOOP_BACKWARD ) {
2018-10-28 19:37:14 +01:00
sign = - 1 ;
}
2020-05-07 13:46:33 -03:00
float base_rate = AudioServer : : get_singleton ( ) - > get_mix_rate ( ) ;
2017-02-02 22:51:26 -03:00
float srate = base - > mix_rate ;
srate * = p_rate_scale ;
2020-05-07 13:46:33 -03:00
float playback_speed_scale = AudioServer : : get_singleton ( ) - > get_playback_speed_scale ( ) ;
float fincrement = ( srate * playback_speed_scale ) / base_rate ;
2020-03-14 13:08:01 +00:00
int32_t increment = int32_t ( MAX ( fincrement * MIX_FRAC_LEN , 1 ) ) ;
2017-02-02 22:51:26 -03:00
increment * = sign ;
//looping
2022-07-23 11:34:36 -03:00
AudioStreamWAV : : LoopMode loop_format = base - > loop_mode ;
AudioStreamWAV : : Format format = base - > format ;
2017-02-02 22:51:26 -03:00
/* audio data */
2024-08-30 10:00:26 -03:00
const uint8_t * data = base - > data . ptr ( ) + AudioStreamWAV : : DATA_PAD ;
2017-02-02 22:51:26 -03:00
AudioFrame * dst_buff = p_buffer ;
2022-07-23 11:34:36 -03:00
if ( format = = AudioStreamWAV : : FORMAT_IMA_ADPCM ) {
if ( loop_format ! = AudioStreamWAV : : LOOP_DISABLED ) {
2017-02-02 22:51:26 -03:00
ima_adpcm [ 0 ] . loop_pos = loop_begin_fp > > MIX_FRAC_BITS ;
ima_adpcm [ 1 ] . loop_pos = loop_begin_fp > > MIX_FRAC_BITS ;
2022-07-23 11:34:36 -03:00
loop_format = AudioStreamWAV : : LOOP_FORWARD ;
2017-02-02 22:51:26 -03:00
}
}
while ( todo > 0 ) {
int64_t limit = 0 ;
int32_t target = 0 , aux = 0 ;
/** LOOP CHECKING **/
if ( increment < 0 ) {
/* going backwards */
2022-07-23 11:34:36 -03:00
if ( loop_format ! = AudioStreamWAV : : LOOP_DISABLED & & offset < loop_begin_fp ) {
2017-02-02 22:51:26 -03:00
/* loopstart reached */
2022-07-23 11:34:36 -03:00
if ( loop_format = = AudioStreamWAV : : LOOP_PINGPONG ) {
2017-02-02 22:51:26 -03:00
/* bounce ping pong */
offset = loop_begin_fp + ( loop_begin_fp - offset ) ;
increment = - increment ;
sign * = - 1 ;
} else {
/* go to loop-end */
offset = loop_end_fp - ( loop_begin_fp - offset ) ;
}
} else {
2017-03-24 21:45:31 +01:00
/* check for sample not reaching beginning */
2017-02-02 22:51:26 -03:00
if ( offset < 0 ) {
active = false ;
break ;
}
}
} else {
/* going forward */
2022-07-23 11:34:36 -03:00
if ( loop_format ! = AudioStreamWAV : : LOOP_DISABLED & & offset > = loop_end_fp ) {
2017-02-02 22:51:26 -03:00
/* loopend reached */
2022-07-23 11:34:36 -03:00
if ( loop_format = = AudioStreamWAV : : LOOP_PINGPONG ) {
2017-02-02 22:51:26 -03:00
/* bounce ping pong */
offset = loop_end_fp - ( offset - loop_end_fp ) ;
increment = - increment ;
sign * = - 1 ;
} else {
/* go to loop-begin */
2022-07-23 11:34:36 -03:00
if ( format = = AudioStreamWAV : : FORMAT_IMA_ADPCM ) {
2017-02-02 22:51:26 -03:00
for ( int i = 0 ; i < 2 ; i + + ) {
ima_adpcm [ i ] . step_index = ima_adpcm [ i ] . loop_step_index ;
ima_adpcm [ i ] . predictor = ima_adpcm [ i ] . loop_predictor ;
ima_adpcm [ i ] . last_nibble = loop_begin_fp > > MIX_FRAC_BITS ;
}
offset = loop_begin_fp ;
} else {
offset = loop_begin_fp + ( offset - loop_end_fp ) ;
}
}
} else {
/* no loop, check for end of sample */
if ( offset > = length_fp ) {
active = false ;
break ;
}
}
}
/** MIXCOUNT COMPUTING **/
/* next possible limit (looppoints or sample begin/end */
limit = ( increment < 0 ) ? begin_limit : end_limit ;
/* compute what is shorter, the todo or the limit? */
aux = ( limit - offset ) / increment + 1 ;
target = ( aux < todo ) ? aux : todo ; /* mix target is the shorter buffer */
/* check just in case */
if ( target < = 0 ) {
active = false ;
break ;
}
todo - = target ;
switch ( base - > format ) {
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_8_BITS : {
2020-05-14 16:41:43 +02:00
if ( is_stereo ) {
2024-04-16 20:33:29 -03:00
do_resample < int8_t , true , false , false > ( ( int8_t * ) data , dst_buff , offset , increment , target , ima_adpcm , & qoa ) ;
2020-05-14 16:41:43 +02:00
} else {
2024-04-16 20:33:29 -03:00
do_resample < int8_t , false , false , false > ( ( int8_t * ) data , dst_buff , offset , increment , target , ima_adpcm , & qoa ) ;
2020-05-14 16:41:43 +02:00
}
2017-02-02 22:51:26 -03:00
} break ;
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_16_BITS : {
2020-05-14 16:41:43 +02:00
if ( is_stereo ) {
2024-04-16 20:33:29 -03:00
do_resample < int16_t , true , false , false > ( ( int16_t * ) data , dst_buff , offset , increment , target , ima_adpcm , & qoa ) ;
2020-05-14 16:41:43 +02:00
} else {
2024-04-16 20:33:29 -03:00
do_resample < int16_t , false , false , false > ( ( int16_t * ) data , dst_buff , offset , increment , target , ima_adpcm , & qoa ) ;
2020-05-14 16:41:43 +02:00
}
2017-02-02 22:51:26 -03:00
} break ;
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_IMA_ADPCM : {
2020-05-14 16:41:43 +02:00
if ( is_stereo ) {
2024-04-16 20:33:29 -03:00
do_resample < int8_t , true , true , false > ( ( int8_t * ) data , dst_buff , offset , increment , target , ima_adpcm , & qoa ) ;
2020-05-14 16:41:43 +02:00
} else {
2024-04-16 20:33:29 -03:00
do_resample < int8_t , false , true , false > ( ( int8_t * ) data , dst_buff , offset , increment , target , ima_adpcm , & qoa ) ;
2020-05-14 16:41:43 +02:00
}
2017-02-02 22:51:26 -03:00
} break ;
2024-04-16 20:33:29 -03:00
case AudioStreamWAV : : FORMAT_QOA : {
if ( is_stereo ) {
do_resample < uint8_t , true , false , true > ( ( uint8_t * ) data , dst_buff , offset , increment , target , ima_adpcm , & qoa ) ;
} else {
do_resample < uint8_t , false , false , true > ( ( uint8_t * ) data , dst_buff , offset , increment , target , ima_adpcm , & qoa ) ;
}
} break ;
2017-02-02 22:51:26 -03:00
}
dst_buff + = target ;
}
2017-11-12 11:11:45 -03:00
if ( todo ) {
2021-08-27 10:27:58 -07:00
int mixed_frames = p_frames - todo ;
2017-11-12 11:11:45 -03:00
//bit was missing from mix
int todo_ofs = p_frames - todo ;
for ( int i = todo_ofs ; i < p_frames ; i + + ) {
p_buffer [ i ] = AudioFrame ( 0 , 0 ) ;
}
2021-08-27 10:27:58 -07:00
return mixed_frames ;
2017-11-12 11:11:45 -03:00
}
2021-08-27 10:27:58 -07:00
return p_frames ;
2017-02-02 22:51:26 -03:00
}
2022-07-23 11:34:36 -03:00
void AudioStreamPlaybackWAV : : tag_used_streams ( ) {
2022-07-21 01:00:58 +02:00
base - > tag_used ( get_playback_position ( ) ) ;
}
2024-04-18 10:50:34 -04:00
void AudioStreamPlaybackWAV : : set_is_sample ( bool p_is_sample ) {
_is_sample = p_is_sample ;
}
bool AudioStreamPlaybackWAV : : get_is_sample ( ) const {
return _is_sample ;
}
Ref < AudioSamplePlayback > AudioStreamPlaybackWAV : : get_sample_playback ( ) const {
return sample_playback ;
}
void AudioStreamPlaybackWAV : : set_sample_playback ( const Ref < AudioSamplePlayback > & p_playback ) {
sample_playback = p_playback ;
2024-09-04 12:04:27 -04:00
if ( sample_playback . is_valid ( ) ) {
sample_playback - > stream_playback = Ref < AudioStreamPlayback > ( this ) ;
}
2024-04-18 10:50:34 -04:00
}
2022-07-23 11:34:36 -03:00
AudioStreamPlaybackWAV : : AudioStreamPlaybackWAV ( ) { }
2017-02-02 22:51:26 -03:00
2024-08-30 10:00:26 -03:00
AudioStreamPlaybackWAV : : ~ AudioStreamPlaybackWAV ( ) { }
2024-04-16 20:33:29 -03:00
2017-02-02 22:51:26 -03:00
/////////////////////
2022-07-23 11:34:36 -03:00
void AudioStreamWAV : : set_format ( Format p_format ) {
2017-02-02 22:51:26 -03:00
format = p_format ;
}
2022-07-23 11:34:36 -03:00
AudioStreamWAV : : Format AudioStreamWAV : : get_format ( ) const {
2017-02-02 22:51:26 -03:00
return format ;
}
2022-07-23 11:34:36 -03:00
void AudioStreamWAV : : set_loop_mode ( LoopMode p_loop_mode ) {
2017-02-02 22:51:26 -03:00
loop_mode = p_loop_mode ;
}
2020-05-14 14:29:06 +02:00
2022-07-23 11:34:36 -03:00
AudioStreamWAV : : LoopMode AudioStreamWAV : : get_loop_mode ( ) const {
2017-02-02 22:51:26 -03:00
return loop_mode ;
}
2022-07-23 11:34:36 -03:00
void AudioStreamWAV : : set_loop_begin ( int p_frame ) {
2017-02-02 22:51:26 -03:00
loop_begin = p_frame ;
}
2020-05-14 14:29:06 +02:00
2022-07-23 11:34:36 -03:00
int AudioStreamWAV : : get_loop_begin ( ) const {
2017-02-02 22:51:26 -03:00
return loop_begin ;
}
2022-07-23 11:34:36 -03:00
void AudioStreamWAV : : set_loop_end ( int p_frame ) {
2017-02-02 22:51:26 -03:00
loop_end = p_frame ;
}
2020-05-14 14:29:06 +02:00
2022-07-23 11:34:36 -03:00
int AudioStreamWAV : : get_loop_end ( ) const {
2017-02-02 22:51:26 -03:00
return loop_end ;
}
2022-07-23 11:34:36 -03:00
void AudioStreamWAV : : set_mix_rate ( int p_hz ) {
2019-11-20 16:22:16 +01:00
ERR_FAIL_COND ( p_hz = = 0 ) ;
2017-02-02 22:51:26 -03:00
mix_rate = p_hz ;
}
2020-05-14 14:29:06 +02:00
2022-07-23 11:34:36 -03:00
int AudioStreamWAV : : get_mix_rate ( ) const {
2017-02-02 22:51:26 -03:00
return mix_rate ;
}
2020-05-14 14:29:06 +02:00
2022-07-23 11:34:36 -03:00
void AudioStreamWAV : : set_stereo ( bool p_enable ) {
2017-02-02 22:51:26 -03:00
stereo = p_enable ;
}
2020-05-14 14:29:06 +02:00
2022-07-23 11:34:36 -03:00
bool AudioStreamWAV : : is_stereo ( ) const {
2017-02-02 22:51:26 -03:00
return stereo ;
}
2022-09-22 08:54:15 -05:00
double AudioStreamWAV : : get_length ( ) const {
2018-01-22 20:35:33 +01:00
int len = data_bytes ;
switch ( format ) {
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_8_BITS :
2020-05-10 13:00:47 +02:00
len / = 1 ;
break ;
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_16_BITS :
2020-05-10 13:00:47 +02:00
len / = 2 ;
break ;
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_IMA_ADPCM :
2020-05-10 13:00:47 +02:00
len * = 2 ;
break ;
2024-04-16 20:33:29 -03:00
case AudioStreamWAV : : FORMAT_QOA :
2024-08-13 00:34:40 -03:00
qoa_desc desc = { } ;
2024-08-30 10:00:26 -03:00
qoa_decode_header ( data . ptr ( ) + DATA_PAD , data_bytes , & desc ) ;
2024-04-16 20:33:29 -03:00
len = desc . samples * desc . channels ;
2024-08-13 00:34:40 -03:00
break ;
2018-01-22 20:35:33 +01:00
}
if ( stereo ) {
len / = 2 ;
}
2022-09-22 08:54:15 -05:00
return double ( len ) / mix_rate ;
2018-01-22 20:35:33 +01:00
}
2022-07-23 11:34:36 -03:00
bool AudioStreamWAV : : is_monophonic ( ) const {
2021-08-27 21:51:03 -07:00
return false ;
}
2022-07-23 11:34:36 -03:00
void AudioStreamWAV : : set_data ( const Vector < uint8_t > & p_data ) {
2017-02-02 22:51:26 -03:00
AudioServer : : get_singleton ( ) - > lock ( ) ;
2024-08-30 10:00:26 -03:00
int src_data_len = p_data . size ( ) ;
data . clear ( ) ;
int alloc_len = src_data_len + DATA_PAD * 2 ;
data . resize ( alloc_len ) ;
memset ( data . ptr ( ) , 0 , alloc_len ) ;
memcpy ( data . ptr ( ) + DATA_PAD , p_data . ptr ( ) , src_data_len ) ;
data_bytes = src_data_len ;
2017-02-02 22:51:26 -03:00
AudioServer : : get_singleton ( ) - > unlock ( ) ;
}
2020-05-14 14:29:06 +02:00
2022-07-23 11:34:36 -03:00
Vector < uint8_t > AudioStreamWAV : : get_data ( ) const {
2020-02-17 18:06:54 -03:00
Vector < uint8_t > pv ;
2017-02-02 22:51:26 -03:00
2024-08-30 10:00:26 -03:00
if ( ! data . is_empty ( ) ) {
2017-02-02 22:51:26 -03:00
pv . resize ( data_bytes ) ;
2024-08-30 10:00:26 -03:00
memcpy ( pv . ptrw ( ) , data . ptr ( ) + DATA_PAD , data_bytes ) ;
2017-02-02 22:51:26 -03:00
}
return pv ;
}
2022-07-23 11:34:36 -03:00
Error AudioStreamWAV : : save_to_wav ( const String & p_path ) {
2024-04-16 20:33:29 -03:00
if ( format = = AudioStreamWAV : : FORMAT_IMA_ADPCM | | format = = AudioStreamWAV : : FORMAT_QOA ) {
WARN_PRINT ( " Saving IMA_ADPCM and QOA samples is not supported yet " ) ;
2019-03-12 15:48:52 +01:00
return ERR_UNAVAILABLE ;
2018-07-23 14:51:23 +02:00
}
int sub_chunk_2_size = data_bytes ; //Subchunk2Size = Size of data in bytes
// Format code
// 1:PCM format (for 8 or 16 bit)
// 3:IEEE float format
int format_code = ( format = = FORMAT_IMA_ADPCM ) ? 3 : 1 ;
int n_channels = stereo ? 2 : 1 ;
long sample_rate = mix_rate ;
int byte_pr_sample = 0 ;
switch ( format ) {
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_8_BITS :
2020-05-10 13:00:47 +02:00
byte_pr_sample = 1 ;
break ;
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_16_BITS :
2024-04-16 20:33:29 -03:00
case AudioStreamWAV : : FORMAT_QOA :
2020-05-10 13:00:47 +02:00
byte_pr_sample = 2 ;
break ;
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_IMA_ADPCM :
2020-05-10 13:00:47 +02:00
byte_pr_sample = 4 ;
break ;
2018-07-23 14:51:23 +02:00
}
String file_path = p_path ;
2024-10-31 21:33:17 -05:00
if ( file_path . substr ( file_path . length ( ) - 4 , 4 ) . to_lower ( ) ! = " .wav " ) {
2018-07-23 14:51:23 +02:00
file_path + = " .wav " ;
}
2022-03-23 11:08:58 +02:00
Ref < FileAccess > file = FileAccess : : open ( file_path , FileAccess : : WRITE ) ; //Overrides existing file if present
2019-03-12 15:48:52 +01:00
2022-03-23 11:08:58 +02:00
ERR_FAIL_COND_V ( file . is_null ( ) , ERR_FILE_CANT_WRITE ) ;
2018-07-23 14:51:23 +02:00
// Create WAV Header
file - > store_string ( " RIFF " ) ; //ChunkID
file - > store_32 ( sub_chunk_2_size + 36 ) ; //ChunkSize = 36 + SubChunk2Size (size of entire file minus the 8 bits for this and previous header)
file - > store_string ( " WAVE " ) ; //Format
file - > store_string ( " fmt " ) ; //Subchunk1ID
file - > store_32 ( 16 ) ; //Subchunk1Size = 16
file - > store_16 ( format_code ) ; //AudioFormat
file - > store_16 ( n_channels ) ; //Number of Channels
file - > store_32 ( sample_rate ) ; //SampleRate
file - > store_32 ( sample_rate * n_channels * byte_pr_sample ) ; //ByteRate
file - > store_16 ( n_channels * byte_pr_sample ) ; //BlockAlign = NumChannels * BytePrSample
file - > store_16 ( byte_pr_sample * 8 ) ; //BitsPerSample
file - > store_string ( " data " ) ; //Subchunk2ID
file - > store_32 ( sub_chunk_2_size ) ; //Subchunk2Size
// Add data
2022-09-29 12:53:28 +03:00
Vector < uint8_t > stream_data = get_data ( ) ;
const uint8_t * read_data = stream_data . ptr ( ) ;
2018-07-23 14:51:23 +02:00
switch ( format ) {
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_8_BITS :
2018-09-26 17:38:02 +02:00
for ( unsigned int i = 0 ; i < data_bytes ; i + + ) {
2018-07-23 14:51:23 +02:00
uint8_t data_point = ( read_data [ i ] + 128 ) ;
file - > store_8 ( data_point ) ;
}
break ;
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_16_BITS :
2024-04-16 20:33:29 -03:00
case AudioStreamWAV : : FORMAT_QOA :
2018-09-26 17:38:02 +02:00
for ( unsigned int i = 0 ; i < data_bytes / 2 ; i + + ) {
2018-07-23 14:51:23 +02:00
uint16_t data_point = decode_uint16 ( & read_data [ i * 2 ] ) ;
file - > store_16 ( data_point ) ;
}
break ;
2022-07-23 11:34:36 -03:00
case AudioStreamWAV : : FORMAT_IMA_ADPCM :
2018-07-23 14:51:23 +02:00
//Unimplemented
break ;
}
2019-03-12 15:48:52 +01:00
return OK ;
2018-01-18 16:03:53 +01:00
}
2022-07-23 11:34:36 -03:00
Ref < AudioStreamPlayback > AudioStreamWAV : : instantiate_playback ( ) {
Ref < AudioStreamPlaybackWAV > sample ;
2021-06-17 16:03:09 -06:00
sample . instantiate ( ) ;
2022-07-23 11:34:36 -03:00
sample - > base = Ref < AudioStreamWAV > ( this ) ;
2024-04-16 20:33:29 -03:00
if ( format = = AudioStreamWAV : : FORMAT_QOA ) {
2024-08-30 10:00:26 -03:00
uint32_t ffp = qoa_decode_header ( data . ptr ( ) + DATA_PAD , data_bytes , & sample - > qoa . desc ) ;
2024-05-08 11:40:19 -03:00
ERR_FAIL_COND_V ( ffp ! = 8 , Ref < AudioStreamPlaybackWAV > ( ) ) ;
2024-08-13 00:34:40 -03:00
sample - > qoa . frame_len = qoa_max_frame_size ( & sample - > qoa . desc ) ;
int samples_len = ( sample - > qoa . desc . samples > QOA_FRAME_LEN ? QOA_FRAME_LEN : sample - > qoa . desc . samples ) ;
2024-08-30 10:00:26 -03:00
int dec_len = sample - > qoa . desc . channels * samples_len ;
sample - > qoa . dec . resize ( dec_len ) ;
2024-04-16 20:33:29 -03:00
}
2017-02-02 22:51:26 -03:00
return sample ;
}
2022-07-23 11:34:36 -03:00
String AudioStreamWAV : : get_stream_name ( ) const {
2017-02-02 22:51:26 -03:00
return " " ;
}
2024-04-18 10:50:34 -04:00
Ref < AudioSample > AudioStreamWAV : : generate_sample ( ) const {
Ref < AudioSample > sample ;
sample . instantiate ( ) ;
sample - > stream = this ;
switch ( loop_mode ) {
case AudioStreamWAV : : LoopMode : : LOOP_DISABLED : {
sample - > loop_mode = AudioSample : : LoopMode : : LOOP_DISABLED ;
} break ;
case AudioStreamWAV : : LoopMode : : LOOP_FORWARD : {
sample - > loop_mode = AudioSample : : LoopMode : : LOOP_FORWARD ;
} break ;
case AudioStreamWAV : : LoopMode : : LOOP_PINGPONG : {
sample - > loop_mode = AudioSample : : LoopMode : : LOOP_PINGPONG ;
} break ;
case AudioStreamWAV : : LoopMode : : LOOP_BACKWARD : {
sample - > loop_mode = AudioSample : : LoopMode : : LOOP_BACKWARD ;
} break ;
}
sample - > loop_begin = loop_begin ;
sample - > loop_end = loop_end ;
sample - > sample_rate = mix_rate ;
return sample ;
}
2022-07-23 11:34:36 -03:00
void AudioStreamWAV : : _bind_methods ( ) {
2024-11-11 12:55:57 -05:00
ClassDB : : bind_static_method ( " AudioStreamWAV " , D_METHOD ( " load_from_file " , " path " , " options " ) , & AudioStreamWAV : : load_from_file , DEFVAL ( Dictionary ( ) ) ) ;
ClassDB : : bind_static_method ( " AudioStreamWAV " , D_METHOD ( " load_from_buffer " , " buffer " , " options " ) , & AudioStreamWAV : : load_from_buffer , DEFVAL ( Dictionary ( ) ) ) ;
2022-07-23 11:34:36 -03:00
ClassDB : : bind_method ( D_METHOD ( " set_data " , " data " ) , & AudioStreamWAV : : set_data ) ;
ClassDB : : bind_method ( D_METHOD ( " get_data " ) , & AudioStreamWAV : : get_data ) ;
2018-04-25 21:04:03 +02:00
2022-07-23 11:34:36 -03:00
ClassDB : : bind_method ( D_METHOD ( " set_format " , " format " ) , & AudioStreamWAV : : set_format ) ;
ClassDB : : bind_method ( D_METHOD ( " get_format " ) , & AudioStreamWAV : : get_format ) ;
2017-02-02 22:51:26 -03:00
2022-07-23 11:34:36 -03:00
ClassDB : : bind_method ( D_METHOD ( " set_loop_mode " , " loop_mode " ) , & AudioStreamWAV : : set_loop_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_loop_mode " ) , & AudioStreamWAV : : get_loop_mode ) ;
2017-02-02 22:51:26 -03:00
2022-07-23 11:34:36 -03:00
ClassDB : : bind_method ( D_METHOD ( " set_loop_begin " , " loop_begin " ) , & AudioStreamWAV : : set_loop_begin ) ;
ClassDB : : bind_method ( D_METHOD ( " get_loop_begin " ) , & AudioStreamWAV : : get_loop_begin ) ;
2017-02-02 22:51:26 -03:00
2022-07-23 11:34:36 -03:00
ClassDB : : bind_method ( D_METHOD ( " set_loop_end " , " loop_end " ) , & AudioStreamWAV : : set_loop_end ) ;
ClassDB : : bind_method ( D_METHOD ( " get_loop_end " ) , & AudioStreamWAV : : get_loop_end ) ;
2017-02-02 22:51:26 -03:00
2022-07-23 11:34:36 -03:00
ClassDB : : bind_method ( D_METHOD ( " set_mix_rate " , " mix_rate " ) , & AudioStreamWAV : : set_mix_rate ) ;
ClassDB : : bind_method ( D_METHOD ( " get_mix_rate " ) , & AudioStreamWAV : : get_mix_rate ) ;
2017-02-02 22:51:26 -03:00
2022-07-23 11:34:36 -03:00
ClassDB : : bind_method ( D_METHOD ( " set_stereo " , " stereo " ) , & AudioStreamWAV : : set_stereo ) ;
ClassDB : : bind_method ( D_METHOD ( " is_stereo " ) , & AudioStreamWAV : : is_stereo ) ;
2017-02-02 22:51:26 -03:00
2022-07-23 11:34:36 -03:00
ClassDB : : bind_method ( D_METHOD ( " save_to_wav " , " path " ) , & AudioStreamWAV : : save_to_wav ) ;
2018-07-23 14:51:23 +02:00
2021-11-03 23:06:17 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : PACKED_BYTE_ARRAY , " data " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR ) , " set_data " , " get_data " ) ;
2024-08-27 12:56:25 -03:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " format " , PROPERTY_HINT_ENUM , " 8-Bit,16-Bit,IMA ADPCM,Quite OK Audio " ) , " set_format " , " get_format " ) ;
2018-10-28 19:37:14 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " loop_mode " , PROPERTY_HINT_ENUM , " Disabled,Forward,Ping-Pong,Backward " ) , " set_loop_mode " , " get_loop_mode " ) ;
2017-02-12 01:11:37 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " loop_begin " ) , " set_loop_begin " , " get_loop_begin " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " loop_end " ) , " set_loop_end " , " get_loop_end " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " mix_rate " ) , " set_mix_rate " , " get_mix_rate " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " stereo " ) , " set_stereo " , " is_stereo " ) ;
2017-09-12 22:09:06 +03:00
BIND_ENUM_CONSTANT ( FORMAT_8_BITS ) ;
BIND_ENUM_CONSTANT ( FORMAT_16_BITS ) ;
BIND_ENUM_CONSTANT ( FORMAT_IMA_ADPCM ) ;
2024-04-16 20:33:29 -03:00
BIND_ENUM_CONSTANT ( FORMAT_QOA ) ;
2017-09-12 22:09:06 +03:00
BIND_ENUM_CONSTANT ( LOOP_DISABLED ) ;
BIND_ENUM_CONSTANT ( LOOP_FORWARD ) ;
2021-10-15 22:25:00 +09:00
BIND_ENUM_CONSTANT ( LOOP_PINGPONG ) ;
2018-10-28 19:37:14 +01:00
BIND_ENUM_CONSTANT ( LOOP_BACKWARD ) ;
2017-02-02 22:51:26 -03:00
}
2024-11-11 12:55:57 -05:00
Ref < AudioStreamWAV > AudioStreamWAV : : load_from_buffer ( const Vector < uint8_t > & p_file_data , const Dictionary & p_options ) {
// /* STEP 1, READ WAVE FILE */
Ref < FileAccessMemory > file ;
file . instantiate ( ) ;
Error err = file - > open_custom ( p_file_data . ptr ( ) , p_file_data . size ( ) ) ;
ERR_FAIL_COND_V_MSG ( err ! = OK , Ref < AudioStreamWAV > ( ) , " Cannot create memfile for WAV file buffer. " ) ;
/* CHECK RIFF */
char riff [ 5 ] ;
riff [ 4 ] = 0 ;
file - > get_buffer ( ( uint8_t * ) & riff , 4 ) ; //RIFF
if ( riff [ 0 ] ! = ' R ' | | riff [ 1 ] ! = ' I ' | | riff [ 2 ] ! = ' F ' | | riff [ 3 ] ! = ' F ' ) {
ERR_FAIL_V_MSG ( Ref < AudioStreamWAV > ( ) , vformat ( " Not a WAV file. File should start with 'RIFF', but found '%s', in file of size %d bytes " , riff , file - > get_length ( ) ) ) ;
}
/* GET FILESIZE */
// The file size in header is 8 bytes less than the actual size.
// See https://docs.fileformat.com/audio/wav/
const int FILE_SIZE_HEADER_OFFSET = 8 ;
uint32_t file_size_header = file - > get_32 ( ) + FILE_SIZE_HEADER_OFFSET ;
uint64_t file_size = file - > get_length ( ) ;
if ( file_size ! = file_size_header ) {
WARN_PRINT ( vformat ( " File size %d is %s than the expected size %d. " , file_size , file_size > file_size_header ? " larger " : " smaller " , file_size_header ) ) ;
}
/* CHECK WAVE */
char wave [ 5 ] ;
wave [ 4 ] = 0 ;
file - > get_buffer ( ( uint8_t * ) & wave , 4 ) ; //WAVE
if ( wave [ 0 ] ! = ' W ' | | wave [ 1 ] ! = ' A ' | | wave [ 2 ] ! = ' V ' | | wave [ 3 ] ! = ' E ' ) {
ERR_FAIL_V_MSG ( Ref < AudioStreamWAV > ( ) , vformat ( " Not a WAV file. Header should contain 'WAVE', but found '%s', in file of size %d bytes " , wave , file - > get_length ( ) ) ) ;
}
// Let users override potential loop points from the WAV.
// We parse the WAV loop points only with "Detect From WAV" (0).
int import_loop_mode = p_options [ " edit/loop_mode " ] ;
int format_bits = 0 ;
int format_channels = 0 ;
AudioStreamWAV : : LoopMode loop_mode = AudioStreamWAV : : LOOP_DISABLED ;
uint16_t compression_code = 1 ;
bool format_found = false ;
bool data_found = false ;
int format_freq = 0 ;
int loop_begin = 0 ;
int loop_end = 0 ;
int frames = 0 ;
Vector < float > data ;
while ( ! file - > eof_reached ( ) ) {
/* chunk */
char chunk_id [ 4 ] ;
file - > get_buffer ( ( uint8_t * ) & chunk_id , 4 ) ; //RIFF
/* chunk size */
uint32_t chunksize = file - > get_32 ( ) ;
uint32_t file_pos = file - > get_position ( ) ; //save file pos, so we can skip to next chunk safely
if ( file - > eof_reached ( ) ) {
//ERR_PRINT("EOF REACH");
break ;
}
if ( chunk_id [ 0 ] = = ' f ' & & chunk_id [ 1 ] = = ' m ' & & chunk_id [ 2 ] = = ' t ' & & chunk_id [ 3 ] = = ' ' & & ! format_found ) {
/* IS FORMAT CHUNK */
//Issue: #7755 : Not a bug - usage of other formats (format codes) are unsupported in current importer version.
//Consider revision for engine version 3.0
compression_code = file - > get_16 ( ) ;
if ( compression_code ! = 1 & & compression_code ! = 3 ) {
ERR_FAIL_V_MSG ( Ref < AudioStreamWAV > ( ) , " Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM or IEEE float instead. " ) ;
}
format_channels = file - > get_16 ( ) ;
if ( format_channels ! = 1 & & format_channels ! = 2 ) {
ERR_FAIL_V_MSG ( Ref < AudioStreamWAV > ( ) , " Format not supported for WAVE file (not stereo or mono). " ) ;
}
format_freq = file - > get_32 ( ) ; //sampling rate
file - > get_32 ( ) ; // average bits/second (unused)
file - > get_16 ( ) ; // block align (unused)
format_bits = file - > get_16 ( ) ; // bits per sample
if ( format_bits % 8 | | format_bits = = 0 ) {
ERR_FAIL_V_MSG ( Ref < AudioStreamWAV > ( ) , " Invalid amount of bits in the sample (should be one of 8, 16, 24 or 32). " ) ;
}
if ( compression_code = = 3 & & format_bits % 32 ) {
ERR_FAIL_V_MSG ( Ref < AudioStreamWAV > ( ) , " Invalid amount of bits in the IEEE float sample (should be 32 or 64). " ) ;
}
/* Don't need anything else, continue */
format_found = true ;
}
if ( chunk_id [ 0 ] = = ' d ' & & chunk_id [ 1 ] = = ' a ' & & chunk_id [ 2 ] = = ' t ' & & chunk_id [ 3 ] = = ' a ' & & ! data_found ) {
/* IS DATA CHUNK */
data_found = true ;
if ( ! format_found ) {
ERR_PRINT ( " 'data' chunk before 'format' chunk found. " ) ;
break ;
}
uint64_t remaining_bytes = file_size - file_pos ;
frames = chunksize ;
if ( remaining_bytes < chunksize ) {
WARN_PRINT ( " Data chunk size is smaller than expected. Proceeding with actual data size. " ) ;
frames = remaining_bytes ;
}
ERR_FAIL_COND_V ( format_channels = = 0 , Ref < AudioStreamWAV > ( ) ) ;
frames / = format_channels ;
frames / = ( format_bits > > 3 ) ;
/*print_line("chunksize: "+itos(chunksize));
print_line ( " channels: " + itos ( format_channels ) ) ;
print_line ( " bits: " + itos ( format_bits ) ) ;
*/
data . resize ( frames * format_channels ) ;
if ( compression_code = = 1 ) {
if ( format_bits = = 8 ) {
for ( int i = 0 ; i < frames * format_channels ; i + + ) {
// 8 bit samples are UNSIGNED
data . write [ i ] = int8_t ( file - > get_8 ( ) - 128 ) / 128.f ;
}
} else if ( format_bits = = 16 ) {
for ( int i = 0 ; i < frames * format_channels ; i + + ) {
//16 bit SIGNED
data . write [ i ] = int16_t ( file - > get_16 ( ) ) / 32768.f ;
}
} else {
for ( int i = 0 ; i < frames * format_channels ; i + + ) {
//16+ bits samples are SIGNED
// if sample is > 16 bits, just read extra bytes
uint32_t s = 0 ;
for ( int b = 0 ; b < ( format_bits > > 3 ) ; b + + ) {
s | = ( ( uint32_t ) file - > get_8 ( ) ) < < ( b * 8 ) ;
}
s < < = ( 32 - format_bits ) ;
data . write [ i ] = ( int32_t ( s ) > > 16 ) / 32768.f ;
}
}
} else if ( compression_code = = 3 ) {
if ( format_bits = = 32 ) {
for ( int i = 0 ; i < frames * format_channels ; i + + ) {
//32 bit IEEE Float
data . write [ i ] = file - > get_float ( ) ;
}
} else if ( format_bits = = 64 ) {
for ( int i = 0 ; i < frames * format_channels ; i + + ) {
//64 bit IEEE Float
data . write [ i ] = file - > get_double ( ) ;
}
}
}
// This is commented out due to some weird edge case seemingly in FileAccessMemory, doesn't seem to have any side effects though.
// if (file->eof_reached()) {
// ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Premature end of file.");
// }
}
if ( import_loop_mode = = 0 & & chunk_id [ 0 ] = = ' s ' & & chunk_id [ 1 ] = = ' m ' & & chunk_id [ 2 ] = = ' p ' & & chunk_id [ 3 ] = = ' l ' ) {
// Loop point info!
/**
* Consider exploring next document :
* http : //www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/RIFFNEW.pdf
* Especially on page :
* 16 - 17
* Timestamp :
* 22 : 38 06.07 .2017 GMT
* */
for ( int i = 0 ; i < 10 ; i + + ) {
file - > get_32 ( ) ; // i wish to know why should i do this... no doc!
}
// only read 0x00 (loop forward), 0x01 (loop ping-pong) and 0x02 (loop backward)
// Skip anything else because it's not supported, reserved for future uses or sampler specific
// from https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl (loop type values table)
int loop_type = file - > get_32 ( ) ;
if ( loop_type = = 0x00 | | loop_type = = 0x01 | | loop_type = = 0x02 ) {
if ( loop_type = = 0x00 ) {
loop_mode = AudioStreamWAV : : LOOP_FORWARD ;
} else if ( loop_type = = 0x01 ) {
loop_mode = AudioStreamWAV : : LOOP_PINGPONG ;
} else if ( loop_type = = 0x02 ) {
loop_mode = AudioStreamWAV : : LOOP_BACKWARD ;
}
loop_begin = file - > get_32 ( ) ;
loop_end = file - > get_32 ( ) ;
}
}
// Move to the start of the next chunk. Note that RIFF requires a padding byte for odd
// chunk sizes.
file - > seek ( file_pos + chunksize + ( chunksize & 1 ) ) ;
}
// STEP 2, APPLY CONVERSIONS
bool is16 = format_bits ! = 8 ;
int rate = format_freq ;
/*
print_line ( " Input Sample: " ) ;
print_line ( " \t frames: " + itos ( frames ) ) ;
print_line ( " \t format_channels: " + itos ( format_channels ) ) ;
print_line ( " \t 16bits: " + itos ( is16 ) ) ;
print_line ( " \t rate: " + itos ( rate ) ) ;
print_line ( " \t loop: " + itos ( loop ) ) ;
print_line ( " \t loop begin: " + itos ( loop_begin ) ) ;
print_line ( " \t loop end: " + itos ( loop_end ) ) ;
*/
//apply frequency limit
bool limit_rate = p_options [ " force/max_rate " ] ;
int limit_rate_hz = p_options [ " force/max_rate_hz " ] ;
if ( limit_rate & & rate > limit_rate_hz & & rate > 0 & & frames > 0 ) {
// resample!
int new_data_frames = ( int ) ( frames * ( float ) limit_rate_hz / ( float ) rate ) ;
Vector < float > new_data ;
new_data . resize ( new_data_frames * format_channels ) ;
for ( int c = 0 ; c < format_channels ; c + + ) {
float frac = 0.0 ;
int ipos = 0 ;
for ( int i = 0 ; i < new_data_frames ; i + + ) {
// Cubic interpolation should be enough.
float y0 = data [ MAX ( 0 , ipos - 1 ) * format_channels + c ] ;
float y1 = data [ ipos * format_channels + c ] ;
float y2 = data [ MIN ( frames - 1 , ipos + 1 ) * format_channels + c ] ;
float y3 = data [ MIN ( frames - 1 , ipos + 2 ) * format_channels + c ] ;
new_data . write [ i * format_channels + c ] = Math : : cubic_interpolate ( y1 , y2 , y0 , y3 , frac ) ;
// update position and always keep fractional part within ]0...1]
// in order to avoid 32bit floating point precision errors
frac + = ( float ) rate / ( float ) limit_rate_hz ;
int tpos = ( int ) Math : : floor ( frac ) ;
ipos + = tpos ;
frac - = tpos ;
}
}
if ( loop_mode ) {
loop_begin = ( int ) ( loop_begin * ( float ) new_data_frames / ( float ) frames ) ;
loop_end = ( int ) ( loop_end * ( float ) new_data_frames / ( float ) frames ) ;
}
data = new_data ;
rate = limit_rate_hz ;
frames = new_data_frames ;
}
bool normalize = p_options [ " edit/normalize " ] ;
if ( normalize ) {
float max = 0.0 ;
for ( int i = 0 ; i < data . size ( ) ; i + + ) {
float amp = Math : : abs ( data [ i ] ) ;
if ( amp > max ) {
max = amp ;
}
}
if ( max > 0 ) {
float mult = 1.0 / max ;
for ( int i = 0 ; i < data . size ( ) ; i + + ) {
data . write [ i ] * = mult ;
}
}
}
bool trim = p_options [ " edit/trim " ] ;
if ( trim & & ( loop_mode = = AudioStreamWAV : : LOOP_DISABLED ) & & format_channels > 0 ) {
int first = 0 ;
int last = ( frames / format_channels ) - 1 ;
bool found = false ;
float limit = Math : : db_to_linear ( TRIM_DB_LIMIT ) ;
for ( int i = 0 ; i < data . size ( ) / format_channels ; i + + ) {
float amp_channel_sum = 0.0 ;
for ( int j = 0 ; j < format_channels ; j + + ) {
amp_channel_sum + = Math : : abs ( data [ ( i * format_channels ) + j ] ) ;
}
float amp = Math : : abs ( amp_channel_sum / ( float ) format_channels ) ;
if ( ! found & & amp > limit ) {
first = i ;
found = true ;
}
if ( found & & amp > limit ) {
last = i ;
}
}
if ( first < last ) {
Vector < float > new_data ;
new_data . resize ( ( last - first ) * format_channels ) ;
for ( int i = first ; i < last ; i + + ) {
float fade_out_mult = 1.0 ;
if ( last - i < TRIM_FADE_OUT_FRAMES ) {
fade_out_mult = ( ( float ) ( last - i - 1 ) / ( float ) TRIM_FADE_OUT_FRAMES ) ;
}
for ( int j = 0 ; j < format_channels ; j + + ) {
new_data . write [ ( ( i - first ) * format_channels ) + j ] = data [ ( i * format_channels ) + j ] * fade_out_mult ;
}
}
data = new_data ;
frames = data . size ( ) / format_channels ;
}
}
if ( import_loop_mode > = 2 ) {
loop_mode = ( AudioStreamWAV : : LoopMode ) ( import_loop_mode - 1 ) ;
loop_begin = p_options [ " edit/loop_begin " ] ;
loop_end = p_options [ " edit/loop_end " ] ;
// Wrap around to max frames, so `-1` can be used to select the end, etc.
if ( loop_begin < 0 ) {
loop_begin = CLAMP ( loop_begin + frames , 0 , frames - 1 ) ;
}
if ( loop_end < 0 ) {
loop_end = CLAMP ( loop_end + frames , 0 , frames - 1 ) ;
}
}
int compression = p_options [ " compress/mode " ] ;
bool force_mono = p_options [ " force/mono " ] ;
if ( force_mono & & format_channels = = 2 ) {
Vector < float > new_data ;
new_data . resize ( data . size ( ) / 2 ) ;
for ( int i = 0 ; i < frames ; i + + ) {
new_data . write [ i ] = ( data [ i * 2 + 0 ] + data [ i * 2 + 1 ] ) / 2.0 ;
}
data = new_data ;
format_channels = 1 ;
}
bool force_8_bit = p_options [ " force/8_bit " ] ;
if ( force_8_bit ) {
is16 = false ;
}
Vector < uint8_t > pcm_data ;
AudioStreamWAV : : Format dst_format ;
if ( compression = = 1 ) {
dst_format = AudioStreamWAV : : FORMAT_IMA_ADPCM ;
if ( format_channels = = 1 ) {
_compress_ima_adpcm ( data , pcm_data ) ;
} else {
//byte interleave
Vector < float > left ;
Vector < float > right ;
int tframes = data . size ( ) / 2 ;
left . resize ( tframes ) ;
right . resize ( tframes ) ;
for ( int i = 0 ; i < tframes ; i + + ) {
left . write [ i ] = data [ i * 2 + 0 ] ;
right . write [ i ] = data [ i * 2 + 1 ] ;
}
Vector < uint8_t > bleft ;
Vector < uint8_t > bright ;
_compress_ima_adpcm ( left , bleft ) ;
_compress_ima_adpcm ( right , bright ) ;
int dl = bleft . size ( ) ;
pcm_data . resize ( dl * 2 ) ;
uint8_t * w = pcm_data . ptrw ( ) ;
const uint8_t * rl = bleft . ptr ( ) ;
const uint8_t * rr = bright . ptr ( ) ;
for ( int i = 0 ; i < dl ; i + + ) {
w [ i * 2 + 0 ] = rl [ i ] ;
w [ i * 2 + 1 ] = rr [ i ] ;
}
}
} else {
dst_format = is16 ? AudioStreamWAV : : FORMAT_16_BITS : AudioStreamWAV : : FORMAT_8_BITS ;
bool enforce16 = is16 | | compression = = 2 ;
pcm_data . resize ( data . size ( ) * ( enforce16 ? 2 : 1 ) ) ;
{
uint8_t * w = pcm_data . ptrw ( ) ;
int ds = data . size ( ) ;
for ( int i = 0 ; i < ds ; i + + ) {
if ( enforce16 ) {
int16_t v = CLAMP ( data [ i ] * 32768 , - 32768 , 32767 ) ;
encode_uint16 ( v , & w [ i * 2 ] ) ;
} else {
int8_t v = CLAMP ( data [ i ] * 128 , - 128 , 127 ) ;
w [ i ] = v ;
}
}
}
}
Vector < uint8_t > dst_data ;
if ( compression = = 2 ) {
dst_format = AudioStreamWAV : : FORMAT_QOA ;
qoa_desc desc = { } ;
uint32_t qoa_len = 0 ;
desc . samplerate = rate ;
desc . samples = frames ;
desc . channels = format_channels ;
void * encoded = qoa_encode ( ( short * ) pcm_data . ptr ( ) , & desc , & qoa_len ) ;
if ( encoded ) {
dst_data . resize ( qoa_len ) ;
memcpy ( dst_data . ptrw ( ) , encoded , qoa_len ) ;
QOA_FREE ( encoded ) ;
}
} else {
dst_data = pcm_data ;
}
Ref < AudioStreamWAV > sample ;
sample . instantiate ( ) ;
sample - > set_data ( dst_data ) ;
sample - > set_format ( dst_format ) ;
sample - > set_mix_rate ( rate ) ;
sample - > set_loop_mode ( loop_mode ) ;
sample - > set_loop_begin ( loop_begin ) ;
sample - > set_loop_end ( loop_end ) ;
sample - > set_stereo ( format_channels = = 2 ) ;
return sample ;
}
Ref < AudioStreamWAV > AudioStreamWAV : : load_from_file ( const String & p_path , const Dictionary & p_options ) {
Vector < uint8_t > file_data = FileAccess : : get_file_as_bytes ( p_path ) ;
ERR_FAIL_COND_V_MSG ( file_data . is_empty ( ) , Ref < AudioStreamWAV > ( ) , vformat ( " Cannot open file '%s'. " , p_path ) ) ;
return load_from_buffer ( file_data , p_options ) ;
}
2022-07-23 11:34:36 -03:00
AudioStreamWAV : : AudioStreamWAV ( ) { }
2017-02-02 22:51:26 -03:00
2024-08-30 10:00:26 -03:00
AudioStreamWAV : : ~ AudioStreamWAV ( ) { }