I2S: add command i2sloop (#22807)

* revert upstream change of the library, fix memory leak by using stack again

* add i2sloop command
This commit is contained in:
Christian Baars 2025-01-12 17:16:20 +01:00 committed by GitHub
parent cf739e9522
commit 03e8497211
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 144 additions and 154 deletions

View File

@ -482,7 +482,7 @@ static int celt_plc_pitch_search(celt_sig *decode_mem[2], int C, int arch)
int pitch_index; int pitch_index;
VARDECL( opus_val16, lp_pitch_buf ); VARDECL( opus_val16, lp_pitch_buf );
SAVE_STACK; SAVE_STACK;
opus_val16 *lp_pitch_buf = (opus_val16*)malloc((DECODE_BUFFER_SIZE>>1) * sizeof(opus_val16)); //ALLOC( lp_pitch_buf, DECODE_BUFFER_SIZE>>1, opus_val16 ); ALLOC( lp_pitch_buf, DECODE_BUFFER_SIZE>>1, opus_val16 );
pitch_downsample(decode_mem, lp_pitch_buf, pitch_downsample(decode_mem, lp_pitch_buf,
DECODE_BUFFER_SIZE, C, arch); DECODE_BUFFER_SIZE, C, arch);
pitch_search(lp_pitch_buf+(PLC_PITCH_LAG_MAX>>1), lp_pitch_buf, pitch_search(lp_pitch_buf+(PLC_PITCH_LAG_MAX>>1), lp_pitch_buf,
@ -490,7 +490,6 @@ static int celt_plc_pitch_search(celt_sig *decode_mem[2], int C, int arch)
PLC_PITCH_LAG_MAX-PLC_PITCH_LAG_MIN, &pitch_index, arch); PLC_PITCH_LAG_MAX-PLC_PITCH_LAG_MIN, &pitch_index, arch);
pitch_index = PLC_PITCH_LAG_MAX-pitch_index; pitch_index = PLC_PITCH_LAG_MAX-pitch_index;
RESTORE_STACK; RESTORE_STACK;
free(lp_pitch_buf);
return pitch_index; return pitch_index;
} }

View File

@ -207,4 +207,3 @@
# define __restrict__ # define __restrict__
#endif #endif
#include <stdlib.h>

View File

@ -239,30 +239,21 @@ opus_int32 opus_repacketizer_out(OpusRepacketizer *rp, unsigned char *data, opus
int opus_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len) int opus_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len)
{ {
OpusRepacketizer *rp = (OpusRepacketizer*)malloc(sizeof(OpusRepacketizer)); OpusRepacketizer rp;
opus_int32 ret; opus_int32 ret;
if (len < 1) { if (len < 1)
free(rp);
return OPUS_BAD_ARG; return OPUS_BAD_ARG;
} if (len==new_len)
if (len==new_len) {
free(rp);
return OPUS_OK; return OPUS_OK;
} else if (len > new_len)
else if (len > new_len) {
free(rp);
return OPUS_BAD_ARG; return OPUS_BAD_ARG;
} opus_repacketizer_init(&rp);
opus_repacketizer_init(rp);
/* Moving payload to the end of the packet so we can do in-place padding */ /* Moving payload to the end of the packet so we can do in-place padding */
OPUS_MOVE(data+new_len-len, data, len); OPUS_MOVE(data+new_len-len, data, len);
ret = opus_repacketizer_cat(rp, data+new_len-len, len); ret = opus_repacketizer_cat(&rp, data+new_len-len, len);
if (ret != OPUS_OK) { if (ret != OPUS_OK)
free(rp);
return ret; return ret;
} ret = opus_repacketizer_out_range_impl(&rp, 0, rp.nb_frames, data, new_len, 0, 1);
ret = opus_repacketizer_out_range_impl(rp, 0, rp->nb_frames, data, new_len, 0, 1);
free(rp);
if (ret > 0) if (ret > 0)
return OPUS_OK; return OPUS_OK;
else else
@ -271,20 +262,15 @@ int opus_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len)
opus_int32 opus_packet_unpad(unsigned char *data, opus_int32 len) opus_int32 opus_packet_unpad(unsigned char *data, opus_int32 len)
{ {
OpusRepacketizer *rp = (OpusRepacketizer*)malloc(sizeof(OpusRepacketizer)); OpusRepacketizer rp;
opus_int32 ret; opus_int32 ret;
if (len < 1) { if (len < 1)
free(rp);
return OPUS_BAD_ARG; return OPUS_BAD_ARG;
} opus_repacketizer_init(&rp);
opus_repacketizer_init(rp); ret = opus_repacketizer_cat(&rp, data, len);
ret = opus_repacketizer_cat(rp, data, len); if (ret < 0)
if (ret < 0) {
free(rp);
return ret; return ret;
} ret = opus_repacketizer_out_range_impl(&rp, 0, rp.nb_frames, data, len, 0, 0);
ret = opus_repacketizer_out_range_impl(rp, 0, rp->nb_frames, data, len, 0, 0);
free(rp);
celt_assert(ret > 0 && ret <= len); celt_assert(ret > 0 && ret <= len);
return ret; return ret;
} }
@ -326,14 +312,12 @@ opus_int32 opus_multistream_packet_unpad(unsigned char *data, opus_int32 len, in
unsigned char toc; unsigned char toc;
opus_int16 size[48]; opus_int16 size[48];
opus_int32 packet_offset; opus_int32 packet_offset;
OpusRepacketizer *rp = (OpusRepacketizer*)malloc(sizeof(OpusRepacketizer)); OpusRepacketizer rp;
unsigned char *dst; unsigned char *dst;
opus_int32 dst_len; opus_int32 dst_len;
if (len < 1){ if (len < 1)
free(rp);
return OPUS_BAD_ARG; return OPUS_BAD_ARG;
}
dst = data; dst = data;
dst_len = 0; dst_len = 0;
/* Unpad all frames */ /* Unpad all frames */
@ -341,34 +325,25 @@ opus_int32 opus_multistream_packet_unpad(unsigned char *data, opus_int32 len, in
{ {
opus_int32 ret; opus_int32 ret;
int self_delimited = s!=nb_streams-1; int self_delimited = s!=nb_streams-1;
if (len<=0) { if (len<=0)
free(rp);
return OPUS_INVALID_PACKET; return OPUS_INVALID_PACKET;
} opus_repacketizer_init(&rp);
opus_repacketizer_init(rp);
ret = opus_packet_parse_impl(data, len, self_delimited, &toc, NULL, ret = opus_packet_parse_impl(data, len, self_delimited, &toc, NULL,
size, NULL, &packet_offset); size, NULL, &packet_offset);
if (ret<0) { if (ret<0)
free(rp);
return ret; return ret;
} ret = opus_repacketizer_cat_impl(&rp, data, packet_offset, self_delimited);
ret = opus_repacketizer_cat_impl(rp, data, packet_offset, self_delimited); if (ret < 0)
if (ret < 0) {
free(rp);
return ret; return ret;
} ret = opus_repacketizer_out_range_impl(&rp, 0, rp.nb_frames, dst, len, self_delimited, 0);
ret = opus_repacketizer_out_range_impl(rp, 0, rp->nb_frames, dst, len, self_delimited, 0); if (ret < 0)
if (ret < 0) {
free(rp);
return ret; return ret;
}
else else
dst_len += ret; dst_len += ret;
dst += ret; dst += ret;
data += packet_offset; data += packet_offset;
len -= packet_offset; len -= packet_offset;
} }
free(rp);
return dst_len; return dst_len;
} }

View File

@ -80,11 +80,10 @@ void silk_NLSF2A(
}; };
const unsigned char *ordering; const unsigned char *ordering;
opus_int k, i, dd; opus_int k, i, dd;
opus_int32 *cos_LSF_QA = (opus_int32*)malloc(sizeof(opus_int32) * SILK_MAX_ORDER_LPC ); opus_int32 cos_LSF_QA[ SILK_MAX_ORDER_LPC ];
opus_int32 *P = (opus_int32*)malloc(sizeof(opus_int32) * (SILK_MAX_ORDER_LPC / 2 + 1)); opus_int32 P[ SILK_MAX_ORDER_LPC / 2 + 1 ], Q[ SILK_MAX_ORDER_LPC / 2 + 1 ];
opus_int32 *Q= (opus_int32*)malloc(sizeof(opus_int32) * (SILK_MAX_ORDER_LPC / 2 + 1));
opus_int32 Ptmp, Qtmp, f_int, f_frac, cos_val, delta; opus_int32 Ptmp, Qtmp, f_int, f_frac, cos_val, delta;
opus_int32 *a32_QA1 = (opus_int32*)malloc(sizeof(opus_int32) * SILK_MAX_ORDER_LPC ); opus_int32 a32_QA1[ SILK_MAX_ORDER_LPC ];
silk_assert( LSF_COS_TAB_SZ_FIX == 128 ); silk_assert( LSF_COS_TAB_SZ_FIX == 128 );
celt_assert( d==10 || d==16 ); celt_assert( d==10 || d==16 );
@ -138,9 +137,5 @@ void silk_NLSF2A(
a_Q12[ k ] = (opus_int16)silk_RSHIFT_ROUND( a32_QA1[ k ], QA + 1 - 12 ); /* QA+1 -> Q12 */ a_Q12[ k ] = (opus_int16)silk_RSHIFT_ROUND( a32_QA1[ k ], QA + 1 - 12 ); /* QA+1 -> Q12 */
} }
} }
free(cos_LSF_QA);
free(P);
free(Q);
free(a32_QA1);
} }

View File

@ -57,12 +57,12 @@ void silk_burg_modified_c(
opus_int k, n, s, lz, rshifts, reached_max_gain; opus_int k, n, s, lz, rshifts, reached_max_gain;
opus_int32 C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2; opus_int32 C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2;
const opus_int16 *x_ptr; const opus_int16 *x_ptr;
opus_int32 *C_first_row = (opus_int32*)malloc(sizeof(opus_int32) * SILK_MAX_ORDER_LPC); opus_int32 C_first_row[ SILK_MAX_ORDER_LPC ];
opus_int32 *C_last_row = (opus_int32*)malloc(sizeof(opus_int32) * SILK_MAX_ORDER_LPC); opus_int32 C_last_row[ SILK_MAX_ORDER_LPC ];
opus_int32 *Af_QA = (opus_int32*)malloc(sizeof(opus_int32) * SILK_MAX_ORDER_LPC); opus_int32 Af_QA[ SILK_MAX_ORDER_LPC ];
opus_int32 *CAf = (opus_int32*)malloc(sizeof(opus_int32) * (SILK_MAX_ORDER_LPC+1)); opus_int32 CAf[ SILK_MAX_ORDER_LPC + 1 ];
opus_int32 *CAb = (opus_int32*)malloc(sizeof(opus_int32) * (SILK_MAX_ORDER_LPC+1)); opus_int32 CAb[ SILK_MAX_ORDER_LPC + 1 ];
opus_int32 *xcorr = (opus_int32*)malloc(sizeof(opus_int32) * SILK_MAX_ORDER_LPC); opus_int32 xcorr[ SILK_MAX_ORDER_LPC ];
opus_int64 C0_64; opus_int64 C0_64;
celt_assert( subfr_length * nb_subfr <= MAX_FRAME_SIZE ); celt_assert( subfr_length * nb_subfr <= MAX_FRAME_SIZE );
@ -277,10 +277,4 @@ void silk_burg_modified_c(
*res_nrg = silk_SMLAWW( nrg, silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ), -tmp1 );/* Q( -rshifts ) */ *res_nrg = silk_SMLAWW( nrg, silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ), -tmp1 );/* Q( -rshifts ) */
*res_nrg_Q = -rshifts; *res_nrg_Q = -rshifts;
} }
free(C_first_row);
free(C_last_row);
free(Af_QA);
free(CAf);
free(CAb);
free(xcorr);
} }

View File

@ -49,8 +49,8 @@ void silk_warped_autocorrelation_FIX_c(
{ {
opus_int n, i, lsh; opus_int n, i, lsh;
opus_int32 tmp1_QS, tmp2_QS; opus_int32 tmp1_QS, tmp2_QS;
opus_int32 *state_QS = (opus_int32*)calloc(MAX_SHAPE_LPC_ORDER + 1, sizeof(opus_int32)); opus_int32 state_QS[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 };
opus_int64 *corr_QC = (opus_int64*)calloc(MAX_SHAPE_LPC_ORDER + 1, sizeof(opus_int64)); opus_int64 corr_QC[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 };
/* Order must be even */ /* Order must be even */
celt_assert( ( order & 1 ) == 0 ); celt_assert( ( order & 1 ) == 0 );
@ -88,7 +88,5 @@ void silk_warped_autocorrelation_FIX_c(
} }
} }
silk_assert( corr_QC[ 0 ] >= 0 ); /* If breaking, decrease QC*/ silk_assert( corr_QC[ 0 ] >= 0 ); /* If breaking, decrease QC*/
free(state_QS);
free(corr_QC);
} }
#endif /* OVERRIDE_silk_warped_autocorrelation_FIX_c */ #endif /* OVERRIDE_silk_warped_autocorrelation_FIX_c */

View File

@ -48,8 +48,7 @@ void silk_resampler_down2_3(
opus_int32 *buf_ptr; opus_int32 *buf_ptr;
SAVE_STACK; SAVE_STACK;
// ALLOC( buf, RESAMPLER_MAX_BATCH_SIZE_IN + ORDER_FIR, opus_int32 ); ALLOC( buf, RESAMPLER_MAX_BATCH_SIZE_IN + ORDER_FIR, opus_int32 );
opus_int32 *buf = (opus_int32*)malloc((RESAMPLER_MAX_BATCH_SIZE_IN + ORDER_FIR) * sizeof(opus_int32));
/* Copy buffered samples to start of buffer */ /* Copy buffered samples to start of buffer */
silk_memcpy( buf, S, ORDER_FIR * sizeof( opus_int32 ) ); silk_memcpy( buf, S, ORDER_FIR * sizeof( opus_int32 ) );
@ -100,6 +99,5 @@ void silk_resampler_down2_3(
/* Copy last part of filtered signal to the state for the next call */ /* Copy last part of filtered signal to the state for the next call */
silk_memcpy( S, &buf[ nSamplesIn ], ORDER_FIR * sizeof( opus_int32 ) ); silk_memcpy( S, &buf[ nSamplesIn ], ORDER_FIR * sizeof( opus_int32 ) );
free(buf);
RESTORE_STACK; RESTORE_STACK;
} }

View File

@ -90,12 +90,12 @@ int op_test(OpusHead *_head,
ogg_sync_init(&oy); ogg_sync_init(&oy);
data=ogg_sync_buffer(&oy,(long)_initial_bytes); data=ogg_sync_buffer(&oy,(long)_initial_bytes);
if(data!=NULL){ if(data!=NULL){
ogg_stream_state *os = (ogg_stream_state*)malloc(sizeof(ogg_stream_state)); ogg_stream_state os;
ogg_page og; ogg_page og;
int ret; int ret;
memcpy(data,_initial_data,_initial_bytes); memcpy(data,_initial_data,_initial_bytes);
ogg_sync_wrote(&oy,(long)_initial_bytes); ogg_sync_wrote(&oy,(long)_initial_bytes);
ogg_stream_init(os,-1); ogg_stream_init(&os,-1);
err=OP_FALSE; err=OP_FALSE;
do{ do{
ogg_packet op; ogg_packet op;
@ -104,11 +104,11 @@ int op_test(OpusHead *_head,
if(ret<0)continue; if(ret<0)continue;
/*Stop if we run out of data.*/ /*Stop if we run out of data.*/
if(!ret)break; if(!ret)break;
ogg_stream_reset_serialno(os,ogg_page_serialno(&og)); ogg_stream_reset_serialno(&os,ogg_page_serialno(&og));
ogg_stream_pagein(os,&og); ogg_stream_pagein(&os,&og);
/*Only process the first packet on this page (if it's a BOS packet, /*Only process the first packet on this page (if it's a BOS packet,
it's required to be the only one).*/ it's required to be the only one).*/
if(ogg_stream_packetout(os,&op)==1){ if(ogg_stream_packetout(&os,&op)==1){
if(op.b_o_s){ if(op.b_o_s){
ret=opus_head_parse(_head,op.packet,op.bytes); ret=opus_head_parse(_head,op.packet,op.bytes);
/*If this didn't look like Opus, keep going.*/ /*If this didn't look like Opus, keep going.*/
@ -122,8 +122,7 @@ int op_test(OpusHead *_head,
} }
} }
while(err==OP_FALSE); while(err==OP_FALSE);
ogg_stream_clear(os); ogg_stream_clear(&os);
free(os);
} }
else err=OP_EFAULT; else err=OP_EFAULT;
ogg_sync_clear(&oy); ogg_sync_clear(&oy);
@ -836,7 +835,7 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of,
ogg_int64_t cur_page_gp; ogg_int64_t cur_page_gp;
ogg_uint32_t serialno; ogg_uint32_t serialno;
opus_int32 total_duration; opus_int32 total_duration;
int *durations = (int*)malloc(255 * sizeof(int)); int durations[255];
int cur_page_eos; int cur_page_eos;
int op_count; int op_count;
int pi; int pi;
@ -853,31 +852,26 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of,
Otherwise there are no audio data packets in the whole logical stream.*/ Otherwise there are no audio data packets in the whole logical stream.*/
if(OP_UNLIKELY(page_offset<0)){ if(OP_UNLIKELY(page_offset<0)){
/*Fail if there was a read error.*/ /*Fail if there was a read error.*/
if(page_offset<OP_FALSE) { free(durations); return (int)page_offset; } if(page_offset<OP_FALSE)return (int)page_offset;
/*Fail if the pre-skip is non-zero, since it's asking us to skip more /*Fail if the pre-skip is non-zero, since it's asking us to skip more
samples than exist.*/ samples than exist.*/
if(_link->head.pre_skip>0) {free(durations); return OP_EBADTIMESTAMP;} if(_link->head.pre_skip>0)return OP_EBADTIMESTAMP;
_link->pcm_file_offset=0; _link->pcm_file_offset=0;
/*Set pcm_end and end_offset so we can skip the call to /*Set pcm_end and end_offset so we can skip the call to
op_find_final_pcm_offset().*/ op_find_final_pcm_offset().*/
_link->pcm_start=_link->pcm_end=0; _link->pcm_start=_link->pcm_end=0;
_link->end_offset=_link->data_offset; _link->end_offset=_link->data_offset;
free(durations);
return 0; return 0;
} }
/*Similarly, if we hit the next link in the chain, we've gone too far.*/ /*Similarly, if we hit the next link in the chain, we've gone too far.*/
if(OP_UNLIKELY(ogg_page_bos(_og))){ if(OP_UNLIKELY(ogg_page_bos(_og))){
if(_link->head.pre_skip>0) { if(_link->head.pre_skip>0)return OP_EBADTIMESTAMP;
free(durations);
return OP_EBADTIMESTAMP;
}
/*Set pcm_end and end_offset so we can skip the call to /*Set pcm_end and end_offset so we can skip the call to
op_find_final_pcm_offset().*/ op_find_final_pcm_offset().*/
_link->pcm_file_offset=0; _link->pcm_file_offset=0;
_link->pcm_start=_link->pcm_end=0; _link->pcm_start=_link->pcm_end=0;
_link->end_offset=_link->data_offset; _link->end_offset=_link->data_offset;
/*Tell the caller we've got a buffered page for them.*/ /*Tell the caller we've got a buffered page for them.*/
free(durations);
return 1; return 1;
} }
/*Ignore pages from other streams (not strictly necessary, because of the /*Ignore pages from other streams (not strictly necessary, because of the
@ -907,10 +901,7 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of,
cur_page_gp=_of->op[op_count-1].granulepos; cur_page_gp=_of->op[op_count-1].granulepos;
/*But getting a packet without a valid granule position on the page is not /*But getting a packet without a valid granule position on the page is not
okay.*/ okay.*/
if(cur_page_gp==-1) { if(cur_page_gp==-1)return OP_EBADTIMESTAMP;
free(durations);
return OP_EBADTIMESTAMP;
}
cur_page_eos=_of->op[op_count-1].e_o_s; cur_page_eos=_of->op[op_count-1].e_o_s;
if(OP_LIKELY(!cur_page_eos)){ if(OP_LIKELY(!cur_page_eos)){
/*The EOS flag wasn't set. /*The EOS flag wasn't set.
@ -919,7 +910,6 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of,
if(OP_UNLIKELY(op_granpos_add(&pcm_start,cur_page_gp,-total_duration)<0)){ if(OP_UNLIKELY(op_granpos_add(&pcm_start,cur_page_gp,-total_duration)<0)){
/*The starting granule position MUST not be smaller than the amount of /*The starting granule position MUST not be smaller than the amount of
audio on the first page with completed packets.*/ audio on the first page with completed packets.*/
free(durations);
return OP_EBADTIMESTAMP; return OP_EBADTIMESTAMP;
} }
} }
@ -933,7 +923,6 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of,
/*However, the end-trimming MUST not ask us to trim more samples than /*However, the end-trimming MUST not ask us to trim more samples than
exist after applying the pre-skip.*/ exist after applying the pre-skip.*/
if(OP_UNLIKELY(op_granpos_cmp(cur_page_gp,_link->head.pre_skip)<0)){ if(OP_UNLIKELY(op_granpos_cmp(cur_page_gp,_link->head.pre_skip)<0)){
free(durations);
return OP_EBADTIMESTAMP; return OP_EBADTIMESTAMP;
} }
} }
@ -968,7 +957,6 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of,
_link->pcm_file_offset=0; _link->pcm_file_offset=0;
_of->prev_packet_gp=_link->pcm_start=pcm_start; _of->prev_packet_gp=_link->pcm_start=pcm_start;
_of->prev_page_offset=page_offset; _of->prev_page_offset=page_offset;
free(durations);
return 0; return 0;
} }
@ -1403,34 +1391,32 @@ static int op_open_seekable2_impl(OggOpusFile *_of){
/*64 seek records should be enough for anybody. /*64 seek records should be enough for anybody.
Actually, with a bisection search in a 63-bit range down to OP_CHUNK_SIZE Actually, with a bisection search in a 63-bit range down to OP_CHUNK_SIZE
granularity, much more than enough.*/ granularity, much more than enough.*/
OpusSeekRecord *sr = (OpusSeekRecord*)malloc(64 * sizeof(OpusSeekRecord)); OpusSeekRecord sr[64];
opus_int64 data_offset; opus_int64 data_offset;
int ret; int ret;
/*We can seek, so set out learning all about this file.*/ /*We can seek, so set out learning all about this file.*/
(*_of->callbacks.seek)(_of->stream,0,SEEK_END); (*_of->callbacks.seek)(_of->stream,0,SEEK_END);
_of->offset=_of->end=(*_of->callbacks.tell)(_of->stream); _of->offset=_of->end=(*_of->callbacks.tell)(_of->stream);
if(OP_UNLIKELY(_of->end<0)){free(sr); return OP_EREAD;} if(OP_UNLIKELY(_of->end<0))return OP_EREAD;
data_offset=_of->links[0].data_offset; data_offset=_of->links[0].data_offset;
if(OP_UNLIKELY(_of->end<data_offset)){ free(sr); return OP_EBADLINK;} if(OP_UNLIKELY(_of->end<data_offset))return OP_EBADLINK;
/*Get the offset of the last page of the physical bitstream, or, if we're /*Get the offset of the last page of the physical bitstream, or, if we're
lucky, the last Opus page of the first link, as most Ogg Opus files will lucky, the last Opus page of the first link, as most Ogg Opus files will
contain a single logical bitstream.*/ contain a single logical bitstream.*/
ret=op_get_prev_page_serial(_of,sr,_of->end, ret=op_get_prev_page_serial(_of,sr,_of->end,
_of->links[0].serialno,_of->serialnos,_of->nserialnos); _of->links[0].serialno,_of->serialnos,_of->nserialnos);
if(OP_UNLIKELY(ret<0)){free(sr); return ret;} if(OP_UNLIKELY(ret<0))return ret;
/*If there's any trailing junk, forget about it.*/ /*If there's any trailing junk, forget about it.*/
_of->end=sr[0].offset+sr[0].size; _of->end=sr[0].offset+sr[0].size;
if(OP_UNLIKELY(_of->end<data_offset)){free(sr); return OP_EBADLINK;} if(OP_UNLIKELY(_of->end<data_offset))return OP_EBADLINK;
/*Now enumerate the bitstream structure.*/ /*Now enumerate the bitstream structure.*/
ret = op_bisect_forward_serialno(_of,data_offset,sr,sizeof(sr)/sizeof(*sr), return op_bisect_forward_serialno(_of,data_offset,sr,sizeof(sr)/sizeof(*sr),
&_of->serialnos,&_of->nserialnos,&_of->cserialnos); &_of->serialnos,&_of->nserialnos,&_of->cserialnos);
free(sr);
return ret;
} }
static int op_open_seekable2(OggOpusFile *_of){ static int op_open_seekable2(OggOpusFile *_of){
ogg_sync_state oy_start; ogg_sync_state oy_start;
ogg_stream_state *os_start = (ogg_stream_state*)malloc(sizeof(ogg_stream_state)); ogg_stream_state os_start;
ogg_packet *op_start; ogg_packet *op_start;
opus_int64 prev_page_offset; opus_int64 prev_page_offset;
opus_int64 start_offset; opus_int64 start_offset;
@ -1449,9 +1435,9 @@ static int op_open_seekable2(OggOpusFile *_of){
start_op_count=_of->op_count; start_op_count=_of->op_count;
/*This is a bit too large to put on the stack unconditionally.*/ /*This is a bit too large to put on the stack unconditionally.*/
op_start=(ogg_packet *)_ogg_malloc(sizeof(*op_start)*start_op_count); op_start=(ogg_packet *)_ogg_malloc(sizeof(*op_start)*start_op_count);
if(op_start==NULL){free(os_start); return OP_EFAULT;} if(op_start==NULL)return OP_EFAULT;
*&oy_start=_of->oy; *&oy_start=_of->oy;
*os_start=_of->os; *&os_start=_of->os;
prev_page_offset=_of->prev_page_offset; prev_page_offset=_of->prev_page_offset;
start_offset=_of->offset; start_offset=_of->offset;
memcpy(op_start,_of->op,sizeof(*op_start)*start_op_count); memcpy(op_start,_of->op,sizeof(*op_start)*start_op_count);
@ -1463,7 +1449,7 @@ static int op_open_seekable2(OggOpusFile *_of){
ogg_stream_clear(&_of->os); ogg_stream_clear(&_of->os);
ogg_sync_clear(&_of->oy); ogg_sync_clear(&_of->oy);
*&_of->oy=*&oy_start; *&_of->oy=*&oy_start;
*&_of->os=*os_start; *&_of->os=*&os_start;
_of->offset=start_offset; _of->offset=start_offset;
_of->op_count=start_op_count; _of->op_count=start_op_count;
memcpy(_of->op,op_start,sizeof(*_of->op)*start_op_count); memcpy(_of->op,op_start,sizeof(*_of->op)*start_op_count);
@ -1471,10 +1457,9 @@ static int op_open_seekable2(OggOpusFile *_of){
_of->prev_packet_gp=_of->links[0].pcm_start; _of->prev_packet_gp=_of->links[0].pcm_start;
_of->prev_page_offset=prev_page_offset; _of->prev_page_offset=prev_page_offset;
_of->cur_discard_count=_of->links[0].head.pre_skip; _of->cur_discard_count=_of->links[0].head.pre_skip;
if(OP_UNLIKELY(ret<0)){free(os_start); return ret;} if(OP_UNLIKELY(ret<0))return ret;
/*And restore the position indicator.*/ /*And restore the position indicator.*/
ret=(*_of->callbacks.seek)(_of->stream,op_position(_of),SEEK_SET); ret=(*_of->callbacks.seek)(_of->stream,op_position(_of),SEEK_SET);
free(os_start);
return OP_UNLIKELY(ret<0)?OP_EREAD:0; return OP_UNLIKELY(ret<0)?OP_EREAD:0;
} }
@ -1995,7 +1980,7 @@ static int op_fetch_and_process_page(OggOpusFile *_of,
ogg_stream_pagein(&_of->os,&og); ogg_stream_pagein(&_of->os,&og);
if(OP_LIKELY(_of->ready_state>=OP_INITSET)){ if(OP_LIKELY(_of->ready_state>=OP_INITSET)){
opus_int32 total_duration; opus_int32 total_duration;
int *durations = (int*)malloc(255 * sizeof(int)); int durations[255];
int op_count; int op_count;
int report_hole; int report_hole;
report_hole=0; report_hole=0;
@ -2052,7 +2037,7 @@ static int op_fetch_and_process_page(OggOpusFile *_of,
Proceed to the next link, rather than risk playing back some Proceed to the next link, rather than risk playing back some
samples that shouldn't have been played.*/ samples that shouldn't have been played.*/
_of->op_count=0; _of->op_count=0;
if(report_hole){ free(durations); return OP_HOLE; } if(report_hole)return OP_HOLE;
continue; continue;
} }
/*By default discard 80 ms of data after a seek, unless we seek /*By default discard 80 ms of data after a seek, unless we seek
@ -2160,9 +2145,9 @@ static int op_fetch_and_process_page(OggOpusFile *_of,
_of->prev_page_offset=_page_offset; _of->prev_page_offset=_page_offset;
_of->op_count=op_count=pi; _of->op_count=op_count=pi;
} }
if(report_hole) { free(durations); return OP_HOLE; } if(report_hole)return OP_HOLE;
/*If end-trimming didn't trim all the packets, we're done.*/ /*If end-trimming didn't trim all the packets, we're done.*/
if(op_count>0) { free(durations); return 0; } if(op_count>0)return 0;
} }
} }
} }
@ -3016,7 +3001,7 @@ static const float OP_STEREO_DOWNMIX[OP_NCHANNELS_MAX-2][OP_NCHANNELS_MAX][2]={
#endif #endif
#if defined(OP_FIXED_POINT) #if defined(OP_FIXED_POINT)
#if 0
/*Matrices for downmixing from the supported channel counts to stereo. /*Matrices for downmixing from the supported channel counts to stereo.
The matrices with 5 or more channels are normalized to a total volume of 2.0, The matrices with 5 or more channels are normalized to a total volume of 2.0,
since most mixes sound too quiet if normalized to 1.0 (as there is generally since most mixes sound too quiet if normalized to 1.0 (as there is generally
@ -3025,6 +3010,7 @@ static const float OP_STEREO_DOWNMIX[OP_NCHANNELS_MAX-2][OP_NCHANNELS_MAX][2]={
32-bit number.*/ 32-bit number.*/
static const opus_int16 OP_STEREO_DOWNMIX_Q14 static const opus_int16 OP_STEREO_DOWNMIX_Q14
[OP_NCHANNELS_MAX-2][OP_NCHANNELS_MAX][2]={ [OP_NCHANNELS_MAX-2][OP_NCHANNELS_MAX][2]={
#if OP_NCHANNELS_MAX>2
/*3.0*/ /*3.0*/
{ {
{9598,0},{6786,6786},{0,9598} {9598,0},{6786,6786},{0,9598}
@ -3051,8 +3037,9 @@ static const opus_int16 OP_STEREO_DOWNMIX_Q14
{6368,0},{4502,4502},{0,6368},{5515,3183},{3183,5515},{5515,3183}, {6368,0},{4502,4502},{0,6368},{5515,3183},{3183,5515},{5515,3183},
{3183,5515},{4502,4502} {3183,5515},{4502,4502}
} }
#endif // OP_NCHANNELS_MAX>2
}; };
#endif
int op_read(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size,int *_li){ int op_read(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size,int *_li){
return op_read_native(_of,_pcm,_buf_size,_li); return op_read_native(_of,_pcm,_buf_size,_li);
} }
@ -3070,7 +3057,6 @@ static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
for(i=0;i<_nsamples;i++)dst[2*i+0]=dst[2*i+1]=_src[i]; for(i=0;i<_nsamples;i++)dst[2*i+0]=dst[2*i+1]=_src[i];
} }
else{ else{
#if 0
for(i=0;i<_nsamples;i++){ for(i=0;i<_nsamples;i++){
opus_int32 l; opus_int32 l;
opus_int32 r; opus_int32 r;
@ -3086,8 +3072,6 @@ static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
dst[2*i+0]=(opus_int16)OP_CLAMP(-32768,l+8192>>14,32767); dst[2*i+0]=(opus_int16)OP_CLAMP(-32768,l+8192>>14,32767);
dst[2*i+1]=(opus_int16)OP_CLAMP(-32768,r+8192>>14,32767); dst[2*i+1]=(opus_int16)OP_CLAMP(-32768,r+8192>>14,32767);
} }
#endif
// noop, removed for RAM savings
} }
} }
return _nsamples; return _nsamples;

