Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
E
ESP32-audioI2S
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
xpstem
ESP32-audioI2S
Commits
8ba32081
Unverified
Commit
8ba32081
authored
Dec 23, 2023
by
Wolle
Committed by
GitHub
Dec 23, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #636 from schreibfaul1/dev
Arduino V3
parents
961b320c
7caef301
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
143 additions
and
134 deletions
+143
-134
src/Audio.cpp
src/Audio.cpp
+54
-111
src/Audio.h
src/Audio.h
+3
-3
src/opus_decoder/opus_decoder.cpp
src/opus_decoder/opus_decoder.cpp
+86
-20
No files found.
src/Audio.cpp
View file @
8ba32081
...
...
@@ -3,8 +3,8 @@
*
* Created on: Oct 26.2018
*
* Version 3.0.
7z
* Updated on: Dec
16
.2023
* Version 3.0.
8
* Updated on: Dec
22
.2023
* Author: Wolle (schreibfaul1)
*
*/
...
...
@@ -2130,30 +2130,33 @@ bool Audio::pauseResume() {
return
retVal
;
}
//---------------------------------------------------------------------------------------------------------------------
bool
Audio
::
playChunk
()
{
// If we've got data, try and pump it out..
void
Audio
::
playChunk
()
{
int16_t
sample
[
2
];
if
(
getBitsPerSample
()
==
8
)
{
if
(
getChannels
()
==
1
)
{
while
(
m_validSamples
)
{
auto
pc
=
[
&
](
int16_t
*
s16
)
{
// lambda, inner function
if
(
playSample
(
s16
)){
m_validSamples
--
;
m_curSample
++
;
return
true
;
}
return
false
;
};
// If we've got data, try and pump it out..
while
(
m_validSamples
){
if
(
getBitsPerSample
()
==
8
)
{
if
(
getChannels
()
==
1
)
{
uint8_t
x
=
m_outBuff
[
m_curSample
]
&
0x00FF
;
uint8_t
y
=
(
m_outBuff
[
m_curSample
]
&
0xFF00
)
>>
8
;
sample
[
RIGHTCHANNEL
]
=
x
;
sample
[
LEFTCHANNEL
]
=
x
;
while
(
1
)
{
if
(
playSample
(
sample
))
break
;
}
// Can't send?
if
(
!
pc
(
sample
)){
break
;}
// playSample in lambda
sample
[
RIGHTCHANNEL
]
=
y
;
sample
[
LEFTCHANNEL
]
=
y
;
while
(
1
)
{
if
(
playSample
(
sample
))
break
;
}
// Can't send?
m_validSamples
--
;
m_curSample
++
;
if
(
!
pc
(
sample
)){
break
;}
// playSample in lambda
}
}
if
(
getChannels
()
==
2
)
{
while
(
m_validSamples
)
{
if
(
getChannels
()
==
2
)
{
uint8_t
x
=
m_outBuff
[
m_curSample
]
&
0x00FF
;
uint8_t
y
=
(
m_outBuff
[
m_curSample
]
&
0xFF00
)
>>
8
;
if
(
!
m_f_forceMono
)
{
// stereo mode
...
...
@@ -2165,54 +2168,29 @@ bool Audio::playChunk() {
sample
[
RIGHTCHANNEL
]
=
xy
;
sample
[
LEFTCHANNEL
]
=
xy
;
}
while
(
1
)
{
if
(
playSample
(
sample
))
break
;
}
// Can't send?
m_validSamples
--
;
m_curSample
++
;
if
(
!
pc
(
sample
)){
break
;}
// playSample in lambda
}
}
m_curSample
=
0
;
return
true
;
}
if
(
getBitsPerSample
()
==
16
)
{
if
(
getChannels
()
==
1
)
{
while
(
m_validSamples
)
{
if
(
getBitsPerSample
()
==
16
)
{
if
(
getChannels
()
==
1
)
{
sample
[
RIGHTCHANNEL
]
=
m_outBuff
[
m_curSample
];
sample
[
LEFTCHANNEL
]
=
m_outBuff
[
m_curSample
];
if
(
!
playSample
(
sample
))
{
log_e
(
"can't send"
);
return
false
;
}
// Can't send
m_validSamples
--
;
m_curSample
++
;
}
}
if
(
getChannels
()
==
2
)
{
m_curSample
=
0
;
while
(
m_validSamples
)
{
if
(
!
m_f_forceMono
)
{
// stereo mode
sample
[
RIGHTCHANNEL
]
=
m_outBuff
[
m_curSample
*
2
];
sample
[
LEFTCHANNEL
]
=
m_outBuff
[
m_curSample
*
2
+
1
];
}
else
{
// mono mode, #100
int16_t
xy
=
(
m_outBuff
[
m_curSample
*
2
]
+
m_outBuff
[
m_curSample
*
2
+
1
])
/
2
;
sample
[
RIGHTCHANNEL
]
=
xy
;
sample
[
LEFTCHANNEL
]
=
xy
;
}
playSample
(
sample
);
m_validSamples
--
;
m_curSample
++
;
if
(
getChannels
()
==
2
)
{
if
(
!
m_f_forceMono
)
{
// stereo mode
sample
[
RIGHTCHANNEL
]
=
m_outBuff
[
m_curSample
*
2
];
sample
[
LEFTCHANNEL
]
=
m_outBuff
[
m_curSample
*
2
+
1
];
}
else
{
// mono mode, #100
int16_t
xy
=
(
m_outBuff
[
m_curSample
*
2
]
+
m_outBuff
[
m_curSample
*
2
+
1
])
/
2
;
sample
[
RIGHTCHANNEL
]
=
xy
;
sample
[
LEFTCHANNEL
]
=
xy
;
}
}
}
m_curSample
=
0
;
return
true
;
if
(
!
pc
(
sample
)){
break
;}
// playSample in lambda
}
log_e
(
"BitsPer Sample must be 8 or 16!"
);
m_validSamples
=
0
;
stopSong
();
return
false
;
}
//---------------------------------------------------------------------------------------------------------------------
void
Audio
::
loop
()
{
...
...
@@ -2952,6 +2930,7 @@ void Audio::processLocalFile() {
if
(
f_fileDataComplete
&&
InBuff
.
bufferFilled
()
<
InBuff
.
getMaxBlockSize
())
{
if
(
InBuff
.
bufferFilled
())
{
if
(
!
readID3V1Tag
())
{
if
(
m_validSamples
)
{
playChunk
();
return
;}
// play samples first
int
bytesDecoded
=
sendBytes
(
InBuff
.
getReadPtr
(),
InBuff
.
bufferFilled
());
if
(
bytesDecoded
<=
InBuff
.
bufferFilled
())
{
// avoid InBuff overrun (can be if file is corrupt)
if
(
m_f_playing
)
{
...
...
@@ -3001,17 +2980,7 @@ void Audio::processLocalFile() {
// play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if
(
f_stream
)
{
static
uint8_t
cnt
=
0
;
uint8_t
compression
;
if
(
m_codec
==
CODEC_WAV
)
compression
=
1
;
if
(
m_codec
==
CODEC_FLAC
)
compression
=
2
;
else
compression
=
3
;
cnt
++
;
if
(
cnt
==
compression
)
{
playAudioData
();
cnt
=
0
;
}
playAudioData
();
}
}
//----------------------------------------------------------------------------------------------------------------------
...
...
@@ -3091,17 +3060,7 @@ void Audio::processWebStream() {
// play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if
(
f_stream
)
{
static
uint8_t
cnt
=
0
;
uint8_t
compression
;
if
(
m_codec
==
CODEC_WAV
)
compression
=
1
;
if
(
m_codec
==
CODEC_FLAC
)
compression
=
2
;
else
compression
=
3
;
cnt
++
;
if
(
cnt
==
compression
)
{
playAudioData
();
cnt
=
0
;
}
playAudioData
();
}
}
//---------------------------------------------------------------------------------------------------------------------
...
...
@@ -3200,6 +3159,7 @@ void Audio::processWebFile() {
if
(
f_webFileDataComplete
&&
InBuff
.
bufferFilled
()
<
InBuff
.
getMaxBlockSize
())
{
if
(
InBuff
.
bufferFilled
())
{
if
(
!
readID3V1Tag
())
{
if
(
m_validSamples
)
{
playChunk
();
return
;}
// play samples first
int
bytesDecoded
=
sendBytes
(
InBuff
.
getReadPtr
(),
InBuff
.
bufferFilled
());
if
(
bytesDecoded
>
2
)
{
InBuff
.
bytesWasRead
(
bytesDecoded
);
...
...
@@ -3233,17 +3193,7 @@ void Audio::processWebFile() {
// play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if
(
f_stream
)
{
static
uint8_t
cnt
=
0
;
uint8_t
compression
;
if
(
m_codec
==
CODEC_WAV
)
compression
=
1
;
if
(
m_codec
==
CODEC_FLAC
)
compression
=
2
;
else
compression
=
3
;
cnt
++
;
if
(
cnt
==
compression
)
{
playAudioData
();
cnt
=
0
;
}
playAudioData
();
}
return
;
}
...
...
@@ -3356,12 +3306,7 @@ void Audio::processWebStreamTS() {
// play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if
(
f_stream
)
{
static
uint8_t
cnt
=
0
;
cnt
++
;
if
(
cnt
==
3
)
{
playAudioData
();
cnt
=
0
;
}
// aac only
playAudioData
();
}
return
;
}
...
...
@@ -3479,17 +3424,14 @@ void Audio::processWebStreamHLS() {
// play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if
(
f_stream
)
{
static
uint8_t
cnt
=
0
;
cnt
++
;
if
(
cnt
==
2
)
{
playAudioData
();
cnt
=
0
;
}
// aac only
playAudioData
();
}
return
;
}
//---------------------------------------------------------------------------------------------------------------------
void
Audio
::
playAudioData
()
{
if
(
m_validSamples
)
{
playChunk
();
return
;}
// play samples first
if
(
InBuff
.
bufferFilled
()
<
InBuff
.
getMaxBlockSize
())
return
;
// guard
int
bytesDecoded
=
sendBytes
(
InBuff
.
getReadPtr
(),
InBuff
.
getMaxBlockSize
());
...
...
@@ -3506,7 +3448,7 @@ void Audio::playAudioData() {
InBuff
.
bytesWasRead
(
bytesDecoded
);
return
;
}
if
(
bytesDecoded
==
0
)
return
;
// syncword at pos0
found
if
(
bytesDecoded
==
0
)
return
;
// syncword at pos0
}
return
;
...
...
@@ -4245,6 +4187,7 @@ void Audio::setDecoderItems() {
}
//---------------------------------------------------------------------------------------------------------------------
int
Audio
::
sendBytes
(
uint8_t
*
data
,
size_t
len
)
{
int
bytesLeft
;
static
bool
f_setDecodeParamsOnce
=
true
;
int
nextSync
=
0
;
...
...
@@ -4365,7 +4308,8 @@ int Audio::sendBytes(uint8_t* data, size_t len) {
audio_process_extern
(
m_outBuff
,
m_validSamples
,
&
continueI2S
);
if
(
!
continueI2S
)
{
return
bytesDecoded
;
}
}
while
(
m_validSamples
)
{
playChunk
();
}
m_curSample
=
0
;
playChunk
();
return
bytesDecoded
;
}
//---------------------------------------------------------------------------------------------------------------------
...
...
@@ -4500,7 +4444,7 @@ void Audio::printDecodeError(int r) {
case
ERR_OPUS_INVALID_SAMPLERATE
:
e
=
"SAMPLERATE IS NOT 48000Hz"
;
break
;
case
ERR_OPUS_EXTRA_CHANNELS_UNSUPPORTED
:
e
=
"EXTRA CHANNELS UNSUPPORTED"
;
break
;
case
ERR_OPUS_SILK_MODE_UNSUPPORTED
:
e
=
"SILK MODE UNSUPPORTED"
;
break
;
case
ERR_OPUS_HYBRID_MODE_UNSUPPORTED
:
e
=
"HYBRID MODE U
M
SUPPORTED"
;
break
;
case
ERR_OPUS_HYBRID_MODE_UNSUPPORTED
:
e
=
"HYBRID MODE U
N
SUPPORTED"
;
break
;
case
ERR_OPUS_CELT_BAD_ARG
:
e
=
"CELT_DECODER_BAD_ARG"
;
break
;
case
ERR_OPUS_CELT_INTERNAL_ERROR
:
e
=
"CELT DECODER INTERNAL ERROR"
;
break
;
case
ERR_OPUS_CELT_UNIMPLEMENTED
:
e
=
"CELT DECODER UNIMPLEMENTED ARG"
;
break
;
...
...
@@ -4835,7 +4779,7 @@ bool Audio::playSample(int16_t sample[2]) {
sample
=
IIR_filterChain2
(
sample
);
//-------------------------------------------
uint32_t
s32
=
Gain
(
sample
);
//
vosample2
lume;
uint32_t
s32
=
Gain
(
sample
);
//
sample2vo
lume;
if
(
audio_process_i2s
)
{
// process audio sample just before writing to i2s
...
...
@@ -4847,16 +4791,15 @@ bool Audio::playSample(int16_t sample[2]) {
if
(
m_f_internalDAC
)
{
s32
+=
0x80008000
;
}
m_i2s_bytesWritten
=
0
;
#if(ESP_IDF_VERSION_MAJOR == 5)
esp_err_t
err
=
i2s_channel_write
(
m_i2s_tx_handle
,
(
const
char
*
)
&
s32
,
sizeof
(
uint32_t
),
&
m_i2s_bytesWritten
,
10
0
);
esp_err_t
err
=
i2s_channel_write
(
m_i2s_tx_handle
,
(
const
char
*
)
&
s32
,
sizeof
(
uint32_t
),
&
m_i2s_bytesWritten
,
0
);
#else
esp_err_t
err
=
i2s_write
((
i2s_port_t
)
m_i2s_num
,
(
const
char
*
)
&
s32
,
sizeof
(
uint32_t
),
&
m_i2s_bytesWritten
,
100
);
esp_err_t
err
=
i2s_write
((
i2s_port_t
)
m_i2s_num
,
(
const
char
*
)
&
s32
,
sizeof
(
uint32_t
),
&
m_i2s_bytesWritten
,
0
);
// no wait
#endif
if
(
err
!=
ESP_OK
)
{
log_e
(
"ESP32 Errorcode %i"
,
err
);
return
false
;
}
if
(
m_i2s_bytesWritten
<
4
)
{
log_e
(
"Can't stuff any more in I2S..."
);
// increase waitingtime or outputbuffer
if
(
m_i2s_bytesWritten
<
4
)
{
// no more space in dma buffer --> break and try it later
return
false
;
}
return
true
;
...
...
src/Audio.h
View file @
8ba32081
...
...
@@ -3,8 +3,8 @@
*
* Created on: Oct 28,2018
*
* Version 3.0.
7z
* Updated on: Dec
16
.2023
* Version 3.0.
8
* Updated on: Dec
22
.2023
* Author: Wolle (schreibfaul1)
*/
...
...
@@ -220,7 +220,7 @@ private:
bool
setBitsPerSample
(
int
bits
);
bool
setChannels
(
int
channels
);
bool
setBitrate
(
int
br
);
bool
playChunk
();
void
playChunk
();
bool
playSample
(
int16_t
sample
[
2
]);
void
computeVUlevel
(
int16_t
sample
[
2
]);
void
computeLimit
();
...
...
src/opus_decoder/opus_decoder.cpp
View file @
8ba32081
...
...
@@ -3,7 +3,7 @@
* based on Xiph.Org Foundation celt decoder
*
* Created on: 26.01.2023
* Updated on:
07.04
.2023
* Updated on:
23.12
.2023
*/
//----------------------------------------------------------------------------------------------------------------------
// O G G / O P U S I M P L.
...
...
@@ -16,7 +16,9 @@ bool s_f_opusSubsequentPage = false;
bool
s_f_opusParseOgg
=
false
;
bool
s_f_newSteamTitle
=
false
;
// streamTitle
bool
s_f_opusFramePacket
=
false
;
bool
s_f_opusStereoFlag
=
false
;
uint8_t
s_opusChannels
=
0
;
uint8_t
s_opusCountCode
=
0
;
uint16_t
s_opusSamplerate
=
0
;
uint32_t
s_opusSegmentLength
=
0
;
char
*
s_opusChbuf
=
NULL
;
...
...
@@ -58,6 +60,7 @@ void OPUSsetDefaults(){
s_f_opusParseOgg
=
false
;
s_f_newSteamTitle
=
false
;
// streamTitle
s_f_opusFramePacket
=
false
;
s_f_opusStereoFlag
=
false
;
s_opusChannels
=
0
;
s_opusSamplerate
=
0
;
s_opusSegmentLength
=
0
;
...
...
@@ -65,6 +68,7 @@ void OPUSsetDefaults(){
s_opusSegmentTableSize
=
0
;
s_opusOldMode
=
0xFF
;
s_opusSegmentTableRdPtr
=
-
1
;
s_opusCountCode
=
0
;
s_opusError
=
0
;
}
...
...
@@ -73,38 +77,100 @@ void OPUSsetDefaults(){
int
OPUSDecode
(
uint8_t
*
inbuf
,
int
*
bytesLeft
,
short
*
outbuf
){
static
uint16_t
fs
=
0
;
static
uint8_t
M
=
0
;
static
uint16_t
paddingBytes
=
0
;
static
uint16_t
samplesPerFrame
=
0
;
int
ret
=
ERR_OPUS_NONE
;
int
len
=
0
;
if
(
s_f_opusParseOgg
){
int
ret
=
OPUSparseOGG
(
inbuf
,
bytesLeft
);
fs
=
0
;
M
=
0
;
paddingBytes
=
0
;
s_opusCountCode
=
0
;
ret
=
OPUSparseOGG
(
inbuf
,
bytesLeft
);
if
(
ret
==
ERR_OPUS_NONE
)
return
OPUS_PARSE_OGG_DONE
;
// ok
else
return
ret
;
// error
}
if
(
s_opusCountCode
==
3
)
goto
FramePacking
;
if
(
s_f_opusFramePacket
){
if
(
s_opusSegmentTableSize
>
0
){
s_opusSegmentTableRdPtr
++
;
s_opusSegmentTableSize
--
;
int
len
=
s_opusSegmentTable
[
s_opusSegmentTableRdPtr
];
len
=
s_opusSegmentTable
[
s_opusSegmentTableRdPtr
];
}
ret
=
parseOpusTOC
(
inbuf
[
0
]);
samplesPerFrame
=
opus_packet_get_samples_per_frame
(
inbuf
,
48000
);
if
(
ret
<
0
)
goto
exit
;
// error
FramePacking:
// https://www.tech-invite.com/y65/tinv-ietf-rfc-6716-2.html 3.2. Frame Packing
if
(
s_opusCountCode
==
0
){
// Code 0: One Frame in the Packet
*
bytesLeft
-=
len
;
int32_t
ret
=
parseOpusTOC
(
inbuf
[
0
]);
if
(
ret
<
0
)
return
ret
;
int
frame_size
=
opus_packet_get_samples_per_frame
(
inbuf
,
48000
);
ret
=
parseOpusTOC
(
inbuf
[
0
]);
if
(
ret
<
0
)
goto
exit
;
// error
len
--
;
inbuf
++
;
ec_dec_init
((
uint8_t
*
)
inbuf
,
len
);
ret
=
celt_decode_with_ec
(
inbuf
,
len
,
(
int16_t
*
)
outbuf
,
frame_siz
e
);
if
(
ret
<
0
)
return
re
t
;
// celt error
ret
=
celt_decode_with_ec
(
inbuf
,
len
,
(
int16_t
*
)
outbuf
,
samplesPerFram
e
);
if
(
ret
<
0
)
goto
exi
t
;
// celt error
s_opusValidSamples
=
ret
;
if
(
s_opusSegmentTableSize
==
0
){
s_opusSegmentTableRdPtr
=
-
1
;
// back to the parking position
s_f_opusFramePacket
=
false
;
s_f_opusParseOgg
=
true
;
ret
=
ERR_OPUS_NONE
;
}
if
(
s_opusCountCode
==
1
){
// Code 1: Two Frames in the Packet, Each with Equal Compressed Size
log_e
(
"OPUS countCode 1 not supported yet"
);
vTaskDelay
(
1000
);
// todo
}
if
(
s_opusCountCode
==
2
){
// Code 2: Two Frames in the Packet, with Different Compressed Sizes
log_e
(
"OPUS countCode 2 not supported yet"
);
vTaskDelay
(
1000
);
// todo
}
if
(
s_opusCountCode
==
3
){
// Code 3: A Signaled Number of Frames in the Packet
if
(
M
==
0
){
bool
v
=
((
inbuf
[
1
]
&
0x80
)
==
0x80
);
// VBR indicator
(
void
)
v
;
bool
p
=
((
inbuf
[
1
]
&
0x40
)
==
0x40
);
// padding bit
M
=
inbuf
[
1
]
&
0x3F
;
// max framecount
// log_i("v %i, p %i, M %i", v, p, M);
paddingBytes
=
0
;
if
(
p
){
int
i
=
0
;
while
(
inbuf
[
2
+
i
]
==
255
){
paddingBytes
+=
inbuf
[
2
+
i
];
i
++
;
}
paddingBytes
+=
inbuf
[
2
+
i
];
fs
=
(
len
-
3
-
i
-
paddingBytes
)
/
M
;
*
bytesLeft
-=
3
+
i
;
inbuf
+=
3
+
i
;
}
}
*
bytesLeft
-=
fs
;
ec_dec_init
((
uint8_t
*
)
inbuf
,
fs
);
ret
=
celt_decode_with_ec
(
inbuf
,
fs
,
(
int16_t
*
)
outbuf
,
samplesPerFrame
);
if
(
ret
<
0
)
goto
exit
;
// celt error
s_opusValidSamples
=
ret
;
M
--
;
// log_i("M %i fs %i spf %i", M, fs, samplesPerFrame);
ret
=
ERR_OPUS_NONE
;
if
(
M
==
0
)
{
s_opusCountCode
=
0
;
*
bytesLeft
-=
paddingBytes
;
goto
exit
;}
return
ret
;
}
}
return
ERR_OPUS_NONE
;
exit:
if
(
s_opusSegmentTableSize
==
0
){
s_opusSegmentTableRdPtr
=
-
1
;
// back to the parking position
s_f_opusFramePacket
=
false
;
s_f_opusParseOgg
=
true
;
}
return
ret
;
}
//----------------------------------------------------------------------------------------------------------------------
int32_t
opus_packet_get_samples_per_frame
(
const
uint8_t
*
data
,
int32_t
Fs
)
{
...
...
@@ -155,8 +221,8 @@ int parseOpusTOC(uint8_t TOC_Byte){ // https://www.rfc-editor.org/rfc/rfc6716
uint8_t
mode
=
0
;
uint8_t
configNr
=
0
;
uint8_t
s
=
0
;
uint8_t
c
=
0
;
(
void
)
c
;
uint8_t
s
=
0
;
// stereo flag
uint8_t
c
=
0
;
(
void
)
c
;
// count code
configNr
=
(
TOC_Byte
&
0b11111000
)
>>
3
;
s
=
(
TOC_Byte
&
0b00000100
)
>>
2
;
...
...
@@ -184,13 +250,13 @@ int parseOpusTOC(uint8_t TOC_Byte){ // https://www.rfc-editor.org/rfc/rfc6716
c = 2: 2 frames in the packet, with different compressed sizes
c = 3: an arbitrary number of frames in the packet
*/
// log_i("configNr %i, s %i, c %i", configNr, s, c)
;
s_opusCountCode
=
c
;
s_f_opusStereoFlag
=
s
;
if
(
configNr
<
12
)
return
ERR_OPUS_SILK_MODE_UNSUPPORTED
;
if
(
configNr
<
16
)
return
ERR_OPUS_HYBRID_MODE_UNSUPPORTED
;
return
s
;
return
configNr
;
}
//----------------------------------------------------------------------------------------------------------------------
int
parseOpusComment
(
uint8_t
*
inbuf
,
int
nBytes
){
// reference https://exiftool.org/TagNames/Vorbis.html#Comments
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment