mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-24 11:16:34 +00:00
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:
parent
cf739e9522
commit
03e8497211
@ -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;
|
||||
}
|
||||
|
||||
|
@ -207,4 +207,3 @@
|
||||
# define __restrict__
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_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
|
||||
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;
|
||||
/*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->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
|
||||
lucky, the last Opus page of the first link, as most Ogg Opus files will
|
||||
contain a single logical bitstream.*/
|
||||
ret=op_get_prev_page_serial(_of,sr,_of->end,
|
||||
_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->end<data_offset)){free(sr); return OP_EBADLINK;}
|
||||
if(OP_UNLIKELY(_of->end<data_offset))return OP_EBADLINK;
|
||||
/*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);
|
||||
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;
|
||||
|
@ -729,7 +729,7 @@ struct OpusServerInfo{
|
||||
/**The software used by the origin server (Server).
|
||||
This is <code>NULL</code> if there was no <code>Server</code> 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 <code>NULL</code> if there was no <code>Content-Type</code>
|
||||
header.*/
|
||||
char *content_type;
|
||||
|
@ -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<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 // defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
@ -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<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);
|
||||
|
||||
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);
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user