Unverified Commit cad2d637 authored by Wolle's avatar Wolle Committed by GitHub

Add files via upload

can compiled with Arduino IDE
mp3 decoder is more reliable now
greater inputbuffer
parent cdc8e61e
/*
* Audio.cpp
*
* Created on: 26.10.2018
* Updated on: 16.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() {
EraseBuffers();
//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() {
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_lastHost=host; // Remember the current host
}
sprintf(chbuf, "Connect to new host: %s", host.c_str());
if(audio_info) audio_info(chbuf);
// initializationsequence
m_f_ctseen=false; // Contents type not seen yet
m_f_mp3=false; // Set if stream is mp3
m_f_aac=false; // Set if stream is aac
m_f_stream_ready=false;
m_f_firstmetabyte=false;
m_f_chunked=false; // Assume not chunked
m_f_ssl=false;
m_f_swm=true; // Assume no metaint
m_f_playing=false;
m_count=0;
m_chunkcount=0;
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_rbwindex=1600;
m_rbrindex=1600;
setDatamode(AUDIO_HEADER); // Handle header
for(int i=0; i< sizeof(m_outBuff)/sizeof(m_outBuff[0]); i++) m_outBuff[i]=0;//Clear OutputBuffer
// memset(&m_outBuff[0], 0, sizeof(m_outBuff)); //Clear OutputBuffer
if(host.startsWith("http://")) {host=host.substring(7); m_f_ssl=false; ;}
if(host.startsWith("https://")){host=host.substring(8); m_f_ssl=true; port=443;}
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);
if(audio_info) audio_info(chbuf); // Most of the time there are zero bytes of metadata
}
// 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(), port)){
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_f_playing=false;
m_rbwindex=1600;
m_rbrindex=1600;
clientsecure.stop(); clientsecure.flush(); // release memory if allocated
m_f_localfile=true;
m_f_webstream=false;
m_f_stream_ready=false;
m_f_mp3=true;
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_playing=false;
m_f_stream_ready=false;
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;
}
m_rbwindex=0;
m_f_mp3=true;
uint16_t res;
while(clientsecure.available()==0){;}
while(clientsecure.available() > 0) {
// 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){
m_f_stream_ready=true;
int x=sendBytes(m_ringbuf,1600 );
m_rbwindex-=x;
memmove(m_ringbuf, m_ringbuf + x , 1600-x);
}
}
m_f_stream_ready=true;
m_rbwindex-=sendBytes(m_ringbuf, 1600-m_ringfree);
if(audio_eof_mp3) audio_eof_mp3(chbuf);
stopSong();
clientsecure.stop(); clientsecure.flush();
m_f_mp3=false;
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_outBuff[m_curSample];
m_lastSample[1] = m_outBuff[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_outBuff[m_curSample * 2];
m_lastSample[1] = m_outBuff[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;
m_f_stream_ready=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_datamode==AUDIO_SWM))&&((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){
if(m_f_stream==false){
m_f_stream=true;
if(audio_info) audio_info("stream ready");
}
m_rbrindex+=sendBytes(m_ringbuf+m_rbrindex, bcr);
}
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){
if(m_datamode==AUDIO_SWM){
m_count=16000; //mms has no metadata
}
else{
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=0;;
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_datamode == AUDIO_SWM) {
m_count =16000;
}
}
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){
m_f_mp3=true;
if(audio_info) audio_info("format is mp3"); //ok is likely mp3
}
else if(ct.indexOf("aac")>=0){
m_f_aac=true;
if(audio_info) audio_info("format is aac");
stopSong();
}
else if(ct.indexOf("mp4")>=0){
m_f_aac=true;
if(audio_info) audio_info("format is aac");
stopSong();
}
else if(ct.indexOf("ogg")>=0){
m_f_running=false;
if(audio_info) audio_info("can't play ogg");
stopSong();
}
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");
stopSong();
// 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
if(m_metaint>0) m_f_swm=false; // Multimediastream
}
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, "Metacount=%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("&Auml;", "Ä" ); st.replace("&auml;", "ä"); //HTML -> ASCII
st.replace("&Ouml;", "Ö" ); st.replace("&ouml;", "o");
st.replace("&Uuml;", "Ü" ); st.replace("&uuml;", "ü");
st.replace("&szlig;","ß" ); st.replace("&amp;", "&");
st.replace("&quot;", "\""); st.replace("&lt;", "<");
st.replace("&gt;", ">" ); st.replace("&apos;", "'");
}
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(m_f_stream_ready==false) return 0;
static int lastret=0, count=0;
if(!m_f_playing){
if(m_f_mp3) 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);
return m_nextSync;
}
count=0;
m_lastRate=0;
m_lastChannels=0;
m_bps=0;
m_bitrate=0;
m_f_playing=true;
}
m_bytesLeft=len;
int ret=0;
if(m_f_mp3) ret = MP3Decode( data, &m_bytesLeft, m_outBuff, 0);
if(ret==0) lastret=0;
int bytesDecoded=len-m_bytesLeft;
if(ret){ // Error, skip the frame...
if(lastret==ret) count++;
if(count>100){ count=0; lastret=0; } // more than 100 errors
if((m_f_mp3)&&(count==100)){
sprintf(chbuf, "MP3 decode error %d", ret);
if(audio_info) audio_info(chbuf);
}
if(ret!=lastret){
lastret=ret; count=0;
i2s_zero_dma_buffer((i2s_port_t)m_i2s_num);
}
if(bytesDecoded<1) return 1; // seek for new syncWord
return bytesDecoded;
}
else{
int SampleRate=MP3GetSampRate();
int Channels=MP3GetChannels();
int BitsPerSample=MP3GetBitsPerSample();
int OutputSamps=MP3GetOutputSamps();
int Bitrate=MP3GetBitrate();
if(m_f_mp3){
if ((int) SampleRate != (int) m_lastRate) {
m_lastRate = SampleRate;
setSampleRate(SampleRate);
sprintf(chbuf,"SampleRate=%i",SampleRate);
if(audio_info) audio_info(chbuf);
}
if((int) SampleRate ==0){ // error can't be
if(audio_info) audio_info("SampleRate=0, try new frame");
m_f_playing=false;
return 1;
}
if (Channels != m_lastChannels) {
setChannels(Channels);
m_lastChannels = Channels;
sprintf(chbuf,"Channels=%i", Channels);
if(audio_info) audio_info(chbuf);
}
if (BitsPerSample != (int)m_bps){
m_bps=BitsPerSample;
sprintf(chbuf,"BitsPerSample=%i", BitsPerSample);
if(audio_info) audio_info(chbuf);
}
if (Bitrate != m_bitrate){
if(m_bitrate==0){
sprintf(chbuf,"Bitrate=%i", Bitrate); // show only the first rate
if(audio_info) audio_info(chbuf);
}
m_bitrate=Bitrate;
}
m_curSample = 0;
m_validSamples = OutputSamps / m_lastChannels;
}
}
if(!playChunk()) {;} // outputbuffer full or not ready?
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);
}
//---------------------------------------------------------------------------------------------------------------------
/*
* Audio.h
*
* Created on: 26.10.2018
* Updated on: 04.11.2018
* Author: Wolle (schreibfaul1)
*/
#ifndef AUDIO_H_
#define AUDIO_H_
#include "Arduino.h"
#include "SPI.h"
#include "SD.h"
#include "FS.h"
#include "WiFiClientSecure.h"
#include "mp3_decoder/mp3_decoder.h"
#include "driver/i2s.h"
extern __attribute__((weak)) void audio_info(const char*);
extern __attribute__((weak)) void audio_id3data(const char*); //ID3 metadata
extern __attribute__((weak)) void audio_eof_mp3(const char*); //end of mp3 file
extern __attribute__((weak)) void audio_showstreamtitle(const char*);
extern __attribute__((weak)) void audio_showstation(const char*);
extern __attribute__((weak)) void audio_showstreaminfo(const char*);
extern __attribute__((weak)) void audio_bitrate(const char*);
extern __attribute__((weak)) void audio_commercial(const char*);
extern __attribute__((weak)) void audio_icyurl(const char*);
extern __attribute__((weak)) void audio_lasthost(const char*);
extern __attribute__((weak)) void audio_eof_speech(const char*);
#define AUDIO_HEADER 2 //const for datamode
#define AUDIO_DATA 4
#define AUDIO_METADATA 8
#define AUDIO_PLAYLISTINIT 16
#define AUDIO_PLAYLISTHEADER 32
#define AUDIO_PLAYLISTDATA 64
#define AUDIO_SWM 128
//
class Audio {
public:
Audio();
~Audio();
bool connecttoSD(String sdfile);
bool connecttohost(String host);
bool connecttospeech(String speech, String lang);
void loop();
uint32_t getFileSize();
uint32_t getFilePos();
bool setFilePos(uint32_t pos);
bool setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT);
void stopSong();
void setVolume(uint8_t vol);
uint8_t getVolume();
uint16_t ringused();
uint16_t ringfree();
private:
int sendBytes(uint8_t *data, size_t len);
void readID3Metadata();
void construct_OutBuf(int buffSizeSamples);
void destruct_OutBuf();
bool setSampleRate(int hz);
bool setBitsPerSample(int bits);
bool setChannels(int channels);
bool playChunk();
bool playSample(int16_t sample[2]) ;
int16_t Gain(int16_t s);
bool fill_InputBuf();
void showstreamtitle(const char *ml, bool full);
bool chkhdrline(const char* str);
void handlebyte(uint8_t b);
esp_err_t I2Sstart(uint8_t i2s_num);
esp_err_t I2Sstop(uint8_t i2s_num);
char* lltoa(long long val, int base);
long long int XL (long long int a, const char* b);
String urlencode(String str);
inline uint8_t getDatamode(){return m_datamode;}
inline void setDatamode(uint8_t dm){m_datamode=dm;}
inline uint32_t streamavail() {if(m_f_ssl==false) return client.available(); else return clientsecure.available();}
private:
enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 };
enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 };
typedef enum { LEFTCHANNEL=0, RIGHTCHANNEL=1 } SampleIndex;
const uint8_t volumetable[22]={ 0, 1, 2, 3, 4 , 6 , 8, 10, 12, 14, 17,
20, 23, 27, 30 ,34, 38, 43 ,48, 52, 58, 64}; //22 elements
File mp3file;
WiFiClient client;
WiFiClientSecure clientsecure;
uint8_t m_ringbuf[0x5000]; // 20480d // ringbuffer for mp3 stream
const uint16_t m_ringbfsize=sizeof(m_ringbuf); // ringbuffer size
uint16_t m_rbwindex=1600; // ringbuffer writeindex
uint16_t m_rbrindex=1600; // ringbuffer readindex
uint16_t m_ringfree=0; // ringbuffer free space
uint16_t m_ringused=0; // ringbuffer used space
char chbuf[256];
char path[256];
int m_id3Size=0; // length id3 tag
int m_LFcount; // Detection of end of header
int m_lastChannels;
int m_buffSize; // size outputBuffer
int m_nextSync=0;
int m_bytesLeft=0;
int m_writePtr; // ptr outputBuffer
int m_readPtr; // ptr outputBuffer
int m_bitrate=0;
int m_readbytes=0; // bytes read
int m_metacount=0; // Number of bytes in metadata
int8_t m_playlist_num = 0 ; // Nonzero for selection from playlist
uint8_t inBuff[1600]; // size inputBuffer
uint8_t m_rev=0; // revision
uint8_t m_BCLK=0; // Bit Clock
uint8_t m_LRC=0; // Left/Right Clock
uint8_t m_DOUT=0; // Data Out
uint8_t m_vol=64; // volume
uint8_t m_bps; // bitsPerSample
uint8_t m_channels;
uint8_t m_i2s_num= I2S_NUM_0; // I2S_NUM_0 or I2S_NUM_1
int16_t m_buffValid;
int16_t m_lastFrameEnd;
int16_t m_outBuff[1152 * 2]; // Interleaved L/R
int16_t m_validSamples;
int16_t m_curSample;
int16_t m_lastSample[2];
int16_t* m_leftSample;
int16_t* m_rightSample;
uint16_t m_datamode=0; // Statemaschine
uint32_t m_metaint = 0; // Number of databytes between metadata
uint32_t m_totalcount = 0; // Counter mp3 data
uint32_t m_chunkcount = 0 ; // Counter for chunked transfer
uint32_t m_t0;
uint32_t m_av=0; // available in stream or SD (uin16_t is to small by playing from SD)
uint32_t m_count=0; // Bytecounter between metadata
String m_mp3title=""; // the name of the file
String m_playlist ; // The URL of the specified playlist
String m_lastHost=""; // Store the last URL to a webstream
String m_metaline ; // Readable line in metadata
String m_icyname ; // Icecast station name
String m_st_remember=""; // Save the last streamtitle
String m_icyurl=""; // Store ie icy-url if received
String m_plsURL;
String m_plsStationName;
String m_icystreamtitle ; // Streamtitle from metadata
boolean m_ctseen=false; // First line of header seen or not
boolean m_f_unsync = false;
boolean m_f_exthdr = false; // ID3 extended header
boolean m_f_localfile = false ; // Play from local mp3-file
boolean m_f_webstream = false ; // Play from URL
boolean m_f_ssl=false;
boolean m_f_running=false;
boolean m_f_stream_ready=false; // Set after connecttohost and first streamdata are available
boolean m_f_ctseen=false; // First line of header seen or not
boolean m_f_chunked = false ; // Station provides chunked transfer
boolean m_f_filled; // outputBuffer
boolean m_f_swm=false;
boolean m_f_firstmetabyte=false; // True if first metabyte (counter)
boolean m_f_plsFile=false; // Set if URL is known
boolean m_f_plsTitle=false; // Set if StationName is known
boolean m_f_stream=false; // Set false if stream is lost
boolean m_f_mp3=false; // indicates mp3
boolean m_f_aac=false; // indicates aac
boolean m_f_playing = false; // valid mp3 stream recognized
unsigned int m_lastRate;
size_t m_bytesWritten=0; // set in i2s_write() but not used
};
#endif /* AUDIO_H_ */
This source diff could not be displayed because it is too large. You can view the blob instead.
// based om helix mp3 decoder
#pragma once
#pragma GCC optimize ("O3")
#include "Arduino.h"
const unsigned short huffTable[4242] PROGMEM = {
/* huffTable01[9] */
0xf003, 0x3112, 0x3101, 0x2011, 0x2011, 0x1000, 0x1000, 0x1000, 0x1000,
/* huffTable02[65] */
0xf006, 0x6222, 0x6201, 0x5212, 0x5212, 0x5122, 0x5122, 0x5021, 0x5021, 0x3112, 0x3112, 0x3112,
0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
/* huffTable03[65] */
0xf006, 0x6222, 0x6201, 0x5212, 0x5212, 0x5122, 0x5122, 0x5021, 0x5021, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112,
0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2101, 0x2101, 0x2101,
0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101,
0x2101, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
/* huffTable05[257] */
0xf008, 0x8332, 0x8322, 0x7232, 0x7232, 0x6132, 0x6132, 0x6132, 0x6132, 0x7312, 0x7312, 0x7301,
0x7301, 0x7031, 0x7031, 0x7222, 0x7222, 0x6212, 0x6212, 0x6212, 0x6212, 0x6122, 0x6122, 0x6122,
0x6122, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021, 0x6021, 0x3112, 0x3112, 0x3112,
0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112,
0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112,
0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
/* huffTable06[129] */
0xf007, 0x7332, 0x7301, 0x6322, 0x6322, 0x6232, 0x6232, 0x6031, 0x6031, 0x5312, 0x5312, 0x5312,
0x5312, 0x5132, 0x5132, 0x5132, 0x5132, 0x5222, 0x5222, 0x5222, 0x5222, 0x5201, 0x5201, 0x5201,
0x5201, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4122, 0x4122, 0x4122,
0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4021, 0x4021, 0x4021, 0x4021, 0x4021, 0x4021, 0x4021,
0x4021, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112,
0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112,
0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112,
0x2112, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000,
0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000,
/* huffTable07[110] */
0xf006, 0x0041, 0x0052, 0x005b, 0x0060, 0x0063, 0x0068, 0x006b, 0x6212, 0x5122, 0x5122, 0x6201,
0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0xf004, 0x4552, 0x4542, 0x4452, 0x4352, 0x3532, 0x3532,
0x3442, 0x3442, 0x3522, 0x3522, 0x3252, 0x3252, 0x2512, 0x2512, 0x2512, 0x2512, 0xf003, 0x2152,
0x2152, 0x3501, 0x3432, 0x2051, 0x2051, 0x3342, 0x3332, 0xf002, 0x2422, 0x2242, 0x1412, 0x1412,
0xf001, 0x1142, 0x1041, 0xf002, 0x2401, 0x2322, 0x2232, 0x2301, 0xf001, 0x1312, 0x1132, 0xf001,
0x1031, 0x1222,
/* huffTable08[280] */
0xf008, 0x0101, 0x010a, 0x010f, 0x8512, 0x8152, 0x0112, 0x0115, 0x8422, 0x8242, 0x8412, 0x7142,
0x7142, 0x8401, 0x8041, 0x8322, 0x8232, 0x8312, 0x8132, 0x8301, 0x8031, 0x6222, 0x6222, 0x6222,
0x6222, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021, 0x6021, 0x4212, 0x4212, 0x4212,
0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212,
0x4212, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122,
0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112,
0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112,
0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112,
0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112,
0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112,
0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0xf003, 0x3552, 0x3452, 0x2542, 0x2542, 0x1352, 0x1352,
0x1352, 0x1352, 0xf002, 0x2532, 0x2442, 0x1522, 0x1522, 0xf001, 0x1252, 0x1501, 0xf001, 0x1432,
0x1342, 0xf001, 0x1051, 0x1332,
/* huffTable09[93] */
0xf006, 0x0041, 0x004a, 0x004f, 0x0052, 0x0057, 0x005a, 0x6412, 0x6142, 0x6322, 0x6232, 0x5312,
0x5312, 0x5132, 0x5132, 0x6301, 0x6031, 0x5222, 0x5222, 0x5201, 0x5201, 0x4212, 0x4212, 0x4212,
0x4212, 0x4122, 0x4122, 0x4122, 0x4122, 0x4021, 0x4021, 0x4021, 0x4021, 0x3112, 0x3112, 0x3112,
0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3000, 0x3000, 0x3000,
0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0xf003, 0x3552, 0x3542, 0x2532, 0x2532, 0x2352, 0x2352,
0x3452, 0x3501, 0xf002, 0x2442, 0x2522, 0x2252, 0x2512, 0xf001, 0x1152, 0x1432, 0xf002, 0x1342,
0x1342, 0x2051, 0x2401, 0xf001, 0x1422, 0x1242, 0xf001, 0x1332, 0x1041,
/* huffTable10[320] */
0xf008, 0x0101, 0x010a, 0x010f, 0x0118, 0x011b, 0x0120, 0x0125, 0x8712, 0x8172, 0x012a, 0x012d,
0x0132, 0x8612, 0x8162, 0x8061, 0x0137, 0x013a, 0x013d, 0x8412, 0x8142, 0x8041, 0x8322, 0x8232,
0x8301, 0x7312, 0x7312, 0x7132, 0x7132, 0x7031, 0x7031, 0x7222, 0x7222, 0x6212, 0x6212, 0x6212,
0x6212, 0x6122, 0x6122, 0x6122, 0x6122, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021,
0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112,
0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0xf003, 0x3772, 0x3762, 0x3672, 0x3752, 0x3572, 0x3662,
0x2742, 0x2742, 0xf002, 0x2472, 0x2652, 0x2562, 0x2732, 0xf003, 0x2372, 0x2372, 0x2642, 0x2642,
0x3552, 0x3452, 0x2362, 0x2362, 0xf001, 0x1722, 0x1272, 0xf002, 0x2462, 0x2701, 0x1071, 0x1071,
0xf002, 0x1262, 0x1262, 0x2542, 0x2532, 0xf002, 0x1601, 0x1601, 0x2352, 0x2442, 0xf001, 0x1632,
0x1622, 0xf002, 0x2522, 0x2252, 0x1512, 0x1512, 0xf002, 0x1152, 0x1152, 0x2432, 0x2342, 0xf001,
0x1501, 0x1051, 0xf001, 0x1422, 0x1242, 0xf001, 0x1332, 0x1401,
/* huffTable11[296] */
0xf008, 0x0101, 0x0106, 0x010f, 0x0114, 0x0117, 0x8722, 0x8272, 0x011c, 0x7172, 0x7172, 0x8712,
0x8071, 0x8632, 0x8362, 0x8061, 0x011f, 0x0122, 0x8512, 0x7262, 0x7262, 0x8622, 0x8601, 0x7612,
0x7612, 0x7162, 0x7162, 0x8152, 0x8432, 0x8051, 0x0125, 0x8422, 0x8242, 0x8412, 0x8142, 0x8401,
0x8041, 0x7322, 0x7322, 0x7232, 0x7232, 0x6312, 0x6312, 0x6312, 0x6312, 0x6132, 0x6132, 0x6132,
0x6132, 0x7301, 0x7301, 0x7031, 0x7031, 0x6222, 0x6222, 0x6222, 0x6222, 0x5122, 0x5122, 0x5122,
0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212,
0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x5201, 0x5201, 0x5201,
0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021,
0x5021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112,
0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112,
0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000,
0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0xf002, 0x2772, 0x2762, 0x2672, 0x2572, 0xf003, 0x2662,
0x2662, 0x2742, 0x2742, 0x2472, 0x2472, 0x3752, 0x3552, 0xf002, 0x2652, 0x2562, 0x1732, 0x1732,
0xf001, 0x1372, 0x1642, 0xf002, 0x2542, 0x2452, 0x2532, 0x2352, 0xf001, 0x1462, 0x1701, 0xf001,
0x1442, 0x1522, 0xf001, 0x1252, 0x1501, 0xf001, 0x1342, 0x1332,
/* huffTable12[185] */
0xf007, 0x0081, 0x008a, 0x008f, 0x0092, 0x0097, 0x009a, 0x009d, 0x00a2, 0x00a5, 0x00a8, 0x7622,
0x7262, 0x7162, 0x00ad, 0x00b0, 0x00b3, 0x7512, 0x7152, 0x7432, 0x7342, 0x00b6, 0x7422, 0x7242,
0x7412, 0x6332, 0x6332, 0x6142, 0x6142, 0x6322, 0x6322, 0x6232, 0x6232, 0x7041, 0x7301, 0x6031,
0x6031, 0x5312, 0x5312, 0x5312, 0x5312, 0x5132, 0x5132, 0x5132, 0x5132, 0x5222, 0x5222, 0x5222,
0x5222, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4122, 0x4122, 0x4122,
0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x5201, 0x5201, 0x5201, 0x5201, 0x5021, 0x5021, 0x5021,
0x5021, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x3112, 0x3112, 0x3112,
0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112,
0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101,
0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0xf003, 0x3772, 0x3762,
0x2672, 0x2672, 0x2752, 0x2752, 0x2572, 0x2572, 0xf002, 0x2662, 0x2742, 0x2472, 0x2562, 0xf001,
0x1652, 0x1732, 0xf002, 0x2372, 0x2552, 0x1722, 0x1722, 0xf001, 0x1272, 0x1642, 0xf001, 0x1462,
0x1712, 0xf002, 0x1172, 0x1172, 0x2701, 0x2071, 0xf001, 0x1632, 0x1362, 0xf001, 0x1542, 0x1452,
0xf002, 0x1442, 0x1442, 0x2601, 0x2501, 0xf001, 0x1612, 0x1061, 0xf001, 0x1532, 0x1352, 0xf001,
0x1522, 0x1252, 0xf001, 0x1051, 0x1401,
/* huffTable13[497] */
0xf006, 0x0041, 0x0082, 0x00c3, 0x00e4, 0x0105, 0x0116, 0x011f, 0x0130, 0x0139, 0x013e, 0x0143,
0x0146, 0x6212, 0x6122, 0x6201, 0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4101, 0x4101, 0x4101,
0x4101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0xf006, 0x0108, 0x0111, 0x011a, 0x0123, 0x012c, 0x0131,
0x0136, 0x013f, 0x0144, 0x0147, 0x014c, 0x0151, 0x0156, 0x015b, 0x6f12, 0x61f2, 0x60f1, 0x0160,
0x0163, 0x0166, 0x62e2, 0x0169, 0x6e12, 0x61e2, 0x016c, 0x016f, 0x0172, 0x0175, 0x0178, 0x017b,
0x66c2, 0x6d32, 0x017e, 0x6d22, 0x62d2, 0x6d12, 0x67b2, 0x0181, 0x0184, 0x63c2, 0x0187, 0x6b42,
0x51d2, 0x51d2, 0x6d01, 0x60d1, 0x6a82, 0x68a2, 0x6c42, 0x64c2, 0x6b62, 0x66b2, 0x5c32, 0x5c32,
0x5c22, 0x5c22, 0x52c2, 0x52c2, 0x5b52, 0x5b52, 0x65b2, 0x6982, 0x5c12, 0x5c12, 0xf006, 0x51c2,
0x51c2, 0x6892, 0x6c01, 0x50c1, 0x50c1, 0x64b2, 0x6a62, 0x66a2, 0x6972, 0x5b32, 0x5b32, 0x53b2,
0x53b2, 0x6882, 0x6a52, 0x5b22, 0x5b22, 0x65a2, 0x6962, 0x54a2, 0x54a2, 0x6872, 0x6782, 0x5492,
0x5492, 0x6772, 0x6672, 0x42b2, 0x42b2, 0x42b2, 0x42b2, 0x4b12, 0x4b12, 0x4b12, 0x4b12, 0x41b2,
0x41b2, 0x41b2, 0x41b2, 0x5b01, 0x5b01, 0x50b1, 0x50b1, 0x5692, 0x5692, 0x5a42, 0x5a42, 0x5a32,
0x5a32, 0x53a2, 0x53a2, 0x5952, 0x5952, 0x5592, 0x5592, 0x4a22, 0x4a22, 0x4a22, 0x4a22, 0x42a2,
0x42a2, 0x42a2, 0x42a2, 0xf005, 0x4a12, 0x4a12, 0x41a2, 0x41a2, 0x5a01, 0x5862, 0x40a1, 0x40a1,
0x5682, 0x5942, 0x4392, 0x4392, 0x5932, 0x5852, 0x5582, 0x5762, 0x4922, 0x4922, 0x4292, 0x4292,
0x5752, 0x5572, 0x4832, 0x4832, 0x4382, 0x4382, 0x5662, 0x5742, 0x5472, 0x5652, 0x5562, 0x5372,
0xf005, 0x3912, 0x3912, 0x3912, 0x3912, 0x3192, 0x3192, 0x3192, 0x3192, 0x4901, 0x4901, 0x4091,
0x4091, 0x4842, 0x4842, 0x4482, 0x4482, 0x4272, 0x4272, 0x5642, 0x5462, 0x3822, 0x3822, 0x3822,
0x3822, 0x3282, 0x3282, 0x3282, 0x3282, 0x3812, 0x3812, 0x3812, 0x3812, 0xf004, 0x4732, 0x4722,
0x3712, 0x3712, 0x3172, 0x3172, 0x4552, 0x4701, 0x4071, 0x4632, 0x4362, 0x4542, 0x4452, 0x4622,
0x4262, 0x4532, 0xf003, 0x2182, 0x2182, 0x3801, 0x3081, 0x3612, 0x3162, 0x3601, 0x3061, 0xf004,
0x4352, 0x4442, 0x3522, 0x3522, 0x3252, 0x3252, 0x3501, 0x3501, 0x2512, 0x2512, 0x2512, 0x2512,
0x2152, 0x2152, 0x2152, 0x2152, 0xf003, 0x3432, 0x3342, 0x3051, 0x3422, 0x3242, 0x3332, 0x2412,
0x2412, 0xf002, 0x1142, 0x1142, 0x2401, 0x2041, 0xf002, 0x2322, 0x2232, 0x1312, 0x1312, 0xf001,
0x1132, 0x1301, 0xf001, 0x1031, 0x1222, 0xf003, 0x0082, 0x008b, 0x008e, 0x0091, 0x0094, 0x0097,
0x3ce2, 0x3dd2, 0xf003, 0x0093, 0x3eb2, 0x3be2, 0x3f92, 0x39f2, 0x3ae2, 0x3db2, 0x3bd2, 0xf003,
0x3f82, 0x38f2, 0x3cc2, 0x008d, 0x3e82, 0x0090, 0x27f2, 0x27f2, 0xf003, 0x2ad2, 0x2ad2, 0x3da2,
0x3cb2, 0x3bc2, 0x36f2, 0x2f62, 0x2f62, 0xf002, 0x28e2, 0x2f52, 0x2d92, 0x29d2, 0xf002, 0x25f2,
0x27e2, 0x2ca2, 0x2bb2, 0xf003, 0x2f42, 0x2f42, 0x24f2, 0x24f2, 0x3ac2, 0x36e2, 0x23f2, 0x23f2,
0xf002, 0x1f32, 0x1f32, 0x2d82, 0x28d2, 0xf001, 0x1f22, 0x12f2, 0xf002, 0x2e62, 0x2c92, 0x1f01,
0x1f01, 0xf002, 0x29c2, 0x2e52, 0x1ba2, 0x1ba2, 0xf002, 0x2d72, 0x27d2, 0x1e42, 0x1e42, 0xf002,
0x28c2, 0x26d2, 0x1e32, 0x1e32, 0xf002, 0x19b2, 0x19b2, 0x2b92, 0x2aa2, 0xf001, 0x1ab2, 0x15e2,
0xf001, 0x14e2, 0x1c82, 0xf001, 0x1d62, 0x13e2, 0xf001, 0x1e22, 0x1e01, 0xf001, 0x10e1, 0x1d52,
0xf001, 0x15d2, 0x1c72, 0xf001, 0x17c2, 0x1d42, 0xf001, 0x1b82, 0x18b2, 0xf001, 0x14d2, 0x1a92,
0xf001, 0x19a2, 0x1c62, 0xf001, 0x13d2, 0x1b72, 0xf001, 0x1c52, 0x15c2, 0xf001, 0x1992, 0x1a72,
0xf001, 0x17a2, 0x1792, 0xf003, 0x0023, 0x3df2, 0x2de2, 0x2de2, 0x1ff2, 0x1ff2, 0x1ff2, 0x1ff2,
0xf001, 0x1fe2, 0x1fd2, 0xf001, 0x1ee2, 0x1fc2, 0xf001, 0x1ed2, 0x1fb2, 0xf001, 0x1bf2, 0x1ec2,
0xf002, 0x1cd2, 0x1cd2, 0x2fa2, 0x29e2, 0xf001, 0x1af2, 0x1dc2, 0xf001, 0x1ea2, 0x1e92, 0xf001,
0x1f72, 0x1e72, 0xf001, 0x1ef2, 0x1cf2,
/* huffTable15[580] */
0xf008, 0x0101, 0x0122, 0x0143, 0x0154, 0x0165, 0x0176, 0x017f, 0x0188, 0x0199, 0x01a2, 0x01ab,
0x01b4, 0x01bd, 0x01c2, 0x01cb, 0x01d4, 0x01d9, 0x01de, 0x01e3, 0x01e8, 0x01ed, 0x01f2, 0x01f7,
0x01fc, 0x0201, 0x0204, 0x0207, 0x020a, 0x020f, 0x0212, 0x0215, 0x021a, 0x021d, 0x0220, 0x8192,
0x0223, 0x0226, 0x0229, 0x022c, 0x022f, 0x8822, 0x8282, 0x8812, 0x8182, 0x0232, 0x0235, 0x0238,
0x023b, 0x8722, 0x8272, 0x8462, 0x8712, 0x8552, 0x8172, 0x023e, 0x8632, 0x8362, 0x8542, 0x8452,
0x8622, 0x8262, 0x8612, 0x0241, 0x8532, 0x7162, 0x7162, 0x8352, 0x8442, 0x7522, 0x7522, 0x7252,
0x7252, 0x7512, 0x7512, 0x7152, 0x7152, 0x8501, 0x8051, 0x7432, 0x7432, 0x7342, 0x7342, 0x7422,
0x7422, 0x7242, 0x7242, 0x7332, 0x7332, 0x6142, 0x6142, 0x6142, 0x6142, 0x7412, 0x7412, 0x7401,
0x7401, 0x6322, 0x6322, 0x6322, 0x6322, 0x6232, 0x6232, 0x6232, 0x6232, 0x7041, 0x7041, 0x7301,
0x7301, 0x6312, 0x6312, 0x6312, 0x6312, 0x6132, 0x6132, 0x6132, 0x6132, 0x6031, 0x6031, 0x6031,
0x6031, 0x5222, 0x5222, 0x5222, 0x5222, 0x5222, 0x5222, 0x5222, 0x5222, 0x5212, 0x5212, 0x5212,
0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122,
0x5122, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5021, 0x5021, 0x5021,
0x5021, 0x5021, 0x5021, 0x5021, 0x5021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112,
0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112,
0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112,
0x3112, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101,
0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011,
0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x3000, 0x3000, 0x3000,
0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000,
0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000,
0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0xf005, 0x5ff2, 0x5fe2, 0x5ef2, 0x5fd2, 0x4ee2, 0x4ee2,
0x5df2, 0x5fc2, 0x5cf2, 0x5ed2, 0x5de2, 0x5fb2, 0x4bf2, 0x4bf2, 0x5ec2, 0x5ce2, 0x4dd2, 0x4dd2,
0x4fa2, 0x4fa2, 0x4af2, 0x4af2, 0x4eb2, 0x4eb2, 0x4be2, 0x4be2, 0x4dc2, 0x4dc2, 0x4cd2, 0x4cd2,
0x4f92, 0x4f92, 0xf005, 0x49f2, 0x49f2, 0x4ae2, 0x4ae2, 0x4db2, 0x4db2, 0x4bd2, 0x4bd2, 0x4f82,
0x4f82, 0x48f2, 0x48f2, 0x4cc2, 0x4cc2, 0x4e92, 0x4e92, 0x49e2, 0x49e2, 0x4f72, 0x4f72, 0x47f2,
0x47f2, 0x4da2, 0x4da2, 0x4ad2, 0x4ad2, 0x4cb2, 0x4cb2, 0x4f62, 0x4f62, 0x5ea2, 0x5f01, 0xf004,
0x3bc2, 0x3bc2, 0x36f2, 0x36f2, 0x4e82, 0x48e2, 0x4f52, 0x4d92, 0x35f2, 0x35f2, 0x3e72, 0x3e72,
0x37e2, 0x37e2, 0x3ca2, 0x3ca2, 0xf004, 0x3ac2, 0x3ac2, 0x3bb2, 0x3bb2, 0x49d2, 0x4d82, 0x3f42,
0x3f42, 0x34f2, 0x34f2, 0x3f32, 0x3f32, 0x33f2, 0x33f2, 0x38d2, 0x38d2, 0xf004, 0x36e2, 0x36e2,
0x3f22, 0x3f22, 0x32f2, 0x32f2, 0x4e62, 0x40f1, 0x3f12, 0x3f12, 0x31f2, 0x31f2, 0x3c92, 0x3c92,
0x39c2, 0x39c2, 0xf003, 0x3e52, 0x3ba2, 0x3ab2, 0x35e2, 0x3d72, 0x37d2, 0x3e42, 0x34e2, 0xf003,
0x3c82, 0x38c2, 0x3e32, 0x3d62, 0x36d2, 0x33e2, 0x3b92, 0x39b2, 0xf004, 0x3e22, 0x3e22, 0x3aa2,
0x3aa2, 0x32e2, 0x32e2, 0x3e12, 0x3e12, 0x31e2, 0x31e2, 0x4e01, 0x40e1, 0x3d52, 0x3d52, 0x35d2,
0x35d2, 0xf003, 0x3c72, 0x37c2, 0x3d42, 0x3b82, 0x24d2, 0x24d2, 0x38b2, 0x3a92, 0xf003, 0x39a2,
0x3c62, 0x36c2, 0x3d32, 0x23d2, 0x23d2, 0x22d2, 0x22d2, 0xf003, 0x3d22, 0x3d01, 0x2d12, 0x2d12,
0x2b72, 0x2b72, 0x27b2, 0x27b2, 0xf003, 0x21d2, 0x21d2, 0x3c52, 0x30d1, 0x25c2, 0x25c2, 0x2a82,
0x2a82, 0xf002, 0x28a2, 0x2c42, 0x24c2, 0x2b62, 0xf003, 0x26b2, 0x26b2, 0x3992, 0x3c01, 0x2c32,
0x2c32, 0x23c2, 0x23c2, 0xf003, 0x2a72, 0x2a72, 0x27a2, 0x27a2, 0x26a2, 0x26a2, 0x30c1, 0x3b01,
0xf002, 0x12c2, 0x12c2, 0x2c22, 0x2b52, 0xf002, 0x25b2, 0x2c12, 0x2982, 0x2892, 0xf002, 0x21c2,
0x2b42, 0x24b2, 0x2a62, 0xf002, 0x2b32, 0x2972, 0x13b2, 0x13b2, 0xf002, 0x2792, 0x2882, 0x2b22,
0x2a52, 0xf002, 0x12b2, 0x12b2, 0x25a2, 0x2b12, 0xf002, 0x11b2, 0x11b2, 0x20b1, 0x2962, 0xf002,
0x2692, 0x2a42, 0x24a2, 0x2872, 0xf002, 0x2782, 0x2a32, 0x13a2, 0x13a2, 0xf001, 0x1952, 0x1592,
0xf001, 0x1a22, 0x12a2, 0xf001, 0x1a12, 0x11a2, 0xf002, 0x2a01, 0x20a1, 0x1862, 0x1862, 0xf001,
0x1682, 0x1942, 0xf001, 0x1492, 0x1932, 0xf002, 0x1392, 0x1392, 0x2772, 0x2901, 0xf001, 0x1852,
0x1582, 0xf001, 0x1922, 0x1762, 0xf001, 0x1672, 0x1292, 0xf001, 0x1912, 0x1091, 0xf001, 0x1842,
0x1482, 0xf001, 0x1752, 0x1572, 0xf001, 0x1832, 0x1382, 0xf001, 0x1662, 0x1742, 0xf001, 0x1472,
0x1801, 0xf001, 0x1081, 0x1652, 0xf001, 0x1562, 0x1732, 0xf001, 0x1372, 0x1642, 0xf001, 0x1701,
0x1071, 0xf001, 0x1601, 0x1061,
/* huffTable16[651] */
0xf008, 0x0101, 0x010a, 0x0113, 0x8ff2, 0x0118, 0x011d, 0x0120, 0x82f2, 0x0131, 0x8f12, 0x81f2,
0x0134, 0x0145, 0x0156, 0x0167, 0x0178, 0x0189, 0x019a, 0x01a3, 0x01ac, 0x01b5, 0x01be, 0x01c7,
0x01d0, 0x01d9, 0x01de, 0x01e3, 0x01e6, 0x01eb, 0x01f0, 0x8152, 0x01f3, 0x01f6, 0x01f9, 0x01fc,
0x8412, 0x8142, 0x01ff, 0x8322, 0x8232, 0x7312, 0x7312, 0x7132, 0x7132, 0x8301, 0x8031, 0x7222,
0x7222, 0x6212, 0x6212, 0x6212, 0x6212, 0x6122, 0x6122, 0x6122, 0x6122, 0x6201, 0x6201, 0x6201,
0x6201, 0x6021, 0x6021, 0x6021, 0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112,
0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4101, 0x4101, 0x4101,
0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101,
0x4101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011,
0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0xf003, 0x3fe2, 0x3ef2, 0x3fd2, 0x3df2, 0x3fc2, 0x3cf2,
0x3fb2, 0x3bf2, 0xf003, 0x2fa2, 0x2fa2, 0x3af2, 0x3f92, 0x39f2, 0x38f2, 0x2f82, 0x2f82, 0xf002,
0x2f72, 0x27f2, 0x2f62, 0x26f2, 0xf002, 0x2f52, 0x25f2, 0x1f42, 0x1f42, 0xf001, 0x14f2, 0x13f2,
0xf004, 0x10f1, 0x10f1, 0x10f1, 0x10f1, 0x10f1, 0x10f1, 0x10f1, 0x10f1, 0x2f32, 0x2f32, 0x2f32,
0x2f32, 0x00e2, 0x00f3, 0x00fc, 0x0105, 0xf001, 0x1f22, 0x1f01, 0xf004, 0x00fa, 0x00ff, 0x0104,
0x0109, 0x010c, 0x0111, 0x0116, 0x0119, 0x011e, 0x0123, 0x0128, 0x43e2, 0x012d, 0x0130, 0x0133,
0x0136, 0xf004, 0x0128, 0x012b, 0x012e, 0x4d01, 0x0131, 0x0134, 0x0137, 0x4c32, 0x013a, 0x4c12,
0x40c1, 0x013d, 0x32e2, 0x32e2, 0x4e22, 0x4e12, 0xf004, 0x43d2, 0x4d22, 0x42d2, 0x41d2, 0x4b32,
0x012f, 0x3d12, 0x3d12, 0x44c2, 0x4b62, 0x43c2, 0x47a2, 0x3c22, 0x3c22, 0x42c2, 0x45b2, 0xf004,
0x41c2, 0x4c01, 0x4b42, 0x44b2, 0x4a62, 0x46a2, 0x33b2, 0x33b2, 0x4a52, 0x45a2, 0x3b22, 0x3b22,
0x32b2, 0x32b2, 0x3b12, 0x3b12, 0xf004, 0x31b2, 0x31b2, 0x4b01, 0x40b1, 0x4962, 0x4692, 0x4a42,
0x44a2, 0x4872, 0x4782, 0x33a2, 0x33a2, 0x4a32, 0x4952, 0x3a22, 0x3a22, 0xf004, 0x4592, 0x4862,
0x31a2, 0x31a2, 0x4682, 0x4772, 0x3492, 0x3492, 0x4942, 0x4752, 0x3762, 0x3762, 0x22a2, 0x22a2,
0x22a2, 0x22a2, 0xf003, 0x2a12, 0x2a12, 0x3a01, 0x30a1, 0x3932, 0x3392, 0x3852, 0x3582, 0xf003,
0x2922, 0x2922, 0x2292, 0x2292, 0x3672, 0x3901, 0x2912, 0x2912, 0xf003, 0x2192, 0x2192, 0x3091,
0x3842, 0x3482, 0x3572, 0x3832, 0x3382, 0xf003, 0x3662, 0x3822, 0x2282, 0x2282, 0x3742, 0x3472,
0x2812, 0x2812, 0xf003, 0x2182, 0x2182, 0x2081, 0x2081, 0x3801, 0x3652, 0x2732, 0x2732, 0xf003,
0x2372, 0x2372, 0x3562, 0x3642, 0x2722, 0x2722, 0x2272, 0x2272, 0xf003, 0x3462, 0x3552, 0x2701,
0x2701, 0x1712, 0x1712, 0x1712, 0x1712, 0xf002, 0x1172, 0x1172, 0x2071, 0x2632, 0xf002, 0x2362,
0x2542, 0x2452, 0x2622, 0xf001, 0x1262, 0x1612, 0xf002, 0x1162, 0x1162, 0x2601, 0x2061, 0xf002,
0x1352, 0x1352, 0x2532, 0x2442, 0xf001, 0x1522, 0x1252, 0xf001, 0x1512, 0x1501, 0xf001, 0x1432,
0x1342, 0xf001, 0x1051, 0x1422, 0xf001, 0x1242, 0x1332, 0xf001, 0x1401, 0x1041, 0xf004, 0x4ec2,
0x0086, 0x3ed2, 0x3ed2, 0x39e2, 0x39e2, 0x4ae2, 0x49d2, 0x2ee2, 0x2ee2, 0x2ee2, 0x2ee2, 0x3de2,
0x3de2, 0x3be2, 0x3be2, 0xf003, 0x2eb2, 0x2eb2, 0x2dc2, 0x2dc2, 0x3cd2, 0x3bd2, 0x2ea2, 0x2ea2,
0xf003, 0x2cc2, 0x2cc2, 0x3da2, 0x3ad2, 0x3e72, 0x3ca2, 0x2ac2, 0x2ac2, 0xf003, 0x39c2, 0x3d72,
0x2e52, 0x2e52, 0x1db2, 0x1db2, 0x1db2, 0x1db2, 0xf002, 0x1e92, 0x1e92, 0x2cb2, 0x2bc2, 0xf002,
0x2e82, 0x28e2, 0x2d92, 0x27e2, 0xf002, 0x2bb2, 0x2d82, 0x28d2, 0x2e62, 0xf001, 0x16e2, 0x1c92,
0xf002, 0x2ba2, 0x2ab2, 0x25e2, 0x27d2, 0xf002, 0x1e42, 0x1e42, 0x24e2, 0x2c82, 0xf001, 0x18c2,
0x1e32, 0xf002, 0x1d62, 0x1d62, 0x26d2, 0x2b92, 0xf002, 0x29b2, 0x2aa2, 0x11e2, 0x11e2, 0xf002,
0x14d2, 0x14d2, 0x28b2, 0x29a2, 0xf002, 0x1b72, 0x1b72, 0x27b2, 0x20d1, 0xf001, 0x1e01, 0x10e1,
0xf001, 0x1d52, 0x15d2, 0xf001, 0x1c72, 0x17c2, 0xf001, 0x1d42, 0x1b82, 0xf001, 0x1a92, 0x1c62,
0xf001, 0x16c2, 0x1d32, 0xf001, 0x1c52, 0x15c2, 0xf001, 0x1a82, 0x18a2, 0xf001, 0x1992, 0x1c42,
0xf001, 0x16b2, 0x1a72, 0xf001, 0x1b52, 0x1982, 0xf001, 0x1892, 0x1972, 0xf001, 0x1792, 0x1882,
0xf001, 0x1ce2, 0x1dd2,
/* huffTable24[705] */
0xf009, 0x8fe2, 0x8fe2, 0x8ef2, 0x8ef2, 0x8fd2, 0x8fd2, 0x8df2, 0x8df2, 0x8fc2, 0x8fc2, 0x8cf2,
0x8cf2, 0x8fb2, 0x8fb2, 0x8bf2, 0x8bf2, 0x7af2, 0x7af2, 0x7af2, 0x7af2, 0x8fa2, 0x8fa2, 0x8f92,
0x8f92, 0x79f2, 0x79f2, 0x79f2, 0x79f2, 0x78f2, 0x78f2, 0x78f2, 0x78f2, 0x8f82, 0x8f82, 0x8f72,
0x8f72, 0x77f2, 0x77f2, 0x77f2, 0x77f2, 0x7f62, 0x7f62, 0x7f62, 0x7f62, 0x76f2, 0x76f2, 0x76f2,
0x76f2, 0x7f52, 0x7f52, 0x7f52, 0x7f52, 0x75f2, 0x75f2, 0x75f2, 0x75f2, 0x7f42, 0x7f42, 0x7f42,
0x7f42, 0x74f2, 0x74f2, 0x74f2, 0x74f2, 0x7f32, 0x7f32, 0x7f32, 0x7f32, 0x73f2, 0x73f2, 0x73f2,
0x73f2, 0x7f22, 0x7f22, 0x7f22, 0x7f22, 0x72f2, 0x72f2, 0x72f2, 0x72f2, 0x71f2, 0x71f2, 0x71f2,
0x71f2, 0x8f12, 0x8f12, 0x80f1, 0x80f1, 0x9f01, 0x0201, 0x0206, 0x020b, 0x0210, 0x0215, 0x021a,
0x021f, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2,
0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2,
0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x0224, 0x0229, 0x0232,
0x0237, 0x023a, 0x023f, 0x0242, 0x0245, 0x024a, 0x024d, 0x0250, 0x0253, 0x0256, 0x0259, 0x025c,
0x025f, 0x0262, 0x0265, 0x0268, 0x026b, 0x026e, 0x0271, 0x0274, 0x0277, 0x027a, 0x027d, 0x0280,
0x0283, 0x0288, 0x028b, 0x028e, 0x0291, 0x0294, 0x0297, 0x029a, 0x029f, 0x94b2, 0x02a4, 0x02a7,
0x02aa, 0x93b2, 0x9882, 0x02af, 0x92b2, 0x02b2, 0x02b5, 0x9692, 0x94a2, 0x02b8, 0x9782, 0x9a32,
0x93a2, 0x9952, 0x9592, 0x9a22, 0x92a2, 0x91a2, 0x9862, 0x9682, 0x9772, 0x9942, 0x9492, 0x9932,
0x9392, 0x9852, 0x9582, 0x9922, 0x9762, 0x9672, 0x9292, 0x9912, 0x9192, 0x9842, 0x9482, 0x9752,
0x9572, 0x9832, 0x9382, 0x9662, 0x9822, 0x9282, 0x9812, 0x9742, 0x9472, 0x9182, 0x02bb, 0x9652,
0x9562, 0x9712, 0x02be, 0x8372, 0x8372, 0x9732, 0x9722, 0x8272, 0x8272, 0x8642, 0x8642, 0x8462,
0x8462, 0x8552, 0x8552, 0x8172, 0x8172, 0x8632, 0x8632, 0x8362, 0x8362, 0x8542, 0x8542, 0x8452,
0x8452, 0x8622, 0x8622, 0x8262, 0x8262, 0x8612, 0x8612, 0x8162, 0x8162, 0x9601, 0x9061, 0x8532,
0x8532, 0x8352, 0x8352, 0x8442, 0x8442, 0x8522, 0x8522, 0x8252, 0x8252, 0x8512, 0x8512, 0x9501,
0x9051, 0x7152, 0x7152, 0x7152, 0x7152, 0x8432, 0x8432, 0x8342, 0x8342, 0x7422, 0x7422, 0x7422,
0x7422, 0x7242, 0x7242, 0x7242, 0x7242, 0x7332, 0x7332, 0x7332, 0x7332, 0x7412, 0x7412, 0x7412,
0x7412, 0x7142, 0x7142, 0x7142, 0x7142, 0x8401, 0x8401, 0x8041, 0x8041, 0x7322, 0x7322, 0x7322,
0x7322, 0x7232, 0x7232, 0x7232, 0x7232, 0x6312, 0x6312, 0x6312, 0x6312, 0x6312, 0x6312, 0x6312,
0x6312, 0x6132, 0x6132, 0x6132, 0x6132, 0x6132, 0x6132, 0x6132, 0x6132, 0x7301, 0x7301, 0x7301,
0x7301, 0x7031, 0x7031, 0x7031, 0x7031, 0x6222, 0x6222, 0x6222, 0x6222, 0x6222, 0x6222, 0x6222,
0x6222, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212,
0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122,
0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x6201, 0x6201, 0x6201,
0x6201, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021, 0x6021, 0x6021, 0x6021, 0x6021,
0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112,
0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112,
0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4101, 0x4101, 0x4101,
0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101,
0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101,
0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011,
0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011,
0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011,
0x4011, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000,
0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000,
0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0xf002, 0x2ee2, 0x2ed2,
0x2de2, 0x2ec2, 0xf002, 0x2ce2, 0x2dd2, 0x2eb2, 0x2be2, 0xf002, 0x2dc2, 0x2cd2, 0x2ea2, 0x2ae2,
0xf002, 0x2db2, 0x2bd2, 0x2cc2, 0x2e92, 0xf002, 0x29e2, 0x2da2, 0x2ad2, 0x2cb2, 0xf002, 0x2bc2,
0x2e82, 0x28e2, 0x2d92, 0xf002, 0x29d2, 0x2e72, 0x27e2, 0x2ca2, 0xf002, 0x2ac2, 0x2bb2, 0x2d82,
0x28d2, 0xf003, 0x3e01, 0x30e1, 0x2d01, 0x2d01, 0x16e2, 0x16e2, 0x16e2, 0x16e2, 0xf002, 0x2e62,
0x2c92, 0x19c2, 0x19c2, 0xf001, 0x1e52, 0x1ab2, 0xf002, 0x15e2, 0x15e2, 0x2ba2, 0x2d72, 0xf001,
0x17d2, 0x14e2, 0xf001, 0x1c82, 0x18c2, 0xf002, 0x2e42, 0x2e22, 0x1e32, 0x1e32, 0xf001, 0x1d62,
0x16d2, 0xf001, 0x13e2, 0x1b92, 0xf001, 0x19b2, 0x1aa2, 0xf001, 0x12e2, 0x1e12, 0xf001, 0x11e2,
0x1d52, 0xf001, 0x15d2, 0x1c72, 0xf001, 0x17c2, 0x1d42, 0xf001, 0x1b82, 0x18b2, 0xf001, 0x14d2,
0x1a92, 0xf001, 0x19a2, 0x1c62, 0xf001, 0x16c2, 0x1d32, 0xf001, 0x13d2, 0x1d22, 0xf001, 0x12d2,
0x1d12, 0xf001, 0x1b72, 0x17b2, 0xf001, 0x11d2, 0x1c52, 0xf001, 0x15c2, 0x1a82, 0xf001, 0x18a2,
0x1992, 0xf001, 0x1c42, 0x14c2, 0xf001, 0x1b62, 0x16b2, 0xf002, 0x20d1, 0x2c01, 0x1c32, 0x1c32,
0xf001, 0x13c2, 0x1a72, 0xf001, 0x17a2, 0x1c22, 0xf001, 0x12c2, 0x1b52, 0xf001, 0x15b2, 0x1c12,
0xf001, 0x1982, 0x1892, 0xf001, 0x11c2, 0x1b42, 0xf002, 0x20c1, 0x2b01, 0x1b32, 0x1b32, 0xf002,
0x20b1, 0x2a01, 0x1a12, 0x1a12, 0xf001, 0x1a62, 0x16a2, 0xf001, 0x1972, 0x1792, 0xf002, 0x20a1,
0x2901, 0x1091, 0x1091, 0xf001, 0x1b22, 0x1a52, 0xf001, 0x15a2, 0x1b12, 0xf001, 0x11b2, 0x1962,
0xf001, 0x1a42, 0x1872, 0xf001, 0x1801, 0x1081, 0xf001, 0x1701, 0x1071,
};
/* pow(2,-i/4) * pow(j,4/3) for i=0..3 j=0..15, Q25 format */
const int pow43_14[4][16] PROGMEM = { /* Q28 */
{ 0x00000000, 0x10000000, 0x285145f3, 0x453a5cdb, 0x0cb2ff53, 0x111989d6,
0x15ce31c8, 0x1ac7f203, 0x20000000, 0x257106b9, 0x2b16b4a3, 0x30ed74b4,
0x36f23fa5, 0x3d227bd3, 0x437be656, 0x49fc823c, },
{ 0x00000000, 0x0d744fcd, 0x21e71f26, 0x3a36abd9, 0x0aadc084, 0x0e610e6e,
0x12560c1d, 0x168523cf, 0x1ae89f99, 0x1f7c03a4, 0x243bae49, 0x29249c67,
0x2e34420f, 0x33686f85, 0x38bf3dff, 0x3e370182, },
{ 0x00000000, 0x0b504f33, 0x1c823e07, 0x30f39a55, 0x08facd62, 0x0c176319,
0x0f6b3522, 0x12efe2ad, 0x16a09e66, 0x1a79a317, 0x1e77e301, 0x2298d5b4,
0x26da56fc, 0x2b3a902a, 0x2fb7e7e7, 0x3450f650, },
{ 0x00000000, 0x09837f05, 0x17f910d7, 0x2929c7a9, 0x078d0dfa, 0x0a2ae661,
0x0cf73154, 0x0fec91cb, 0x1306fe0a, 0x16434a6c, 0x199ee595, 0x1d17ae3d,
0x20abd76a, 0x2459d551, 0x28204fbb, 0x2bfe1808, },
};
/* pow(j,4/3) for j=16..63, Q23 format */
const int pow43[48] PROGMEM = {
0x1428a2fa, 0x15db1bd6, 0x1796302c, 0x19598d85, 0x1b24e8bb, 0x1cf7fcfa,
0x1ed28af2, 0x20b4582a, 0x229d2e6e, 0x248cdb55, 0x26832fda, 0x28800000,
0x2a832287, 0x2c8c70a8, 0x2e9bc5d8, 0x30b0ff99, 0x32cbfd4a, 0x34eca001,
0x3712ca62, 0x393e6088, 0x3b6f47e0, 0x3da56717, 0x3fe0a5fc, 0x4220ed72,
0x44662758, 0x46b03e7c, 0x48ff1e87, 0x4b52b3f3, 0x4daaebfd, 0x5007b497,
0x5268fc62, 0x54ceb29c, 0x5738c721, 0x59a72a59, 0x5c19cd35, 0x5e90a129,
0x610b9821, 0x638aa47f, 0x660db90f, 0x6894c90b, 0x6b1fc80c, 0x6daeaa0d,
0x70416360, 0x72d7e8b0, 0x75722ef9, 0x78102b85, 0x7ab1d3ec, 0x7d571e09,
};
const uint32_t polyCoef[264] PROGMEM = {
/* shuffled vs. original from 0, 1, ... 15 to 0, 15, 2, 13, ... 14, 1 */
0x00000000, 0x00000074, 0x00000354, 0x0000072c, 0x00001fd4, 0x00005084, 0x000066b8, 0x000249c4,
0x00049478, 0xfffdb63c, 0x000066b8, 0xffffaf7c, 0x00001fd4, 0xfffff8d4, 0x00000354, 0xffffff8c,
0xfffffffc, 0x00000068, 0x00000368, 0x00000644, 0x00001f40, 0x00004ad0, 0x00005d1c, 0x00022ce0,
0x000493c0, 0xfffd9960, 0x00006f78, 0xffffa9cc, 0x0000203c, 0xfffff7e4, 0x00000340, 0xffffff84,
0xfffffffc, 0x00000060, 0x00000378, 0x0000056c, 0x00001e80, 0x00004524, 0x000052a0, 0x00020ffc,
0x000491a0, 0xfffd7ca0, 0x00007760, 0xffffa424, 0x00002080, 0xfffff6ec, 0x00000328, 0xffffff74,
0xfffffffc, 0x00000054, 0x00000384, 0x00000498, 0x00001d94, 0x00003f7c, 0x00004744, 0x0001f32c,
0x00048e18, 0xfffd6008, 0x00007e70, 0xffff9e8c, 0x0000209c, 0xfffff5ec, 0x00000310, 0xffffff68,
0xfffffffc, 0x0000004c, 0x0000038c, 0x000003d0, 0x00001c78, 0x000039e4, 0x00003b00, 0x0001d680,
0x00048924, 0xfffd43ac, 0x000084b0, 0xffff990c, 0x00002094, 0xfffff4e4, 0x000002f8, 0xffffff5c,
0xfffffffc, 0x00000044, 0x00000390, 0x00000314, 0x00001b2c, 0x0000345c, 0x00002ddc, 0x0001ba04,
0x000482d0, 0xfffd279c, 0x00008a20, 0xffff93a4, 0x0000206c, 0xfffff3d4, 0x000002dc, 0xffffff4c,
0xfffffffc, 0x00000040, 0x00000390, 0x00000264, 0x000019b0, 0x00002ef0, 0x00001fd4, 0x00019dc8,
0x00047b1c, 0xfffd0be8, 0x00008ecc, 0xffff8e64, 0x00002024, 0xfffff2c0, 0x000002c0, 0xffffff3c,
0xfffffff8, 0x00000038, 0x0000038c, 0x000001bc, 0x000017fc, 0x0000299c, 0x000010e8, 0x000181d8,
0x0004720c, 0xfffcf09c, 0x000092b4, 0xffff894c, 0x00001fc0, 0xfffff1a4, 0x000002a4, 0xffffff2c,
0xfffffff8, 0x00000034, 0x00000380, 0x00000120, 0x00001618, 0x00002468, 0x00000118, 0x00016644,
0x000467a4, 0xfffcd5cc, 0x000095e0, 0xffff8468, 0x00001f44, 0xfffff084, 0x00000284, 0xffffff18,
0xfffffff8, 0x0000002c, 0x00000374, 0x00000090, 0x00001400, 0x00001f58, 0xfffff068, 0x00014b14,
0x00045bf0, 0xfffcbb88, 0x00009858, 0xffff7fbc, 0x00001ea8, 0xffffef60, 0x00000268, 0xffffff04,
0xfffffff8, 0x00000028, 0x0000035c, 0x00000008, 0x000011ac, 0x00001a70, 0xffffded8, 0x00013058,
0x00044ef8, 0xfffca1d8, 0x00009a1c, 0xffff7b54, 0x00001dfc, 0xffffee3c, 0x0000024c, 0xfffffef0,
0xfffffff4, 0x00000024, 0x00000340, 0xffffff8c, 0x00000f28, 0x000015b0, 0xffffcc70, 0x0001161c,
0x000440bc, 0xfffc88d8, 0x00009b3c, 0xffff7734, 0x00001d38, 0xffffed18, 0x0000022c, 0xfffffedc,
0xfffffff4, 0x00000020, 0x00000320, 0xffffff1c, 0x00000c68, 0x0000111c, 0xffffb92c, 0x0000fc6c,
0x00043150, 0xfffc708c, 0x00009bb8, 0xffff7368, 0x00001c64, 0xffffebf4, 0x00000210, 0xfffffec4,
0xfffffff0, 0x0000001c, 0x000002f4, 0xfffffeb4, 0x00000974, 0x00000cb8, 0xffffa518, 0x0000e350,
0x000420b4, 0xfffc5908, 0x00009b9c, 0xffff6ff4, 0x00001b7c, 0xffffead0, 0x000001f4, 0xfffffeac,
0xfffffff0, 0x0000001c, 0x000002c4, 0xfffffe58, 0x00000648, 0x00000884, 0xffff9038, 0x0000cad0,
0x00040ef8, 0xfffc425c, 0x00009af0, 0xffff6ce0, 0x00001a88, 0xffffe9b0, 0x000001d4, 0xfffffe94,
0xffffffec, 0x00000018, 0x0000028c, 0xfffffe04, 0x000002e4, 0x00000480, 0xffff7a90, 0x0000b2fc,
0x0003fc28, 0xfffc2c90, 0x000099b8, 0xffff6a3c, 0x00001988, 0xffffe898, 0x000001bc, 0xfffffe7c,
0x000001a0, 0x0000187c, 0x000097fc, 0x0003e84c, 0xffff6424, 0xffffff4c, 0x00000248, 0xffffffec,
};
/* format = Q30, range = [0.0981, 1.9976]
*
* n = 16;
* k = 0;
* for(i=0; i<5; i++, n=n/2) {
* for(p=0; p<n; p++, k++) {
* t = (PI / (4*n)) * (2*p + 1);
* coef32[k] = 2.0 * cos(t);
* }
* }
* coef32[30] *= 0.5; / *** for initial back butterfly (i.e. two-point DCT) *** /
*/
const int coef32[31] PROGMEM = {
0x7fd8878d, 0x7e9d55fc, 0x7c29fbee, 0x78848413, 0x73b5ebd0, 0x6dca0d14, 0x66cf811f, 0x5ed77c89,
0x55f5a4d2, 0x4c3fdff3, 0x41ce1e64, 0x36ba2013, 0x2b1f34eb, 0x1f19f97b, 0x12c8106e, 0x0647d97c,
0x7f62368f, 0x7a7d055b, 0x70e2cbc6, 0x62f201ac, 0x5133cc94, 0x3c56ba70, 0x25280c5d, 0x0c8bd35e,
0x7d8a5f3f, 0x6a6d98a4, 0x471cece6, 0x18f8b83c, 0x7641af3c, 0x30fbc54d, 0x2d413ccc,
};
/* let c(j) = cos(M_PI/36 * ((j)+0.5)), s(j) = sin(M_PI/36 * ((j)+0.5))
* then fastWin[2*j+0] = c(j)*(s(j) + c(j)), j = [0, 8]
* fastWin[2*j+1] = c(j)*(s(j) - c(j))
* format = Q30
*/
const uint32_t fastWin36[18] PROGMEM = {
0x42aace8b, 0xc2e92724, 0x47311c28, 0xc95f619a, 0x4a868feb, 0xd0859d8c,
0x4c913b51, 0xd8243ea0, 0x4d413ccc, 0xe0000000, 0x4c913b51, 0xe7dbc161,
0x4a868feb, 0xef7a6275, 0x47311c28, 0xf6a09e67, 0x42aace8b, 0xfd16d8dd
};
/* tables for quadruples
* format 0xAB
* A = length of codeword
* B = codeword
*/
const unsigned char quadTable[64+16] PROGMEM = {
/* table A */
0x6b, 0x6f, 0x6d, 0x6e, 0x67, 0x65, 0x59, 0x59, 0x56, 0x56, 0x53, 0x53, 0x5a, 0x5a, 0x5c, 0x5c,
0x42, 0x42, 0x42, 0x42, 0x41, 0x41, 0x41, 0x41, 0x44, 0x44, 0x44, 0x44, 0x48, 0x48, 0x48, 0x48,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
/* table B */
0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40,
};
/* indexing = [version][layer][bitrate index]
* bitrate (kbps) of frame
* - bitrate index == 0 is "free" mode (bitrate determined on the fly by
* counting bits between successive sync words)
*/
const int/*short*/bitrateTab[3][3][15] PROGMEM = { {
/* MPEG-1 */
{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 }, /* Layer 1 */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 }, /* Layer 2 */
{ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }, /* Layer 3 */
}, {
/* MPEG-2 */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 }, /* Layer 1 */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, /* Layer 2 */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, /* Layer 3 */
}, {
/* MPEG-2.5 */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 }, /* Layer 1 */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, /* Layer 2 */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, /* Layer 3 */
}, };
/* indexing = [version][sampleRate][bitRate]
* for layer3, nSlots = floor(samps/frame * bitRate / sampleRate / 8)
* - add one pad slot if necessary
*/
const int/*short*/slotTab[3][3][15] PROGMEM = {
{ /* MPEG-1 */
{ 0, 104, 130, 156, 182, 208, 261, 313, 365, 417, 522, 626, 731, 835, 1044 }, /* 44 kHz */
{ 0, 96, 120, 144, 168, 192, 240, 288, 336, 384, 480, 576, 672, 768, 960 }, /* 48 kHz */
{ 0, 144, 180, 216, 252, 288, 360, 432, 504, 576, 720, 864, 1008, 1152, 1440 }, /* 32 kHz */
},
{ /* MPEG-2 */
{ 0, 26, 52, 78, 104, 130, 156, 182, 208, 261, 313, 365, 417, 470, 522 }, /* 22 kHz */
{ 0, 24, 48, 72, 96, 120, 144, 168, 192, 240, 288, 336, 384, 432, 480 }, /* 24 kHz */
{ 0, 36, 72, 108, 144, 180, 216, 252, 288, 360, 432, 504, 576, 648, 720 }, /* 16 kHz */
},
{ /* MPEG-2.5 */
{ 0, 52, 104, 156, 208, 261, 313, 365, 417, 522, 626, 731, 835, 940, 1044 }, /* 11 kHz */
{ 0, 48, 96, 144, 192, 240, 288, 336, 384, 480, 576, 672, 768, 864, 960 }, /* 12 kHz */
{ 0, 72, 144, 216, 288, 360, 432, 504, 576, 720, 864, 1008, 1152, 1296, 1440 }, /* 8 kHz */
},
};
const uint32_t imdctWin[4][36] PROGMEM = {
{
0x02aace8b, 0x07311c28, 0x0a868fec, 0x0c913b52, 0x0d413ccd, 0x0c913b52, 0x0a868fec, 0x07311c28,
0x02aace8b, 0xfd16d8dd, 0xf6a09e66, 0xef7a6275, 0xe7dbc161, 0xe0000000, 0xd8243e9f, 0xd0859d8b,
0xc95f619a, 0xc2e92723, 0xbd553175, 0xb8cee3d8, 0xb5797014, 0xb36ec4ae, 0xb2bec333, 0xb36ec4ae,
0xb5797014, 0xb8cee3d8, 0xbd553175, 0xc2e92723, 0xc95f619a, 0xd0859d8b, 0xd8243e9f, 0xe0000000,
0xe7dbc161, 0xef7a6275, 0xf6a09e66, 0xfd16d8dd },
{
0x02aace8b, 0x07311c28, 0x0a868fec, 0x0c913b52, 0x0d413ccd, 0x0c913b52, 0x0a868fec, 0x07311c28,
0x02aace8b, 0xfd16d8dd, 0xf6a09e66, 0xef7a6275, 0xe7dbc161, 0xe0000000, 0xd8243e9f, 0xd0859d8b,
0xc95f619a, 0xc2e92723, 0xbd44ef14, 0xb831a052, 0xb3aa3837, 0xafb789a4, 0xac6145bb, 0xa9adecdc,
0xa864491f, 0xad1868f0, 0xb8431f49, 0xc8f42236, 0xdda8e6b1, 0xf47755dc, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{
0x07311c28, 0x0d413ccd, 0x07311c28, 0xf6a09e66, 0xe0000000, 0xc95f619a, 0xb8cee3d8, 0xb2bec333,
0xb8cee3d8, 0xc95f619a, 0xe0000000, 0xf6a09e66, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x028e9709, 0x04855ec0,
0x026743a1, 0xfcde2c10, 0xf515dc82, 0xec93e53b, 0xe4c880f8, 0xdd5d0b08, 0xd63510b7, 0xcf5e834a,
0xc8e6b562, 0xc2da4105, 0xbd553175, 0xb8cee3d8, 0xb5797014, 0xb36ec4ae, 0xb2bec333, 0xb36ec4ae,
0xb5797014, 0xb8cee3d8, 0xbd553175, 0xc2e92723, 0xc95f619a, 0xd0859d8b, 0xd8243e9f, 0xe0000000,
0xe7dbc161, 0xef7a6275, 0xf6a09e66, 0xfd16d8dd },
};
const int ISFMpeg1[2][7] PROGMEM = {
{0x00000000, 0x0d8658ba, 0x176cf5d0, 0x20000000, 0x28930a2f, 0x3279a745, 0x40000000},
{0x00000000, 0x13207f5c, 0x2120fb83, 0x2d413ccc, 0x39617e16, 0x4761fa3d, 0x5a827999}
};
const int ISFMpeg2[2][2][16] PROGMEM = {
{ { /* intensityScale off, mid-side off */
0x40000000, 0x35d13f32, 0x2d413ccc, 0x260dfc14, 0x1fffffff, 0x1ae89f99, 0x16a09e66, 0x1306fe0a,
0x0fffffff, 0x0d744fcc, 0x0b504f33, 0x09837f05, 0x07ffffff, 0x06ba27e6, 0x05a82799, 0x04c1bf82 },
{ /* intensityScale off, mid-side on */
0x5a827999, 0x4c1bf827, 0x3fffffff, 0x35d13f32, 0x2d413ccc, 0x260dfc13, 0x1fffffff, 0x1ae89f99,
0x16a09e66, 0x1306fe09, 0x0fffffff, 0x0d744fcc, 0x0b504f33, 0x09837f04, 0x07ffffff, 0x06ba27e6 }, },
{ { /* intensityScale on, mid-side off */
0x40000000, 0x2d413ccc, 0x20000000, 0x16a09e66, 0x10000000, 0x0b504f33, 0x08000000, 0x05a82799,
0x04000000, 0x02d413cc, 0x02000000, 0x016a09e6, 0x01000000, 0x00b504f3, 0x00800000, 0x005a8279 },
{ /* intensityScale on, mid-side on */
0x5a827999, 0x3fffffff, 0x2d413ccc, 0x1fffffff, 0x16a09e66, 0x0fffffff, 0x0b504f33, 0x07ffffff,
0x05a82799, 0x03ffffff, 0x02d413cc, 0x01ffffff, 0x016a09e6, 0x00ffffff, 0x00b504f3, 0x007fffff } }
};
const uint32_t m_COS0_0 = 0x4013c251; /* Q31 */
const uint32_t m_COS0_1 = 0x40b345bd; /* Q31 */
const uint32_t m_COS0_2 = 0x41fa2d6d; /* Q31 */
const uint32_t m_COS0_3 = 0x43f93421; /* Q31 */
const uint32_t m_COS0_4 = 0x46cc1bc4; /* Q31 */
const uint32_t m_COS0_5 = 0x4a9d9cf0; /* Q31 */
const uint32_t m_COS0_6 = 0x4fae3711; /* Q31 */
const uint32_t m_COS0_7 = 0x56601ea7; /* Q31 */
const uint32_t m_COS0_8 = 0x5f4cf6eb; /* Q31 */
const uint32_t m_COS0_9 = 0x6b6fcf26; /* Q31 */
const uint32_t m_COS0_10= 0x7c7d1db3; /* Q31 */
const uint32_t m_COS0_11= 0x4ad81a97; /* Q30 */
const uint32_t m_COS0_12= 0x5efc8d96; /* Q30 */
const uint32_t m_COS0_13= 0x41d95790; /* Q29 */
const uint32_t m_COS0_14= 0x6d0b20cf; /* Q29 */
const uint32_t m_COS0_15= 0x518522fb; /* Q27 */
const uint32_t m_COS1_0 = 0x404f4672; /* Q31 */
const uint32_t m_COS1_1 = 0x42e13c10; /* Q31 */
const uint32_t m_COS1_2 = 0x48919f44; /* Q31 */
const uint32_t m_COS1_3 = 0x52cb0e63; /* Q31 */
const uint32_t m_COS1_4 = 0x64e2402e; /* Q31 */
const uint32_t m_COS1_5 = 0x43e224a9; /* Q30 */
const uint32_t m_COS1_6 = 0x6e3c92c1; /* Q30 */
const uint32_t m_COS1_7 = 0x519e4e04; /* Q28 */
const uint32_t m_COS2_0 = 0x4140fb46; /* Q31 */
const uint32_t m_COS2_1 = 0x4cf8de88; /* Q31 */
const uint32_t m_COS2_2 = 0x73326bbf; /* Q31 */
const uint32_t m_COS2_3 = 0x52036742; /* Q29 */
const uint32_t m_COS3_0 = 0x4545e9ef; /* Q31 */
const uint32_t m_COS3_1 = 0x539eba45; /* Q30 */
const uint32_t m_COS4_0 = 0x5a82799a; /* Q31 */
const uint32_t m_dcttab[48] PROGMEM = { // faster in ROM
/* first pass */
m_COS0_0, m_COS0_15, m_COS1_0, /* 31, 27, 31 */
m_COS0_1, m_COS0_14, m_COS1_1, /* 31, 29, 31 */
m_COS0_2, m_COS0_13, m_COS1_2, /* 31, 29, 31 */
m_COS0_3, m_COS0_12, m_COS1_3, /* 31, 30, 31 */
m_COS0_4, m_COS0_11, m_COS1_4, /* 31, 30, 31 */
m_COS0_5, m_COS0_10, m_COS1_5, /* 31, 31, 30 */
m_COS0_6, m_COS0_9, m_COS1_6, /* 31, 31, 30 */
m_COS0_7, m_COS0_8, m_COS1_7, /* 31, 31, 28 */
/* second pass */
m_COS2_0, m_COS2_3, m_COS3_0, /* 31, 29, 31 */
m_COS2_1, m_COS2_2, m_COS3_1, /* 31, 31, 30 */
-m_COS2_0, -m_COS2_3, m_COS3_0, /* 31, 29, 31 */
-m_COS2_1, -m_COS2_2, m_COS3_1, /* 31, 31, 30 */
m_COS2_0, m_COS2_3, m_COS3_0, /* 31, 29, 31 */
m_COS2_1, m_COS2_2, m_COS3_1, /* 31, 31, 30 */
-m_COS2_0, -m_COS2_3, m_COS3_0, /* 31, 29, 31 */
-m_COS2_1, -m_COS2_2, m_COS3_1, /* 31, 31, 30 */
};
const uint8_t m_SYNCWORDH =0xff;
const uint8_t m_SYNCWORDL =0xf0;
const uint8_t m_DQ_FRACBITS_OUT =25; // number of fraction bits in output of dequant
const uint8_t m_CSHIFT =12; // coefficients have 12 leading sign bits for early-terminating mulitplies
const uint8_t m_HUFF_PAIRTABS =32;
const uint8_t m_SIBYTES_MPEG1_MONO =17;
const uint8_t m_SIBYTES_MPEG1_STEREO =32;
const uint8_t m_SIBYTES_MPEG2_MONO =9;
const uint8_t m_SIBYTES_MPEG2_STEREO =17;
const uint8_t m_IMDCT_SCALE =2; // additional scaling (by sqrt(2)) for fast IMDCT36
const uint8_t m_BLOCK_SIZE =18;
const uint8_t m_NBANDS =32;
const uint8_t m_MAX_REORDER_SAMPS =(192-126)*3; // largest critical band for short blocks (see sfBandTable)
const uint16_t m_VBUF_LENGTH =17*2* m_NBANDS; // for double-sized vbuf FIFO
const uint8_t m_MAX_SCFBD =4; // max scalefactor bands per channel
const uint8_t m_NGRANS_MPEG1 =2;
const uint8_t m_NGRANS_MPEG2 =1;
const uint16_t m_MAINBUF_SIZE =1940;
const uint8_t m_MAX_NGRAN =2; // max granules
const uint8_t m_MAX_NCHAN =2; // max channels
const uint16_t m_MAX_NSAMP =576; // max samples per channel, per granule
const uint32_t m_SQRTHALF =0x5a82799a; // sqrt(0.5) in Q31 format
enum {
ERR_MP3_NONE = 0,
ERR_MP3_INDATA_UNDERFLOW = -1,
ERR_MP3_MAINDATA_UNDERFLOW = -2,
ERR_MP3_FREE_BITRATE_SYNC = -3,
ERR_MP3_OUT_OF_MEMORY = -4,
ERR_MP3_NULL_POINTER = -5,
ERR_MP3_INVALID_FRAMEHEADER = -6,
ERR_MP3_INVALID_SIDEINFO = -7,
ERR_MP3_INVALID_SCALEFACT = -8,
ERR_MP3_INVALID_HUFFCODES = -9,
ERR_MP3_INVALID_DEQUANTIZE = -10,
ERR_MP3_INVALID_IMDCT = -11,
ERR_MP3_INVALID_SUBBAND = -12,
ERR_UNKNOWN = -9999
};
typedef struct MP3FrameInfo {
int bitrate;
int nChans;
int samprate;
int bitsPerSample;
int outputSamps;
int layer;
int version;
} MP3FrameInfo_t;
typedef struct SFBandTable {
int/*short*/ l[23];
int/*short*/ s[14];
} SFBandTable_t;
typedef struct BitStreamInfo {
unsigned char *bytePtr;
unsigned int iCache;
int cachedBits;
int nBytes;
} BitStreamInfo_t;
typedef enum { /* map these to the corresponding 2-bit values in the frame header */
Stereo = 0x00, /* two independent channels, but L and R frames might have different # of bits */
Joint = 0x01, /* coupled channels - layer III: mix of M-S and intensity, Layers I/II: intensity and direct coding only */
Dual = 0x02, /* two independent channels, L and R always have exactly 1/2 the total bitrate */
Mono = 0x03 /* one channel */
} StereoMode_t;
typedef enum { /* map to 0,1,2 to make table indexing easier */
MPEG1 = 0,
MPEG2 = 1,
MPEG25 = 2
} MPEGVersion_t;
typedef struct FrameHeader {
int layer; /* layer index (1, 2, or 3) */
int crc; /* CRC flag: 0 = disabled, 1 = enabled */
int brIdx; /* bitrate index (0 - 15) */
int srIdx; /* sample rate index (0 - 2) */
int paddingBit; /* padding flag: 0 = no padding, 1 = single pad byte */
int privateBit; /* unused */
int modeExt; /* used to decipher joint stereo mode */
int copyFlag; /* copyright flag: 0 = no, 1 = yes */
int origFlag; /* original flag: 0 = copy, 1 = original */
int emphasis; /* deemphasis mode */
int CRCWord; /* CRC word (16 bits, 0 if crc not enabled) */
} FrameHeader_t;
typedef struct SideInfoSub {
int part23Length; /* number of bits in main data */
int nBigvals; /* 2x this = first set of Huffman cw's (maximum amplitude can be > 1) */
int globalGain; /* overall gain for dequantizer */
int sfCompress; /* unpacked to figure out number of bits in scale factors */
int winSwitchFlag; /* window switching flag */
int blockType; /* block type */
int mixedBlock; /* 0 = regular block (all short or long), 1 = mixed block */
int tableSelect[3]; /* index of Huffman tables for the big values regions */
int subBlockGain[3]; /* subblock gain offset, relative to global gain */
int region0Count; /* 1+region0Count = num scale factor bands in first region of bigvals */
int region1Count; /* 1+region1Count = num scale factor bands in second region of bigvals */
int preFlag; /* for optional high frequency boost */
int sfactScale; /* scaling of the scalefactors */
int count1TableSelect; /* index of Huffman table for quad codewords */
} SideInfoSub_t;
typedef struct SideInfo {
int mainDataBegin;
int privateBits;
int scfsi[m_MAX_NCHAN][m_MAX_SCFBD]; /* 4 scalefactor bands per channel */
} SideInfo_t;
typedef struct {
int cbType; /* pure long = 0, pure short = 1, mixed = 2 */
int cbEndS[3]; /* number nonzero short cb's, per subbblock */
int cbEndSMax; /* max of cbEndS[] */
int cbEndL; /* number nonzero long cb's */
} CriticalBandInfo_t;
typedef struct DequantInfo {
int workBuf[m_MAX_REORDER_SAMPS]; /* workbuf for reordering short blocks */
} DequantInfo_t;
typedef struct HuffmanInfo {
int huffDecBuf[m_MAX_NCHAN][m_MAX_NSAMP]; /* used both for decoded Huffman values and dequantized coefficients */
int nonZeroBound[m_MAX_NCHAN]; /* number of coeffs in huffDecBuf[ch] which can be > 0 */
int gb[m_MAX_NCHAN]; /* minimum number of guard bits in huffDecBuf[ch] */
} HuffmanInfo_t;
typedef enum HuffTabType {
noBits,
oneShot,
loopNoLinbits,
loopLinbits,
quadA,
quadB,
invalidTab
} HuffTabType_t;
typedef struct HuffTabLookup {
int linBits;
int tabType; /*HuffTabType*/
} HuffTabLookup_t;
typedef struct IMDCTInfo {
int outBuf[m_MAX_NCHAN][m_BLOCK_SIZE][m_NBANDS]; /* output of IMDCT */
int overBuf[m_MAX_NCHAN][m_MAX_NSAMP / 2]; /* overlap-add buffer (by symmetry, only need 1/2 size) */
int numPrevIMDCT[m_MAX_NCHAN]; /* how many IMDCT's calculated in this channel on prev. granule */
int prevType[m_MAX_NCHAN];
int prevWinSwitch[m_MAX_NCHAN];
int gb[m_MAX_NCHAN];
} IMDCTInfo_t;
typedef struct BlockCount {
int nBlocksLong;
int nBlocksTotal;
int nBlocksPrev;
int prevType;
int prevWinSwitch;
int currWinSwitch;
int gbIn;
int gbOut;
} BlockCount_t;
typedef struct ScaleFactorInfoSub { /* max bits in scalefactors = 5, so use char's to save space */
char l[23]; /* [band] */
char s[13][3]; /* [band][window] */
} ScaleFactorInfoSub_t;
typedef struct ScaleFactorJS { /* used in MPEG 2, 2.5 intensity (joint) stereo only */
int intensityScale;
int slen[4];
int nr[4];
} ScaleFactorJS_t;
/* NOTE - could get by with smaller vbuf if memory is more important than speed
* (in Subband, instead of replicating each block in FDCT32 you would do a memmove on the
* last 15 blocks to shift them down one, a hardware style FIFO)
*/
typedef struct SubbandInfo {
int vbuf[m_MAX_NCHAN * m_VBUF_LENGTH]; /* vbuf for fast DCT-based synthesis PQMF - double size for speed (no modulo indexing) */
int vindex; /* internal index for tracking position in vbuf */
} SubbandInfo_t;
typedef struct MP3DecInfo {
/* buffer which must be large enough to hold largest possible main_data section */
unsigned char mainBuf[m_MAINBUF_SIZE];
/* special info for "free" bitrate files */
int freeBitrateFlag;
int freeBitrateSlots;
/* user-accessible info */
int bitrate;
int nChans;
int samprate;
int nGrans; /* granules per frame */
int nGranSamps; /* samples per granule */
int nSlots;
int layer;
int mainDataBegin;
int mainDataBytes;
int part23Length[m_MAX_NGRAN][m_MAX_NCHAN];
} MP3DecInfo_t;
__attribute__((unused))static MP3FrameInfo_t m_MP3FrameInfo;
__attribute__((unused))static SFBandTable_t m_SFBandTable;
__attribute__((unused))static StereoMode_t m_sMode; /* mono/stereo mode */
__attribute__((unused))static MPEGVersion_t m_MPEGVersion; /* version ID */
__attribute__((unused))static FrameHeader_t m_FrameHeader;
__attribute__((unused))static SideInfoSub_t m_SideInfoSub[m_MAX_NGRAN][m_MAX_NCHAN];
__attribute__((unused))static SideInfo_t m_SideInfo;
__attribute__((unused))static CriticalBandInfo_t m_CriticalBandInfo[m_MAX_NCHAN]; /* filled in dequantizer, used in joint stereo reconstruction */
__attribute__((unused))static DequantInfo_t m_DequantInfo;
__attribute__((unused))static HuffmanInfo_t m_HuffmanInfo;
__attribute__((unused))static IMDCTInfo_t m_IMDCTInfo;
__attribute__((unused))static ScaleFactorInfoSub_t m_ScaleFactorInfoSub[m_MAX_NGRAN][m_MAX_NCHAN];
__attribute__((unused))static ScaleFactorJS_t m_ScaleFactorJS;
__attribute__((unused))static SubbandInfo_t m_SubbandInfo;
__attribute__((unused))static MP3DecInfo_t m_MP3DecInfo;
/* format = Q31
* #define M_PI 3.14159265358979323846
* double u = 2.0 * M_PI / 9.0;
* float c0 = sqrt(3.0) / 2.0;
* float c1 = cos(u);
* float c2 = cos(2*u);
* float c3 = sin(u);
* float c4 = sin(2*u);
*/
const int c9_0 = 0x6ed9eba1;
const int c9_1 = 0x620dbe8b;
const int c9_2 = 0x163a1a7e;
const int c9_3 = 0x5246dd49;
const int c9_4 = 0x7e0e2e32;
const int c3_0 = 0x6ed9eba1; /* format = Q31, cos(pi/6) */
const int c6[3] = { 0x7ba3751d, 0x5a82799a, 0x2120fb83 }; /* format = Q31, cos(((0:2) + 0.5) * (pi/6)) */
/* format = Q31
* cos(((0:8) + 0.5) * (pi/18))
*/
const uint32_t c18[9] = { 0x7f834ed0, 0x7ba3751d, 0x7401e4c1, 0x68d9f964, 0x5a82799a, 0x496af3e2, 0x36185aee, 0x2120fb83, 0x0b27eb5c};
/* scale factor lengths (num bits) */
const char m_SFLenTab[16][2] = { {0, 0}, {0, 1}, {0, 2}, {0, 3}, {3, 0}, {1, 1}, {1, 2}, {1, 3},
{2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}, {4, 2}, {4, 3}};
/* NRTab[size + 3*is_right][block type][partition]
* block type index: 0 = (bt0,bt1,bt3), 1 = bt2 non-mixed, 2 = bt2 mixed
* partition: scale factor groups (sfb1 through sfb4)
* for block type = 2 (mixed or non-mixed) / by 3 is rolled into this table
* (for 3 short blocks per long block)
* see 2.4.3.2 in MPEG 2 (low sample rate) spec
* stuff rolled into this table:
* NRTab[x][1][y] --> (NRTab[x][1][y]) / 3
* NRTab[x][2][>=1] --> (NRTab[x][2][>=1]) / 3 (first partition is long block)
*/
const char NRTab[6][3][4] = {
{{ 6, 5, 5, 5}, {3, 3, 3, 3}, {6, 3, 3, 3}},
{{ 6, 5, 7, 3}, {3, 3, 4, 2}, {6, 3, 4, 2}},
{{11, 10, 0, 0}, {6, 6, 0, 0}, {6, 3, 6, 0}},
{{ 7, 7, 7, 0}, {4, 4, 4, 0}, {6, 5, 4, 0}},
{{ 6, 6, 6, 3}, {4, 3, 3, 2}, {6, 4, 3, 2}},
{{ 8, 8, 5, 0}, {5, 4, 3, 0}, {6, 6, 3, 0}}
};
/* optional pre-emphasis for high-frequency scale factor bands */
static const char preTab[22] = { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,3,3,3,2,0 };
/* pow(2,-i/4) for i=0..3, Q31 format */
const int pow14[4] PROGMEM = {
0x7fffffff, 0x6ba27e65, 0x5a82799a, 0x4c1bf829
};
/*
* Minimax polynomial approximation to pow(x, 4/3), over the range
* poly43lo: x = [0.5, 0.7071]
* poly43hi: x = [0.7071, 1.0]
*
* Relative error < 1E-7
* Coefs are scaled by 4, 2, 1, 0.5, 0.25
*/
static const unsigned int poly43lo[5] PROGMEM = { 0x29a0bda9, 0xb02e4828, 0x5957aa1b, 0x236c498d, 0xff581859 };
static const unsigned int poly43hi[5] PROGMEM = { 0x10852163, 0xd333f6a4, 0x46e9408b, 0x27c2cef0, 0xfef577b4 };
/* pow(2, i*4/3) as exp and frac */
const int pow2exp[8] PROGMEM = { 14, 13, 11, 10, 9, 7, 6, 5 };
const int pow2frac[8] PROGMEM = {
0x6597fa94, 0x50a28be6, 0x7fffffff, 0x6597fa94,
0x50a28be6, 0x7fffffff, 0x6597fa94, 0x50a28be6
};
const uint16_t m_HUFF_OFFSET_01= 0;
const uint16_t m_HUFF_OFFSET_02= 9 + m_HUFF_OFFSET_01;
const uint16_t m_HUFF_OFFSET_03= 65 + m_HUFF_OFFSET_02;
const uint16_t m_HUFF_OFFSET_05= 65 + m_HUFF_OFFSET_03;
const uint16_t m_HUFF_OFFSET_06=257 + m_HUFF_OFFSET_05;
const uint16_t m_HUFF_OFFSET_07=129 + m_HUFF_OFFSET_06;
const uint16_t m_HUFF_OFFSET_08=110 + m_HUFF_OFFSET_07;
const uint16_t m_HUFF_OFFSET_09=280 + m_HUFF_OFFSET_08;
const uint16_t m_HUFF_OFFSET_10= 93 + m_HUFF_OFFSET_09;
const uint16_t m_HUFF_OFFSET_11=320 + m_HUFF_OFFSET_10;
const uint16_t m_HUFF_OFFSET_12=296 + m_HUFF_OFFSET_11;
const uint16_t m_HUFF_OFFSET_13=185 + m_HUFF_OFFSET_12;
const uint16_t m_HUFF_OFFSET_15=497 + m_HUFF_OFFSET_13;
const uint16_t m_HUFF_OFFSET_16=580 + m_HUFF_OFFSET_15;
const uint16_t m_HUFF_OFFSET_24=651 + m_HUFF_OFFSET_16;
const int huffTabOffset[m_HUFF_PAIRTABS] PROGMEM = {
0, m_HUFF_OFFSET_01, m_HUFF_OFFSET_02, m_HUFF_OFFSET_03,
0, m_HUFF_OFFSET_05, m_HUFF_OFFSET_06, m_HUFF_OFFSET_07,
m_HUFF_OFFSET_08, m_HUFF_OFFSET_09, m_HUFF_OFFSET_10, m_HUFF_OFFSET_11,
m_HUFF_OFFSET_12, m_HUFF_OFFSET_13, 0, m_HUFF_OFFSET_15,
m_HUFF_OFFSET_16, m_HUFF_OFFSET_16, m_HUFF_OFFSET_16, m_HUFF_OFFSET_16,
m_HUFF_OFFSET_16, m_HUFF_OFFSET_16, m_HUFF_OFFSET_16, m_HUFF_OFFSET_16,
m_HUFF_OFFSET_24, m_HUFF_OFFSET_24, m_HUFF_OFFSET_24, m_HUFF_OFFSET_24,
m_HUFF_OFFSET_24, m_HUFF_OFFSET_24, m_HUFF_OFFSET_24, m_HUFF_OFFSET_24,};
const HuffTabLookup_t huffTabLookup[m_HUFF_PAIRTABS] PROGMEM = {
{ 0, noBits },
{ 0, oneShot },
{ 0, oneShot },
{ 0, oneShot },
{ 0, invalidTab },
{ 0, oneShot },
{ 0, oneShot },
{ 0, loopNoLinbits },
{ 0, loopNoLinbits },
{ 0, loopNoLinbits },
{ 0, loopNoLinbits },
{ 0, loopNoLinbits },
{ 0, loopNoLinbits },
{ 0, loopNoLinbits },
{ 0, invalidTab },
{ 0, loopNoLinbits },
{ 1, loopLinbits },
{ 2, loopLinbits },
{ 3, loopLinbits },
{ 4, loopLinbits },
{ 6, loopLinbits },
{ 8, loopLinbits },
{ 10, loopLinbits },
{ 13, loopLinbits },
{ 4, loopLinbits },
{ 5, loopLinbits },
{ 6, loopLinbits },
{ 7, loopLinbits },
{ 8, loopLinbits },
{ 9, loopLinbits },
{ 11, loopLinbits },
{ 13, loopLinbits },
};
const int quadTabOffset[2] PROGMEM = {0, 64};
const int quadTabMaxBits[2] PROGMEM = {6, 4};
/* indexing = [version][samplerate index]
* sample rate of frame (Hz)
*/
const int samplerateTab[3][3] PROGMEM = {
{ 44100, 48000, 32000 }, /* MPEG-1 */
{ 22050, 24000, 16000 }, /* MPEG-2 */
{ 11025, 12000, 8000 }, /* MPEG-2.5 */
};
/* indexing = [version][layer]
* number of samples in one frame (per channel)
*/
const int/*short*/samplesPerFrameTab[3][3] PROGMEM = { { 384, 1152, 1152 }, /* MPEG1 */
{ 384, 1152, 576 }, /* MPEG2 */
{ 384, 1152, 576 }, /* MPEG2.5 */
};
/* layers 1, 2, 3 */
const short bitsPerSlotTab[3] = { 32, 8, 8 };
/* indexing = [version][mono/stereo]
* number of bytes in side info section of bitstream
*/
const int/*short*/sideBytesTab[3][2] PROGMEM = { { 17, 32 }, /* MPEG-1: mono, stereo */
{ 9, 17 }, /* MPEG-2: mono, stereo */
{ 9, 17 }, /* MPEG-2.5: mono, stereo */
};
/* indexing = [version][sampleRate][long (.l) or short (.s) block]
* sfBandTable[v][s].l[cb] = index of first bin in critical band cb (long blocks)
* sfBandTable[v][s].s[cb] = index of first bin in critical band cb (short blocks)
*/
const SFBandTable_t sfBandTable[3][3] PROGMEM = {
{ /* MPEG-1 (44, 48, 32 kHz) */
{ {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 52, 62, 74, 90, 110, 134, 162, 196, 238, 288, 342, 418, 576 },
{0, 4, 8, 12, 16, 22, 30, 40, 52, 66, 84, 106, 136, 192} },
{ {0, 4, 8, 12, 16, 20, 24, 30, 36, 42, 50, 60, 72, 88, 106, 128, 156, 190, 230, 276, 330, 384, 576 },
{0, 4, 8, 12, 16, 22, 28, 38, 50, 64, 80, 100, 126, 192} },
{ {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 54, 66, 82, 102, 126, 156, 194, 240, 296, 364, 448, 550, 576 },
{0, 4, 8, 12, 16, 22, 30, 42, 58, 78, 104, 138, 180, 192} } },
{ /* MPEG-2 (22, 24, 16 kHz) */
{ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
{0, 4, 8, 12, 18, 24, 32, 42, 56, 74, 100, 132, 174, 192} },
{ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 114, 136, 162, 194, 232, 278, 332, 394, 464, 540, 576 },
{0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 136, 180, 192} },
{ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
{0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192} }, },
{ /* MPEG-2.5 (11, 12, 8 kHz) */
{ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
{0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192 } },
{ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
{0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192 } },
{ {0, 12, 24, 36, 48, 60, 72, 88, 108, 132, 160, 192, 232, 280, 336, 400, 476, 566, 568, 570, 572, 574, 576 },
{0, 8, 16, 24, 36, 52, 72, 96, 124, 160, 162, 164, 166, 192 } }, },
};
/* indexing = [intensity scale on/off][left/right]
* format = Q30, range = [0.0, 1.414]
*
* illegal intensity position scalefactors (see comments on ISFMpeg1)
*/
const int ISFIIP[2][2] PROGMEM = {
{0x40000000, 0x00000000}, /* mid-side off */
{0x40000000, 0x40000000}, /* mid-side on */
};
const unsigned char uniqueIDTab[8] = {0x5f, 0x4b, 0x43, 0x5f, 0x5f, 0x4a, 0x52, 0x5f};
/* anti-alias coefficients - see spec Annex B, table 3-B.9
* csa[0][i] = CSi, csa[1][i] = CAi
* format = Q31
*/
const uint32_t csa[8][2] PROGMEM = {
{0x6dc253f0, 0xbe2500aa},
{0x70dcebe4, 0xc39e4949},
{0x798d6e73, 0xd7e33f4a},
{0x7ddd40a7, 0xe8b71176},
{0x7f6d20b7, 0xf3e4fe2f},
{0x7fe47e40, 0xfac1a3c7},
{0x7ffcb263, 0xfe2ebdc6},
{0x7fffc694, 0xff86c25d},
};
/* format = Q30, right shifted by 12 (sign bits only in top 12 - undo this when rounding to short)
* this is to enable early-terminating multiplies on ARM
* range = [-1.144287109, 1.144989014]
* max gain of filter (per output sample) ~= 2.731
*
* new (properly sign-flipped) values
* - these actually are correct to 32 bits, (floating-pt coefficients in spec
* chosen such that only ~20 bits are required)
*
* Reordering - see table 3-B.3 in spec (appendix B)
*
* polyCoef[i] =
* D[ 0, 32, 64, ... 480], i = [ 0, 15]
* D[ 1, 33, 65, ... 481], i = [ 16, 31]
* D[ 2, 34, 66, ... 482], i = [ 32, 47]
* ...
* D[15, 47, 79, ... 495], i = [240,255]
*
* also exploits symmetry: D[i] = -D[512 - i], for i = [1, 255]
*
* polyCoef[256, 257, ... 263] are for special case of sample 16 (out of 0)
* see PolyphaseStereo() and PolyphaseMono()
*/
// prototypes
void EraseBuffers(void);
int MP3Decode( unsigned char *inbuf, int *bytesLeft, short *outbuf, int useSize);
void MP3GetLastFrameInfo();
int MP3GetNextFrameInfo(unsigned char *buf);
int MP3FindSyncWord(unsigned char *buf, int nBytes);
int MP3GetSampRate();
int MP3GetChannels();
int MP3GetBitsPerSample();
int MP3GetBitrate();
int MP3GetOutputSamps();
//internally used
void PolyphaseMono(short *pcm, int *vbuf, const uint32_t *coefBase);
void PolyphaseStereo(short *pcm, int *vbuf, const uint32_t *coefBase);
void SetBitstreamPointer(BitStreamInfo_t *bsi, int nBytes, unsigned char *buf);
unsigned int GetBits(BitStreamInfo_t *bsi, int nBits);
int CalcBitsUsed(BitStreamInfo_t *bsi, unsigned char *startBuf, int startOffset);
int DequantChannel(int *sampleBuf, int *workBuf, int *nonZeroBound, SideInfoSub_t *sis, ScaleFactorInfoSub_t *sfis, CriticalBandInfo_t *cbi);
void MidSideProc(int x[m_MAX_NCHAN][m_MAX_NSAMP], int nSamps, int mOut[2]);
void IntensityProcMPEG1(int x[m_MAX_NCHAN][m_MAX_NSAMP], int nSamps, ScaleFactorInfoSub_t *sfis, CriticalBandInfo_t *cbi, int midSideFlag, int mixFlag, int mOut[2]);
void IntensityProcMPEG2(int x[m_MAX_NCHAN][m_MAX_NSAMP], int nSamps, ScaleFactorInfoSub_t *sfis, CriticalBandInfo_t *cbi, ScaleFactorJS_t *sfjs, int midSideFlag, int mixFlag, int mOut[2]);
void FDCT32(int *x, int *d, int offset, int oddBlock, int gb);// __attribute__ ((section (".data")));
void FreeBuffers();
int CheckPadBit();
int UnpackFrameHeader(unsigned char *buf);
int UnpackSideInfo(unsigned char *buf);
int DecodeHuffman( unsigned char *buf, int *bitOffset, int huffBlockBits, int gr, int ch);
int Dequantize( int gr);
int IMDCT( int gr, int ch);
int UnpackScaleFactors( unsigned char *buf, int *bitOffset, int bitsAvail, int gr, int ch);
int Subband(short *pcmBuf);
short ClipToShort(int x, int fracBits);
void RefillBitstreamCache(BitStreamInfo_t *bsi);
void UnpackSFMPEG1(BitStreamInfo_t *bsi, SideInfoSub_t *sis, ScaleFactorInfoSub_t *sfis, int *scfsi, int gr, ScaleFactorInfoSub_t *sfisGr0);
void UnpackSFMPEG2(BitStreamInfo_t *bsi, SideInfoSub_t *sis, ScaleFactorInfoSub_t *sfis, int gr, int ch, int modeExt, ScaleFactorJS_t *sfjs);
int MP3FindFreeSync(unsigned char *buf, unsigned char firstFH[4], int nBytes);
void MP3ClearBadFrame( short *outbuf);
int DecodeHuffmanPairs(int *xy, int nVals, int tabIdx, int bitsLeft, unsigned char *buf, int bitOffset);
int DecodeHuffmanQuads(int *vwxy, int nVals, int tabIdx, int bitsLeft, unsigned char *buf, int bitOffset);
int DequantBlock(int *inbuf, int *outbuf, int num, int scale);
void AntiAlias(int *x, int nBfly);
void WinPrevious(int *xPrev, int *xPrevWin, int btPrev);
int FreqInvertRescale(int *y, int *xPrev, int blockIdx, int es);
void idct9(int *x);
int IMDCT36(int *xCurr, int *xPrev, int *y, int btCurr, int btPrev, int blockIdx, int gb);
void imdct12(int *x, int *out);
int IMDCT12x3(int *xCurr, int *xPrev, int *y, int btPrev, int blockIdx, int gb);
int HybridTransform(int *xCurr, int *xPrev, int y[m_BLOCK_SIZE][m_NBANDS], SideInfoSub_t *sis, BlockCount_t *bc);
inline uint64_t SAR64(uint64_t x, int n) {return x >> n;}
inline int MULSHIFT32(int x, int y) { int z; z = (uint64_t) x * (uint64_t) y >> 32; return z;}
inline uint64_t MADD64(uint64_t sum64, int x, int y) {sum64 += (uint64_t) x * (uint64_t) y; return sum64;}/* returns 64-bit value in [edx:eax] */
inline int CLZ(int x){int numZeros; if (!x) return(sizeof(int) * 8); numZeros = 0; while (!(x & 0x80000000)){numZeros++; x <<= 1;} return numZeros;}
inline uint64_t xSAR64(uint64_t x, int n){return x >> n;}
inline int FASTABS(int x){ int sign; sign=x>>(sizeof(int)*8-1); x^=sign; x-=sign; return x;}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment