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
4c2d7fcc
Unverified
Commit
4c2d7fcc
authored
Nov 21, 2018
by
Wolle
Committed by
GitHub
Nov 21, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Delete Audio.cpp
parent
258c29bc
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
0 additions
and
1450 deletions
+0
-1450
audioI2S/Audio.cpp
audioI2S/Audio.cpp
+0
-1450
No files found.
audioI2S/Audio.cpp
deleted
100644 → 0
View file @
258c29bc
/*
* Audio.cpp
*
* Created on: 26.10.2018
* Updated on: 04.11.2018
* Author: Wolle
*
* This library plays mp3 files from SD card or icy-webstream via I2S
* no DAC, no DeltSigma
*
* etrernal HW on I2S nessesary, e.g.MAX98357A
*/
#include "Audio.h"
//-----------------------------------------------------------------------------------------------------------------------------------
Audio
::
Audio
()
{
helixMP3Decoder
=
MP3InitDecoder
();
if
(
!
helixMP3Decoder
)
{
sprintf
(
chbuf
,
"Reading file: %s"
,
path
);
if
(
audio_info
)
audio_info
(
"MP3Decoder Out of memory error!"
);
}
//i2s configuration
m_i2s_num
=
I2S_NUM_0
;
// i2s port number
i2s_config_t
i2s_config
=
{
.
mode
=
(
i2s_mode_t
)(
I2S_MODE_MASTER
|
I2S_MODE_TX
),
.
sample_rate
=
16000
,
.
bits_per_sample
=
I2S_BITS_PER_SAMPLE_16BIT
,
.
channel_format
=
I2S_CHANNEL_FMT_RIGHT_LEFT
,
.
communication_format
=
(
i2s_comm_format_t
)(
I2S_COMM_FORMAT_I2S
|
I2S_COMM_FORMAT_I2S_MSB
),
.
intr_alloc_flags
=
ESP_INTR_FLAG_LEVEL1
,
// high interrupt priority
.
dma_buf_count
=
8
,
.
dma_buf_len
=
64
,
//Interrupt level 1
.
use_apll
=
APLL_DISABLE
,
.
fixed_mclk
=-
1
};
i2s_driver_install
((
i2s_port_t
)
m_i2s_num
,
&
i2s_config
,
0
,
NULL
);
m_BCLK
=
26
;
// Bit Clock
m_LRC
=
25
;
// Left/Right Clock
m_DOUT
=
27
;
// Data Out
setPinout
(
m_BCLK
,
m_LRC
,
m_DOUT
);
construct_OutBuf
(
1600
);
}
//-----------------------------------------------------------------------------------------------------------------------------------
esp_err_t
Audio
::
I2Sstart
(
uint8_t
i2s_num
){
return
i2s_start
((
i2s_port_t
)
i2s_num
);
}
esp_err_t
Audio
::
I2Sstop
(
uint8_t
i2s_num
){
return
i2s_stop
((
i2s_port_t
)
i2s_num
);
}
//-----------------------------------------------------------------------------------------------------------------------------------
Audio
::~
Audio
()
{
MP3FreeDecoder
(
helixMP3Decoder
);
destruct_OutBuf
();
}
//-----------------------------------------------------------------------------------------------------------------------------------
bool
Audio
::
connecttohost
(
String
host
){
int
inx
;
// Position of ":" in hostname
int
port
=
80
;
// Port number for host
String
extension
=
"/"
;
// May be like "/mp3" in "skonto.ls.lv:8002/mp3"
String
hostwoext
;
// Host without extension and portnumber
String
headerdata
=
""
;
stopSong
();
m_f_localfile
=
false
;
m_f_webstream
=
true
;
if
(
m_lastHost
!=
host
){
// New host or reconnection?
m_f_stream_ready
=
false
;
m_lastHost
=
host
;
// Remember the current host
}
sprintf
(
chbuf
,
"Connect to new host: %s"
,
host
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
// initializationsequence
m_count
=
0
;
m_f_ctseen
=
false
;
// Contents type not seen yet
m_metaint
=
0
;
// No metaint yet
m_LFcount
=
0
;
// For detection end of header
m_bitrate
=
0
;
// Bitrate still unknown
m_totalcount
=
0
;
// Reset totalcount
m_metaline
=
""
;
// No metadata yet
m_icyname
=
""
;
// No StationName yet
m_st_remember
=
""
;
// Delete the last streamtitle
m_bitrate
=
0
;
// No bitrate yet
m_f_chunked
=
false
;
// Assume not chunked
m_f_ssl
=
false
;
m_rbwindex
=
1600
;
m_rbrindex
=
1600
;
setDatamode
(
AUDIO_HEADER
);
// Handle header
if
(
host
.
startsWith
(
"http://"
))
{
host
=
host
.
substring
(
7
);
m_f_ssl
=
false
;
;}
if
(
host
.
startsWith
(
"https://"
)){
host
=
host
.
substring
(
8
);
m_f_ssl
=
true
;}
clientsecure
.
stop
();
clientsecure
.
flush
();
// release memory
if
(
host
.
endsWith
(
".m3u"
)
||
host
.
endsWith
(
".pls"
)
||
host
.
endsWith
(
"asx"
)){
// Is it an m3u or pls or asx playlist?
m_playlist
=
host
;
// Save copy of playlist URL
m_datamode
=
AUDIO_PLAYLISTINIT
;
// Yes, start in PLAYLIST mode
if
(
m_playlist_num
==
0
){
// First entry to play?
m_playlist_num
=
1
;
// Yes, set index
}
sprintf
(
chbuf
,
"Playlist request, entry %d"
,
m_playlist_num
);
// Most of the time there are zero bytes of metadata
if
(
audio_info
)
audio_info
(
chbuf
);
}
// In the URL there may be an extension, like noisefm.ru:8000/play.m3u&t=.m3u
inx
=
host
.
indexOf
(
"/"
);
// Search for begin of extension
if
(
inx
>
0
){
// Is there an extension?
extension
=
host
.
substring
(
inx
);
// Yes, change the default
hostwoext
=
host
.
substring
(
0
,
inx
);
// Host without extension
}
// In the URL there may be a portnumber
inx
=
host
.
indexOf
(
":"
);
// Search for separator
if
(
inx
>=
0
){
// Portnumber available?
port
=
host
.
substring
(
inx
+
1
).
toInt
();
// Get portnumber as integer
hostwoext
=
host
.
substring
(
0
,
inx
);
// Host without portnumber
}
sprintf
(
chbuf
,
"Connect to %s on port %d, extension %s"
,
hostwoext
.
c_str
(),
port
,
extension
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
if
(
audio_showstreaminfo
)
audio_showstreaminfo
(
chbuf
);
String
resp
=
String
(
"GET "
)
+
extension
+
String
(
" HTTP/1.1
\r\n
"
)
+
String
(
"Host: "
)
+
hostwoext
+
String
(
"
\r\n
"
)
+
String
(
"Icy-MetaData:1
\r\n
"
)
+
String
(
"Connection: close
\r\n\r\n
"
);
if
(
m_f_ssl
==
false
){
if
(
client
.
connect
(
hostwoext
.
c_str
(),
port
)){
if
(
audio_info
)
audio_info
(
"Connected to server"
);
client
.
print
(
resp
);
m_f_running
=
true
;
return
true
;
}
}
if
(
m_f_ssl
==
true
){
if
(
clientsecure
.
connect
(
hostwoext
.
c_str
(),
443
)){
if
(
audio_info
)
audio_info
(
"SSL/TLS Connected to server"
);
clientsecure
.
print
(
resp
);
m_f_running
=
true
;
return
true
;
}
}
sprintf
(
chbuf
,
"Request %s failed!"
,
host
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
if
(
audio_showstation
)
audio_showstation
(
""
);
if
(
audio_showstreamtitle
)
audio_showstreamtitle
(
""
);
if
(
audio_showstreaminfo
)
audio_showstreaminfo
(
""
);
return
false
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
bool
Audio
::
connecttoSD
(
String
sdfile
){
const
uint8_t
ascii
[
60
]
=
{
//196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, ISO
142
,
143
,
146
,
128
,
000
,
144
,
000
,
000
,
000
,
000
,
000
,
000
,
000
,
165
,
000
,
000
,
000
,
000
,
153
,
000
,
//ASCII
//216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, ISO
000
,
000
,
000
,
000
,
154
,
000
,
000
,
225
,
133
,
000
,
000
,
000
,
132
,
143
,
145
,
135
,
138
,
130
,
136
,
137
,
//ASCII
//236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 ISO
000
,
161
,
140
,
139
,
000
,
164
,
000
,
162
,
147
,
000
,
148
,
000
,
000
,
000
,
163
,
150
,
129
,
000
,
000
,
152
};
//ASCII
uint16_t
i
=
0
,
s
=
0
;
stopSong
();
m_rbwindex
=
1600
;
m_rbrindex
=
1600
;
clientsecure
.
stop
();
clientsecure
.
flush
();
// release memory if allocated
m_f_localfile
=
true
;
m_f_webstream
=
false
;
if
(
!
sdfile
.
startsWith
(
"/"
))
sdfile
=
"/"
+
sdfile
;
while
(
sdfile
[
i
]
!=
0
){
//convert UTF8 to ASCII
path
[
i
]
=
sdfile
[
i
];
if
(
path
[
i
]
>
195
){
s
=
ascii
[
path
[
i
]
-
196
];
if
(
s
!=
0
)
path
[
i
]
=
s
;
// found a related ASCII sign
}
i
++
;
}
path
[
i
]
=
0
;
m_mp3title
=
sdfile
.
substring
(
sdfile
.
lastIndexOf
(
'/'
)
+
1
,
sdfile
.
length
());
sprintf
(
chbuf
,
"Reading file: %s"
,
m_mp3title
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
fs
::
FS
&
fs
=
SD
;
mp3file
=
fs
.
open
(
path
);
if
(
!
mp3file
){
if
(
audio_info
)
audio_info
(
"Failed to open file for reading"
);
return
false
;
}
mp3file
.
readBytes
(
chbuf
,
10
);
if
((
chbuf
[
0
]
!=
'I'
)
||
(
chbuf
[
1
]
!=
'D'
)
||
(
chbuf
[
2
]
!=
'3'
))
{
if
(
audio_info
)
audio_info
(
"file has no mp3 tag, skip metadata"
);
setFilePos
(
0
);
m_f_running
=
true
;
return
false
;
}
m_rev
=
chbuf
[
3
];
switch
(
m_rev
)
{
case
2
:
m_f_unsync
=
(
chbuf
[
5
]
&
0x80
);
m_f_exthdr
=
false
;
break
;
case
3
:
case
4
:
m_f_unsync
=
(
chbuf
[
5
]
&
0x80
);
// bit7
m_f_exthdr
=
(
chbuf
[
5
]
&
0x40
);
// bit6 extended header
break
;
};
m_id3Size
=
chbuf
[
6
];
m_id3Size
=
m_id3Size
<<
7
;
m_id3Size
|=
chbuf
[
7
];
m_id3Size
=
m_id3Size
<<
7
;
m_id3Size
|=
chbuf
[
8
];
m_id3Size
=
m_id3Size
<<
7
;
m_id3Size
|=
chbuf
[
9
];
// Every read from now may be unsync'd
sprintf
(
chbuf
,
"ID3 version=%i"
,
m_rev
);
if
(
audio_info
)
audio_info
(
chbuf
);
sprintf
(
chbuf
,
"ID3 framesSize=%i"
,
m_id3Size
);
if
(
audio_info
)
audio_info
(
chbuf
);
readID3Metadata
();
m_f_running
=
true
;
return
true
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
bool
Audio
::
connecttospeech
(
String
speech
,
String
lang
){
String
host
=
"translate.google.com"
;
String
path
=
"/translate_tts"
;
m_f_localfile
=
false
;
m_f_webstream
=
false
;
m_f_ssl
=
true
;
stopSong
();
clientsecure
.
stop
();
clientsecure
.
flush
();
// release memory if allocated
String
resp
=
String
(
"GET / HTTP/1.0
\r\n
"
)
+
String
(
"Host: "
)
+
host
+
String
(
"
\r\n
"
)
+
String
(
"User-Agent: GoogleTTS for ESP32/1.0.0
\r\n
"
)
+
String
(
"Accept-Encoding: identity
\r\n
"
)
+
String
(
"Accept: text/html
\r\n\r\n
"
);
if
(
!
clientsecure
.
connect
(
host
.
c_str
(),
443
))
{
Serial
.
println
(
"Connection failed"
);
return
false
;
}
clientsecure
.
print
(
resp
);
while
(
clientsecure
.
connected
())
{
// read the header
String
line
=
clientsecure
.
readStringUntil
(
'\n'
);
line
+=
"
\n
"
;
if
(
line
==
"
\r\n
"
)
break
;
}
String
tkkFunc
;
char
ch
;
do
{
// search for TKK
tkkFunc
=
""
;
clientsecure
.
readBytes
(
&
ch
,
1
);
if
(
ch
!=
'T'
)
continue
;
tkkFunc
+=
String
(
ch
);
clientsecure
.
readBytes
(
&
ch
,
1
);
if
(
ch
!=
'K'
)
continue
;
tkkFunc
+=
String
(
ch
);
clientsecure
.
readBytes
(
&
ch
,
1
);
if
(
ch
!=
'K'
)
continue
;
tkkFunc
+=
String
(
ch
);
}
while
(
tkkFunc
.
length
()
<
3
);
tkkFunc
+=
clientsecure
.
readStringUntil
(
';'
);
// "TKK='xxxxxxxxx.yyyyyyyyy'"
tkkFunc
=
tkkFunc
.
substring
(
5
/* length of "TKK='" */
,
tkkFunc
.
lastIndexOf
(
'\''
));
// log_i("tkk=%s", tkkFunc.c_str());
// create token
int
periodPos
=
tkkFunc
.
indexOf
(
'.'
);
String
key1
=
tkkFunc
.
substring
(
0
,
periodPos
);
String
key2
=
tkkFunc
.
substring
(
periodPos
+
1
);
long
long
int
a
,
b
;
a
=
b
=
strtoll
(
key1
.
c_str
(),
NULL
,
10
);
int
f
;
int
len
=
strlen
(
speech
.
c_str
());
for
(
f
=
0
;
f
<
len
;
f
++
)
{
a
+=
speech
[
f
];
a
=
XL
(
a
,
"+-a^+6"
);
}
a
=
XL
(
a
,
"+-3^+b+-f"
);
a
=
a
^
(
strtoll
(
key2
.
c_str
(),
NULL
,
10
));
if
(
0
>
a
)
{
a
=
(
a
&
2147483647
)
+
2147483648
;
}
a
=
a
%
1000000
;
String
token
=
String
(
lltoa
(
a
,
10
))
+
'.'
+
lltoa
(
a
^
b
,
10
);
int
i
,
j
;
const
char
*
t
=
speech
.
c_str
();
for
(
i
=
0
,
j
=
0
;
i
<
strlen
(
t
);
i
++
)
{
if
(
t
[
i
]
<
0x80
||
t
[
i
]
>
0xbf
)
{
j
++
;
}
}
// log_i("Token=%s", token.c_str());
String
tts
=
String
(
"https://"
)
+
host
+
path
+
"?ie=UTF-8&q="
+
urlencode
(
speech
)
+
"&tl="
+
lang
+
"&textlen="
+
String
(
j
)
+
"&tk="
+
token
+
"&total=1&idx=0&client=t&prev=input&ttsspeed=1"
;
clientsecure
.
stop
();
clientsecure
.
flush
();
resp
=
String
(
"GET "
)
+
tts
+
String
(
"HTTP/1.1
\r\n
"
)
+
String
(
"Host: "
)
+
host
+
String
(
"
\r\n
"
)
+
String
(
"Connection: close
\r\n\r\n
"
);
if
(
!
clientsecure
.
connect
(
host
.
c_str
(),
443
))
{
Serial
.
println
(
"Connection failed"
);
return
false
;
}
clientsecure
.
print
(
resp
);
while
(
clientsecure
.
connected
())
{
String
line
=
clientsecure
.
readStringUntil
(
'\n'
);
line
+=
"
\n
"
;
if
(
audio_info
)
audio_info
(
line
.
c_str
());
if
(
line
==
"
\r\n
"
)
break
;
}
// while(clientsecure.available() > 0) {
// uint8_t bytesread = clientsecure.readBytes(mp3buff, 32);
// sdi_send_buffer(mp3buff, bytesread);
// }
m_rbwindex
=
0
;
uint16_t
res
;
while
(
clientsecure
.
available
()
==
0
){;}
while
(
clientsecure
.
available
()
>
0
)
{
log_i
(
"client"
);
// use only a small part of ringbuffer
m_ringfree
=
1600
-
m_rbwindex
;
// free space
res
=
clientsecure
.
read
(
m_ringbuf
+
m_rbwindex
,
m_ringfree
);
if
(
res
>
0
){
m_rbwindex
+=
res
;
m_ringfree
-=
res
;
}
if
(
m_ringfree
==
0
){
int
x
=
sendBytes
(
m_ringbuf
,
1600
);
m_rbwindex
-=
x
;
memmove
(
m_ringbuf
,
m_ringbuf
+
x
,
1600
-
x
);
}
}
m_rbwindex
-=
sendBytes
(
m_ringbuf
,
1600
);
if
(
audio_eof_mp3
)
audio_eof_mp3
(
chbuf
);
stopSong
();
clientsecure
.
stop
();
clientsecure
.
flush
();
if
(
audio_eof_speech
)
audio_eof_speech
(
speech
.
c_str
());
return
true
;
}
//---------------------------------------------------------------------------------------
long
long
int
Audio
::
XL
(
long
long
int
a
,
const
char
*
b
)
{
int
len
=
strlen
(
b
);
for
(
int
c
=
0
;
c
<
len
-
2
;
c
+=
3
)
{
int
d
=
(
long
long
int
)
b
[
c
+
2
];
d
=
d
>=
97
?
d
-
87
:
d
-
48
;
d
=
(
b
[
c
+
1
]
==
'+'
?
a
>>
d
:
a
<<
d
);
a
=
b
[
c
]
==
'+'
?
(
a
+
d
)
&
4294967295
:
a
^
d
;
}
return
a
;
}
//---------------------------------------------------------------------------------------
char
*
Audio
::
lltoa
(
long
long
val
,
int
base
){
static
char
buf
[
64
]
=
{
0
};
static
char
chn
=
0
;
int
i
=
62
;
int
sign
=
(
val
<
0
);
if
(
sign
)
val
=
-
val
;
if
(
val
==
0
)
return
&
chn
;
for
(;
val
&&
i
;
--
i
,
val
/=
base
)
{
buf
[
i
]
=
"0123456789abcdef"
[
val
%
base
];
}
if
(
sign
)
{
buf
[
i
--
]
=
'-'
;
}
return
&
buf
[
i
+
1
];
}
//-----------------------------------------------------------------------------------------------------------------------------------
String
Audio
::
urlencode
(
String
str
){
String
encodedString
=
""
;
char
c
;
char
code0
;
char
code1
;
for
(
int
i
=
0
;
i
<
str
.
length
();
i
++
){
c
=
str
.
charAt
(
i
);
if
(
c
==
' '
)
encodedString
+=
'+'
;
else
if
(
isalnum
(
c
))
encodedString
+=
c
;
else
{
code1
=
(
c
&
0xf
)
+
'0'
;
if
((
c
&
0xf
)
>
9
)
code1
=
(
c
&
0xf
)
-
10
+
'A'
;
c
=
(
c
>>
4
)
&
0xf
;
code0
=
c
+
'0'
;
if
(
c
>
9
)
code0
=
c
-
10
+
'A'
;
encodedString
+=
'%'
;
encodedString
+=
code0
;
encodedString
+=
code1
;
}
}
return
encodedString
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
void
Audio
::
readID3Metadata
(){
char
frameid
[
5
];
int
framesize
=
0
;
bool
compressed
;
char
value
[
256
];
bool
bitorder
=
false
;
uint8_t
uni_h
=
0
;
uint8_t
uni_l
=
0
;
String
tag
=
""
;
if
(
m_f_exthdr
)
{
if
(
audio_info
)
audio_info
(
"ID3 extended header"
);
int
ehsz
=
(
mp3file
.
read
()
<<
24
)
|
(
mp3file
.
read
()
<<
16
)
|
(
mp3file
.
read
()
<<
8
)
|
(
mp3file
.
read
());
m_id3Size
-=
4
;
for
(
int
j
=
0
;
j
<
ehsz
-
4
;
j
++
)
{
mp3file
.
read
();
m_id3Size
--
;
}
// Throw it away
}
else
if
(
audio_info
)
audio_info
(
"ID3 normal frames"
);
do
{
frameid
[
0
]
=
mp3file
.
read
();
frameid
[
1
]
=
mp3file
.
read
();
frameid
[
2
]
=
mp3file
.
read
();
m_id3Size
-=
3
;
if
(
m_rev
==
2
)
frameid
[
3
]
=
0
;
else
{
frameid
[
3
]
=
mp3file
.
read
();
m_id3Size
--
;}
frameid
[
4
]
=
0
;
// terminate the string
tag
=
frameid
;
if
(
frameid
[
0
]
==
0
&&
frameid
[
1
]
==
0
&&
frameid
[
2
]
==
0
&&
frameid
[
3
]
==
0
){
// We're in padding
while
(
m_id3Size
!=
0
){
mp3file
.
read
();
m_id3Size
--
;}
}
else
{
if
(
m_rev
==
2
){
framesize
=
(
mp3file
.
read
()
<<
16
)
|
(
mp3file
.
read
()
<<
8
)
|
(
mp3file
.
read
());
m_id3Size
-=
3
;
compressed
=
false
;
}
else
{
framesize
=
(
mp3file
.
read
()
<<
24
)
|
(
mp3file
.
read
()
<<
16
)
|
(
mp3file
.
read
()
<<
8
)
|
(
mp3file
.
read
());
m_id3Size
-=
4
;
mp3file
.
read
();
// skip 1st flag
m_id3Size
--
;
compressed
=
mp3file
.
read
()
&
0x80
;
m_id3Size
--
;
}
if
(
compressed
){
log_i
(
"iscompressed"
);
int
decompsize
=
(
mp3file
.
read
()
<<
24
)
|
(
mp3file
.
read
()
<<
16
)
|
(
mp3file
.
read
()
<<
8
)
|
(
mp3file
.
read
());
m_id3Size
-=
4
;
(
void
)
decompsize
;
for
(
int
j
=
0
;
j
<
framesize
;
j
++
){
mp3file
.
read
();
m_id3Size
--
;
}
}
// Read the value
uint32_t
i
=
0
;
uint16_t
j
=
0
,
k
=
0
,
m
=
0
;
bool
isUnicode
;
if
(
framesize
>
0
){
isUnicode
=
(
mp3file
.
read
()
==
1
)
?
true
:
false
;
m_id3Size
--
;
if
(
framesize
<
256
){
mp3file
.
readBytes
(
value
,
framesize
-
1
);
m_id3Size
-=
framesize
-
1
;
i
=
framesize
-
1
;
value
[
framesize
-
1
]
=
0
;
}
else
{
if
(
tag
==
"APIC"
){
// a image embedded in file, skip it
//log_i("it's a image");
setFilePos
(
getFilePos
()
+
framesize
-
1
);
m_id3Size
-=
framesize
-
1
;
}
else
{
// store the first 255 bytes in buffer and cut the remains
mp3file
.
readBytes
(
value
,
255
);
m_id3Size
-=
255
;
value
[
255
]
=
0
;
i
=
255
;
// big block, skip it
setFilePos
(
getFilePos
()
+
framesize
-
1
-
255
);
m_id3Size
-=
framesize
-
1
;
}
}
if
(
isUnicode
){
// convert unicode to utf-8 U+0020...U+07FF
j
=
0
;
m
=
0
;
while
(
m
<
i
-
1
){
if
((
value
[
m
]
==
0xFE
)
&&
(
value
[
m
+
1
]
==
0xFF
)){
bitorder
=
true
;
j
=
m
+
2
;}
// MSB/LSB
if
((
value
[
m
]
==
0xFF
)
&&
(
value
[
m
+
1
]
==
0xFE
)){
bitorder
=
false
;
j
=
m
+
2
;}
//LSB/MSB
m
++
;
}
// seek for last bitorder
m
=
0
;
if
(
j
>
0
){
for
(
k
=
j
;
k
<
i
-
1
;
k
+=
2
){
if
(
bitorder
==
true
){
uni_h
=
value
[
k
];
uni_l
=
value
[
k
+
1
];}
else
{
uni_l
=
value
[
k
];
uni_h
=
value
[
k
+
1
];}
uint16_t
uni_hl
=
(
uni_h
<<
8
)
+
uni_l
;
uint8_t
utf8_h
=
(
uni_hl
>>
6
);
// div64
uint8_t
utf8_l
=
uni_l
;
if
(
utf8_h
>
3
){
utf8_h
+=
0xC0
;
if
(
uni_l
<
0x40
)
utf8_l
=
uni_l
+
0x80
;
else
if
(
uni_l
<
0x80
)
utf8_l
=
uni_l
+=
0x40
;
else
if
(
uni_l
<
0xC0
)
utf8_l
=
uni_l
;
else
utf8_l
=
uni_l
-
0x40
;
}
if
(
utf8_h
>
3
)
{
value
[
m
]
=
utf8_h
;
m
++
;}
value
[
m
]
=
utf8_l
;
m
++
;
}
}
value
[
m
]
=
0
;
i
=
m
;
}
}
chbuf
[
0
]
=
0
;
j
=
0
;
k
=
0
;
while
(
j
<
i
){
if
(
value
[
j
]
>
0x19
){
value
[
k
]
=
value
[
j
];
k
++
;}
else
{
i
--
;}
j
++
;}
//remove non printables
value
[
i
]
=
0
;
// new termination
// Revision 2
if
(
tag
==
"CNT"
)
sprintf
(
chbuf
,
"Play counter: %s"
,
value
);
if
(
tag
==
"COM"
)
sprintf
(
chbuf
,
"Comments: %s"
,
value
);
if
(
tag
==
"CRA"
)
sprintf
(
chbuf
,
"Audio encryption: %s"
,
value
);
if
(
tag
==
"CRM"
)
sprintf
(
chbuf
,
"Encrypted meta frame: %s"
,
value
);
if
(
tag
==
"ETC"
)
sprintf
(
chbuf
,
"Event timing codes: %s"
,
value
);
if
(
tag
==
"EQU"
)
sprintf
(
chbuf
,
"Equalization: %s"
,
value
);
if
(
tag
==
"IPL"
)
sprintf
(
chbuf
,
"Involved people list: %s"
,
value
);
if
(
tag
==
"PIC"
)
sprintf
(
chbuf
,
"Attached picture: %s"
,
value
);
if
(
tag
==
"SLT"
)
sprintf
(
chbuf
,
"Synchronized lyric/text: %s"
,
value
);
if
(
tag
==
"TAL"
)
sprintf
(
chbuf
,
"Album/Movie/Show title: %s"
,
value
);
if
(
tag
==
"TBP"
)
sprintf
(
chbuf
,
"BPM (Beats Per Minute): %s"
,
value
);
if
(
tag
==
"TCM"
)
sprintf
(
chbuf
,
"Composer: %s"
,
value
);
if
(
tag
==
"TCO"
)
sprintf
(
chbuf
,
"Content type: %s"
,
value
);
if
(
tag
==
"TCR"
)
sprintf
(
chbuf
,
"Copyright message: %s"
,
value
);
if
(
tag
==
"TDA"
)
sprintf
(
chbuf
,
"Date: %s"
,
value
);
if
(
tag
==
"TDY"
)
sprintf
(
chbuf
,
"Playlist delay: %s"
,
value
);
if
(
tag
==
"TEN"
)
sprintf
(
chbuf
,
"Encoded by: %s"
,
value
);
if
(
tag
==
"TFT"
)
sprintf
(
chbuf
,
"File type: %s"
,
value
);
if
(
tag
==
"TIM"
)
sprintf
(
chbuf
,
"Time: %s"
,
value
);
if
(
tag
==
"TKE"
)
sprintf
(
chbuf
,
"Initial key: %s"
,
value
);
if
(
tag
==
"TLA"
)
sprintf
(
chbuf
,
"Language(s): %s"
,
value
);
if
(
tag
==
"TLE"
)
sprintf
(
chbuf
,
"Length: %s"
,
value
);
if
(
tag
==
"TMT"
)
sprintf
(
chbuf
,
"Media type: %s"
,
value
);
if
(
tag
==
"TOA"
)
sprintf
(
chbuf
,
"Original artist(s)/performer(s): %s"
,
value
);
if
(
tag
==
"TOF"
)
sprintf
(
chbuf
,
"Original filename: %s"
,
value
);
if
(
tag
==
"TOL"
)
sprintf
(
chbuf
,
"Original Lyricist(s)/text writer(s): %s"
,
value
);
if
(
tag
==
"TOR"
)
sprintf
(
chbuf
,
"Original release year: %s"
,
value
);
if
(
tag
==
"TOT"
)
sprintf
(
chbuf
,
"Original album/Movie/Show title: %s"
,
value
);
if
(
tag
==
"TP1"
)
sprintf
(
chbuf
,
"Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group: %s"
,
value
);
if
(
tag
==
"TP2"
)
sprintf
(
chbuf
,
"Band/Orchestra/Accompaniment: %s"
,
value
);
if
(
tag
==
"TP3"
)
sprintf
(
chbuf
,
"Conductor/Performer refinement: %s"
,
value
);
if
(
tag
==
"TP4"
)
sprintf
(
chbuf
,
"Interpreted, remixed, or otherwise modified by: %s"
,
value
);
if
(
tag
==
"TPA"
)
sprintf
(
chbuf
,
"Part of a set: %s"
,
value
);
if
(
tag
==
"TPB"
)
sprintf
(
chbuf
,
"Publisher: %s"
,
value
);
if
(
tag
==
"TRC"
)
sprintf
(
chbuf
,
"ISRC (International Standard Recording Code): %s"
,
value
);
if
(
tag
==
"TRD"
)
sprintf
(
chbuf
,
"Recording dates: %s"
,
value
);
if
(
tag
==
"TRK"
)
sprintf
(
chbuf
,
"Track number/Position in set: %s"
,
value
);
if
(
tag
==
"TSI"
)
sprintf
(
chbuf
,
"Size: %s"
,
value
);
if
(
tag
==
"TSS"
)
sprintf
(
chbuf
,
"Software/hardware and settings used for encoding: %s"
,
value
);
if
(
tag
==
"TT1"
)
sprintf
(
chbuf
,
"Content group description: %s"
,
value
);
if
(
tag
==
"TT2"
)
sprintf
(
chbuf
,
"Title/Songname/Content description: %s"
,
value
);
if
(
tag
==
"TT3"
)
sprintf
(
chbuf
,
"Subtitle/Description refinement: %s"
,
value
);
if
(
tag
==
"TXT"
)
sprintf
(
chbuf
,
"Lyricist/text writer: %s"
,
value
);
if
(
tag
==
"TXX"
)
sprintf
(
chbuf
,
"User defined text information frame: %s"
,
value
);
if
(
tag
==
"TYE"
)
sprintf
(
chbuf
,
"Year: %s"
,
value
);
if
(
tag
==
"UFI"
)
sprintf
(
chbuf
,
"Unique file identifier: %s"
,
value
);
if
(
tag
==
"ULT"
)
sprintf
(
chbuf
,
"Unsychronized lyric/text transcription: %s"
,
value
);
if
(
tag
==
"WAF"
)
sprintf
(
chbuf
,
"Official audio file webpage: %s"
,
value
);
if
(
tag
==
"WAR"
)
sprintf
(
chbuf
,
"Official artist/performer webpage: %s"
,
value
);
if
(
tag
==
"WAS"
)
sprintf
(
chbuf
,
"Official audio source webpage: %s"
,
value
);
if
(
tag
==
"WCM"
)
sprintf
(
chbuf
,
"Commercial information: %s"
,
value
);
if
(
tag
==
"WCP"
)
sprintf
(
chbuf
,
"Copyright/Legal information: %s"
,
value
);
if
(
tag
==
"WPB"
)
sprintf
(
chbuf
,
"Publishers official webpage: %s"
,
value
);
if
(
tag
==
"WXX"
)
sprintf
(
chbuf
,
"User defined URL link frame: %s"
,
value
);
// Revision 3
if
(
tag
==
"COMM"
)
sprintf
(
chbuf
,
"Comment: %s"
,
value
);
if
(
tag
==
"OWNE"
)
sprintf
(
chbuf
,
"Ownership: %s"
,
value
);
if
(
tag
==
"PRIV"
)
sprintf
(
chbuf
,
"Private: %s"
,
value
);
if
(
tag
==
"SYLT"
)
sprintf
(
chbuf
,
"SynLyrics: %s"
,
value
);
if
(
tag
==
"TALB"
)
sprintf
(
chbuf
,
"Album: %s"
,
value
);
if
(
tag
==
"TBPM"
)
sprintf
(
chbuf
,
"BeatsPerMinute: %s"
,
value
);
if
(
tag
==
"TCMP"
)
sprintf
(
chbuf
,
"Compilation: %s"
,
value
);
if
(
tag
==
"TCOM"
)
sprintf
(
chbuf
,
"Composer: %s"
,
value
);
if
(
tag
==
"TCOP"
)
sprintf
(
chbuf
,
"Copyright: %s"
,
value
);
if
(
tag
==
"TDAT"
)
sprintf
(
chbuf
,
"Date: %s"
,
value
);
if
(
tag
==
"TEXT"
)
sprintf
(
chbuf
,
"Lyricist: %s"
,
value
);
if
(
tag
==
"TIME"
)
sprintf
(
chbuf
,
"Time: %s"
,
value
);
if
(
tag
==
"TIT1"
)
sprintf
(
chbuf
,
"Grouping: %s"
,
value
);
if
(
tag
==
"TIT2"
)
sprintf
(
chbuf
,
"Title: %s"
,
value
);
if
(
tag
==
"TIT3"
)
sprintf
(
chbuf
,
"Subtitle: %s"
,
value
);
if
(
tag
==
"TLAN"
)
sprintf
(
chbuf
,
"Language: %s"
,
value
);
if
(
tag
==
"TLEN"
)
sprintf
(
chbuf
,
"Length: %s"
,
value
);
if
(
tag
==
"TMED"
)
sprintf
(
chbuf
,
"Media: %s"
,
value
);
if
(
tag
==
"TOAL"
)
sprintf
(
chbuf
,
"OriginalAlbum: %s"
,
value
);
if
(
tag
==
"TOPE"
)
sprintf
(
chbuf
,
"OriginalArtist: %s"
,
value
);
if
(
tag
==
"TORY"
)
sprintf
(
chbuf
,
"OriginalReleaseYear: %s"
,
value
);
if
(
tag
==
"TPE1"
)
sprintf
(
chbuf
,
"Artist: %s"
,
value
);
if
(
tag
==
"TPE2"
)
sprintf
(
chbuf
,
"Band: %s"
,
value
);
if
(
tag
==
"TPE3"
)
sprintf
(
chbuf
,
"Conductor: %s"
,
value
);
if
(
tag
==
"TPE4"
)
sprintf
(
chbuf
,
"InterpretedBy: %s"
,
value
);
if
(
tag
==
"TPOS"
)
sprintf
(
chbuf
,
"PartOfSet: %s"
,
value
);
if
(
tag
==
"TPUB"
)
sprintf
(
chbuf
,
"Publisher: %s"
,
value
);
if
(
tag
==
"TRCK"
)
sprintf
(
chbuf
,
"Track: %s"
,
value
);
if
(
tag
==
"TRDA"
)
sprintf
(
chbuf
,
"RecordingDates: %s"
,
value
);
if
(
tag
==
"TXXX"
)
sprintf
(
chbuf
,
"UserDefinedText: %s"
,
value
);
if
(
tag
==
"TYER"
)
sprintf
(
chbuf
,
"Year: %s"
,
value
);
if
(
tag
==
"USER"
)
sprintf
(
chbuf
,
"TermsOfUse: %s"
,
value
);
if
(
tag
==
"USLT"
)
sprintf
(
chbuf
,
"Lyrics: %s"
,
value
);
if
(
tag
==
"XDOR"
)
sprintf
(
chbuf
,
"OriginalReleaseTime: %s"
,
value
);
if
(
chbuf
[
0
]
!=
0
)
if
(
audio_id3data
)
audio_id3data
(
chbuf
);
}
}
while
(
m_id3Size
>
0
);
}
//-----------------------------------------------------------------------------------------------------------------------------------
void
Audio
::
stopSong
(){
if
(
m_f_running
){
m_f_running
=
false
;
mp3file
.
close
();
}
i2s_zero_dma_buffer
((
i2s_port_t
)
m_i2s_num
);
}
//-----------------------------------------------------------------------------------------------------------------------------------
bool
Audio
::
playChunk
(){
// If we've got data, try and pump it out...
if
(
m_channels
==
1
){
while
(
m_validSamples
)
{
m_lastSample
[
0
]
=
m_outSample
[
m_curSample
];
m_lastSample
[
1
]
=
m_outSample
[
m_curSample
];
if
(
!
playSample
(
m_lastSample
))
{
return
false
;}
// Can't send
m_validSamples
--
;
m_curSample
++
;
}
}
if
(
m_channels
==
2
){
while
(
m_validSamples
)
{
m_lastSample
[
0
]
=
m_outSample
[
m_curSample
*
2
];
m_lastSample
[
1
]
=
m_outSample
[
m_curSample
*
2
+
1
];
if
(
!
playSample
(
m_lastSample
))
{
return
false
;}
// Can't send
m_validSamples
--
;
m_curSample
++
;
}
}
return
true
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
uint16_t
Audio
::
ringused
(){
return
(
m_ringused
);
}
uint16_t
Audio
::
ringfree
(){
return
(
m_ringfree
);
// Free space available
}
//-----------------------------------------------------------------------------------------------------------------------------------
void
Audio
::
loop
()
{
uint16_t
bcw
=
0
;
// bytes can write (in ringbuffer)
uint16_t
bcr
=
0
;
// bytes can read (out ringbuffer)
int16_t
res
=
0
;
// number of bytes getting from client
static
uint32_t
i
=
0
;
// Count loops if clientbuffer is empty
static
uint32_t
chunksize
=
0
;
// Chunkcount read from stream
if
(
!
m_f_running
)
return
;
// Nothing to do here!
// - localfile - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if
(
m_f_localfile
){
m_av
=
mp3file
.
available
();
if
(
m_rbrindex
==
m_rbwindex
)
bcw
=
m_ringbfsize
-
m_rbwindex
;
// only at the start, otherwise ever m_rbrindex!=m_rbwindex
if
((
m_rbrindex
<
m_rbwindex
)
&&
(
m_rbrindex
>=
1600
))
bcw
=
m_ringbfsize
-
m_rbwindex
;
// Part of length to xfer
if
(
m_rbrindex
>
m_rbwindex
)
bcw
=
m_rbrindex
-
m_rbwindex
-
1
;
res
=
mp3file
.
read
(
m_ringbuf
+
m_rbwindex
,
bcw
);
if
(
res
<
0
){;}
// nothing to do
if
(
res
>
0
){
m_count
-=
res
;
m_rbwindex
+=
res
;
}
if
(
m_rbwindex
>
m_rbrindex
){
m_ringused
=
m_rbwindex
-
m_rbrindex
+
1
;
m_ringfree
=
m_ringbfsize
-
1600
-
m_ringused
;}
else
{
m_ringused
=
m_ringbfsize
-
1600
-
(
m_rbrindex
-
m_rbwindex
)
+
1
;
m_ringfree
=
m_ringbfsize
-
1600
-
m_ringused
;}
if
((
m_ringbfsize
==
m_rbwindex
)
&&
(
m_rbrindex
>
1600
))
m_rbwindex
=
1600
;
// at end of the rinbuffer
if
(
m_rbrindex
<
m_rbwindex
){
bcr
=
m_rbwindex
-
m_rbrindex
-
1
;
}
if
(
m_rbrindex
>
m_rbwindex
){
bcr
=
m_ringbfsize
-
m_rbrindex
;
if
(
bcr
<
1600
){
memmove
(
m_ringbuf
+
1600
-
bcr
,
m_ringbuf
+
m_rbrindex
,
bcr
);
m_rbrindex
=
1600
-
bcr
;
bcr
=
m_rbwindex
-
m_rbrindex
;
}
}
if
(
bcr
>=
1600
){
m_rbrindex
+=
sendBytes
(
m_ringbuf
+
m_rbrindex
,
bcr
);
if
(
m_f_stream
==
false
){
m_f_stream
=
true
;
if
(
audio_info
)
audio_info
(
"stream ready"
);
}
}
if
((
m_av
==
0
)
&&
(
m_ringused
<
1600
)){
// No more data from SD Card
mp3file
.
close
();
m_f_localfile
=
false
;
sprintf
(
chbuf
,
"End of mp3file %s"
,
m_mp3title
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
if
(
audio_eof_mp3
)
audio_eof_mp3
(
m_mp3title
.
c_str
());
}
}
// - webstream - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if
(
m_f_webstream
){
// Playing file from URL?
if
(
m_f_ssl
==
false
)
m_av
=
client
.
available
();
// Available from stream
if
(
m_f_ssl
==
true
)
m_av
=
clientsecure
.
available
();
// Available from stream
if
((
m_f_stream_ready
==
false
)
&&
(
m_av
>
0
)){
// first streamdata recognised
m_f_stream_ready
=
true
;
m_f_stream
=
false
;
// unmute
}
if
((
m_datamode
==
AUDIO_DATA
)
&&
((
m_count
>
0
))){
if
(
m_rbrindex
==
m_rbwindex
)
bcw
=
m_ringbfsize
-
m_rbwindex
;
// only at the start, otherwise ever m_rbrindex!=m_rbwindex
if
((
m_rbrindex
<
m_rbwindex
)
&&
(
m_rbrindex
>=
1600
))
bcw
=
m_ringbfsize
-
m_rbwindex
;
// Part of length to xfer
if
(
m_rbrindex
>
m_rbwindex
)
bcw
=
m_rbrindex
-
m_rbwindex
-
1
;
uint
x
;
if
(
m_count
>=
bcw
)
x
=
bcw
;
else
x
=
m_count
;
if
((
m_f_chunked
)
&&
(
x
>
m_chunkcount
))
x
=
m_chunkcount
;
if
(
m_f_ssl
==
false
)
res
=
client
.
read
(
m_ringbuf
+
m_rbwindex
,
x
);
if
(
m_f_ssl
==
true
)
res
=
clientsecure
.
read
(
m_ringbuf
+
m_rbwindex
,
x
);
if
(
res
<
0
){;}
// nothing to do
// log_i("bcw=%i res=%i rbrindex=%i rbwindex=%i",bcw, res, m_rbrindex, m_rbwindex);
if
(
res
>
0
){
m_count
-=
res
;
m_rbwindex
+=
res
;
if
(
m_rbwindex
>
m_rbrindex
){
m_ringused
=
m_rbwindex
-
m_rbrindex
+
1
;
m_ringfree
=
m_ringbfsize
-
1600
-
m_ringused
;}
else
{
m_ringused
=
m_ringbfsize
-
1600
-
(
m_rbrindex
-
m_rbwindex
)
+
1
;
m_ringfree
=
m_ringbfsize
-
1600
-
m_ringused
;}
if
(
m_f_chunked
)
m_chunkcount
-=
res
;
}
if
((
m_ringbfsize
==
m_rbwindex
)
&&
(
m_rbrindex
>
1600
))
m_rbwindex
=
1600
;
// at end of the rinbuffer
if
(
m_rbrindex
<
m_rbwindex
){
bcr
=
m_rbwindex
-
m_rbrindex
-
1
;
}
if
(
m_rbrindex
>
m_rbwindex
){
bcr
=
m_ringbfsize
-
m_rbrindex
;
if
(
bcr
<
1600
){
memmove
(
m_ringbuf
+
1600
-
bcr
,
m_ringbuf
+
m_rbrindex
,
bcr
);
m_rbrindex
=
1600
-
bcr
;
bcr
=
m_rbwindex
-
m_rbrindex
;
}
}
if
(
bcr
>=
1600
){
m_rbrindex
+=
sendBytes
(
m_ringbuf
+
m_rbrindex
,
bcr
);
if
(
m_f_stream
==
false
){
m_f_stream
=
true
;
if
(
audio_info
)
audio_info
(
"stream ready"
);
}
}
else
{
if
(
m_f_stream
==
true
){
m_f_stream
=
false
;
i2s_zero_dma_buffer
((
i2s_port_t
)
m_i2s_num
);
if
(
audio_info
)
audio_info
(
"stream lost"
);
}
}
if
(
m_count
==
0
){
m_datamode
=
AUDIO_METADATA
;
m_f_firstmetabyte
=
true
;
}
}
else
{
//!=DATA
if
(
m_datamode
==
AUDIO_PLAYLISTDATA
)
{
if
(
m_t0
+
49
<
millis
())
{
handlebyte
(
'\n'
);
// send LF
}
}
int16_t
x
;
if
(
m_f_chunked
&&
(
m_datamode
!=
AUDIO_HEADER
)){
if
(
m_chunkcount
>
0
){
if
(
m_f_ssl
==
false
)
x
=
(
client
.
read
());
if
(
m_f_ssl
==
true
)
x
=
(
clientsecure
.
read
());
if
(
x
>=
0
){
handlebyte
(
x
);
m_chunkcount
--
;
}
}
}
else
{
if
(
m_f_ssl
==
false
)
x
=
(
client
.
read
());
if
(
m_f_ssl
==
true
)
x
=
(
clientsecure
.
read
());
if
(
x
>=
0
){
handlebyte
(
x
);
}
}
if
(
m_datamode
==
AUDIO_DATA
)
{
m_count
=
m_metaint
;
if
(
m_metaint
==
0
)
m_datamode
=
AUDIO_SWM
;
// stream without metadata, can be mms
}
}
if
(
m_f_stream_ready
==
true
){
if
(
m_av
==
0
){
// empty buffer, broken stream or bad bitrate?
i
++
;
if
(
i
>
200000
){
// wait several seconds
i
=
0
;
if
(
audio_info
)
audio_info
(
"Stream lost -> try new connection"
);
connecttohost
(
m_lastHost
);
// try a new connection
}
}
else
i
=
0
;
}
if
(
m_f_chunked
==
false
){
return
;}
else
{
if
(
m_datamode
==
AUDIO_HEADER
)
return
;
if
((
m_chunkcount
)
==
0
){
// Expecting a new chunkcount?
int
b
=
(
client
.
read
());
if
(
b
<
1
)
return
;
if
(
b
==
'\r'
){}
else
if
(
b
==
'\n'
){
m_chunkcount
=
chunksize
;
chunksize
=
0
;
}
else
{
// We have received a hexadecimal character. Decode it and add to the result.
b
=
toupper
(
b
)
-
'0'
;
// Be sure we have uppercase
if
(
b
>
9
)
b
=
b
-
7
;
// Translate A..F to 10..15
chunksize
=
(
chunksize
<<
4
)
+
b
;
}
}
}
}
}
//---------------------------------------------------------------------------------------
void
Audio
::
handlebyte
(
uint8_t
b
){
static
uint16_t
playlistcnt
;
// Counter to find right entry in playlist
String
lcml
;
// Lower case metaline
static
String
ct
;
// Contents type
static
String
host
;
int
inx
;
// Pointer in metaline
static
boolean
f_entry
=
false
;
// entryflag for asx playlist
if
(
m_datamode
==
AUDIO_HEADER
){
// Handle next byte of MP3 header
if
((
b
>
0x7F
)
||
(
b
==
'\r'
)
||
(
b
==
'\0'
)){
// Ignore unprintable characters, ignore CR, ignore NULL
;
// Yes, ignore
}
else
if
(
b
==
'\n'
){
// Linefeed ?
m_LFcount
++
;
// Count linefeeds
if
(
chkhdrline
(
m_metaline
.
c_str
())){
// Reasonable input?
lcml
=
m_metaline
;
// Use lower case for compare
lcml
.
toLowerCase
();
lcml
.
trim
();
if
(
lcml
.
indexOf
(
"content-type:"
)
>=
0
){
// Line with "Content-Type: xxxx/yyy"
if
(
lcml
.
indexOf
(
"audio"
)
>=
0
){
// Is ct audio?
m_ctseen
=
true
;
// Yes, remember seeing this
ct
=
m_metaline
.
substring
(
13
);
// Set contentstype. Not used yet
ct
.
trim
();
sprintf
(
chbuf
,
"%s seen."
,
ct
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
if
(
ct
.
indexOf
(
"mpeg"
)
>=
0
){;}
//ok is likely mp3
else
if
(
ct
.
indexOf
(
"aac"
)
>=
0
){
m_f_running
=
false
;
if
(
audio_info
)
audio_info
(
"can't play aac"
);
}
else
if
(
ct
.
indexOf
(
"ogg"
)
>=
0
){
m_f_running
=
false
;
if
(
audio_info
)
audio_info
(
"can't play ogg"
);
}
else
{
m_f_running
=
false
;
if
(
audio_info
)
audio_info
(
"unknown audio format"
);
}
}
if
(
lcml
.
indexOf
(
"ogg"
)
>=
0
){
// Is ct ogg?
m_f_running
=
false
;
m_ctseen
=
true
;
// Yes, remember seeing this
ct
=
m_metaline
.
substring
(
13
);
ct
.
trim
();
sprintf
(
chbuf
,
"%s seen."
,
ct
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
if
(
audio_info
)
audio_info
(
"no ogg decoder implemented"
);
// m_metaint=0; // has no metadata
// m_bitrate=0;
// m_icyname=="";
// m_f_swm=true;
}
}
else
if
(
lcml
.
startsWith
(
"location:"
)){
host
=
m_metaline
.
substring
(
lcml
.
indexOf
(
"http"
),
lcml
.
length
());
// use metaline instead lcml
if
(
host
.
indexOf
(
"&"
)
>
0
)
host
=
host
.
substring
(
0
,
host
.
indexOf
(
"&"
));
// remove parameter
sprintf
(
chbuf
,
"redirect to new host %s"
,
host
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
connecttohost
(
host
);
}
else
if
(
lcml
.
startsWith
(
"icy-br:"
)){
m_bitrate
=
m_metaline
.
substring
(
7
).
toInt
();
// Found bitrate tag, read the bitrate
sprintf
(
chbuf
,
"%d"
,
m_bitrate
);
if
(
audio_bitrate
)
audio_bitrate
(
chbuf
);
}
else
if
(
lcml
.
startsWith
(
"icy-metaint:"
)){
m_metaint
=
m_metaline
.
substring
(
12
).
toInt
();
// Found metaint tag, read the value
//log_i("m_metaint=%i",m_metaint);
if
(
m_metaint
==
0
)
m_metaint
=
16000
;
// if no set to default
}
else
if
(
lcml
.
startsWith
(
"icy-name:"
)){
m_icyname
=
m_metaline
.
substring
(
9
);
// Get station name
m_icyname
.
trim
();
// Remove leading and trailing spaces
if
(
m_icyname
!=
""
){
if
(
audio_showstation
)
audio_showstation
(
m_icyname
.
c_str
());
}
}
else
if
(
lcml
.
startsWith
(
"transfer-encoding:"
)){
// Station provides chunked transfer
if
(
m_metaline
.
endsWith
(
"chunked"
)){
m_f_chunked
=
true
;
if
(
audio_info
)
audio_info
(
"chunked data transfer"
);
m_chunkcount
=
0
;
// Expect chunkcount in DATA
}
}
else
if
(
lcml
.
startsWith
(
"icy-url:"
)){
m_icyurl
=
m_metaline
.
substring
(
8
);
// Get the URL
m_icyurl
.
trim
();
if
(
audio_icyurl
)
audio_icyurl
(
m_icyurl
.
c_str
());
}
else
{
// all other
sprintf
(
chbuf
,
"%s"
,
m_metaline
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
}
}
m_metaline
=
""
;
// Reset this line
if
((
m_LFcount
==
2
)
&&
m_ctseen
){
// Some data seen and a double LF?
if
(
m_icyname
==
""
){
if
(
audio_showstation
)
audio_showstation
(
""
);}
// no icyname available
if
(
m_bitrate
==
0
){
if
(
audio_bitrate
)
audio_bitrate
(
""
);}
// no bitrate received
if
(
m_f_swm
==
true
){
// stream without metadata
m_datamode
=
AUDIO_SWM
;
// Overwrite m_datamode
sprintf
(
chbuf
,
"Switch to SWM, bitrate is %d, metaint is %d"
,
m_bitrate
,
m_metaint
);
// Show bitrate and metaint
if
(
audio_info
)
audio_info
(
chbuf
);
String
lasthost
=
m_lastHost
;
uint
idx
=
lasthost
.
indexOf
(
'?'
);
if
(
idx
>
0
)
lasthost
=
lasthost
.
substring
(
0
,
idx
);
if
(
audio_lasthost
)
audio_lasthost
(
lasthost
.
c_str
());
m_f_swm
=
false
;
}
else
{
m_datamode
=
AUDIO_DATA
;
// Expecting data now
sprintf
(
chbuf
,
"Switch to DATA, bitrate is %d, metaint is %d"
,
m_bitrate
,
m_metaint
);
// Show bitrate and metaint
if
(
audio_info
)
audio_info
(
chbuf
);
String
lasthost
=
m_lastHost
;
uint
idx
=
lasthost
.
indexOf
(
'?'
);
if
(
idx
>
0
)
lasthost
=
lasthost
.
substring
(
0
,
idx
);
if
(
audio_lasthost
)
audio_lasthost
(
lasthost
.
c_str
());
}
delay
(
1000
);
}
}
else
{
m_metaline
+=
(
char
)
b
;
// Normal character, put new char in metaline
m_LFcount
=
0
;
// Reset double CRLF detection
}
return
;
}
// end AUDIO_HEADER
if
(
m_datamode
==
AUDIO_METADATA
){
// Handle next byte of metadata
if
(
m_f_firstmetabyte
){
// First byte of metadata?
m_f_firstmetabyte
=
false
;
// Not the first anymore
m_metacount
=
b
*
16
+
1
;
// New count for metadata including length byte
if
(
m_metacount
>
1
){
sprintf
(
chbuf
,
"Metadata block %d bytes"
,
// Most of the time there are zero bytes of metadata
m_metacount
-
1
);
if
(
audio_info
)
audio_info
(
chbuf
);
}
m_metaline
=
""
;
// Set to empty
}
else
{
m_metaline
+=
(
char
)
b
;
// Normal character, put new char in metaline
}
if
(
--
m_metacount
==
0
){
if
(
m_metaline
.
length
()){
// Any info present?
// metaline contains artist and song name. For example:
// "StreamTitle='Don McLean - American Pie';StreamUrl='';"
// Sometimes it is just other info like:
// "StreamTitle='60s 03 05 Magic60s';StreamUrl='';"
// Isolate the StreamTitle, remove leading and trailing quotes if present.
//log_i("ST %s", m_metaline.c_str());
if
(
!
m_f_localfile
)
showstreamtitle
(
m_metaline
.
c_str
(),
true
);
// Show artist and title if present in metadata
}
if
(
m_metaline
.
length
()
>
1500
){
// Unlikely metaline length?
if
(
audio_info
)
audio_info
(
"Metadata block to long! Skipping all Metadata from now on."
);
m_metaint
=
16000
;
// Probably no metadata
m_metaline
=
""
;
// Do not waste memory on this
}
m_datamode
=
AUDIO_DATA
;
// Expecting data
}
}
if
(
m_datamode
==
AUDIO_PLAYLISTINIT
)
// Initialize for receive .m3u file
{
// We are going to use metadata to read the lines from the .m3u file
// Sometimes this will only contain a single line
f_entry
=
false
;
// no entry found yet (asx playlist)
m_metaline
=
""
;
// Prepare for new line
m_LFcount
=
0
;
// For detection end of header
m_datamode
=
AUDIO_PLAYLISTHEADER
;
// Handle playlist data
playlistcnt
=
1
;
// Reset for compare
m_totalcount
=
0
;
// Reset totalcount
if
(
audio_info
)
audio_info
(
"Read from playlist"
);
}
if
(
m_datamode
==
AUDIO_PLAYLISTHEADER
){
// Read header
if
((
b
>
0x7F
)
||
(
b
==
'\r'
)
||
(
b
==
'\0'
)){
// Ignore unprintable characters, ignore CR, ignore NULL // Ignore CR
;
// Yes, ignore
}
else
if
(
b
==
'\n'
){
// Linefeed ?
m_LFcount
++
;
// Count linefeeds
sprintf
(
chbuf
,
"Playlistheader: %s"
,
m_metaline
.
c_str
());
// Show playlistheader
if
(
audio_info
)
audio_info
(
chbuf
);
lcml
=
m_metaline
;
// Use lower case for compare
lcml
.
toLowerCase
();
lcml
.
trim
();
if
(
lcml
.
startsWith
(
"location:"
)){
host
=
m_metaline
.
substring
(
lcml
.
indexOf
(
"http"
),
lcml
.
length
());
// use metaline instead lcml
if
(
host
.
indexOf
(
"&"
)
>
0
)
host
=
host
.
substring
(
0
,
host
.
indexOf
(
"&"
));
// remove parameter
sprintf
(
chbuf
,
"redirect to new host %s"
,
host
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
connecttohost
(
host
);
}
m_metaline
=
""
;
// Ready for next line
if
(
m_LFcount
==
2
)
{
if
(
audio_info
)
audio_info
(
"Switch to PLAYLISTDATA"
);
m_datamode
=
AUDIO_PLAYLISTDATA
;
// Expecting data now
m_t0
=
millis
();
return
;
}
}
else
{
m_metaline
+=
(
char
)
b
;
// Normal character, put new char in metaline
m_LFcount
=
0
;
// Reset double CRLF detection
}
}
if
(
m_datamode
==
AUDIO_PLAYLISTDATA
){
// Read next byte of .m3u file data
m_t0
=
millis
();
if
((
b
>
0x7F
)
||
// Ignore unprintable characters
(
b
==
'\r'
)
||
// Ignore CR
(
b
==
'\0'
))
// Ignore NULL
{
/* Yes, ignore */
}
else
if
(
b
==
'\n'
){
// Linefeed or end of string?
sprintf
(
chbuf
,
"Playlistdata: %s"
,
m_metaline
.
c_str
());
// Show playlistdata
if
(
audio_info
)
audio_info
(
chbuf
);
if
(
m_playlist
.
endsWith
(
"m3u"
)){
if
(
m_metaline
.
length
()
<
5
)
{
// Skip short lines
m_metaline
=
""
;
// Flush line
return
;}
if
(
m_metaline
.
indexOf
(
"#EXTINF:"
)
>=
0
){
// Info?
if
(
m_playlist_num
==
playlistcnt
){
// Info for this entry?
inx
=
m_metaline
.
indexOf
(
","
);
// Comma in this line?
if
(
inx
>
0
){
// Show artist and title if present in metadata
//if(vs1053_showstation) vs1053_showstation(m_metaline.substring(inx + 1).c_str());
if
(
audio_info
)
audio_info
(
m_metaline
.
substring
(
inx
+
1
).
c_str
());
}
}
}
if
(
m_metaline
.
startsWith
(
"#"
)){
// Commentline?
m_metaline
=
""
;
return
;}
// Ignore commentlines
// Now we have an URL for a .mp3 file or stream. Is it the rigth one?
//if(metaline.indexOf("&")>0)metaline=host.substring(0,metaline.indexOf("&"));
sprintf
(
chbuf
,
"Entry %d in playlist found: %s"
,
playlistcnt
,
m_metaline
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
if
(
m_metaline
.
indexOf
(
"&"
)){
m_metaline
=
m_metaline
.
substring
(
0
,
m_metaline
.
indexOf
(
"&"
));}
if
(
m_playlist_num
==
playlistcnt
){
inx
=
m_metaline
.
indexOf
(
"http://"
);
// Search for "http://"
if
(
inx
>=
0
){
// Does URL contain "http://"?
host
=
m_metaline
.
substring
(
inx
+
7
);}
// Yes, remove it and set host
else
{
host
=
m_metaline
;}
// Yes, set new host
//log_i("connecttohost %s", host.c_str());
connecttohost
(
host
);
// Connect to it
}
m_metaline
=
""
;
host
=
m_playlist
;
// Back to the .m3u host
playlistcnt
++
;
// Next entry in playlist
}
//m3u
if
(
m_playlist
.
endsWith
(
"pls"
)){
if
(
m_metaline
.
startsWith
(
"File1"
)){
inx
=
m_metaline
.
indexOf
(
"http://"
);
// Search for "http://"
if
(
inx
>=
0
){
// Does URL contain "http://"?
m_plsURL
=
m_metaline
.
substring
(
inx
+
7
);
// Yes, remove it
if
(
m_plsURL
.
indexOf
(
"&"
)
>
0
)
m_plsURL
=
m_plsURL
.
substring
(
0
,
m_plsURL
.
indexOf
(
"&"
));
// remove parameter
// Now we have an URL for a .mp3 file or stream in host.
m_f_plsFile
=
true
;
}
}
if
(
m_metaline
.
startsWith
(
"Title1"
)){
m_plsStationName
=
m_metaline
.
substring
(
7
);
if
(
audio_showstation
)
audio_showstation
(
m_plsStationName
.
c_str
());
sprintf
(
chbuf
,
"StationName: %s"
,
m_plsStationName
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
m_f_plsTitle
=
true
;
}
if
(
m_metaline
.
startsWith
(
"Length1"
))
m_f_plsTitle
=
true
;
// if no Title is available
if
((
m_f_plsFile
==
true
)
&&
(
m_metaline
.
length
()
==
0
))
m_f_plsTitle
=
true
;
m_metaline
=
""
;
if
(
m_f_plsFile
&&
m_f_plsTitle
){
//we have both StationName and StationURL
m_f_plsFile
=
false
;
m_f_plsTitle
=
false
;
//log_i("connecttohost %s", m_plsURL.c_str());
connecttohost
(
m_plsURL
);
// Connect to it
}
}
//pls
if
(
m_playlist
.
endsWith
(
"asx"
)){
String
ml
=
m_metaline
;
ml
.
toLowerCase
();
// use lowercases
if
(
ml
.
indexOf
(
"<entry>"
)
>=
0
)
f_entry
=
true
;
// found entry tag (returns -1 if not found)
if
(
f_entry
){
if
(
ml
.
indexOf
(
"ref href"
)
>
0
){
inx
=
ml
.
indexOf
(
"http://"
);
if
(
inx
>
0
){
m_plsURL
=
m_metaline
.
substring
(
inx
+
7
);
// Yes, remove it
if
(
m_plsURL
.
indexOf
(
'"'
)
>
0
)
m_plsURL
=
m_plsURL
.
substring
(
0
,
m_plsURL
.
indexOf
(
'"'
));
// remove rest
// Now we have an URL for a stream in host.
m_f_plsFile
=
true
;
}
}
if
(
ml
.
indexOf
(
"<title>"
)
>=
0
){
m_plsStationName
=
m_metaline
.
substring
(
7
);
if
(
m_plsURL
.
indexOf
(
'<'
)
>
0
)
m_plsURL
=
m_plsURL
.
substring
(
0
,
m_plsURL
.
indexOf
(
'<'
));
// remove rest
if
(
audio_showstation
)
audio_showstation
(
m_plsStationName
.
c_str
());
sprintf
(
chbuf
,
"StationName: %s"
,
m_plsStationName
.
c_str
());
if
(
audio_info
)
audio_info
(
chbuf
);
m_f_plsTitle
=
true
;
}
}
//entry
m_metaline
=
""
;
if
(
m_f_plsFile
&&
m_f_plsTitle
){
//we have both StationName and StationURL
m_f_plsFile
=
false
;
m_f_plsTitle
=
false
;
//log_i("connecttohost %s", host.c_str());
connecttohost
(
m_plsURL
);
// Connect to it
}
}
//asx
}
else
{
m_metaline
+=
(
char
)
b
;
// Normal character, add it to metaline
}
return
;
}
}
//-----------------------------------------------------------------------------------------------------------------------------------
void
Audio
::
showstreamtitle
(
const
char
*
ml
,
bool
full
){
// example for ml:
// StreamTitle='Oliver Frank - Mega Hitmix';StreamUrl='www.radio-welle-woerthersee.at';
// or adw_ad='true';durationMilliseconds='10135';adId='34254';insertionType='preroll';
int16_t
pos1
=
0
,
pos2
=
0
,
pos3
=
0
,
pos4
=
0
;
String
mline
=
ml
,
st
=
""
,
su
=
""
,
ad
=
""
,
artist
=
""
,
title
=
""
,
icyurl
=
""
;
//log_i("%s",mline.c_str());
pos1
=
mline
.
indexOf
(
"StreamTitle="
);
if
(
pos1
!=-
1
){
// StreamTitle found
pos1
=
pos1
+
12
;
st
=
mline
.
substring
(
pos1
);
// remove "StreamTitle="
// log_i("st_orig %s", st.c_str());
if
(
st
.
startsWith
(
"'{"
)){
// special codig like '{"t":"\u041f\u0438\u043a\u043d\u0438\u043a - \u0418...."m":"mdb","lAU":0,"lAuU":18}
pos2
=
st
.
indexOf
(
'"'
,
8
);
// end of '{"t":".......", seek for double quote at pos 8
st
=
st
.
substring
(
0
,
pos2
);
pos2
=
st
.
lastIndexOf
(
'"'
);
st
=
st
.
substring
(
pos2
+
1
);
// remove '{"t":"
pos2
=
0
;
String
uni
=
""
;
String
st1
=
""
;
uint16_t
u
=
0
;
uint8_t
v
=
0
,
w
=
0
;
for
(
int
i
=
0
;
i
<
st
.
length
();
i
++
){
if
(
pos2
>
1
)
pos2
++
;
if
((
st
[
i
]
==
'\\'
)
&&
(
pos2
==
0
))
pos2
=
1
;
// backslash found
if
((
st
[
i
]
==
'u'
)
&&
(
pos2
==
1
))
pos2
=
2
;
// "\u" found
if
(
pos2
>
2
)
uni
=
uni
+
st
[
i
];
// next 4 values are unicode
if
(
pos2
==
0
)
st1
+=
st
[
i
];
// normal character
if
(
pos2
>
5
){
pos2
=
0
;
u
=
strtol
(
uni
.
c_str
(),
0
,
16
);
// convert hex to int
v
=
u
/
64
+
0xC0
;
st1
+=
char
(
v
);
// compute UTF-8
w
=
u
%
64
+
0x80
;
st1
+=
char
(
w
);
//log_i("uni %i %i", v, w );
uni
=
""
;
}
}
log_i
(
"st1 %s"
,
st1
.
c_str
());
st
=
st1
;
}
else
{
// normal coding
if
(
st
.
indexOf
(
'&'
)
!=-
1
){
// maybe html coded
st
.
replace
(
"Ä"
,
"Ä"
);
st
.
replace
(
"ä"
,
"ä"
);
//HTML -> ASCII
st
.
replace
(
"Ö"
,
"Ö"
);
st
.
replace
(
"ö"
,
"o"
);
st
.
replace
(
"Ü"
,
"Ü"
);
st
.
replace
(
"ü"
,
"ü"
);
st
.
replace
(
"ß"
,
"ß"
);
st
.
replace
(
"&"
,
"&"
);
st
.
replace
(
"""
,
"
\"
"
);
st
.
replace
(
"<"
,
"<"
);
st
.
replace
(
">"
,
">"
);
st
.
replace
(
"'"
,
"'"
);
}
pos2
=
st
.
indexOf
(
';'
,
1
);
// end of StreamTitle, first occurence of ';'
if
(
pos2
!=-
1
)
st
=
st
.
substring
(
0
,
pos2
);
// extract StreamTitle
if
(
st
.
startsWith
(
"'"
))
st
=
st
.
substring
(
1
,
st
.
length
()
-
1
);
// if exists remove ' at the begin and end
pos3
=
st
.
lastIndexOf
(
" - "
);
// separator artist - title
if
(
pos3
!=-
1
){
// found separator? yes
artist
=
st
.
substring
(
0
,
pos3
);
// artist not used yet
title
=
st
.
substring
(
pos3
+
3
);
// title not used yet
}
}
if
(
m_st_remember
!=
st
){
// show only changes
if
(
audio_showstreamtitle
)
audio_showstreamtitle
(
st
.
c_str
());
}
m_st_remember
=
st
;
st
=
"StreamTitle="
+
st
;
if
(
audio_info
)
audio_info
(
st
.
c_str
());
}
pos4
=
mline
.
indexOf
(
"StreamUrl="
);
if
(
pos4
!=-
1
){
// StreamUrl found
pos4
=
pos4
+
10
;
su
=
mline
.
substring
(
pos4
);
// remove "StreamUrl="
pos2
=
su
.
indexOf
(
';'
,
1
);
// end of StreamUrl, first occurence of ';'
if
(
pos2
!=-
1
)
su
=
su
.
substring
(
0
,
pos2
);
// extract StreamUrl
if
(
su
.
startsWith
(
"'"
))
su
=
su
.
substring
(
1
,
su
.
length
()
-
1
);
// if exists remove ' at the begin and end
su
=
"StreamUrl="
+
su
;
if
(
audio_info
)
audio_info
(
su
.
c_str
());
}
pos2
=
mline
.
indexOf
(
"adw_ad="
);
// advertising,
if
(
pos2
!=-
1
){
ad
=
mline
.
substring
(
pos2
);
if
(
audio_info
)
audio_info
(
ad
.
c_str
());
pos2
=
mline
.
indexOf
(
"durationMilliseconds="
);
if
(
pos2
!=-
1
){
pos2
+=
22
;
mline
=
mline
.
substring
(
pos2
);
mline
=
mline
.
substring
(
0
,
mline
.
indexOf
(
"'"
)
-
3
);
// extract duration in sec
if
(
audio_commercial
)
audio_commercial
(
mline
.
c_str
());
}
}
if
(
!
full
){
m_icystreamtitle
=
""
;
// Unknown type
return
;
// Do not show
}
if
(
pos1
==-
1
&&
pos4
==-
1
){
// Info probably from playlist
st
=
mline
;
if
(
audio_showstreamtitle
)
audio_showstreamtitle
(
st
.
c_str
());
st
=
"Streamtitle: "
+
st
;
if
(
audio_info
)
audio_info
(
chbuf
);
}
}
//---------------------------------------------------------------------------------------
bool
Audio
::
chkhdrline
(
const
char
*
str
){
char
b
;
// Byte examined
int
len
=
0
;
// Lengte van de string
while
((
b
=
*
str
++
)){
// Search to end of string
len
++
;
// Update string length
if
(
!
isalpha
(
b
)){
// Alpha (a-z, A-Z)
if
(
b
!=
'-'
){
// Minus sign is allowed
if
((
b
==
':'
)
||
(
b
==
';'
)){
// Found a colon or semicolon?
return
((
len
>
5
)
&&
(
len
<
200
));
// Yes, okay if length is okay
}
else
{
return
false
;
// Not a legal character
}
}
}
}
return
false
;
// End of string without colon
}
//-----------------------------------------------------------------------------------------------------------------------------------
int
Audio
::
sendBytes
(
uint8_t
*
data
,
size_t
len
)
{
if
(
!
playChunk
())
{;}
// outputbuffer full or not ready?
m_nextSync
=
MP3FindSyncWord
(
data
,
len
);
if
(
m_nextSync
==-
1
)
{
log_i
(
"syncword not found"
);
return
0
;}
if
(
m_nextSync
>
0
){
log_i
(
"nextSync=%i"
,
m_nextSync
);
//memmove(data, data + m_nextSync, len - m_nextSync); // throw out prior to nextSync
return
m_nextSync
;
}
m_bytesLeft
=
len
;
int
ret
=
MP3Decode
(
helixMP3Decoder
,
data
,
&
m_bytesLeft
,
m_outSample
,
0
);
int
bytesDecoded
=
len
-
m_bytesLeft
;
//memmove(data, data + bytesDecoded, len - bytesDecoded);
if
(
ret
){
// Error, skip the frame...
sprintf
(
chbuf
,
"MP3 decode error %d"
,
ret
);
if
(
audio_info
)
audio_info
(
chbuf
);
}
else
{
MP3FrameInfo
fi
;
MP3GetLastFrameInfo
(
helixMP3Decoder
,
&
fi
);
if
((
int
)
fi
.
samprate
!=
(
int
)
m_lastRate
)
{
m_lastRate
=
fi
.
samprate
;
setSampleRate
(
fi
.
samprate
);
sprintf
(
chbuf
,
"SampleRate=%i"
,
fi
.
samprate
);
if
(
audio_info
)
audio_info
(
chbuf
);
}
if
(
fi
.
nChans
!=
m_lastChannels
)
{
setChannels
(
fi
.
nChans
);
m_lastChannels
=
fi
.
nChans
;
sprintf
(
chbuf
,
"Channels=%i"
,
fi
.
nChans
);
if
(
audio_info
)
audio_info
(
chbuf
);
}
if
(
fi
.
bitsPerSample
!=
(
int
)
m_bps
){
m_bps
=
fi
.
bitsPerSample
;
sprintf
(
chbuf
,
"BitsPerSample=%i"
,
fi
.
bitsPerSample
);
if
(
audio_info
)
audio_info
(
chbuf
);
}
// if (fi.bitrate != m_bitrate){
// m_bitrate=fi.bitrate;
// sprintf(chbuf,"Bitrate=%i", fi.bitrate);
// if(audio_info) audio_info(chbuf);
// }
m_curSample
=
0
;
m_validSamples
=
fi
.
outputSamps
/
m_lastChannels
;
}
return
bytesDecoded
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
bool
Audio
::
setPinout
(
uint8_t
BCLK
,
uint8_t
LRC
,
uint8_t
DOUT
){
m_BCLK
=
BCLK
;
// Bit Clock
m_LRC
=
LRC
;
// Left/Right Clock
m_DOUT
=
DOUT
;
// Data Out
i2s_pin_config_t
pins
=
{
.
bck_io_num
=
m_BCLK
,
.
ws_io_num
=
m_LRC
,
// wclk,
.
data_out_num
=
m_DOUT
,
.
data_in_num
=
I2S_PIN_NO_CHANGE
};
i2s_set_pin
((
i2s_port_t
)
m_i2s_num
,
&
pins
);
return
true
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
uint32_t
Audio
::
getFileSize
(){
if
(
!
mp3file
)
return
0
;
return
mp3file
.
size
();
}
//-----------------------------------------------------------------------------------------------------------------------------------
uint32_t
Audio
::
getFilePos
(){
if
(
!
mp3file
)
return
0
;
return
mp3file
.
position
();
}
//-----------------------------------------------------------------------------------------------------------------------------------
bool
Audio
::
setFilePos
(
uint32_t
pos
){
if
(
!
mp3file
)
return
false
;
return
mp3file
.
seek
(
pos
);
}
//-----------------------------------------------------------------------------------------------------------------------------------
void
Audio
::
construct_OutBuf
(
int
buffSizeSamples
)
{
m_buffSize
=
buffSizeSamples
;
m_leftSample
=
(
int16_t
*
)
malloc
(
sizeof
(
int16_t
)
*
m_buffSize
);
m_rightSample
=
(
int16_t
*
)
malloc
(
sizeof
(
int16_t
)
*
m_buffSize
);
m_writePtr
=
0
;
m_readPtr
=
0
;
m_f_filled
=
false
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
void
Audio
::
destruct_OutBuf
()
{
free
(
m_leftSample
);
free
(
m_rightSample
);
}
//-----------------------------------------------------------------------------------------------------------------------------------
bool
Audio
::
setSampleRate
(
int
freq
)
{
i2s_set_sample_rates
((
i2s_port_t
)
m_i2s_num
,
freq
);
return
true
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
bool
Audio
::
setBitsPerSample
(
int
bits
)
{
//return sink->SetBitsPerSample(bits);
if
(
(
bits
!=
16
)
&&
(
bits
!=
8
)
)
return
false
;
m_bps
=
bits
;
return
true
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
bool
Audio
::
setChannels
(
int
ch
)
{
if
(
(
ch
<
1
)
||
(
ch
>
2
)
)
return
false
;
m_channels
=
ch
;
return
true
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
bool
Audio
::
playSample
(
int16_t
sample
[
2
])
{
// First, try and fill I2S...
if
(
m_f_filled
)
{
while
(
m_readPtr
!=
m_writePtr
)
{
int16_t
s
[
2
]
=
{
m_leftSample
[
m_readPtr
],
m_rightSample
[
m_readPtr
]
};
// Mono to "stereo" conversion
if
(
m_channels
==
1
)
// s[RIGHTCHANNEL] = s[LEFTCHANNEL];
if
(
m_bps
==
8
)
{
// Upsample from unsigned 8 bits to signed 16 bits
s
[
LEFTCHANNEL
]
=
(((
int16_t
)(
s
[
LEFTCHANNEL
]
&
0xff
))
-
128
)
<<
8
;
s
[
RIGHTCHANNEL
]
=
(((
int16_t
)(
s
[
RIGHTCHANNEL
]
&
0xff
))
-
128
)
<<
8
;
}
uint32_t
s32
;
s32
=
((
Gain
(
s
[
RIGHTCHANNEL
]))
<<
16
)
|
(
Gain
(
s
[
LEFTCHANNEL
])
&
0xffff
);
// volume
esp_err_t
err
=
i2s_write
((
i2s_port_t
)
m_i2s_num
,
(
const
char
*
)
&
s32
,
sizeof
(
uint32_t
),
&
m_bytesWritten
,
100
);
if
(
err
!=
ESP_OK
){
log_e
(
"ESP32 Errorcode %i"
,
err
);
break
;
// Can't stuff any more in I2S...
}
m_readPtr
=
(
m_readPtr
+
1
)
%
m_buffSize
;
}
}
// Now, do we have space for a new sample?
int
nextWritePtr
=
(
m_writePtr
+
1
)
%
m_buffSize
;
if
(
nextWritePtr
==
m_readPtr
)
{
m_f_filled
=
true
;
return
false
;
}
m_leftSample
[
m_writePtr
]
=
sample
[
LEFTCHANNEL
];
m_rightSample
[
m_writePtr
]
=
sample
[
RIGHTCHANNEL
];
m_writePtr
=
nextWritePtr
;
return
true
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
void
Audio
::
setVolume
(
uint8_t
vol
){
// vol 22 steps, 0...21
if
(
vol
>
21
)
vol
=
21
;
m_vol
=
volumetable
[
vol
];
}
//-----------------------------------------------------------------------------------------------------------------------------------
uint8_t
Audio
::
getVolume
(){
return
m_vol
;
}
//-----------------------------------------------------------------------------------------------------------------------------------
int16_t
Audio
::
Gain
(
int16_t
s
)
{
int32_t
v
;
v
=
(
s
*
m_vol
)
>>
6
;
return
(
int16_t
)(
v
&
0xffff
);
}
//-----------------------------------------------------------------------------------------------------------------------------------
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