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;
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;
}

View File

@ -207,4 +207,3 @@
# define __restrict__
#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)
{
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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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();