View File

@ -729,7 +729,7 @@ struct OpusServerInfo{
/**The software used by the origin server (Server). /**The software used by the origin server (Server).
This is <code>NULL</code> if there was no <code>Server</code> header.*/ This is <code>NULL</code> if there was no <code>Server</code> header.*/
char *server; char *server;
/**The media type of the entity sent to the recipient (Content-Type). /**The media type of the entity sent to the recepient (Content-Type).
This is <code>NULL</code> if there was no <code>Content-Type</code> This is <code>NULL</code> if there was no <code>Content-Type</code>
header.*/ header.*/
char *content_type; char *content_type;

View File

@ -877,5 +877,36 @@ int16_t TasmotaI2S::lowpassFilter(int16_t pcm_in) {
return pcm_out; return pcm_out;
} }
#include "AudioFileSource.h"
class AudioFileSourceLoopBuffer : public AudioFileSource
{
public:
AudioFileSourceLoopBuffer(void *inBuff, uint32_t buffSizeBytes, uint32_t restartOffset = 0){
readPtr = 0;
buffer = reinterpret_cast<uint8_t*>(inBuff);
buffSize = buffSizeBytes;
restart = restartOffset;
}
virtual ~AudioFileSourceLoopBuffer() override {};
virtual uint32_t read(void *data, uint32_t len) override {
uint32_t _availableBytes = len > (buffSize - readPtr) ? buffSize - readPtr : len;
memcpy(reinterpret_cast<uint8_t*>(data), buffer + readPtr, _availableBytes);
readPtr += len;
if(readPtr >= buffSize) {readPtr = restart;}
return _availableBytes;
}
virtual bool seek(int32_t pos, int dir) override {return false;}
virtual bool close() override {return true;}
virtual bool isOpen() override {return true;}
virtual uint32_t getSize() override {return buffSize;}
virtual uint32_t getPos() override {return readPtr;}
private:
uint32_t buffSize;
uint8_t *buffer;
uint32_t readPtr;
uint32_t restart;
};
#endif // USE_I2S_AUDIO #endif // USE_I2S_AUDIO
#endif // defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5 #endif // defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5

