From 03e8497211774a4a5a5588dfa7779733f7afccb1 Mon Sep 17 00:00:00 2001 From: Christian Baars Date: Sun, 12 Jan 2025 17:16:20 +0100 Subject: [PATCH] I2S: add command i2sloop (#22807) * revert upstream change of the library, fix memory leak by using stack again * add i2sloop command --- .../src/libopus/celt/celt_decoder.c | 3 +- .../ESP8266Audio/src/libopus/config.h | 1 - .../ESP8266Audio/src/libopus/repacketizer.c | 71 ++++++----------- .../ESP8266Audio/src/libopus/silk/NLSF2A.c | 11 +-- .../libopus/silk/fixed/burg_modified_FIX.c | 18 ++--- .../silk/fixed/warped_autocorrelation_FIX.c | 6 +- .../src/libopus/silk/resampler_down2_3.c | 4 +- .../ESP8266Audio/src/opusfile/opusfile.c | 76 ++++++++----------- .../ESP8266Audio/src/opusfile/opusfile.h | 2 +- .../xdrv_42_0_i2s_3_lib_idf51.ino | 31 ++++++++ .../xdrv_42_0_i2s_audio_idf51.ino | 44 ++++++++--- .../xdrv_42_7_i2s_webradio_idf51.ino | 31 ++++---- 12 files changed, 144 insertions(+), 154 deletions(-) diff --git a/lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_decoder.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_decoder.c index 7e7296ed5..329b6f6cc 100644 --- a/lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_decoder.c +++ b/lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_decoder.c @@ -482,7 +482,7 @@ static int celt_plc_pitch_search(celt_sig *decode_mem[2], int C, int arch) int pitch_index; VARDECL( opus_val16, lp_pitch_buf ); 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, DECODE_BUFFER_SIZE, C, arch); 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); pitch_index = PLC_PITCH_LAG_MAX-pitch_index; RESTORE_STACK; - free(lp_pitch_buf); return pitch_index; } diff --git a/lib/lib_audio/ESP8266Audio/src/libopus/config.h b/lib/lib_audio/ESP8266Audio/src/libopus/config.h index dc1ee1e98..52bcb1667 100644 --- a/lib/lib_audio/ESP8266Audio/src/libopus/config.h +++ b/lib/lib_audio/ESP8266Audio/src/libopus/config.h @@ -207,4 +207,3 @@ # define __restrict__ #endif -#include diff --git a/lib/lib_audio/ESP8266Audio/src/libopus/repacketizer.c b/lib/lib_audio/ESP8266Audio/src/libopus/repacketizer.c index 36732293f..5a1eb675e 100644 --- a/lib/lib_audio/ESP8266Audio/src/libopus/repacketizer.c +++ b/lib/lib_audio/ESP8266Audio/src/libopus/repacketizer.c @@ -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) { - OpusRepacketizer *rp = (OpusRepacketizer*)malloc(sizeof(OpusRepacketizer)); + OpusRepacketizer rp; opus_int32 ret; - if (len < 1) { - free(rp); + if (len < 1) return OPUS_BAD_ARG; - } - if (len==new_len) { - free(rp); + if (len==new_len) return OPUS_OK; - } - else if (len > new_len) { - free(rp); + else if (len > new_len) 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 */ OPUS_MOVE(data+new_len-len, data, len); - ret = opus_repacketizer_cat(rp, data+new_len-len, len); - if (ret != OPUS_OK) { - free(rp); + ret = opus_repacketizer_cat(&rp, data+new_len-len, len); + if (ret != OPUS_OK) return ret; - } - ret = opus_repacketizer_out_range_impl(rp, 0, rp->nb_frames, data, new_len, 0, 1); - free(rp); + ret = opus_repacketizer_out_range_impl(&rp, 0, rp.nb_frames, data, new_len, 0, 1); if (ret > 0) return OPUS_OK; 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) { - OpusRepacketizer *rp = (OpusRepacketizer*)malloc(sizeof(OpusRepacketizer)); + OpusRepacketizer rp; opus_int32 ret; - if (len < 1) { - free(rp); + if (len < 1) return OPUS_BAD_ARG; - } - opus_repacketizer_init(rp); - ret = opus_repacketizer_cat(rp, data, len); - if (ret < 0) { - free(rp); + opus_repacketizer_init(&rp); + ret = opus_repacketizer_cat(&rp, data, len); + if (ret < 0) return ret; - } - ret = opus_repacketizer_out_range_impl(rp, 0, rp->nb_frames, data, len, 0, 0); - free(rp); + ret = opus_repacketizer_out_range_impl(&rp, 0, rp.nb_frames, data, len, 0, 0); celt_assert(ret > 0 && ret <= len); return ret; } @@ -326,14 +312,12 @@ opus_int32 opus_multistream_packet_unpad(unsigned char *data, opus_int32 len, in unsigned char toc; opus_int16 size[48]; opus_int32 packet_offset; - OpusRepacketizer *rp = (OpusRepacketizer*)malloc(sizeof(OpusRepacketizer)); + OpusRepacketizer rp; unsigned char *dst; opus_int32 dst_len; - if (len < 1){ - free(rp); + if (len < 1) return OPUS_BAD_ARG; - } dst = data; dst_len = 0; /* Unpad all frames */ @@ -341,34 +325,25 @@ opus_int32 opus_multistream_packet_unpad(unsigned char *data, opus_int32 len, in { opus_int32 ret; int self_delimited = s!=nb_streams-1; - if (len<=0) { - free(rp); + if (len<=0) return OPUS_INVALID_PACKET; - } - opus_repacketizer_init(rp); + opus_repacketizer_init(&rp); ret = opus_packet_parse_impl(data, len, self_delimited, &toc, NULL, size, NULL, &packet_offset); - if (ret<0) { - free(rp); + if (ret<0) return ret; - } - ret = opus_repacketizer_cat_impl(rp, data, packet_offset, self_delimited); - if (ret < 0) { - free(rp); + ret = opus_repacketizer_cat_impl(&rp, data, packet_offset, self_delimited); + if (ret < 0) return ret; - } - ret = opus_repacketizer_out_range_impl(rp, 0, rp->nb_frames, dst, len, self_delimited, 0); - if (ret < 0) { - free(rp); + ret = opus_repacketizer_out_range_impl(&rp, 0, rp.nb_frames, dst, len, self_delimited, 0); + if (ret < 0) return ret; - } else dst_len += ret; dst += ret; data += packet_offset; len -= packet_offset; } - free(rp); return dst_len; } diff --git a/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF2A.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF2A.c index 2b3e3340d..40718e7a8 100644 --- a/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF2A.c +++ b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF2A.c @@ -80,11 +80,10 @@ void silk_NLSF2A( }; const unsigned char *ordering; opus_int k, i, dd; - opus_int32 *cos_LSF_QA = (opus_int32*)malloc(sizeof(opus_int32) * SILK_MAX_ORDER_LPC ); - opus_int32 *P = (opus_int32*)malloc(sizeof(opus_int32) * (SILK_MAX_ORDER_LPC / 2 + 1)); - opus_int32 *Q= (opus_int32*)malloc(sizeof(opus_int32) * (SILK_MAX_ORDER_LPC / 2 + 1)); + opus_int32 cos_LSF_QA[ SILK_MAX_ORDER_LPC ]; + opus_int32 P[ SILK_MAX_ORDER_LPC / 2 + 1 ], Q[ SILK_MAX_ORDER_LPC / 2 + 1 ]; 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 ); 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 */ } } - free(cos_LSF_QA); - free(P); - free(Q); - free(a32_QA1); } diff --git a/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.c index dd7862983..b4a31d605 100644 --- a/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.c +++ b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.c @@ -57,12 +57,12 @@ void silk_burg_modified_c( 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; const opus_int16 *x_ptr; - opus_int32 *C_first_row = (opus_int32*)malloc(sizeof(opus_int32) * SILK_MAX_ORDER_LPC); - opus_int32 *C_last_row = (opus_int32*)malloc(sizeof(opus_int32) * SILK_MAX_ORDER_LPC); - opus_int32 *Af_QA = (opus_int32*)malloc(sizeof(opus_int32) * SILK_MAX_ORDER_LPC); - opus_int32 *CAf = (opus_int32*)malloc(sizeof(opus_int32) * (SILK_MAX_ORDER_LPC+1)); - opus_int32 *CAb = (opus_int32*)malloc(sizeof(opus_int32) * (SILK_MAX_ORDER_LPC+1)); - opus_int32 *xcorr = (opus_int32*)malloc(sizeof(opus_int32) * SILK_MAX_ORDER_LPC); + opus_int32 C_first_row[ SILK_MAX_ORDER_LPC ]; + opus_int32 C_last_row[ SILK_MAX_ORDER_LPC ]; + opus_int32 Af_QA[ SILK_MAX_ORDER_LPC ]; + opus_int32 CAf[ SILK_MAX_ORDER_LPC + 1 ]; + opus_int32 CAb[ SILK_MAX_ORDER_LPC + 1 ]; + opus_int32 xcorr[ SILK_MAX_ORDER_LPC ]; opus_int64 C0_64; 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_Q = -rshifts; } - free(C_first_row); - free(C_last_row); - free(Af_QA); - free(CAf); - free(CAb); - free(xcorr); } diff --git a/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.c index 9c21f2a9b..7ef3a7efc 100644 --- a/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.c +++ b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.c @@ -49,8 +49,8 @@ void silk_warped_autocorrelation_FIX_c( { opus_int n, i, lsh; opus_int32 tmp1_QS, tmp2_QS; - opus_int32 *state_QS = (opus_int32*)calloc(MAX_SHAPE_LPC_ORDER + 1, sizeof(opus_int32)); - opus_int64 *corr_QC = (opus_int64*)calloc(MAX_SHAPE_LPC_ORDER + 1, sizeof(opus_int64)); + opus_int32 state_QS[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 }; + opus_int64 corr_QC[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 }; /* Order must be even */ 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*/ - free(state_QS); - free(corr_QC); } #endif /* OVERRIDE_silk_warped_autocorrelation_FIX_c */ diff --git a/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2_3.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2_3.c index 62aab5682..d8ce95c68 100644 --- a/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2_3.c +++ b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2_3.c @@ -48,8 +48,7 @@ void silk_resampler_down2_3( opus_int32 *buf_ptr; SAVE_STACK; -// 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)); + ALLOC( buf, RESAMPLER_MAX_BATCH_SIZE_IN + ORDER_FIR, opus_int32 ); /* Copy buffered samples to start of buffer */ 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 */ silk_memcpy( S, &buf[ nSamplesIn ], ORDER_FIR * sizeof( opus_int32 ) ); - free(buf); RESTORE_STACK; } diff --git a/lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.c b/lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.c index 7ffe32f89..a9c44ef46 100644 --- a/lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.c +++ b/lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.c @@ -90,12 +90,12 @@ int op_test(OpusHead *_head, ogg_sync_init(&oy); data=ogg_sync_buffer(&oy,(long)_initial_bytes); if(data!=NULL){ - ogg_stream_state *os = (ogg_stream_state*)malloc(sizeof(ogg_stream_state)); + ogg_stream_state os; ogg_page og; int ret; memcpy(data,_initial_data,_initial_bytes); ogg_sync_wrote(&oy,(long)_initial_bytes); - ogg_stream_init(os,-1); + ogg_stream_init(&os,-1); err=OP_FALSE; do{ ogg_packet op; @@ -104,11 +104,11 @@ int op_test(OpusHead *_head, if(ret<0)continue; /*Stop if we run out of data.*/ if(!ret)break; - ogg_stream_reset_serialno(os,ogg_page_serialno(&og)); - ogg_stream_pagein(os,&og); + ogg_stream_reset_serialno(&os,ogg_page_serialno(&og)); + ogg_stream_pagein(&os,&og); /*Only process the first packet on this page (if it's a BOS packet, 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){ ret=opus_head_parse(_head,op.packet,op.bytes); /*If this didn't look like Opus, keep going.*/ @@ -122,8 +122,7 @@ int op_test(OpusHead *_head, } } while(err==OP_FALSE); - ogg_stream_clear(os); - free(os); + ogg_stream_clear(&os); } else err=OP_EFAULT; ogg_sync_clear(&oy); @@ -836,7 +835,7 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of, ogg_int64_t cur_page_gp; ogg_uint32_t serialno; opus_int32 total_duration; - int *durations = (int*)malloc(255 * sizeof(int)); + int durations[255]; int cur_page_eos; int op_count; 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.*/ if(OP_UNLIKELY(page_offset<0)){ /*Fail if there was a read error.*/ - if(page_offsethead.pre_skip>0) {free(durations); return OP_EBADTIMESTAMP;} + if(_link->head.pre_skip>0)return OP_EBADTIMESTAMP; _link->pcm_file_offset=0; /*Set pcm_end and end_offset so we can skip the call to op_find_final_pcm_offset().*/ _link->pcm_start=_link->pcm_end=0; _link->end_offset=_link->data_offset; - free(durations); return 0; } /*Similarly, if we hit the next link in the chain, we've gone too far.*/ if(OP_UNLIKELY(ogg_page_bos(_og))){ - if(_link->head.pre_skip>0) { - free(durations); - return OP_EBADTIMESTAMP; - } + if(_link->head.pre_skip>0)return OP_EBADTIMESTAMP; /*Set pcm_end and end_offset so we can skip the call to op_find_final_pcm_offset().*/ _link->pcm_file_offset=0; _link->pcm_start=_link->pcm_end=0; _link->end_offset=_link->data_offset; /*Tell the caller we've got a buffered page for them.*/ - free(durations); return 1; } /*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; /*But getting a packet without a valid granule position on the page is not okay.*/ - if(cur_page_gp==-1) { - free(durations); - return OP_EBADTIMESTAMP; - } + if(cur_page_gp==-1)return OP_EBADTIMESTAMP; cur_page_eos=_of->op[op_count-1].e_o_s; if(OP_LIKELY(!cur_page_eos)){ /*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)){ /*The starting granule position MUST not be smaller than the amount of audio on the first page with completed packets.*/ - free(durations); 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 exist after applying the pre-skip.*/ if(OP_UNLIKELY(op_granpos_cmp(cur_page_gp,_link->head.pre_skip)<0)){ - free(durations); return OP_EBADTIMESTAMP; } } @@ -968,7 +957,6 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of, _link->pcm_file_offset=0; _of->prev_packet_gp=_link->pcm_start=pcm_start; _of->prev_page_offset=page_offset; - free(durations); return 0; } @@ -1403,34 +1391,32 @@ static int op_open_seekable2_impl(OggOpusFile *_of){ /*64 seek records should be enough for anybody. Actually, with a bisection search in a 63-bit range down to OP_CHUNK_SIZE granularity, much more than enough.*/ - OpusSeekRecord *sr = (OpusSeekRecord*)malloc(64 * sizeof(OpusSeekRecord)); + OpusSeekRecord sr[64]; opus_int64 data_offset; int ret; /*We can seek, so set out learning all about this file.*/ (*_of->callbacks.seek)(_of->stream,0,SEEK_END); _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; - if(OP_UNLIKELY(_of->endendend, _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.*/ _of->end=sr[0].offset+sr[0].size; - if(OP_UNLIKELY(_of->endendserialnos,&_of->nserialnos,&_of->cserialnos); - free(sr); - return ret; } static int op_open_seekable2(OggOpusFile *_of){ 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; opus_int64 prev_page_offset; opus_int64 start_offset; @@ -1449,9 +1435,9 @@ static int op_open_seekable2(OggOpusFile *_of){ start_op_count=_of->op_count; /*This is a bit too large to put on the stack unconditionally.*/ 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; - *os_start=_of->os; + *&os_start=_of->os; prev_page_offset=_of->prev_page_offset; start_offset=_of->offset; 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_sync_clear(&_of->oy); *&_of->oy=*&oy_start; - *&_of->os=*os_start; + *&_of->os=*&os_start; _of->offset=start_offset; _of->op_count=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_page_offset=prev_page_offset; _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.*/ ret=(*_of->callbacks.seek)(_of->stream,op_position(_of),SEEK_SET); - free(os_start); 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); if(OP_LIKELY(_of->ready_state>=OP_INITSET)){ opus_int32 total_duration; - int *durations = (int*)malloc(255 * sizeof(int)); + int durations[255]; int op_count; int report_hole; 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 samples that shouldn't have been played.*/ _of->op_count=0; - if(report_hole){ free(durations); return OP_HOLE; } + if(report_hole)return OP_HOLE; continue; } /*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->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(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 #if defined(OP_FIXED_POINT) -#if 0 + /*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, 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.*/ static const opus_int16 OP_STEREO_DOWNMIX_Q14 [OP_NCHANNELS_MAX-2][OP_NCHANNELS_MAX][2]={ +#if OP_NCHANNELS_MAX>2 /*3.0*/ { {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}, {3183,5515},{4502,4502} } +#endif // OP_NCHANNELS_MAX>2 }; -#endif + int op_read(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size,int *_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]; } else{ -#if 0 for(i=0;i<_nsamples;i++){ opus_int32 l; 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+1]=(opus_int16)OP_CLAMP(-32768,r+8192>>14,32767); } -#endif - // noop, removed for RAM savings } } return _nsamples; diff --git a/lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.h b/lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.h index 11cc07a16..7a007d24d 100644 --- a/lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.h +++ b/lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.h @@ -729,7 +729,7 @@ struct OpusServerInfo{ /**The software used by the origin server (Server). This is NULL if there was no Server header.*/ 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 NULL if there was no Content-Type header.*/ char *content_type; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino index f77bbdfb8..098a4c4f9 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino @@ -877,5 +877,36 @@ int16_t TasmotaI2S::lowpassFilter(int16_t pcm_in) { 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(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(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 // defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino index 0ccd69e6e..d8c872767 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino @@ -79,9 +79,11 @@ void CmndI2SMP3Stream(void); struct AUDIO_I2S_MP3_t { #ifdef USE_I2S_MP3 AudioGenerator *decoder = nullptr; - AudioFileSourceFS *file = nullptr; - AudioFileSourceID3 *id3 = nullptr; + AudioFileSource *file = nullptr; + AudioFileSource *id3 = nullptr; + AudioFileSource *buff = NULL; + void *preallocateBuffer = NULL; void *preallocateCodec = NULL; #endif // USE_I2S_MP3 @@ -96,6 +98,7 @@ struct AUDIO_I2S_MP3_t { bool use_stream = false; bool task_running = false; bool task_has_ended = false; + bool task_loop_mode = false; // SHINE uint32_t recdur; @@ -124,7 +127,7 @@ struct AUDIO_I2S_MP3_t { const char kI2SAudio_Commands[] PROGMEM = "I2S|" "Gain|Rec|Stop|Config" #ifdef USE_I2S_MP3 - "|Play" + "|Play|Loop" #endif #ifdef USE_I2S_DEBUG "|Mic" // debug only @@ -153,6 +156,7 @@ void (* const I2SAudio_Command[])(void) PROGMEM = { &CmndI2SGain, &CmndI2SMicRec, &CmndI2SStop, &CmndI2SConfig, #ifdef USE_I2S_MP3 &CmndI2SPlay, + &CmndI2SLoop, #endif #ifdef USE_I2S_DEBUG &CmndI2SMic, @@ -449,7 +453,7 @@ void I2sMicTask(void *arg){ } } audio_i2s_mp3.recdur = TasmotaGlobal.uptime - ctime; - + vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS(timeForOneRead)); } @@ -493,7 +497,7 @@ int32_t I2sRecordShine(char *path) { esp_err_t err = ESP_OK; switch(audio_i2s.Settings->rx.sample_rate){ - case 32000: case 48000: case 44100: + case 32000: case 48000: case 44100: break; // supported default: 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 int32_t I2SPrepareTx(void) { I2sStopPlaying(); - + AddLog(LOG_LEVEL_DEBUG, "I2S: I2SPrepareTx out=%p", audio_i2s.out); if (!audio_i2s.out) { return I2S_ERR_OUTPUT_NOT_CONFIGURED; } @@ -831,12 +835,13 @@ void I2sMp3Task(void *arg) { audio_i2s_mp3.task_running = true; while (audio_i2s_mp3.decoder->isRunning() && audio_i2s_mp3.task_running) { if (!audio_i2s_mp3.decoder->loop()) { - audio_i2s_mp3.task_running = false; + audio_i2s_mp3.task_running = false; } vTaskDelay(pdMS_TO_TICKS(1)); } audio_i2s.out->flush(); audio_i2s_mp3.decoder->stop(); + audio_i2s_mp3.task_loop_mode = false; mp3_delete(); audio_i2s_mp3.mp3_task_handle = nullptr; 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 i2s_err = I2SPrepareTx(); 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 char fname[64]; @@ -946,8 +951,17 @@ int32_t I2SPlayFile(const char *path, uint32_t decoder_type) { 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(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); 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; } - 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 - wr_tasksize = 26000; + play_tasksize = 26000; } // 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; } void mp3_delete(void) { + delete audio_i2s_mp3.buff; delete audio_i2s_mp3.file; delete audio_i2s_mp3.id3; delete audio_i2s_mp3.decoder; @@ -1018,6 +1033,11 @@ void CmndI2SStop(void) { } #ifdef USE_I2S_MP3 +void CmndI2SLoop(void) { + audio_i2s_mp3.task_loop_mode = 1; + CmndI2SPlay(); +} + void CmndI2SPlay(void) { if (XdrvMailbox.data_len > 0) { int32_t err = I2SPlayFile(XdrvMailbox.data, XdrvMailbox.index); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_7_i2s_webradio_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_7_i2s_webradio_idf51.ino index c19fe7cb2..7f8133c07 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_7_i2s_webradio_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_7_i2s_webradio_idf51.ino @@ -21,11 +21,8 @@ #if defined(USE_I2S_AUDIO) && defined(USE_I2S_WEBRADIO) struct AUDIO_I2S_WEBRADIO_t { - // Webradio AudioFileSourceICYStream *ifile = NULL; - AudioFileSourceBuffer *buff = NULL; char wr_title[64]; - void *preallocateBuffer = NULL; } Audio_webradio; 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 - if (Audio_webradio.preallocateBuffer == NULL) { - Audio_webradio.preallocateBuffer = special_malloc(preallocateBufferSize); + if (audio_i2s_mp3.preallocateBuffer == NULL) { + audio_i2s_mp3.preallocateBuffer = special_malloc(preallocateBufferSize); } if (audio_i2s_mp3.preallocateCodec == NULL) { audio_i2s_mp3.preallocateCodec = special_malloc(preallocateCodecSize); } // 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"); - if (Audio_webradio.preallocateBuffer != NULL) { - free(Audio_webradio.preallocateBuffer); - Audio_webradio.preallocateBuffer = NULL; + if (audio_i2s_mp3.preallocateBuffer != NULL) { + free(audio_i2s_mp3.preallocateBuffer); + audio_i2s_mp3.preallocateBuffer = NULL; } if (audio_i2s_mp3.preallocateCodec != NULL) { 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); I2SAudioPower(true); - Audio_webradio.buff = new AudioFileSourceBuffer(Audio_webradio.ifile, Audio_webradio.preallocateBuffer, preallocateBufferSize); - if(Audio_webradio.buff == nullptr){ + audio_i2s_mp3.buff = new AudioFileSourceBuffer(Audio_webradio.ifile, audio_i2s_mp3.preallocateBuffer, preallocateBufferSize); + if(audio_i2s_mp3.buff == nullptr){ goto i2swr_fail; } - Audio_webradio.buff->RegisterStatusCB(I2sStatusCallback, NULL); + audio_i2s_mp3.buff->RegisterStatusCB(I2sStatusCallback, NULL); if(I2SinitDecoder(decoder_type) == false){ 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); - 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"); } else { goto i2swr_fail; @@ -146,10 +143,10 @@ void I2sWebRadioStopPlaying() { delete audio_i2s_mp3.decoder; audio_i2s_mp3.decoder = nullptr; } - if (Audio_webradio.buff) { - Audio_webradio.buff->close(); - delete Audio_webradio.buff; - Audio_webradio.buff = NULL; + if (audio_i2s_mp3.buff) { + audio_i2s_mp3.buff->close(); + delete audio_i2s_mp3.buff; + audio_i2s_mp3.buff = NULL; } if (Audio_webradio.ifile) { Audio_webradio.ifile->close();