View File

@ -79,9 +79,11 @@ void CmndI2SMP3Stream(void);
struct AUDIO_I2S_MP3_t { struct AUDIO_I2S_MP3_t {
#ifdef USE_I2S_MP3 #ifdef USE_I2S_MP3
AudioGenerator *decoder = nullptr; AudioGenerator *decoder = nullptr;
AudioFileSourceFS *file = nullptr; AudioFileSource *file = nullptr;
AudioFileSourceID3 *id3 = nullptr; AudioFileSource *id3 = nullptr;
AudioFileSource *buff = NULL;
void *preallocateBuffer = NULL;
void *preallocateCodec = NULL; void *preallocateCodec = NULL;
#endif // USE_I2S_MP3 #endif // USE_I2S_MP3
@ -96,6 +98,7 @@ struct AUDIO_I2S_MP3_t {
bool use_stream = false; bool use_stream = false;
bool task_running = false; bool task_running = false;
bool task_has_ended = false; bool task_has_ended = false;
bool task_loop_mode = false;
// SHINE // SHINE
uint32_t recdur; uint32_t recdur;
@ -124,7 +127,7 @@ struct AUDIO_I2S_MP3_t {
const char kI2SAudio_Commands[] PROGMEM = "I2S|" const char kI2SAudio_Commands[] PROGMEM = "I2S|"
"Gain|Rec|Stop|Config" "Gain|Rec|Stop|Config"
#ifdef USE_I2S_MP3 #ifdef USE_I2S_MP3
"|Play" "|Play|Loop"
#endif #endif
#ifdef USE_I2S_DEBUG #ifdef USE_I2S_DEBUG
"|Mic" // debug only "|Mic" // debug only
@ -153,6 +156,7 @@ void (* const I2SAudio_Command[])(void) PROGMEM = {
&CmndI2SGain, &CmndI2SMicRec, &CmndI2SStop, &CmndI2SConfig, &CmndI2SGain, &CmndI2SMicRec, &CmndI2SStop, &CmndI2SConfig,
#ifdef USE_I2S_MP3 #ifdef USE_I2S_MP3
&CmndI2SPlay, &CmndI2SPlay,
&CmndI2SLoop,
#endif #endif
#ifdef USE_I2S_DEBUG #ifdef USE_I2S_DEBUG
&CmndI2SMic, &CmndI2SMic,
@ -449,7 +453,7 @@ void I2sMicTask(void *arg){
} }
} }
audio_i2s_mp3.recdur = TasmotaGlobal.uptime - ctime; audio_i2s_mp3.recdur = TasmotaGlobal.uptime - ctime;
vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS(timeForOneRead)); vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS(timeForOneRead));
} }
@ -493,7 +497,7 @@ int32_t I2sRecordShine(char *path) {
esp_err_t err = ESP_OK; esp_err_t err = ESP_OK;
switch(audio_i2s.Settings->rx.sample_rate){ switch(audio_i2s.Settings->rx.sample_rate){
case 32000: case 48000: case 44100: case 32000: case 48000: case 44100:
break; // supported break; // supported
default: default:
AddLog(LOG_LEVEL_INFO, PSTR("I2S: unsupported sample rate for MP3 encoding: %d"), audio_i2s.Settings->rx.sample_rate); AddLog(LOG_LEVEL_INFO, PSTR("I2S: unsupported sample rate for MP3 encoding: %d"), audio_i2s.Settings->rx.sample_rate);
@ -796,7 +800,7 @@ void I2sInit(void) {
// Returns `I2S_OK` if ok to send to output or error code // Returns `I2S_OK` if ok to send to output or error code
int32_t I2SPrepareTx(void) { int32_t I2SPrepareTx(void) {
I2sStopPlaying(); I2sStopPlaying();
AddLog(LOG_LEVEL_DEBUG, "I2S: I2SPrepareTx out=%p", audio_i2s.out); AddLog(LOG_LEVEL_DEBUG, "I2S: I2SPrepareTx out=%p", audio_i2s.out);
if (!audio_i2s.out) { return I2S_ERR_OUTPUT_NOT_CONFIGURED; } if (!audio_i2s.out) { return I2S_ERR_OUTPUT_NOT_CONFIGURED; }
@ -831,12 +835,13 @@ void I2sMp3Task(void *arg) {
audio_i2s_mp3.task_running = true; audio_i2s_mp3.task_running = true;
while (audio_i2s_mp3.decoder->isRunning() && audio_i2s_mp3.task_running) { while (audio_i2s_mp3.decoder->isRunning() && audio_i2s_mp3.task_running) {
if (!audio_i2s_mp3.decoder->loop()) { if (!audio_i2s_mp3.decoder->loop()) {
audio_i2s_mp3.task_running = false; audio_i2s_mp3.task_running = false;
} }
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
} }
audio_i2s.out->flush(); audio_i2s.out->flush();
audio_i2s_mp3.decoder->stop(); audio_i2s_mp3.decoder->stop();
audio_i2s_mp3.task_loop_mode = false;
mp3_delete(); mp3_delete();
audio_i2s_mp3.mp3_task_handle = nullptr; audio_i2s_mp3.mp3_task_handle = nullptr;
audio_i2s_mp3.task_has_ended = true; audio_i2s_mp3.task_has_ended = true;
@ -933,7 +938,7 @@ bool I2SinitDecoder(uint32_t decoder_type){
int32_t I2SPlayFile(const char *path, uint32_t decoder_type) { int32_t I2SPlayFile(const char *path, uint32_t decoder_type) {
int32_t i2s_err = I2SPrepareTx(); int32_t i2s_err = I2SPrepareTx();
if ((i2s_err) != I2S_OK) { return i2s_err; } if ((i2s_err) != I2S_OK) { return i2s_err; }
if (audio_i2s_mp3.decoder) return I2S_ERR_DECODER_IN_USE; if (audio_i2s_mp3.decoder != nullptr) return I2S_ERR_DECODER_IN_USE;
// check if the filename starts with '/', if not add it // check if the filename starts with '/', if not add it
char fname[64]; char fname[64];
@ -946,8 +951,17 @@ int32_t I2SPlayFile(const char *path, uint32_t decoder_type) {
I2SAudioPower(true); I2SAudioPower(true);
audio_i2s_mp3.file = new AudioFileSourceFS(*ufsp, fname); if (audio_i2s_mp3.task_loop_mode == true){
File _loopFile = ufsp->open(fname);
size_t _fsize = _loopFile.size();
audio_i2s_mp3.preallocateBuffer = special_realloc(audio_i2s_mp3.preallocateBuffer,_fsize);
size_t _received = _loopFile.read(reinterpret_cast<uint8_t*>(audio_i2s_mp3.preallocateBuffer),_fsize);
_loopFile.close();
audio_i2s_mp3.file = new AudioFileSourceLoopBuffer (audio_i2s_mp3.preallocateBuffer, _fsize); // use the id3 var to make the code shorter down the line
} else {
audio_i2s_mp3.file = new AudioFileSourceFS(*ufsp, fname);
}
audio_i2s_mp3.id3 = new AudioFileSourceID3(audio_i2s_mp3.file); audio_i2s_mp3.id3 = new AudioFileSourceID3(audio_i2s_mp3.file);
if(I2SinitDecoder(decoder_type)){ if(I2SinitDecoder(decoder_type)){
@ -956,17 +970,18 @@ int32_t I2SPlayFile(const char *path, uint32_t decoder_type) {
return I2S_ERR_DECODER_FAILED_TO_INIT; return I2S_ERR_DECODER_FAILED_TO_INIT;
} }
size_t wr_tasksize = 8000; // suitable for ACC and MP3 size_t play_tasksize = 8000; // suitable for ACC and MP3
if(decoder_type == 2){ // opus needs a ton of stack if(decoder_type == 2){ // opus needs a ton of stack
wr_tasksize = 26000; play_tasksize = 26000;
} }
// Always use a task // Always use a task
xTaskCreatePinnedToCore(I2sMp3Task, "PLAYFILE", wr_tasksize, NULL, 3, &audio_i2s_mp3.mp3_task_handle, 1); xTaskCreatePinnedToCore(I2sMp3Task, "PLAYFILE", play_tasksize, NULL, 3, &audio_i2s_mp3.mp3_task_handle, 1);
return I2S_OK; return I2S_OK;
} }
void mp3_delete(void) { void mp3_delete(void) {
delete audio_i2s_mp3.buff;
delete audio_i2s_mp3.file; delete audio_i2s_mp3.file;
delete audio_i2s_mp3.id3; delete audio_i2s_mp3.id3;
delete audio_i2s_mp3.decoder; delete audio_i2s_mp3.decoder;
@ -1018,6 +1033,11 @@ void CmndI2SStop(void) {
} }
#ifdef USE_I2S_MP3 #ifdef USE_I2S_MP3
void CmndI2SLoop(void) {
audio_i2s_mp3.task_loop_mode = 1;
CmndI2SPlay();
}
void CmndI2SPlay(void) { void CmndI2SPlay(void) {
if (XdrvMailbox.data_len > 0) { if (XdrvMailbox.data_len > 0) {
int32_t err = I2SPlayFile(XdrvMailbox.data, XdrvMailbox.index); int32_t err = I2SPlayFile(XdrvMailbox.data, XdrvMailbox.index);

View File

@ -21,11 +21,8 @@
#if defined(USE_I2S_AUDIO) && defined(USE_I2S_WEBRADIO) #if defined(USE_I2S_AUDIO) && defined(USE_I2S_WEBRADIO)
struct AUDIO_I2S_WEBRADIO_t { struct AUDIO_I2S_WEBRADIO_t {
// Webradio
AudioFileSourceICYStream *ifile = NULL; AudioFileSourceICYStream *ifile = NULL;
AudioFileSourceBuffer *buff = NULL;
char wr_title[64]; char wr_title[64];
void *preallocateBuffer = NULL;
} Audio_webradio; } Audio_webradio;
void I2sMDCallback(void *cbData, const char *type, bool isUnicode, const char *str) { void I2sMDCallback(void *cbData, const char *type, bool isUnicode, const char *str) {
@ -53,18 +50,18 @@ bool I2SWebradio(const char *url, uint32_t decoder_type) {
} }
// allocate buffers if not already done // allocate buffers if not already done
if (Audio_webradio.preallocateBuffer == NULL) { if (audio_i2s_mp3.preallocateBuffer == NULL) {
Audio_webradio.preallocateBuffer = special_malloc(preallocateBufferSize); audio_i2s_mp3.preallocateBuffer = special_malloc(preallocateBufferSize);
} }
if (audio_i2s_mp3.preallocateCodec == NULL) { if (audio_i2s_mp3.preallocateCodec == NULL) {
audio_i2s_mp3.preallocateCodec = special_malloc(preallocateCodecSize); audio_i2s_mp3.preallocateCodec = special_malloc(preallocateCodecSize);
} }
// check if we have buffers // check if we have buffers
if (Audio_webradio.preallocateBuffer == NULL || audio_i2s_mp3.preallocateCodec == NULL) { if (audio_i2s_mp3.preallocateBuffer == NULL || audio_i2s_mp3.preallocateCodec == NULL) {
AddLog(LOG_LEVEL_INFO, "I2S: cannot allocate buffers"); AddLog(LOG_LEVEL_INFO, "I2S: cannot allocate buffers");
if (Audio_webradio.preallocateBuffer != NULL) { if (audio_i2s_mp3.preallocateBuffer != NULL) {
free(Audio_webradio.preallocateBuffer); free(audio_i2s_mp3.preallocateBuffer);
Audio_webradio.preallocateBuffer = NULL; audio_i2s_mp3.preallocateBuffer = NULL;
} }
if (audio_i2s_mp3.preallocateCodec != NULL) { if (audio_i2s_mp3.preallocateCodec != NULL) {
free(audio_i2s_mp3.preallocateCodec); free(audio_i2s_mp3.preallocateCodec);
@ -82,11 +79,11 @@ bool I2SWebradio(const char *url, uint32_t decoder_type) {
AddLog(LOG_LEVEL_INFO, "I2S: did connect to %s",url); AddLog(LOG_LEVEL_INFO, "I2S: did connect to %s",url);
I2SAudioPower(true); I2SAudioPower(true);
Audio_webradio.buff = new AudioFileSourceBuffer(Audio_webradio.ifile, Audio_webradio.preallocateBuffer, preallocateBufferSize); audio_i2s_mp3.buff = new AudioFileSourceBuffer(Audio_webradio.ifile, audio_i2s_mp3.preallocateBuffer, preallocateBufferSize);
if(Audio_webradio.buff == nullptr){ if(audio_i2s_mp3.buff == nullptr){
goto i2swr_fail; goto i2swr_fail;
} }
Audio_webradio.buff->RegisterStatusCB(I2sStatusCallback, NULL); audio_i2s_mp3.buff->RegisterStatusCB(I2sStatusCallback, NULL);
if(I2SinitDecoder(decoder_type) == false){ if(I2SinitDecoder(decoder_type) == false){
AddLog(LOG_LEVEL_DEBUG, "I2S: decoder init failed"); AddLog(LOG_LEVEL_DEBUG, "I2S: decoder init failed");
@ -94,7 +91,7 @@ bool I2SWebradio(const char *url, uint32_t decoder_type) {
} }
audio_i2s_mp3.decoder->RegisterStatusCB(I2sStatusCallback, NULL); audio_i2s_mp3.decoder->RegisterStatusCB(I2sStatusCallback, NULL);
if(audio_i2s_mp3.decoder->begin(Audio_webradio.buff, audio_i2s.out)){ if(audio_i2s_mp3.decoder->begin(audio_i2s_mp3.buff, audio_i2s.out)){
AddLog(LOG_LEVEL_DEBUG, "I2S: decoder started"); AddLog(LOG_LEVEL_DEBUG, "I2S: decoder started");
} else { } else {
goto i2swr_fail; goto i2swr_fail;
@ -146,10 +143,10 @@ void I2sWebRadioStopPlaying() {
delete audio_i2s_mp3.decoder; delete audio_i2s_mp3.decoder;
audio_i2s_mp3.decoder = nullptr; audio_i2s_mp3.decoder = nullptr;
} }
if (Audio_webradio.buff) { if (audio_i2s_mp3.buff) {
Audio_webradio.buff->close(); audio_i2s_mp3.buff->close();
delete Audio_webradio.buff; delete audio_i2s_mp3.buff;
Audio_webradio.buff = NULL; audio_i2s_mp3.buff = NULL;
} }
if (Audio_webradio.ifile) { if (Audio_webradio.ifile) {
Audio_webradio.ifile->close(); Audio_webradio.ifile->close();