code upload
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# yoradio
|
||||
# ёRadio
|
||||

|
||||
Web-radio based on ESP32-audioI2S library
|
||||
--
|
||||
|
||||
BIN
images/page1.jpg
Normal file
BIN
images/page1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
BIN
images/page2.jpg
Normal file
BIN
images/page2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 98 KiB |
BIN
images/page3.jpg
Normal file
BIN
images/page3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 202 KiB |
BIN
images/page4.jpg
Normal file
BIN
images/page4.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
39
yoRadio/audiohandlers.ino
Normal file
39
yoRadio/audiohandlers.ino
Normal file
@@ -0,0 +1,39 @@
|
||||
void audio_info(const char *info) {
|
||||
if(config.store.audioinfo) telnet.printf("##AUDIO.INFO#: %s\n", info);
|
||||
if (strstr(info, "failed!") != NULL) {
|
||||
display.title("[Request failed!]");
|
||||
player.mode = STOPPED;
|
||||
player.stopInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void audio_bitrate(const char *info)
|
||||
{
|
||||
telnet.printf("%s %s\n", "##AUDIO.BITRATE#:", info);
|
||||
config.station.bitrate = atoi(info) / 1000;
|
||||
netserver.requestOnChange(BITRATE, 0);
|
||||
}
|
||||
|
||||
void audio_showstation(const char *info) {
|
||||
if (strlen(info) > 0) {
|
||||
display.title(info);
|
||||
if (player.requesToStart) {
|
||||
telnet.info();
|
||||
player.requesToStart = false;
|
||||
} else {
|
||||
telnet.printf("##CLI.ICY0#: %s\n", info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_showstreamtitle(const char *info) {
|
||||
if (strlen(info) > 0) {
|
||||
display.title(info);
|
||||
if (player.requesToStart) {
|
||||
telnet.info();
|
||||
player.requesToStart = false;
|
||||
} else {
|
||||
telnet.printf("##CLI.META#: %s\n", info);
|
||||
}
|
||||
}
|
||||
}
|
||||
328
yoRadio/config.cpp
Normal file
328
yoRadio/config.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
#include "config.h"
|
||||
#include <EEPROM.h>
|
||||
#include <SPIFFS.h>
|
||||
|
||||
Config config;
|
||||
|
||||
void Config::init() {
|
||||
eepromRead(EEPROM_START, store);
|
||||
if (store.config_set != 4256) setDefaults();
|
||||
//if (!SPIFFS.begin(false, "/spiffs", 30)) {
|
||||
if (!SPIFFS.begin(false)) {
|
||||
return;
|
||||
}
|
||||
ssidsCount = 0;
|
||||
initPlaylist();
|
||||
if (store.lastStation == 0 && store.countStation > 0) {
|
||||
store.lastStation = 1;
|
||||
save();
|
||||
}
|
||||
loadStation(store.lastStation);
|
||||
}
|
||||
|
||||
template <class T> int Config::eepromWrite(int ee, const T& value) {
|
||||
const byte* p = (const byte*)(const void*)&value;
|
||||
int i;
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
for (i = 0; i < sizeof(value); i++)
|
||||
EEPROM.write(ee++, *p++);
|
||||
EEPROM.commit();
|
||||
delay(20);
|
||||
EEPROM.end();
|
||||
return i;
|
||||
}
|
||||
|
||||
template <class T> int Config::eepromRead(int ee, T& value) {
|
||||
byte* p = (byte*)(void*)&value;
|
||||
int i;
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
for (i = 0; i < sizeof(value); i++)
|
||||
*p++ = EEPROM.read(ee++);
|
||||
EEPROM.end();
|
||||
return i;
|
||||
}
|
||||
|
||||
void Config::setDefaults() {
|
||||
store.config_set = 4256;
|
||||
store.volume = 12;
|
||||
store.balance = 0;
|
||||
store.trebble = 0;
|
||||
store.middle = 0;
|
||||
store.bass = 0;
|
||||
store.lastStation = 0;
|
||||
store.countStation = 0;
|
||||
store.lastSSID = 0;
|
||||
store.audioinfo = false;
|
||||
store.smartstart = 2;
|
||||
}
|
||||
|
||||
void Config::save() {
|
||||
eepromWrite(EEPROM_START, store);
|
||||
}
|
||||
|
||||
byte Config::setVolume(byte val, bool dosave) {
|
||||
store.volume = val;
|
||||
if (dosave) save();
|
||||
return store.volume;
|
||||
}
|
||||
|
||||
void Config::setTone(int8_t bass, int8_t middle, int8_t trebble) {
|
||||
store.bass = bass;
|
||||
store.middle = middle;
|
||||
store.trebble = trebble;
|
||||
save();
|
||||
}
|
||||
|
||||
void Config::setSmartStart(byte ss) {
|
||||
if (store.smartstart < 2) {
|
||||
store.smartstart = ss;
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
void Config::setBalance(int8_t balance) {
|
||||
store.balance = balance;
|
||||
save();
|
||||
}
|
||||
|
||||
byte Config::setLastStation(byte val) {
|
||||
store.lastStation = val;
|
||||
save();
|
||||
return store.lastStation;
|
||||
}
|
||||
|
||||
byte Config::setCountStation(byte val) {
|
||||
store.countStation = val;
|
||||
save();
|
||||
return store.countStation;
|
||||
}
|
||||
|
||||
byte Config::setLastSSID(byte val) {
|
||||
store.lastSSID = val;
|
||||
save();
|
||||
return store.lastSSID;
|
||||
}
|
||||
|
||||
void Config::indexPlaylist() {
|
||||
File playlist = SPIFFS.open(PLAYLIST_PATH, "r");
|
||||
if (!playlist) {
|
||||
return;
|
||||
}
|
||||
char sName[BUFLEN], sUrl[BUFLEN];
|
||||
int sOvol;
|
||||
File index = SPIFFS.open(INDEX_PATH, "w");
|
||||
while (playlist.available()) {
|
||||
uint32_t pos = playlist.position();
|
||||
if (parseCSV(playlist.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) {
|
||||
index.write((byte *) &pos, 4);
|
||||
}
|
||||
}
|
||||
index.close();
|
||||
playlist.close();
|
||||
}
|
||||
|
||||
void Config::initPlaylist() {
|
||||
store.countStation = 0;
|
||||
if (!SPIFFS.exists(INDEX_PATH)) indexPlaylist();
|
||||
|
||||
if (SPIFFS.exists(INDEX_PATH)) {
|
||||
File index = SPIFFS.open(INDEX_PATH, "r");
|
||||
store.countStation = index.size() / 4;
|
||||
index.close();
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
void Config::loadStation(uint16_t ls) {
|
||||
char sName[BUFLEN], sUrl[BUFLEN];
|
||||
int sOvol;
|
||||
if (store.countStation == 0) {
|
||||
memset(station.url, 0, BUFLEN);
|
||||
memset(station.name, 0, BUFLEN);
|
||||
strncpy(station.name, "ёRadio", BUFLEN);
|
||||
station.ovol = 0;
|
||||
return;
|
||||
}
|
||||
if (ls > store.countStation) {
|
||||
ls = 1;
|
||||
}
|
||||
File playlist = SPIFFS.open(PLAYLIST_PATH, "r");
|
||||
|
||||
File index = SPIFFS.open(INDEX_PATH, "r");
|
||||
index.seek((ls - 1) * 4, SeekSet);
|
||||
uint32_t pos;
|
||||
|
||||
index.readBytes((char *) &pos, 4);
|
||||
|
||||
index.close();
|
||||
playlist.seek(pos, SeekSet);
|
||||
if (parseCSV(playlist.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) {
|
||||
memset(station.url, 0, BUFLEN);
|
||||
memset(station.name, 0, BUFLEN);
|
||||
strncpy(station.name, sName, BUFLEN);
|
||||
strncpy(station.url, sUrl, BUFLEN);
|
||||
station.ovol = sOvol;
|
||||
setLastStation(ls);
|
||||
}
|
||||
playlist.close();
|
||||
}
|
||||
|
||||
void Config::fillPlMenu(char plmenu[][40], int from, byte count) {
|
||||
int ls = from;
|
||||
byte c = 0;
|
||||
bool finded = false;
|
||||
char sName[BUFLEN], sUrl[BUFLEN];
|
||||
int sOvol;
|
||||
if (store.countStation == 0) {
|
||||
return;
|
||||
}
|
||||
File playlist = SPIFFS.open(PLAYLIST_PATH, "r");
|
||||
File index = SPIFFS.open(INDEX_PATH, "r");
|
||||
while (true) {
|
||||
if (ls < 1) {
|
||||
ls++;
|
||||
c++;
|
||||
continue;
|
||||
}
|
||||
if (!finded) {
|
||||
index.seek((ls - 1) * 4, SeekSet);
|
||||
uint32_t pos;
|
||||
index.readBytes((char *) &pos, 4);
|
||||
finded = true;
|
||||
index.close();
|
||||
playlist.seek(pos, SeekSet);
|
||||
}
|
||||
while (playlist.available()) {
|
||||
if (parseCSV(playlist.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) {
|
||||
strlcpy(plmenu[c], sName, 39);
|
||||
c++;
|
||||
}
|
||||
if (c >= count) break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
playlist.close();
|
||||
}
|
||||
|
||||
bool Config::parseCSV(const char* line, char* name, char* url, int &ovol) {
|
||||
char *tmpe;
|
||||
const char* cursor = line;
|
||||
char buf[4];
|
||||
tmpe=strstr(cursor, "\t");
|
||||
if(tmpe==NULL) return false;
|
||||
strlcpy(name, cursor, tmpe-cursor+1);
|
||||
if(strlen(name)==0) return false;
|
||||
cursor=tmpe+1;
|
||||
tmpe=strstr(cursor, "\t");
|
||||
if(tmpe==NULL) return false;
|
||||
strlcpy(url, cursor, tmpe-cursor+1);
|
||||
if(strlen(url)==0) return false;
|
||||
cursor=tmpe+1;
|
||||
if (strlen(cursor) == 0) return false;
|
||||
strlcpy(buf, cursor, 3);
|
||||
ovol = atoi(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Config::parseJSON(const char* line, char* name, char* url, int &ovol) {
|
||||
char* tmps, *tmpe;
|
||||
const char* cursor = line;
|
||||
char port[8], host[254], file[254];
|
||||
tmps=strstr(cursor, "\":\"");
|
||||
if(tmps==NULL) return false;
|
||||
tmpe=strstr(tmps, "\",\"");
|
||||
if(tmpe==NULL) return false;
|
||||
strlcpy(name, tmps+3, tmpe-tmps-3+1);
|
||||
if(strlen(name)==0) return false;
|
||||
cursor=tmpe+3;
|
||||
tmps=strstr(cursor, "\":\"");
|
||||
if(tmps==NULL) return false;
|
||||
tmpe=strstr(tmps, "\",\"");
|
||||
if(tmpe==NULL) return false;
|
||||
strlcpy(host, tmps+3, tmpe-tmps-3+1);
|
||||
if(strlen(host)==0) return false;
|
||||
if(strstr(host,"http://")==NULL && strstr(host,"https://")==NULL) {
|
||||
sprintf(file, "http://%s", host);
|
||||
strlcpy(host, file, strlen(file)+1);
|
||||
}
|
||||
cursor=tmpe+3;
|
||||
tmps=strstr(cursor, "\":\"");
|
||||
if(tmps==NULL) return false;
|
||||
tmpe=strstr(tmps, "\",\"");
|
||||
if(tmpe==NULL) return false;
|
||||
strlcpy(file, tmps+3, tmpe-tmps-3+1);
|
||||
cursor=tmpe+3;
|
||||
tmps=strstr(cursor, "\":\"");
|
||||
if(tmps==NULL) return false;
|
||||
tmpe=strstr(tmps, "\",\"");
|
||||
if(tmpe==NULL) return false;
|
||||
strlcpy(port, tmps+3, tmpe-tmps-3+1);
|
||||
int p = atoi(port);
|
||||
if(p>0){
|
||||
sprintf(url, "%s:%d%s", host, p, file);
|
||||
}else{
|
||||
sprintf(url, "%s%s", host, file);
|
||||
}
|
||||
cursor=tmpe+3;
|
||||
tmps=strstr(cursor, "\":\"");
|
||||
if(tmps==NULL) return false;
|
||||
tmpe=strstr(tmps, "\"}");
|
||||
if(tmpe==NULL) return false;
|
||||
strlcpy(port, tmps+3, tmpe-tmps-3+1);
|
||||
ovol = atoi(port);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Config::parseWsCommand(const char* line, char* cmd, char* val, byte cSize) {
|
||||
char *tmpe;
|
||||
tmpe=strstr(line, "=");
|
||||
if(tmpe==NULL) return false;
|
||||
memset(cmd, 0, cSize);
|
||||
strlcpy(cmd, line, tmpe-line+1);
|
||||
if (strlen(tmpe+1) == 0) return false;
|
||||
memset(val, 0, cSize);
|
||||
strlcpy(val, tmpe+1, tmpe+1-line+1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Config::parseSsid(const char* line, char* ssid, char* pass) {
|
||||
char *tmpe;
|
||||
tmpe=strstr(line, "\t");
|
||||
if(tmpe==NULL) return false;
|
||||
uint16_t pos= tmpe-line;
|
||||
if (pos > 19 || strlen(line) > 61) return false;
|
||||
memset(ssid, 0, 20);
|
||||
strlcpy(ssid, line, pos + 1);
|
||||
memset(pass, 0, 40);
|
||||
strlcpy(pass, line + pos + 1, strlen(line) - pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Config::saveWifi(const char* post) {
|
||||
File file = SPIFFS.open(SSIDS_PATH, "w");
|
||||
if (!file) {
|
||||
return false;
|
||||
} else {
|
||||
file.print(post);
|
||||
file.close();
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::initNetwork() {
|
||||
File file = SPIFFS.open(SSIDS_PATH, "r");
|
||||
if (!file || file.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
char ssidval[20], passval[40];
|
||||
byte c = 0;
|
||||
while (file.available()) {
|
||||
if (parseSsid(file.readStringUntil('\n').c_str(), ssidval, passval)) {
|
||||
strlcpy(ssids[c].ssid, ssidval, 20);
|
||||
strlcpy(ssids[c].password, passval, 40);
|
||||
ssidsCount++;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
78
yoRadio/config.h
Normal file
78
yoRadio/config.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#ifndef config_h
|
||||
#define config_h
|
||||
#include "Arduino.h"
|
||||
|
||||
#define EEPROM_SIZE 1024
|
||||
#define EEPROM_START 0
|
||||
#define BUFLEN 140
|
||||
#define PLAYLIST_PATH "/data/playlist.csv"
|
||||
#define SSIDS_PATH "/data/wifi.csv"
|
||||
#define TMP_PATH "/data/tmpfile.txt"
|
||||
#define INDEX_PATH "/data/index.dat"
|
||||
|
||||
struct config_t
|
||||
{
|
||||
unsigned int config_set; //must be 4256
|
||||
byte volume;
|
||||
int8_t balance;
|
||||
int8_t trebble;
|
||||
int8_t middle;
|
||||
int8_t bass;
|
||||
uint16_t lastStation;
|
||||
uint16_t countStation;
|
||||
byte lastSSID;
|
||||
bool audioinfo;
|
||||
byte smartstart;
|
||||
};
|
||||
|
||||
struct station_t
|
||||
{
|
||||
char name[BUFLEN];
|
||||
char url[BUFLEN];
|
||||
char title[BUFLEN];
|
||||
uint16_t bitrate;
|
||||
int ovol;
|
||||
};
|
||||
|
||||
struct neworkItem
|
||||
{
|
||||
char ssid[20];
|
||||
char password[40];
|
||||
};
|
||||
|
||||
class Config {
|
||||
public:
|
||||
config_t store;
|
||||
station_t station;
|
||||
neworkItem ssids[5];
|
||||
byte ssidsCount;
|
||||
public:
|
||||
Config() {};
|
||||
void save();
|
||||
void init();
|
||||
byte setVolume(byte val, bool dosave);
|
||||
void setTone(int8_t bass, int8_t middle, int8_t trebble);
|
||||
void setBalance(int8_t balance);
|
||||
byte setLastStation(byte val);
|
||||
byte setCountStation(byte val);
|
||||
byte setLastSSID(byte val);
|
||||
bool parseCSV(const char* line, char* name, char* url, int &ovol);
|
||||
bool parseJSON(const char* line, char* name, char* url, int &ovol);
|
||||
bool parseWsCommand(const char* line, char* cmd, char* val, byte cSize);
|
||||
bool parseSsid(const char* line, char* ssid, char* pass);
|
||||
void loadStation(uint16_t station);
|
||||
bool initNetwork();
|
||||
bool saveWifi(const char* post);
|
||||
void setSmartStart(byte ss);
|
||||
void initPlaylist();
|
||||
void indexPlaylist();
|
||||
void fillPlMenu(char plmenu[][40], int from, byte count);
|
||||
private:
|
||||
template <class T> int eepromWrite(int ee, const T& value);
|
||||
template <class T> int eepromRead(int ee, T& value);
|
||||
void setDefaults();
|
||||
};
|
||||
|
||||
extern Config config;
|
||||
|
||||
#endif
|
||||
101
yoRadio/controls.cpp
Normal file
101
yoRadio/controls.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "controls.h"
|
||||
#include "options.h"
|
||||
#include "config.h"
|
||||
#include "player.h"
|
||||
#include "display.h"
|
||||
|
||||
#include <ESP32Encoder.h>
|
||||
#include "OneButton.h"
|
||||
|
||||
long encOldPosition = 0;
|
||||
|
||||
ESP32Encoder encoder;
|
||||
OneButton encbutton(ENC_BTNB, true);
|
||||
|
||||
OneButton btnleft(BTN_LEFT, true);
|
||||
OneButton btncenter(BTN_CENTER, true);
|
||||
OneButton btnright(BTN_RIGHT, true);
|
||||
|
||||
void initControls() {
|
||||
ESP32Encoder::useInternalWeakPullResistors = UP;
|
||||
encoder.attachHalfQuad(ENC_BTNL, ENC_BTNR);
|
||||
encbutton.attachClick(onEncClick);
|
||||
encbutton.attachDoubleClick(onEncDoubleClick);
|
||||
encbutton.attachLongPressStart(onEncLPStart);
|
||||
|
||||
btnleft.attachClick(onLeftClick);
|
||||
btnleft.attachDoubleClick(onLeftDoubleClick);
|
||||
|
||||
btncenter.attachClick(onEncClick);
|
||||
btncenter.attachDoubleClick(onEncDoubleClick);
|
||||
btncenter.attachLongPressStart(onEncLPStart);
|
||||
|
||||
btnright.attachClick(onRightClick);
|
||||
btnright.attachDoubleClick(onRightDoubleClick);
|
||||
}
|
||||
|
||||
void loopControls() {
|
||||
encbutton.tick();
|
||||
encoderLoop();
|
||||
yield();
|
||||
}
|
||||
|
||||
void encoderLoop() {
|
||||
long encNewPosition = encoder.getCount() / 2;
|
||||
if (encNewPosition != 0 && encNewPosition != encOldPosition) {
|
||||
encOldPosition = encNewPosition;
|
||||
encoder.setCount(0);
|
||||
controlsEvent(encNewPosition > 0);
|
||||
}
|
||||
}
|
||||
|
||||
void onEncClick() {
|
||||
if (display.mode == PLAYER) {
|
||||
player.toggle();
|
||||
}
|
||||
if (display.mode == STATIONS) {
|
||||
display.swichMode(PLAYER);
|
||||
player.play(display.currentPlItem);
|
||||
}
|
||||
}
|
||||
|
||||
void onEncDoubleClick() {
|
||||
display.swichMode(display.mode == PLAYER ? STATIONS : PLAYER);
|
||||
}
|
||||
|
||||
void onEncLPStart() {
|
||||
display.swichMode(display.mode == PLAYER ? STATIONS : PLAYER);
|
||||
}
|
||||
|
||||
void controlsEvent(bool toRight) {
|
||||
if (display.mode != STATIONS) {
|
||||
display.swichMode(VOL);
|
||||
player.stepVol(toRight);
|
||||
}
|
||||
if (display.mode == STATIONS) {
|
||||
int p = toRight ? display.currentPlItem + 1 : display.currentPlItem - 1;
|
||||
if (p < 1) p = 1;
|
||||
if (p > config.store.countStation) p = config.store.countStation;
|
||||
display.currentPlItem = p;
|
||||
display.clear();
|
||||
display.drawPlaylist();
|
||||
}
|
||||
}
|
||||
|
||||
void onLeftClick() {
|
||||
controlsEvent(false);
|
||||
}
|
||||
|
||||
void onLeftDoubleClick() {
|
||||
display.swichMode(PLAYER);
|
||||
player.prev();
|
||||
}
|
||||
|
||||
void onRightClick() {
|
||||
controlsEvent(true);
|
||||
}
|
||||
|
||||
void onRightDoubleClick() {
|
||||
display.swichMode(PLAYER);
|
||||
player.next();
|
||||
}
|
||||
18
yoRadio/controls.h
Normal file
18
yoRadio/controls.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef controls_h
|
||||
#define controls_h
|
||||
|
||||
void initControls();
|
||||
void loopControls();
|
||||
void onEncClick();
|
||||
void onEncDoubleClick();
|
||||
void onEncLPStart();
|
||||
void encoderLoop();
|
||||
|
||||
void controlsEvent(bool toRight);
|
||||
|
||||
void onLeftClick();
|
||||
void onLeftDoubleClick();
|
||||
void onRightClick();
|
||||
void onRightDoubleClick();
|
||||
|
||||
#endif
|
||||
0
yoRadio/data/data/wifi.csv
Normal file
0
yoRadio/data/data/wifi.csv
Normal file
|
|
40
yoRadio/data/www/dragpl.js
Normal file
40
yoRadio/data/www/dragpl.js
Normal file
@@ -0,0 +1,40 @@
|
||||
let dragged;
|
||||
let id;
|
||||
let index;
|
||||
let indexDrop;
|
||||
let list;
|
||||
|
||||
document.addEventListener("dragstart", ({target}) => {
|
||||
dragged = target.parentNode;
|
||||
id = target.parentNode.id;
|
||||
list = target.parentNode.parentNode.children;
|
||||
for(let i = 0; i < list.length; i += 1) {
|
||||
if(list[i] === dragged){
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("dragover", (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener("drop", ({target}) => {
|
||||
if(target.parentNode.className == "pleitem" && target.parentNode.id !== id) {
|
||||
dragged.remove( dragged );
|
||||
for(let i = 0; i < list.length; i += 1) {
|
||||
if(list[i] === target.parentNode){
|
||||
indexDrop = i;
|
||||
}
|
||||
}
|
||||
if(index > indexDrop) {
|
||||
target.parentNode.before( dragged );
|
||||
} else {
|
||||
target.parentNode.after( dragged );
|
||||
}
|
||||
let items=document.getElementById('pleditorcontent').getElementsByTagName('li');
|
||||
for (let i = 0; i <= items.length-1; i++) {
|
||||
items[i].getElementsByTagName('span')[0].innerText=("00"+(i+1)).slice(-3);
|
||||
}
|
||||
}
|
||||
});
|
||||
BIN
yoRadio/data/www/elogo.png
Normal file
BIN
yoRadio/data/www/elogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
BIN
yoRadio/data/www/elogo100.png
Normal file
BIN
yoRadio/data/www/elogo100.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
130
yoRadio/data/www/index.html
Normal file
130
yoRadio/data/www/index.html
Normal file
@@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=0.25">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link rel="apple-touch-icon" type="image/png" href="elogo.png">
|
||||
<link rel="icon" type="image/png" href="elogo.png">
|
||||
<link rel="stylesheet" title="base" href="/style.css" type="text/css">
|
||||
<title>ёRadio</title>
|
||||
<style> </style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<div class="logo"></div>
|
||||
<div id="navbar"%APMODE%>
|
||||
<div class="playerbytton navbutton" id="playlistbutton" onclick="showEditor()"></div>
|
||||
<div class="playerbytton navbutton" id="settingsbutton" onclick="showSettings()"></div>
|
||||
</div>
|
||||
<div class="playerwrap">
|
||||
<div class="player">
|
||||
<div id="nameset"> </div>
|
||||
<div id="meta"> </div>
|
||||
<div class="playerbyttonwrap">
|
||||
<div class="playerbytton" id="prevbutton"></div>
|
||||
<div class="stopped" id="playbutton"></div>
|
||||
<div class="playerbytton" id="nextbutton"></div>
|
||||
<div class="playerbytton" id="volmbutton"></div>
|
||||
<div class="playerbytton" id="volpbutton"></div>
|
||||
<div class="playerbytton" id="eqbutton" onclick="showEqualizer(true)"></div>
|
||||
</div>
|
||||
<div id="equalizerwrap">
|
||||
<div id="equalizerbg" class="hidden">
|
||||
<ul id="equalizer">
|
||||
<li>
|
||||
<li>
|
||||
balance<span class="eqinfo" id="eqbalinfo">0</span>
|
||||
<input type="range" id="eqbal" class="slider" data-slaveid="eqbalinfo" onchange="onRangeBalChange(this)" name="lovpass" min="-16" max="16" value="0">
|
||||
</li>
|
||||
<li>
|
||||
<li>
|
||||
treble<span class="eqinfo" id="eqtrebleinfo">0</span>
|
||||
<input type="range" id="eqtreble" class="slider" data-slaveid="eqtrebleinfo" onchange="onRangeEqChange(this)" name="lovpass" min="-16" max="16" value="0">
|
||||
</li>
|
||||
<li>
|
||||
middle<span class="eqinfo" id="eqmiddleinfo">0</span>
|
||||
<input type="range" id="eqmiddle" class="slider" data-slaveid="eqmiddleinfo" onchange="onRangeEqChange(this)" name="bandpass" min="-16" max="16" value="0">
|
||||
</li>
|
||||
<li>
|
||||
bass<span class="eqinfo" id="eqbassinfo">0</span>
|
||||
<input type="range" id="eqbass" class="slider" data-slaveid="eqbassinfo" onchange="onRangeEqChange(this)" name="highpass" min="-16" max="16" value="0">
|
||||
</li>
|
||||
<li class="formbuttons">
|
||||
<div class="button" id="accept_button" onclick="showEqualizer(false)">Acceptable...</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<input type="range" id="volrange" class="slider" name="vol" data-slaveid="volinfo" onchange="onRangeVolChange(this.value)" min="0" max="254" value="0">
|
||||
<div class="infowrap">
|
||||
<div class="infoitem">volume: <span id="volinfo">0</span></div>
|
||||
<div class="infoitem" id="bitinfo">bitrate: 0kBit</div>
|
||||
<div class="infoitem" id="rsiinfo">rssi: 0dBm</div>
|
||||
</div>
|
||||
<ul id="playlist">
|
||||
</ul>
|
||||
</div><!--equalizerwrap-->
|
||||
</div><!--player-->
|
||||
<div id="settings"%NOTAPMODE%>
|
||||
<h2>WiFi Settings</h2>
|
||||
<ul id="credentialwrap">
|
||||
<li class="credentialitem">
|
||||
<span>1.</span>
|
||||
<div class="textinput"><label for="ssid0">SSID</label><input name="ssid" id="ssid0" type="text" value="%SSID%" maxlength="20"/ ></div>
|
||||
<div class="textinput"><label for"pass0">Password</label><input name="pass" id="pass0" type="password" value="%PASS%" maxlength="40" /></div>
|
||||
</li>
|
||||
<li class="credentialitem">
|
||||
<span>2.</span>
|
||||
<div class="textinput"><label for="ssid1">SSID</label><input name="ssid" id="ssid1" type="text" value="%SSID%" maxlength="20" /></div>
|
||||
<div class="textinput"><label for"pass1">Password</label><input name="pass" id="pass1" type="password" value="%PASS%" maxlength="40" /></div>
|
||||
</li>
|
||||
<li class="credentialitem">
|
||||
<span>3.</span>
|
||||
<div class="textinput"><label for="ssid2">SSID</label><input name="ssid" id="ssid2" type="text" value="%SSID%" maxlength="20" /></div>
|
||||
<div class="textinput"><label for"pass2">Password</label><input name="pass" id="pass2" type="password" value="%PASS%" maxlength="40" /></div>
|
||||
</li>
|
||||
<li class="credentialitem">
|
||||
<span>4.</span>
|
||||
<div class="textinput"><label for="ssid3">SSID</label><input name="ssid" id="ssid3" type="text" value="%SSID%" maxlength="20" /></div>
|
||||
<div class="textinput"><label for"pass3">Password</label><input name="pass" id="pass3" type="password" value="%PASS%" maxlength="40" /></div>
|
||||
</li>
|
||||
<li class="credentialitem">
|
||||
<span>5.</span>
|
||||
<div class="textinput"><label for="ssid4">SSID</label><input name="ssid" id="ssid4" type="text" value="%SSID%" maxlength="20" /></div>
|
||||
<div class="textinput"><label for"pass4">Password</label><input name="pass" id="pass4" type="password" value="%PASS%" maxlength="40" /></div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="formbuttons">
|
||||
<div class="button" id="cancel_button" onclick="doCancel()"%APMODE%>Cancel</div>
|
||||
<div class="button" id="save_button" onclick="submitWiFi()">Save</div>
|
||||
</div>
|
||||
</div><!--settings-->
|
||||
<div id="pleditorwrap" hidden>
|
||||
<div id="pleditor">
|
||||
<h2>Playlist Editor<span onclick="doCancel()"></span></h2>
|
||||
<div id="pleheader"><span class="space"></span><span class="plename">Name</span><span class="pleurl">URL</span><span class="pleovol">Ovol</span></div>
|
||||
<ol id="pleditorcontent">
|
||||
<li class="pleitem">
|
||||
<span>1.</span>
|
||||
<input class="pleinput plename" type="text" value="" maxlength="140" />
|
||||
<input class="pleinput pleurl" type="text" value="" maxlength="140" />
|
||||
<input class="pleinput pleovol" type="number" min="-30" max="30" step="1" value="0" />
|
||||
</li>
|
||||
</ol><!--pleditorcontent-->
|
||||
<div class="formbuttons">
|
||||
<label for="file-upload" class="button">Import</label><input id="file-upload" type="file" accept=".txt, .csv" onchange="doUpload(this)" hidden/>
|
||||
<div class="button" onclick="doExport()">Export</div>
|
||||
<div class="button" onclick="doAdd()">Add</div>
|
||||
<div class="button" onclick="doRemove()">Remove</div>
|
||||
<!--<div class="button" onclick="doCancel()">Cancel</div>-->
|
||||
<div class="button" onclick="submitPlaylist()">Save</div>
|
||||
</div>
|
||||
</div><!--pleditor-->
|
||||
</div><!--pleditorwrap-->
|
||||
</div><!--playerwrap-->
|
||||
<div id="copy">powered by <a target="_blank" href="https://github.com/e2002/yoradio/">ёRadio</a> | v%VERSION%</div>
|
||||
</div>
|
||||
<script src="script.js"></script>
|
||||
<script src="dragpl.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
347
yoRadio/data/www/script.js
Normal file
347
yoRadio/data/www/script.js
Normal file
@@ -0,0 +1,347 @@
|
||||
var gateway = `ws://${window.location.hostname}/ws`;
|
||||
var websocket;
|
||||
var currentItem = 0;
|
||||
|
||||
window.addEventListener('load', onLoad);
|
||||
function initWebSocket() {
|
||||
console.log('Trying to open a WebSocket connection...');
|
||||
websocket = new WebSocket(gateway);
|
||||
websocket.onopen = onOpen;
|
||||
websocket.onclose = onClose;
|
||||
websocket.onmessage = onMessage;
|
||||
}
|
||||
function onOpen(event) {
|
||||
console.log('Connection opened');
|
||||
}
|
||||
function onClose(event) {
|
||||
console.log('Connection closed');
|
||||
document.getElementById('playbutton').setAttribute("class", "stopped");
|
||||
setTimeout(initWebSocket, 2000);
|
||||
}
|
||||
function cleanString(input) {
|
||||
var output = "";
|
||||
for (var i=0; i<input.length; i++) {
|
||||
if (input.charCodeAt(i) <= 127 || input.charCodeAt(i) >= 160 && input.charCodeAt(i) <= 255) {
|
||||
output += input.charAt(i);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
function onMessage(event) {
|
||||
var data = JSON.parse(event.data);
|
||||
if(data.nameset) document.getElementById('nameset').innerHTML = data.nameset;
|
||||
if(data.meta) document.getElementById('meta').innerHTML = cleanString(data.meta);
|
||||
if(data.vol) {
|
||||
setVolRangeValue(document.getElementById('volrange'),data.vol);
|
||||
}
|
||||
if(data.current) setCurrentItem(data.current);
|
||||
if(data.file) generatePlaylist(data.file);
|
||||
if(data.bitrate) document.getElementById('bitinfo').innerText = 'bitrate: '+data.bitrate+'kBits';
|
||||
if(data.rssi) document.getElementById('rsiinfo').innerText = 'rssi: '+data.rssi+'dBm';
|
||||
if(data.mode) {
|
||||
document.getElementById('playbutton').setAttribute("class",data.mode);
|
||||
}
|
||||
if(data.bass) {
|
||||
setVolRangeValue(document.getElementById('eqbass'),data.bass);
|
||||
setVolRangeValue(document.getElementById('eqmiddle'),data.middle);
|
||||
setVolRangeValue(document.getElementById('eqtreble'),data.trebble);
|
||||
}
|
||||
if(data.balance) {
|
||||
setVolRangeValue(document.getElementById('eqbal'),data.balance);
|
||||
}
|
||||
}
|
||||
function scrollToCurrent(){
|
||||
var pl = document.getElementById('playlist');
|
||||
var lis = pl.getElementsByTagName('li');
|
||||
var plh = pl.offsetHeight;
|
||||
var plt = pl.offsetTop;
|
||||
var topPos = 0;
|
||||
var lih = 0;
|
||||
for (var i = 0; i <= lis.length - 1; i++) {
|
||||
if(i+1==currentItem) {
|
||||
topPos = lis[i].offsetTop;
|
||||
lih = lis[i].offsetHeight;
|
||||
}
|
||||
}
|
||||
pl.scrollTo({
|
||||
top: topPos-plt-plh/2+lih/2,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
function setCurrentItem(item){
|
||||
currentItem=item;
|
||||
var pl = document.getElementById('playlist');
|
||||
var lis = pl.getElementsByTagName('li');
|
||||
for (var i = 0; i <= lis.length - 1; i++) {
|
||||
lis[i].removeAttribute('class');
|
||||
if(i+1==currentItem) {
|
||||
lis[i].setAttribute("class","active");
|
||||
|
||||
}
|
||||
}
|
||||
scrollToCurrent();
|
||||
}
|
||||
function generatePlaylist(lines){
|
||||
var ul = document.getElementById('playlist');
|
||||
ul.innerHTML="";
|
||||
lines.forEach((line, index) => {
|
||||
li = document.createElement('li');
|
||||
li.setAttribute('onclick','playStation(this);');
|
||||
li.setAttribute('attr-id', index+1);
|
||||
li.setAttribute('attr-name', line.name);
|
||||
li.setAttribute('attr-url', line.url);
|
||||
li.setAttribute('attr-ovol', line.ovol);
|
||||
if(index+1==currentItem){
|
||||
li.setAttribute("class","active");
|
||||
}
|
||||
var span = document.createElement('span');
|
||||
span.innerHTML = index+1;
|
||||
li.appendChild(document.createTextNode(line.name));
|
||||
li.appendChild(span);
|
||||
ul.appendChild(li);
|
||||
initPLEditor();
|
||||
});
|
||||
scrollToCurrent();
|
||||
}
|
||||
function initPLEditor(){
|
||||
ple= document.getElementById('pleditorcontent');
|
||||
ple.innerHTML="";
|
||||
pllines = document.getElementById('playlist').getElementsByTagName('li');
|
||||
for (let i = 0; i <= pllines.length - 1; i++) {
|
||||
plitem = document.createElement('li');
|
||||
plitem.setAttribute('class', 'pleitem');
|
||||
plitem.setAttribute('id', 'plitem'+i);
|
||||
let pName = pllines[i].getAttribute('attr-name');
|
||||
let pUrl = pllines[i].getAttribute('attr-url');
|
||||
let pOvol = pllines[i].getAttribute('attr-ovol');
|
||||
plitem.innerHTML = '<span class="grabbable" draggable="true">'+("00"+(i+1)).slice(-3)+'</span>\
|
||||
<span class="pleinput plecheck"><input type="checkbox" /></span>\
|
||||
<input class="pleinput plename" type="text" value="'+pName+'" maxlength="140" />\
|
||||
<input class="pleinput pleurl" type="text" value="'+pUrl+'" maxlength="140" />\
|
||||
<input class="pleinput pleovol" type="number" min="-30" max="30" step="1" value="'+pOvol+'" />';
|
||||
ple.appendChild(plitem);
|
||||
}
|
||||
}
|
||||
function playStation(el){
|
||||
let lis = document.getElementById('playlist').getElementsByTagName('li');
|
||||
|
||||
for (let i = 0; i <= lis.length - 1; i++) {
|
||||
lis[i].removeAttribute('class');
|
||||
}
|
||||
el.setAttribute("class","active");
|
||||
id=el.getAttribute('attr-id');
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("POST","/",true);
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhr.send("playstation="+id);
|
||||
}
|
||||
function setVolRangeValue(el, val=null){
|
||||
let slave = el.getAttribute('data-slaveid');
|
||||
if(val){
|
||||
el.value = val;
|
||||
document.getElementById(slave).innerText=val;
|
||||
}
|
||||
document.getElementById(slave).innerText=el.value;
|
||||
var value = (el.value-el.min)/(el.max-el.min)*100;
|
||||
el.style.background = 'linear-gradient(to right, #bfa73e 0%, #bfa73e ' + value + '%, #272727 ' + value + '%, #272727 100%)';
|
||||
}
|
||||
|
||||
function onRangeVolChange(value) {
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("POST","/",true);
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhr.send("vol=" + value+"&");
|
||||
}
|
||||
function onRangeEqChange(el){
|
||||
let trebble = document.getElementById('eqtreble').value;
|
||||
let middle = document.getElementById('eqmiddle').value;
|
||||
let bass = document.getElementById('eqbass').value;
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("POST","/",true);
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhr.send("trebble=" + trebble + "&middle=" + middle + "&bass=" + bass + "&");
|
||||
}
|
||||
function onRangeBalChange(el){
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("POST","/",true);
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhr.send("ballance=" + el.value+"&");
|
||||
}
|
||||
function showSettings(){
|
||||
document.getElementById('pleditorwrap').hidden=true;
|
||||
document.getElementById('settings').hidden=false;
|
||||
}
|
||||
function showEditor(){
|
||||
document.getElementById('settings').hidden=true;
|
||||
initPLEditor();
|
||||
document.getElementById('pleditorwrap').hidden=false;
|
||||
}
|
||||
function doCancel() {
|
||||
document.getElementById('settings').hidden=true;
|
||||
document.getElementById('pleditorwrap').hidden=true;
|
||||
}
|
||||
function doExport() {
|
||||
window.open("/data/playlist.csv");
|
||||
}
|
||||
function doUpload(finput) {
|
||||
var formData = new FormData();
|
||||
formData.append("plfile", finput.files[0]);
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST","/upload",true);
|
||||
xhr.send(formData);
|
||||
}
|
||||
function doAdd(){
|
||||
let ple=document.getElementById('pleditorcontent');
|
||||
let plitem = document.createElement('li');
|
||||
let cnt=ple.getElementsByTagName('li');
|
||||
plitem.setAttribute('class', 'pleitem');
|
||||
plitem.setAttribute('id', 'plitem'+(cnt.length));
|
||||
plitem.innerHTML = '<span class="grabbable" draggable="true">'+("00"+(cnt.length+1)).slice(-3)+'</span>\
|
||||
<span class="pleinput plecheck"><input type="checkbox" /></span>\
|
||||
<input class="pleinput plename" type="text" value="" maxlength="140" />\
|
||||
<input class="pleinput pleurl" type="text" value="" maxlength="140" />\
|
||||
<input class="pleinput pleovol" type="number" min="-30" max="30" step="1" value="0" />';
|
||||
ple.appendChild(plitem);
|
||||
ple.scrollTo({
|
||||
top: ple.scrollHeight,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
function doRemove(){
|
||||
let items=document.getElementById('pleditorcontent').getElementsByTagName('li');
|
||||
let pass=[];
|
||||
for (let i = 0; i <= items.length - 1; i++) {
|
||||
if(items[i].getElementsByTagName('span')[1].getElementsByTagName('input')[0].checked) {
|
||||
pass.push(items[i]);
|
||||
}
|
||||
}
|
||||
if(pass.length==0) {
|
||||
alert('Choose something first');
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < pass.length; i++)
|
||||
{
|
||||
pass[i].remove();
|
||||
}
|
||||
items=document.getElementById('pleditorcontent').getElementsByTagName('li');
|
||||
for (let i = 0; i <= items.length-1; i++) {
|
||||
items[i].getElementsByTagName('span')[0].innerText=("00"+(i+1)).slice(-3);
|
||||
}
|
||||
}
|
||||
function showEqualizer(isshowing){
|
||||
document.getElementById('equalizerbg').classList.toggle('hidden');
|
||||
}
|
||||
function submitWiFi(){
|
||||
var items=document.getElementById("credentialwrap").getElementsByTagName("li");
|
||||
var output="";
|
||||
for (var i = 0; i <= items.length - 1; i++) {
|
||||
inputs=items[i].getElementsByTagName("input");
|
||||
if(inputs[0].value == "") continue;
|
||||
output+=inputs[0].value+"\t"+inputs[1].value+"\n";
|
||||
}
|
||||
if(output!=""){ // Well, let's say, quack.
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("POST","/",true);
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhr.send("wifisettings="+output);
|
||||
document.getElementById("settings").innerHTML="<h2>Settings saved. Rebooting...</h2>";
|
||||
setTimeout(function(){ window.location.reload(); }, 10000);
|
||||
}
|
||||
}
|
||||
function submitPlaylist(){
|
||||
var items=document.getElementById("pleditorcontent").getElementsByTagName("li");
|
||||
var output="";
|
||||
for (var i = 0; i <= items.length - 1; i++) {
|
||||
inputs=items[i].getElementsByTagName("input");
|
||||
if(inputs[1].value == "" || inputs[2].value == "") continue;
|
||||
let ovol = inputs[3].value;
|
||||
if(ovol < -30) ovol = -30;
|
||||
if(ovol > 30) ovol = 30;
|
||||
output+=inputs[1].value+"\t"+inputs[2].value+"\t"+inputs[3].value+"\n";
|
||||
}
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("POST","/",true);
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhr.send("playlist="+output.slice(0, -1));
|
||||
document.getElementById('pleditorwrap').hidden=true;
|
||||
}
|
||||
function initSliers(){
|
||||
var sliders = document.getElementsByClassName('slider');
|
||||
for (var i = 0; i <= sliders.length - 1; i++) {
|
||||
sliders[i].oninput = function() {
|
||||
setVolRangeValue(this);
|
||||
};
|
||||
setVolRangeValue(sliders[i], 0);
|
||||
}
|
||||
return;
|
||||
var volslider = document.getElementById("volrange");
|
||||
var eqvolslider = document.getElementById("eqvol");
|
||||
var balslider = document.getElementById("eqbal");
|
||||
volslider.oninput = function() {
|
||||
setVolRangeValue(this);
|
||||
};
|
||||
eqvolslider.oninput = function() {
|
||||
setVolRangeValue(this);
|
||||
};
|
||||
balslider.oninput = function() {
|
||||
setVolRangeValue(this);
|
||||
};
|
||||
setVolRangeValue(volslider, 0);
|
||||
setVolRangeValue(eqvolslider, 0);
|
||||
setVolRangeValue(balslider, 0);
|
||||
}
|
||||
function onLoad(event) {
|
||||
initWebSocket();
|
||||
initButton();
|
||||
initSliers();
|
||||
document.getElementById("volrange").addEventListener("wheel", function(e){
|
||||
if (e.deltaY < 0){
|
||||
this.valueAsNumber += 1;
|
||||
}else{
|
||||
this.value -= 1;
|
||||
}
|
||||
websocket.send('volume='+this.value);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}, {passive: false});
|
||||
}
|
||||
function initButton() {
|
||||
document.getElementById('playbutton').addEventListener('click', playbutton);
|
||||
document.getElementById('prevbutton').addEventListener('click', prevbutton);
|
||||
document.getElementById('nextbutton').addEventListener('click', nextbutton);
|
||||
document.getElementById('volmbutton').addEventListener('click', volmbutton);
|
||||
document.getElementById('volpbutton').addEventListener('click', volpbutton);
|
||||
}
|
||||
|
||||
function playercommand(cmd){
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("POST","/",true);
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhr.send(cmd+"=1");
|
||||
}
|
||||
function playbutton(){
|
||||
var btn=document.getElementById('playbutton');
|
||||
if(btn.getAttribute("class")=="stopped") {
|
||||
btn.setAttribute("class", "playing");
|
||||
playercommand("start");
|
||||
return;
|
||||
}
|
||||
if(btn.getAttribute("class")=="playing") {
|
||||
btn.setAttribute("class", "stopped");
|
||||
playercommand("stop");
|
||||
}
|
||||
}
|
||||
function prevbutton(){
|
||||
playercommand("prev");
|
||||
}
|
||||
function nextbutton(){
|
||||
playercommand("next");
|
||||
}
|
||||
function volmbutton(){
|
||||
playercommand("volm");
|
||||
}
|
||||
function volpbutton(){
|
||||
playercommand("volp");
|
||||
}
|
||||
505
yoRadio/data/www/style.css
Normal file
505
yoRadio/data/www/style.css
Normal file
@@ -0,0 +1,505 @@
|
||||
html, body {margin: 0; padding: 0; height: 100%; }
|
||||
body {
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
font-family: Times, "Times New Roman", serif;
|
||||
}
|
||||
a { color: #e3d25f; text-decoration: none; font-weight: bold }
|
||||
a:hover { text-decoration: underline }
|
||||
.logo {
|
||||
display: block;
|
||||
width: 155px;
|
||||
height: 100px;
|
||||
background-color: transparent;
|
||||
background-image: url(elogo100.png);
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
overflow: hidden;
|
||||
text-indent: -2022px;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
}
|
||||
#nameset {
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
color: #e3d25f;
|
||||
margin: 20px 0 8px 0;
|
||||
}
|
||||
#meta {
|
||||
text-transform: uppercase;
|
||||
font-size: 16px;
|
||||
margin-bottom: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 30px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
.playerwrap {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
.player {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#equalizerwrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#equalizerbg {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 2px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
z-index: 7;
|
||||
}
|
||||
#equalizerbg.hidden {
|
||||
display: none;
|
||||
}
|
||||
#equalizer {
|
||||
padding: 20px;
|
||||
background: #000;
|
||||
box-shadow: #000 0 10px 20px;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
}
|
||||
#equalizer li {
|
||||
margin-bottom: 10px;
|
||||
color: #ccc;
|
||||
}
|
||||
#equalizer li input {
|
||||
width: 100%;
|
||||
height: 25px!important;
|
||||
border-radius: 13px;
|
||||
}
|
||||
.eqinfo {
|
||||
float: right;
|
||||
}
|
||||
#settings, #pleditorwrap {
|
||||
position: absolute;
|
||||
background: rgba(0,0,0,1);
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
padding: 30px 10px 20px 0;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#credentialwrap {
|
||||
max-width: 520px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#settings h2, #pleditor h2{
|
||||
text-align: center;
|
||||
padding-top: 40px 0;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
font-size: 26px;
|
||||
color: #e3d25f;
|
||||
overflow-y: auto;
|
||||
}
|
||||
#pleditor h2 {
|
||||
line-height: 48px;
|
||||
margin: 5px 0;
|
||||
padding-left: 48px;
|
||||
}
|
||||
#pleditor h2 span {
|
||||
display: block;
|
||||
float: right;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAA2FBMVEUAAAC+pDnm12HXxVHk1l65mjLJskO9oTjo3GPEqD3cylWyljDfz1rOuknBpjq+oDbt4Gjz6W6zli/17HC1mTLw5Wupjyq8nTOuky3Tv07p22PGq0G1mDDx5Wzw42vj1l7ez1nFqUHCpz64mjKzljDx5WzHq0G1lzHby1a2mTHbyla7nTTXxVHUwU/LtEXIr0HFqj3DpzrApDm9nzW5mzKyli/063Dz6G7v5Wvi013g0FvdzVfZx1POuUi+ojjs4Gbo22PVwk/RvkzQu0qvky2skSyqjyvl119lX1m8AAAAKXRSTlMAAgKgoaCfo6KioaGgn5+fLaSko6OioqGhoEItLfX19fX19fX1pEJCLVUTdPcAAAEKSURBVDjL1ZLZdoJADEDJoKCiqHVt7b7DgOybIO72//+o9FSdSeWhr+YtuXdyMjkRLik6BOdwi/Obuy5BfPTygd7fx1GPcFym2ivfg7xFYTggJ656vnYFAm90wyiukQOv5/TImdGLF8sK+eWO5/ePnBmDxTJpkoJLdk6rjDOj9pWkIgHJcjzGkVGZ6cZ1y7LdEePYaOqrzdRy2jzHhmjOp7bK+Lmw/kcDlx8R84axnkuqSzUFynlmbiSAtq8FYyj9gmG2CgDVINgO4XwJabb64YWhbPc7BcqWdCjCeL/TZMBrTtJMPJVgGPh5nTc6DzNd5A+mT92nCTq5xwY+Ofn5HQ/x+fdoJ8IFxTeZ6Bp9c7k3QwAAAABJRU5ErkJggg==');
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
border-radius: 24px;
|
||||
}
|
||||
#pleditor h2 span:hover {
|
||||
background-color: #272727;
|
||||
}
|
||||
#pleheader {
|
||||
display: flex;
|
||||
}
|
||||
#pleheader span {
|
||||
padding: 8px;
|
||||
font-weight: bold;
|
||||
box-sizing: border-box;
|
||||
color: #e3d25f!important;
|
||||
}
|
||||
#pleheader .space {
|
||||
width: 70px;
|
||||
}
|
||||
#pleditor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-width: 900px;
|
||||
height: 100%;
|
||||
}
|
||||
#settings::-webkit-scrollbar, #pleditorcontent::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 8px;
|
||||
background-color: #000;
|
||||
}
|
||||
#settings::-webkit-scrollbar-thumb, #pleditorcontent::-webkit-scrollbar-thumb {
|
||||
background: #e3d25f;
|
||||
}
|
||||
#pleditorcontent {
|
||||
flex: 1 1 auto;
|
||||
overflow-y: scroll;
|
||||
height: 0px;
|
||||
margin: 0;
|
||||
padding: 0 0px 0 0;
|
||||
list-style-type: none;
|
||||
border: #e3d25f 2px solid;
|
||||
box-sizing: border-box;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAABAAgMAAACrYYWUAAAACVBMVEUnJycAAAAtLS3JOrneAAAAGElEQVQI12NYtYKBZgBoOAiFhtAIAQ0HALK+GReOr3bpAAAAAElFTkSuQmCC');
|
||||
}
|
||||
.grabbable {
|
||||
cursor: move;
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
}
|
||||
.pleitem {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: #000;
|
||||
}
|
||||
.pleitem span {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
text-align: right;
|
||||
color: #d7d7d7;
|
||||
width: 40px;
|
||||
padding: 0 8px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
border: #2d2d2d 1px solid;
|
||||
}
|
||||
.pleitem input {
|
||||
font-family: Times, "Times New Roman", serif;
|
||||
background: #000;
|
||||
color: #e3d25f;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
text-indent: 8px;
|
||||
font-size: 18px;
|
||||
border: #2d2d2d 1px solid;
|
||||
font-weight: normal;
|
||||
box-sizing: border-box;
|
||||
margin-top: 4px;
|
||||
border-radius: 0px;
|
||||
outline: none;
|
||||
margin: 0;
|
||||
}
|
||||
#pleditorcontent li:nth-child(odd) input, #pleditorcontent li:nth-child(odd) span {
|
||||
background: #272727;
|
||||
}
|
||||
.plecheck {
|
||||
width: 30px!important;
|
||||
}
|
||||
.plecheck input {
|
||||
line-height: 10px;
|
||||
height: auto;
|
||||
}
|
||||
.plename {
|
||||
width: 300px;
|
||||
}
|
||||
.pleurl {
|
||||
flex: 1;
|
||||
color: #d7d7d7!important;
|
||||
}
|
||||
.pleovol {
|
||||
width: 53px;
|
||||
font-size: 16px!important;
|
||||
}
|
||||
#pleheader .pleovol {
|
||||
width: 62px;
|
||||
}
|
||||
.credentialitem {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.credentialitem span {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
.credentialitem .textinput {
|
||||
width: 47%;
|
||||
}
|
||||
.credentialitem input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #272727;
|
||||
color: #e3d25f;
|
||||
padding: 6px 12px;
|
||||
font-size: 20px;
|
||||
border: #2d2d2d 1px solid;
|
||||
font-weight: normal;
|
||||
box-sizing: border-box;
|
||||
margin-top: 4px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
.credentialitem label {
|
||||
text-transform: uppercase;
|
||||
color: #ccc;
|
||||
}
|
||||
.credentialitem input:focus {
|
||||
outline: none;
|
||||
}
|
||||
.formbuttons {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
max-width: 280px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
margin-top: 30px;
|
||||
}
|
||||
#pleditor .formbuttons {
|
||||
max-width: 100%;
|
||||
}
|
||||
.button {
|
||||
padding: 8px 22px;
|
||||
font-size: 18px;
|
||||
border: #e3d25f 2px solid;
|
||||
font-weight: normal;
|
||||
box-sizing: border-box;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
min-width: 120px;
|
||||
margin-left: 20px;
|
||||
text-transform: uppercase;
|
||||
color: #e3d25f;
|
||||
background: #272727;
|
||||
}
|
||||
.button:hover {
|
||||
background: #e3d25f;
|
||||
color: #000;
|
||||
}
|
||||
.button:active {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
#save_button {
|
||||
background: #e3d25f;
|
||||
color: #000;
|
||||
}
|
||||
#navbar {
|
||||
display: flex;
|
||||
width: 400px;
|
||||
justify-content: space-between;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
padding: 30px 0 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.playerbyttonwrap {
|
||||
display: flex;
|
||||
width: 400px;
|
||||
justify-content: space-between;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.playerbytton, .stopped, .playing {
|
||||
background-color: transparent;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
border: #e3d25f 2px solid;
|
||||
border-radius: 26px;
|
||||
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
.navbutton {
|
||||
border-color: transparent;
|
||||
}
|
||||
.navbutton:hover {
|
||||
border-color: #e3d25f;
|
||||
}
|
||||
.playerbytton:hover, .stopped:hover, .playing:hover {
|
||||
background-color: #272727;
|
||||
}
|
||||
.playerbytton:active, .stopped:active, .playing:active {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
.stopped {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAvVBMVEUAAADg0Vvez1fYx1Lr4V/q3l7l2F3o3F/h01nw52rr4WDx53Dez1nZyVL69o707Xju5Gbt42X38oXx6XP584n17X3t42v584fw6HX27nzs4Wzdz1Tx6HTw523h0lvez1naylXl11/m2GHj1V7f0FrdzVjq3WTn2mLZx1L38IDv5Wjo3GPi01zczFfcy1bz6m/s4Wf7+JP69o317nnt4mju42Pu5W7x6Gzw5Wvr32Xl2F/59If07HP38oTj1lmRTpVfAAAAHnRSTlMAEkkE/taStm377SkpKe3t7e3W1ra2tpKSbW1tSQQSPakHAAAA9klEQVQ4y82R2XaCMBRFtdLaWjvXWRMCQkhQQcIkDv//WSaRpWI0vnpe9+aek0XtgfL/fkdYLnt1vUCiz8FEJwQBiX7HOiHLA7Lu3JwSZTmN45SQfv2GkOdxGLpuyr6GjasCjcOV6/sWTtnf0xVhLTm2LGRb6abbVoUjn4Epom8vl1O2kheSw/kC0Z/XRlUQXGAAOTc9EyXPlSnEx4fPp5J73s64EMp66MxNzoHRGlUrGDrj0DCUkcy2geD8/AInzQ/lmWxWcrMQ69RsyvN20pLvUwXJQZiU5UoCfh669FiuCo6D6alcTVbQb16uEcTv0aXZrj1u9gzXJnV9YpvtAAAAAElFTkSuQmCC');
|
||||
}
|
||||
.playing {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAP1BMVEUAAADv42vy52369Ivu4Wnz6W7x5Wv162/79o/7+JLt32js3Gb48YP584j484Xn12L17Xv17XHq2mXz6XTk0mCFQY+JAAAAAXRSTlMAQObYZgAAALFJREFUOMvlz90OgyAMhmH+CqJQcXr/17p+3cySsjuwRyTvQ5O6Z815nmOM1pxrzD3KWHBd+/7aBPCaUvLegqEdoGufgfaNAdDzYoH2tTsXPfoEmvYb5BAmgPwFWXqoFqCnFAUgB5qAdgXIVCxgdO8VkPQJ/K5fAvoxA/QMgG433NfjuIr/hRYL0D+gHNJDtAA5KDiK9PwfUMUGku67BchUAChIT2zAgqm16itG5uYeNW/M9AgMwr5N0gAAAABJRU5ErkJggg==');
|
||||
}
|
||||
#prevbutton {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABR1BMVEUAAADRu03Zxz7g0Fbn2lvx6Hbp22jXxFC/qTvo2WS2nTXFrkPt4WfbyUHl11rk1Vfs4Wbn11jj1FPYxT/27X/x53LVwj3TwDro2F3Wwz/byUnRvTzp3WbSvz/k1Fvq3GbezVTNuDvOu0DItDnh0FvJszzTv0rErjjezFrWwk7Xw1K/qDjbyVi7pDi3nzbZxVXTvlDBqUHKs0jezETcyUC9pT3t4GPq3FzWwVLj1E7FrUO/pz7u4mbi0F3o2VzcyFjUv1HOt0vIsEa7ojvTvjPNtyv17nzz6XTw5Wvi0l3ey1rax1Xl1VLXw1LSvU/Qu03h0Urgz0jDrEDVwjj38ILz6Xbx5m/q3Wnr3l/fzFvi0ljl1lbZxlLVwkDPu0DXxDvQuy7LtCrl1GHm1mDl1VrYxkzbyUfJs0XIsj/KtT3ItDvBqzvKsin8qlL8AAAAL3RSTlMAFv6t66tWVi0VCwX19e3t7Ozs7Ovr4uLU1MXFsbGvrZ6ejIx2dmRkWVk9PR4eFHLHxygAAAE0SURBVDjL7ZHndoJAFISVJCYxvffeKx0FxIYlKAhI1CT29Pb+v13KXV/C+QFzznyzZ+/d0EhD/feH/m4P2/AYuH4e3MzZHAt+cvwJbB6Amy22Y0M9pgkY6Hm/+yOuXeWtoJ4sUxjo5dzvxQrxrcq06NdfKCqdACCHgKkdrsPzMl1FwNVSslWh4sYDBpzoCdH+U91c0r16JR03SAw4zgbxq6o8TUtSVl9ItkyUk2SqDkC3u8798LJMS9ks05xH10N5OVUqAkBw0WP28wvlDJOJhfeFNxL1S88Y4Dh0yW3xg0F5Bm3ncjGhoFypAcB62ztfbhoZ0xTcMQ/qDUXRChiw/eUdxt7jlD98ZK3YeMWAbQXmelPQgu1MTxRqjwBYIn6s01k8fGQVA6IewrrdBYcOCY0EGgATKDjicNrzUgAAAABJRU5ErkJggg==');
|
||||
}
|
||||
#nextbutton {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABCFBMVEUAAAC+ojXKsDi0mC/awzndxjvXwTTgzUjRuTTUvDjj1VbUvDrHrjHEqjLXxEu6ny/Sv0vWwTXhzkXUvjXq3lzl1lDRujPQujLbxj7RujTcyEfMtTPXwT7byUjRuDnJsTLSukDEqzHHqzm5oC/JsD+1mjDbxDfVwDS0ly+7nDO1mDDYwjW4mjLizULEqTzBpTnAozjUvTG3mTHFqz7fyTzKsDnUvjTSvDDq3Vjl1Evj0UbHrkDdxjvCpzq8njTIsEHgyz7awza/oTbQuTTs4V3k1lPn2VDMt0bQuUXLtUXKskPVvz7VvTvJsTO7oDDXxUzYwkDYwTzDpjrFqjjErDPDqzK/ozLCqTEl328YAAAAJnRSTlMAFVgI7ez7+sWuq6uMY1ctFfXs7Ovr4uLU1LGxr62ennZ2PT0eHggzQnUAAAEdSURBVDjL7ZFncsIwFITtQEJ6771KNrIt4QoOdmwTAoSQXu5/kzwZJE1mcgT215u3366kkTaV1OK6HA+v1RohMd1vr4oxrp1X/gG6TVESD+39SwHkAmj2X7o74xKbfbz5p3o550QCvUGv1jrROVAUaYCqK3xNFDB4vLvtx5szmpalqWmmHTS/BgAWwAP4nuO92se6b5qmZbnf4dxyBSuA+w5lQbuKSr9eNz7xbiiBFvgJZQUHwAfbML7wXqMhAc95p4w++Qt623Jd8J87cESoAMhD/yxcEvF6d4T5JVVDnNCEx2FEEP8Jt8pnRpEAbBb4PM4BaCdngP4FhtmSPh7zET64mqwjeUQG8YnIxoX8LPVMiAsd3WhSmGhTSf0Cx6gshxH98P4AAAAASUVORK5CYII=');
|
||||
}
|
||||
#volpbutton {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABp1BMVEUAAADTv03byVXSvkzax1PYxEvbyVLWw07Svkrj0Ufr31vt4WPr317q3V7m2lvj1Vjm2mHdzE3cy1DRu0PbylHXxEzYxlHWw0/u5Gbs4V7r31nu5Gft4mPq3Vnq31/n2lfm2Fjn21zl11fo3WDk1lbj1VXl2FrdzErh0VPg0VPYxEXfz1Lcykzf0FTayEzWwUfcy0/ezlbfz1nUwEvPuUXXxVLGqjvNt0Xv52rs4V3s313k0kjs4V/o2lTm1lDp21fq3Vvu5Gnn2FPr3l3k003s4mfq317q3mHs4mbk1FHbx0Hp3mHo21/cx0Lfz0zdy0jo22Hfz07h0VDj1Vfezk7h01fk2F/YxEfj1Fvh0Vbj1F3g0FjQuD7h1FzZxk7g0lvdzVfez1nTv0nNt0XGrTzaylbYyFTYx1PXxVHt42XayVXv5Wjs4mbu42Ps4WHs4Fzez1np3Fjn2VXn22Dcy1fi0lDgz03w52ru5Wjq3mLk1l3o21rdzlbk1VTayFTo2VDWxFDk1U/dyknv5WXu42Dg0Vvs4Frq3lfk1lfg0Vbp21TUwU3gzUTlFyGcAAAAZXRSTlMAASgEEVk8JhX+9O7k1LaWjo1kTElFGQ78/Pzv5+XX0sm7uLe0sKWjmJCPiYV9cmppT0AtHRQUCPr69vby7+/t6+rq6OXf39zYzMrJwry4tLGxqpqaj4iGhXdsbGZeXllVNDQkGQe+VJQAAAGqSURBVDjLzdJlc9tAEIDh15Jsx2zHThxsqGkDDadJmZmZuZVkmSkOYxl+dCVZmlEyU3/s5Pm2s7d7t3fHvuN656URbygXEBrkj15drmSj/JN4Kx9423lexNbEbs9ylSjRxfd27Lko4TTdtpKdYnrpNZaHhWYcmrqW0xkBKfME0wyJQr9zwIFstXhMxNV2z4yfB5n3d9ineJnTtMxXuXgN6LqJIVwQGCwlqAuspBRFlouXkxA6ALTyeLWZ0XXrEJK2oaZlOV3VBqAvCJEIkz/GGF84gknQ9HJZUdSi1sqhU3DiIC3lIdwLh62Bs3q5nk+l8gki7XCuF6H8gJbaHavDkp5X9fzaF72DH/xGh0e4a1YHabGqGAuUfBj6umF4iEl9//Gdp9R1Voz6rY0rSegJma8Q255gZGeCuheZz1pubUs1JuzuxXB/c4a7vrjjJsP5lHpcxNUxaMbuS675k74552cIrv4ueJG+j2KaJe67jZOn/WdpCk/pDZaRP6/YZezb+jCxXx/s+NONuT0/tr901n36gohNZI9kT7m2HaOB2eubZ7w0FP/I//cX2VZVBFBJqXUAAAAASUVORK5CYII=');
|
||||
}
|
||||
#volmbutton {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABd1BMVEUAAAC4njLo11PYxDrm00/hzlDaxEnIrUDHrUG9ojnk0Uvn1lHk0U3SvTPWwTfp2lnj0E3gzUniz0/dyEXj0VHgzU/bxkbaxUjfzVLey1HHsDTaxlHFrTq7ojLTu0i9ozXUvUyxlyyulSzNtEXGrECzmTDTvjLeykLq3Vzp2lnaxT7QujHm01HRuzPm1FPdyELYxEDNuC/WwTvLtS/l1Vfjz1Dm1ljaxkTJszDaxUbKtDLWwULVwELfzFLKszjey1LOuD7AqS/Zw0zKtDy7oyzHsDu+pjK5oC7Wv0zayFTXwk7XxFHTvU3TvU2pjyu9ojq6nze1mzO0mTLKr0LApDy2nDXUvzSxljHp2Vbhz0jgy0PMs0HaxTy+ozu8oDi4nTbQujLTvTGymDDr3Vvo2FnfzE7gzEbcyUDSvDbVwTXi0VXaxEzFrD/EqD/IsD7Cpj7OuD2+pTm6oDm+pDe7ozbMtzW5oTTKtDPIsTO+pTPCqjLFrjGuky/WrPCGAAAAT3RSTlMAJ/7+/bOWGQ4E/vby7u3s5OLX1Mm5tqWQjo5bS0A9NyUlFRQSEf377+/u7uvn5ubf3NjV09HMycK7uLaxl46IiIV7bGxeWVVPTTQoHgUEcf+/bgAAAS1JREFUOMvNzkV3wkAYheGLS4sVKS4F6u7ubgQCQQtJ8Lr7jy90l55Mlj082/fOdwad5yAkmUMznEEGsr4p9p11k/v5ImvYNxp1INni3txYLwRI3df/2fDCV9gjfWDshVfIEKlvEgbLTPxVoQMGVkXiLsc0mx+J52kAEwsig5Gv73Q6EX+a1AJWKwCNsEcZPs0nUqWH+gqwZAHUagjImNbzVIkq3tY06O0GulQQCDR+eyyWzHmh1gOjtj8XFPEU9Ri7SOb14heiBYoqtvp11gHYzYBzDULD9+1+R1u0wFwPgDCEdnK5WjZ/Q7fbuA3iHFk6r9QBchVhEDZXaXkQkcwGCPzKK+Uh/BkPSLblVRdc5WMQ2TMmz5DpDETa2cplxQkJp/PlwRNIOgri//0AbVY1/4adyuMAAAAASUVORK5CYII=');
|
||||
}
|
||||
#playlistbutton {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABTVBMVEUAAADr4GLo3GTSvUrn2mLVwlDh0lLq32Xm2l/f0Ffi013o3Gbs4Wrg0VvXxlDf0Frz7Hncx0Dz63jfzUjm2Vjo3F3x6HPq4GTfzkrbyUXk2Fnx6XXu5W7y63fo3WLk11rw6HPVvTvr4Wnr4Gnf0VTv5nLUvUDZyEzs4m3v5XHo3GbMsjri1Fzl2WLUwEnHrDjaylPdzlfQukbXx1DGqjnWxE/r3mjYxlPPukjy6nTx6HHn2mPm2GHj1F3ez1nayFPfzUj07Xrw527u5W3u5Gns4mbg0Frdzljl1lPRvUnJsELGrT/s4mvq3mHk1l/h0lzm2VjczFfh0lLk1VDWxE/i0k7Tv03i0Uzgz0rNt0TKs0PdyULcxj/Eqj3Dpzvz63fr4Gfq3mfo3GPn22Dm2Vbby1XYyFHfy0Xbx0Tv5W3Tv0nZxEPTvT7Ruj7Irjt5kAYfAAAAOXRSTlMA+40mCQPLurZ0V01CPjYW/Pz6+Pf27+7r5uPi3t3Z0cnGw62roqGVhHt3dmtkXVNNREQ6MiwiHhGFwXcaAAABNUlEQVQ4y83SR3OCUBQFYHuNMb333nujWlCBBJQmgiHGrqn/f5mXZCZA5rr3bjjM+ebCm3me4Z2g/yw6oEomjn2RFa8oSUC5v702gzN1gmJTut4GQIZpEBTNdrqapvMcBMgiVaDlFJquBgOSJIimKE6vbnEqAJ4xfCnsOzq/RZkrgyDwl9UKADDMzmUQ4A5gQCBr50oaALgDGBDIvtg53YMA4wCPAGByv0//yYW/B4HcD7gOqUa63/8HAnHfQSyXR+lySuHvH55qlqsfWcSEeqOJQHBBMasIfFqufgwnSwW2g8CoxJvfC97XnWAZL1IlWi54EWjzaEHtwzp19DcZAl0EVm5F0CdCymvVfBs/dP3hhIAW0C1vAr3c7c5Nzu9cuc+whwkkkZ+NeQZOfHMjHE16hnS+AIiQOxEJiBv0AAAAAElFTkSuQmCC');
|
||||
}
|
||||
#eqbutton {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAwFBMVEUAAAD27X/QvErn2WH27oH0633v43Hj01vcyk7w5XDj02HbyVf28IPx53Xu4m7s4Gni0ljgz1TayUvq3Wfo2mPy53Tt4G7s3Wrt4Gn27oDx5nP48of274Li0lT38YT17H306nru4mv59Irz6Hbr32bq3GTl1ljfz07v5G3p2mHo2l7gzl3m2Fvgz1LOuEzXxEDeylrczEvLtEjbykfZx0TVwj7SvznQuzXKtS7j02HcyVHSvVDZx07dzEzNuDTMtzDLJSoKAAAAGHRSTlMA4Uvh0tLS0tJLS0vh4eHh4eHh0tJLS0sGkbICAAAA4klEQVQ4y83Sx9KCMBSG4SN2/XtNCCGEKgjYe7v/uzJRR8YZOePSd5l5FpkvgSet+/PdQUE1Dt5Q4A/7Jty0PCzms8F0EmU8TOMzcCi1CrDXYDKKeBgmARANhAYlyf7KA6x3Il9R0Gk0/u8cG62WAVhf2/UHCqJxHkJJc7WBBikEPumZniOoZdk2uwI10giycZ5AQKQGtABFPN/EgPWZ8SoKjGazC4/1V6+3UVBxnAoKKJE23KTemadJ7BNpmp5QgNjAmOtewU5/lOQy0gkwYK4CJVmyx/BLCvGCgnat9gvP1BGHfhnUEswA0wAAAABJRU5ErkJggg==');
|
||||
}
|
||||
#settingsbutton {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAB8lBMVEUAAACDbR+QdyKgkjzEqirCrDWReCCyp0WJciCJcyKYhzChkzqTgS3Vw0G6oSe6oSjRw0q1nCm8oy3TyFO4nyrIuEKxlyfHu0qiiSKegyGijCyRdyKmlziomjqklTexqEmvpkmKdiXFrCzJry7n3mLf0lLXxUPHrS7cz0/KrzDZy0rPuDfBpyrn32jSwEDKsjTEqi3BqCzIsDTp4264nynPv0Pj3GfWyU/c01yzmybh2mSulSS+pi/Zz1rBrDazmSiqkCXFt0Pa1GOliiOtkynNxFTZ0mOpkCrBtUmqkivIv1OpkyybgSK+s0rJwlqplzKUeyGQdx++tU6ypUHCu1WWfSOnmDjCvFexpkSahCqNdSGHcB+XgimonECAax7OtDDLsC7Hrivt42js4mXq3l3h0Uy8oC/38Xv07XPv5Wvt5GPs4mHn21jez1fm2FLXxVDUwUneyULLtEHbxT7JrzzFqDS/oDTUuTPDpjH59ILz63Hp3GLi1Vzf0Frh0lbby1DUwk/ezUfSvUPQu0PNszvBpTrZwDjVvDfKsDa7nTPVujLRtjHPtDG2mS+xlS7Jry3Eqiz27nfx6W/k12Dq4F7bylXYx07j1E3Qu0rhz0fMtkTJsUHcxz/Vvj/Otj/Eqj7NsTTRtjPHqjO+oDLApDC+pCyMAG9RAAAAX3RSTlMADUYD/NtgQy4hFxQK+fTt49/e1tTT0bizqGVOSEE8Jg8G/vz6+vr49/f29vX09PTz8vHv6efm5uXl4eDf2NXRysbCu7i1s6Keko+Li31zcXFwal9dWU9NS0s+OjcbEJmukO4AAAFySURBVDjLYhiCIJZdlguXnLyXrUpWdkpOXiqvnTcnhnSIVUZVUnXt1PyGGc3Nc9vahUJRpMON06uSJtfU5ubWTysomt9aVhrfzc8El2Z2q0zPrE5OTs5OURUwVSssbl9Q2pvWnygJU+BXOTEpc1KmsEcwN0+YjCKXlLtgfEJa39JENpgKp/SMDAv2CHEBjbzpM+do27gwSZskTEiUQNhhphskLzwlpa6+YNbskpKOhfFinAF81kiOVFBwTc6qyW/IL2ia11La2VkRryTByIjsDfak7Nw6YBDosbLql3f1LOmbwBKNGg6+WSk5orLMXFKRPMxMYgn9iYHoIQWYuLmMnChvYXFbl7o9B5OlJGZQczumNjaVtJaVVcQnOscxYFGglVpU1NKxqCKtN4GPEVtsyTUWFhuJiBjGJ7BwYI9Pf9YYBkYehihBNpzJwVOzvFvHB096ESpfnBbvgEcBo0FPPL8iviTHoczCiT9RskkzDEcAAG+FX5nDaQtoAAAAAElFTkSuQmCC');
|
||||
}
|
||||
.infowrap {
|
||||
display: flex;
|
||||
width: 400px;
|
||||
justify-content: space-around;
|
||||
font-size: 14px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
#volrange, .slider {
|
||||
overflow: hidden;
|
||||
width: 432px;
|
||||
margin-top: 34px;
|
||||
background: linear-gradient(to right, #bfa73e 0%, #bfa73e 0%, #272727 0%, #272727 100%);
|
||||
border: solid 2px #e3d25f;
|
||||
border-radius: 13px;
|
||||
height: 25px!important;
|
||||
outline: none;
|
||||
transition: background 450ms ease-in;
|
||||
-webkit-appearance: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#volrange::-webkit-slider-thumb, .slider::-webkit-slider-thumb {
|
||||
width: 10px;
|
||||
-webkit-appearance: none;Раменское
|
||||
height: 10px;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
.slider {
|
||||
width: 400px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
#playlist {
|
||||
width: 400px;
|
||||
scroll-behavior: smooth;
|
||||
overflow: auto;
|
||||
margin: 0 0 0 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
border: solid 1px #e3d25f;
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
height: 0px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#playlist::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 8px;
|
||||
background-color: #000;
|
||||
}
|
||||
#playlist::-webkit-scrollbar-thumb {
|
||||
background: #e3d25f;
|
||||
}
|
||||
#playlist li {
|
||||
text-transform: uppercase;
|
||||
font-size: 20px;
|
||||
padding: 8px 16px;
|
||||
color: #bfa73e;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
#playlist li span {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
padding: 0 10px;
|
||||
background: #000;
|
||||
width: 44px;
|
||||
box-sizing: border-box;
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#playlist li:nth-child(odd), #playlist li:nth-child(odd) span {
|
||||
background: #272727;
|
||||
}
|
||||
|
||||
#playlist li:hover, #playlist li:hover span {
|
||||
color: #fff;
|
||||
background: #323232;
|
||||
}
|
||||
#playlist li.active, #playlist li.active span {
|
||||
background: #bfa73e;
|
||||
color: #000;
|
||||
}
|
||||
#copy {
|
||||
padding-top: 14px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@media screen and (max-width: 480px) {
|
||||
.content { padding: 20px; }
|
||||
#playlist { width: 100%; }
|
||||
#playlist li { font-size: 16px; }
|
||||
#volrange { width: 100%; margin-top: 24px;}
|
||||
.playerbyttonwrap { width: 100%; margin-top: 8px; }
|
||||
#navbar { width: 100%; padding: 20px 20px 0 20px; }
|
||||
#meta { margin-bottom: 8px; }
|
||||
.infowrap {width: 100%; padding: 0 10px;}
|
||||
.logo { width: 100px; height: 65px; background-size: cover; }
|
||||
#copy { font-size: 10px; padding-top: 10px; }
|
||||
}
|
||||
@media screen and (max-width: 480px) {
|
||||
.playerbytton, #playbutton {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
background-size: 75%;
|
||||
}
|
||||
}
|
||||
|
||||
311
yoRadio/display.cpp
Normal file
311
yoRadio/display.cpp
Normal file
@@ -0,0 +1,311 @@
|
||||
#include "WiFi.h"
|
||||
#include "time.h"
|
||||
#include "display.h"
|
||||
|
||||
#include "player.h"
|
||||
#include "netserver.h"
|
||||
#include "options.h"
|
||||
#include "network.h"
|
||||
|
||||
/*
|
||||
#include "displayDummy.h"
|
||||
DisplayDummy dsp;
|
||||
*/
|
||||
#include "displayST7735.h"
|
||||
DisplayST7735 dsp;
|
||||
|
||||
Display display;
|
||||
|
||||
void ticks() {
|
||||
display.clockRequest = true;
|
||||
}
|
||||
|
||||
#define STARTTIME 5000
|
||||
#define SCROLLTIME 83
|
||||
#define SCROLLDELTA 3
|
||||
|
||||
void Scroll::init(char *sep, byte tsize, byte top, uint16_t dlay, uint16_t fgcolor, uint16_t bgcolor) {
|
||||
textsize = tsize;
|
||||
texttop = top;
|
||||
fg = fgcolor;
|
||||
bg = bgcolor;
|
||||
delayStartScroll=dlay;
|
||||
memset(separator, 0, 4);
|
||||
strlcpy(separator, sep, 4);
|
||||
locked = false;
|
||||
}
|
||||
|
||||
void Scroll::setText(const char *txt) {
|
||||
memset(text, 0, BUFLEN / 2);
|
||||
strlcpy(text, txt, BUFLEN / 2);
|
||||
getbounds(textwidth, textheight, sepwidth);
|
||||
if (!locked) {
|
||||
clearscrolls();
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Scroll::lock() { locked = true; }
|
||||
|
||||
void Scroll::unlock() { locked = false; }
|
||||
|
||||
void Scroll::reset() {
|
||||
locked = false;
|
||||
clear();
|
||||
setTextParams();
|
||||
dsp.set_Cursor(TFT_FRAMEWDT, texttop);
|
||||
dsp.printText(text);
|
||||
drawFrame();
|
||||
}
|
||||
|
||||
void Scroll::setTextParams() {
|
||||
dsp.set_TextSize(textsize);
|
||||
dsp.set_TextColor(fg, bg);
|
||||
}
|
||||
|
||||
void Scroll::clearscrolls() {
|
||||
x = TFT_FRAMEWDT;
|
||||
scrolldelay = millis();
|
||||
clear();
|
||||
}
|
||||
|
||||
void Scroll::loop() {
|
||||
if (checkdelay(x == TFT_FRAMEWDT ? delayStartScroll : SCROLLTIME, scrolldelay)) {
|
||||
scroll();
|
||||
ticks();
|
||||
}
|
||||
yield();
|
||||
}
|
||||
|
||||
boolean Scroll::checkdelay(int m, unsigned long &tstamp) {
|
||||
if (millis() - tstamp > m) {
|
||||
tstamp = millis();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Scroll::drawFrame() {
|
||||
dsp.drawScrollFrame(texttop, textheight, bg);
|
||||
}
|
||||
|
||||
void Scroll::ticks() {
|
||||
if (!doscroll || locked) return;
|
||||
setTextParams();
|
||||
dsp.set_Cursor(x, texttop);
|
||||
dsp.printText(text);
|
||||
dsp.printText(separator);
|
||||
dsp.printText(text);
|
||||
drawFrame();
|
||||
}
|
||||
|
||||
void Scroll::scroll() {
|
||||
if (!doscroll) return;
|
||||
if (textwidth > display.screenwidth) {
|
||||
x -= SCROLLDELTA;
|
||||
if (-x >= textwidth + sepwidth - TFT_FRAMEWDT) x = TFT_FRAMEWDT;
|
||||
}
|
||||
}
|
||||
|
||||
void Scroll::clear() {
|
||||
dsp.clearScroll(texttop, textheight, bg);
|
||||
}
|
||||
|
||||
void Scroll::getbounds(uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) {
|
||||
if (strlen(text) == 0) {
|
||||
dsp.getScrolBbounds("EMPTY", separator, textsize, tWidth, tHeight, sWidth);
|
||||
} else {
|
||||
dsp.getScrolBbounds(text, separator, textsize, tWidth, tHeight, sWidth);
|
||||
}
|
||||
doscroll = (tWidth > display.screenwidth);
|
||||
}
|
||||
|
||||
void Display::init() {
|
||||
dsp.initD(screenwidth, screenheight);
|
||||
dsp.drawLogo();
|
||||
meta.init(" * ", 2, TFT_FRAMEWDT, STARTTIME, TFT_LOGO, TFT_BG);
|
||||
title1.init(" * ", 1, TFT_FRAMEWDT + 2 * TFT_LINEHGHT, STARTTIME, TFT_FG, TFT_BG);
|
||||
title2.init(" * ", 1, TFT_FRAMEWDT + 3 * TFT_LINEHGHT, STARTTIME, TFT_FG, TFT_BG);
|
||||
plCurrent.init(" * ", 2, 57, 0, TFT_BG, TFT_LOGO);
|
||||
plCurrent.lock();
|
||||
}
|
||||
|
||||
void Display::apScreen() {
|
||||
meta.setText(dsp.utf8Rus("ёRADIO * ёRADIO * ёRADIO", false));
|
||||
dsp.apScreen();
|
||||
}
|
||||
|
||||
void Display::start() {
|
||||
clear();
|
||||
if (network.status != CONNECTED) {
|
||||
apScreen();
|
||||
return;
|
||||
}
|
||||
mode = PLAYER;
|
||||
title("[READY]");
|
||||
ip();
|
||||
volume();
|
||||
station();
|
||||
rssi();
|
||||
time();
|
||||
configTime(TIMEZONE, OFFSET, "pool.ntp.org", "ru.pool.ntp.org");
|
||||
timer.attach_ms(1000, ticks);
|
||||
// Экстреминатус секвестирован
|
||||
}
|
||||
|
||||
void Display::clear() {
|
||||
dsp.clearDsp();
|
||||
}
|
||||
|
||||
void Display::swichMode(displayMode_e newmode) {
|
||||
if (newmode == VOL) {
|
||||
volDelay = millis();
|
||||
}
|
||||
if (newmode == mode) return;
|
||||
clear();
|
||||
mode = newmode;
|
||||
if (newmode != STATIONS) {
|
||||
ip();
|
||||
volume();
|
||||
}
|
||||
if (newmode == PLAYER) {
|
||||
meta.reset();
|
||||
title1.reset();
|
||||
title2.reset();
|
||||
plCurrent.lock();
|
||||
time();
|
||||
} else {
|
||||
meta.lock();
|
||||
title1.lock();
|
||||
title2.lock();
|
||||
}
|
||||
if (newmode == VOL) {
|
||||
dsp.frameTitle("VOLUME");
|
||||
}
|
||||
if (newmode == STATIONS) {
|
||||
currentPlItem = config.store.lastStation;
|
||||
plCurrent.reset();
|
||||
drawPlaylist();
|
||||
}
|
||||
}
|
||||
|
||||
void Display::drawPlayer() {
|
||||
if (clockRequest) {
|
||||
if (syncTicks % 21600 == 0) { //6hours
|
||||
configTime(TIMEZONE, OFFSET, "pool.ntp.org", "ru.pool.ntp.org");
|
||||
yield();
|
||||
syncTicks = 0;
|
||||
}
|
||||
getLocalTime(&timeinfo);
|
||||
time();
|
||||
syncTicks++;
|
||||
clockRequest = false;
|
||||
}
|
||||
meta.loop();
|
||||
title1.loop();
|
||||
title2.loop();
|
||||
}
|
||||
|
||||
void Display::drawVolume() {
|
||||
if (millis() - volDelay > 3000) {
|
||||
volDelay = millis();
|
||||
swichMode(PLAYER);
|
||||
}
|
||||
}
|
||||
|
||||
void Display::drawPlaylist() {
|
||||
char buf[PLMITEMLENGHT];
|
||||
dsp.drawPlaylist(currentPlItem, buf);
|
||||
plCurrent.setText(dsp.utf8Rus(buf, true));
|
||||
}
|
||||
|
||||
void Display::loop() {
|
||||
switch (mode) {
|
||||
case PLAYER: {
|
||||
drawPlayer();
|
||||
break;
|
||||
}
|
||||
case VOL: {
|
||||
drawVolume();
|
||||
break;
|
||||
}
|
||||
case STATIONS: {
|
||||
plCurrent.loop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
yield();
|
||||
}
|
||||
|
||||
void Display::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
|
||||
dsp.centerText(text, y, fg, bg);
|
||||
}
|
||||
|
||||
void Display::bootString(const char* text, byte y) {
|
||||
dsp.centerText(text, y, TFT_LOGO, TFT_BG);
|
||||
}
|
||||
|
||||
void Display::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
|
||||
dsp.rightText(text, y, fg, bg);
|
||||
}
|
||||
|
||||
void Display::station() {
|
||||
meta.setText(dsp.utf8Rus(config.station.name, true));
|
||||
netserver.requestOnChange(STATION, 0);
|
||||
}
|
||||
|
||||
void Display::title(const char *str) {
|
||||
const char *title = str;
|
||||
char ttl[BUFLEN / 2] = { 0 };
|
||||
char sng[BUFLEN / 2] = { 0 };
|
||||
memset(config.station.title, 0, BUFLEN);
|
||||
strlcpy(config.station.title, title, BUFLEN);
|
||||
if (strlen(config.station.title) > 0) {
|
||||
char* ici;
|
||||
if ((ici = strstr(config.station.title, " - ")) != NULL) {
|
||||
strlcpy(sng, ici + 3, BUFLEN / 2);
|
||||
strlcpy(ttl, config.station.title, strlen(config.station.title) - strlen(ici) + 1);
|
||||
} else {
|
||||
strlcpy(ttl, config.station.title, BUFLEN / 2);
|
||||
sng[0] = '\0';
|
||||
}
|
||||
title1.setText(dsp.utf8Rus(ttl, true));
|
||||
title2.setText(dsp.utf8Rus(sng, true));
|
||||
}
|
||||
netserver.requestOnChange(TITLE, 0);
|
||||
}
|
||||
|
||||
void Display::heap() {
|
||||
if(config.store.audioinfo) dsp.displayHeapForDebug();
|
||||
}
|
||||
|
||||
void Display::rssi() {
|
||||
char buf[20];
|
||||
int rssi = WiFi.RSSI();
|
||||
sprintf(buf, "%ddBm", rssi);
|
||||
dsp.rssi(buf);
|
||||
netserver.setRSSI(rssi);
|
||||
}
|
||||
|
||||
void Display::ip() {
|
||||
dsp.ip(WiFi.localIP().toString().c_str());
|
||||
}
|
||||
|
||||
void Display::time() {
|
||||
char timeStringBuff[20] = { 0 };
|
||||
if (!dt) {
|
||||
heap();
|
||||
rssi();
|
||||
strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M", &timeinfo);
|
||||
} else {
|
||||
strftime(timeStringBuff, sizeof(timeStringBuff), "%H %M", &timeinfo);
|
||||
}
|
||||
dsp.printClock(timeStringBuff);
|
||||
dt = !dt;
|
||||
}
|
||||
|
||||
void Display::volume() {
|
||||
dsp.drawVolumeBar(mode == VOL);
|
||||
netserver.requestOnChange(VOLUME, 0);
|
||||
}
|
||||
81
yoRadio/display.h
Normal file
81
yoRadio/display.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#ifndef display_h
|
||||
#define display_h
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <Ticker.h>
|
||||
#include "config.h"
|
||||
|
||||
enum displayMode_e { PLAYER, VOL, STATIONS };
|
||||
|
||||
#define TIMEZONE 10800 // 3600*3=10800 (UTC+3)
|
||||
#define OFFSET 0 // Daylight Offset (sec.)
|
||||
|
||||
class Scroll {
|
||||
public:
|
||||
Scroll() { };
|
||||
void init(char *sep, byte tsize, byte top, uint16_t dlay, uint16_t fgcolor, uint16_t bgcolor);
|
||||
void setText(const char *txt);
|
||||
void loop();
|
||||
void reset();
|
||||
void lock();
|
||||
void unlock();
|
||||
private:
|
||||
byte textsize, texttop;
|
||||
char text[BUFLEN/2];
|
||||
char separator[4];
|
||||
uint16_t fg, bg;
|
||||
uint16_t delayStartScroll;
|
||||
uint16_t textwidth, textheight, sepwidth, startticks, scrollticks;
|
||||
int x;
|
||||
bool doscroll, locked;
|
||||
unsigned long scrolldelay;
|
||||
void clearscrolls();
|
||||
void getbounds(uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth);
|
||||
boolean checkdelay(int m, unsigned long &tstamp);
|
||||
void scroll();
|
||||
void ticks();
|
||||
void clear();
|
||||
void setTextParams();
|
||||
void drawFrame();
|
||||
};
|
||||
|
||||
class Display {
|
||||
public:
|
||||
struct tm timeinfo;
|
||||
uint16_t syncTicks;
|
||||
bool clockRequest;
|
||||
uint16_t screenwidth, screenheight;
|
||||
displayMode_e mode;
|
||||
uint16_t currentPlItem;
|
||||
public:
|
||||
Display() {};
|
||||
void init();
|
||||
void clear();
|
||||
void loop();
|
||||
void start();
|
||||
void centerText(const char* text, byte y, uint16_t fg, uint16_t bg);
|
||||
void rightText(const char* text, byte y, uint16_t fg, uint16_t bg);
|
||||
void bootString(const char* text, byte y);
|
||||
void station();
|
||||
void title(const char *str);
|
||||
void time();
|
||||
void volume();
|
||||
void ip();
|
||||
void swichMode(displayMode_e newmode);
|
||||
void drawPlaylist();
|
||||
private:
|
||||
Ticker timer;
|
||||
Scroll meta, title1, title2, plCurrent;
|
||||
bool dt; // dots
|
||||
unsigned long volDelay;
|
||||
void heap();
|
||||
void rssi();
|
||||
void apScreen();
|
||||
void drawPlayer();
|
||||
void drawVolume();
|
||||
|
||||
};
|
||||
|
||||
extern Display display;
|
||||
|
||||
#endif
|
||||
163
yoRadio/displayDummy.cpp
Normal file
163
yoRadio/displayDummy.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "displayDummy.h"
|
||||
#include <SPI.h>
|
||||
#include "player.h"
|
||||
#include "config.h"
|
||||
#include "network.h"
|
||||
|
||||
DisplayDummy::DisplayDummy() {
|
||||
|
||||
}
|
||||
|
||||
char* DisplayDummy::utf8Rus(const char* str, bool uppercase) {
|
||||
int index = 0;
|
||||
static char strn[BUFLEN];
|
||||
bool E = false;
|
||||
strlcpy(strn, str, BUFLEN);
|
||||
if (uppercase) {
|
||||
bool next = false;
|
||||
for (char *iter = strn; *iter != '\0'; ++iter)
|
||||
{
|
||||
if (E) {
|
||||
E = false;
|
||||
continue;
|
||||
}
|
||||
byte rus = (byte) * iter;
|
||||
if (rus == 208 && (byte) * (iter + 1) == 129) { // ёКостыли
|
||||
*iter = (char)209;
|
||||
*(iter + 1) = (char)145;
|
||||
E = true;
|
||||
continue;
|
||||
}
|
||||
if (rus == 209 && (byte) * (iter + 1) == 145) {
|
||||
*iter = (char)209;
|
||||
*(iter + 1) = (char)145;
|
||||
E = true;
|
||||
continue;
|
||||
}
|
||||
if (next) {
|
||||
if (rus >= 128 && rus <= 143) *iter = (char)(rus + 32);
|
||||
if (rus >= 176 && rus <= 191) *iter = (char)(rus - 32);
|
||||
next = false;
|
||||
}
|
||||
if (rus == 208) next = true;
|
||||
if (rus == 209) {
|
||||
*iter = (char)208;
|
||||
next = true;
|
||||
}
|
||||
*iter = toupper(*iter);
|
||||
}
|
||||
}
|
||||
while (strn[index])
|
||||
{
|
||||
if (strn[index] >= 0xBF)
|
||||
{
|
||||
switch (strn[index]) {
|
||||
case 0xD0: {
|
||||
if (strn[index + 1] == 0x81) {
|
||||
strn[index] = 0xA8;
|
||||
break;
|
||||
}
|
||||
if (strn[index + 1] >= 0x90 && strn[index + 1] <= 0xBF) strn[index] = strn[index + 1] + 0x30;
|
||||
break;
|
||||
}
|
||||
case 0xD1: {
|
||||
if (strn[index + 1] == 0x91) {
|
||||
//strn[index] = 0xB7;
|
||||
strn[index] = 0xB8;
|
||||
break;
|
||||
}
|
||||
if (strn[index + 1] >= 0x80 && strn[index + 1] <= 0x8F) strn[index] = strn[index + 1] + 0x70;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int sind = index + 2;
|
||||
while (strn[sind]) {
|
||||
strn[sind - 1] = strn[sind];
|
||||
sind++;
|
||||
}
|
||||
strn[sind - 1] = 0;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return strn;
|
||||
}
|
||||
|
||||
void DisplayDummy::apScreen() {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::initD(uint16_t &screenwidth, uint16_t &screenheight) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::drawLogo() {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::drawPlaylist(uint16_t currentItem, char* currentItemText) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::clearDsp() {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::displayHeapForDebug() {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::printClock(const char* timestr) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::drawVolumeBar(bool withNumber) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::frameTitle(const char* str) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::rssi(const char* str) {
|
||||
;
|
||||
}
|
||||
|
||||
void DisplayDummy::ip(const char* str) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::set_TextSize(uint8_t s) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::set_TextColor(uint16_t fg, uint16_t bg) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::set_Cursor(int16_t x, int16_t y) {
|
||||
|
||||
}
|
||||
|
||||
void DisplayDummy::printText(const char* txt) {
|
||||
|
||||
}
|
||||
80
yoRadio/displayDummy.h
Normal file
80
yoRadio/displayDummy.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef displayDummy_h
|
||||
#define displayDummy_h
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "options.h"
|
||||
|
||||
#define TFT_ROTATE 3
|
||||
#define TFT_LINEHGHT 10
|
||||
#define TFT_FRAMEWDT 4
|
||||
|
||||
#define PLMITEMS 7
|
||||
#define PLMITEMLENGHT 40
|
||||
#define PLMITEMHEIGHT 22
|
||||
|
||||
class DisplayDummy {
|
||||
public:
|
||||
DisplayDummy();
|
||||
char plMenu[PLMITEMS][PLMITEMLENGHT];
|
||||
uint16_t clockY;
|
||||
void initD(uint16_t &screenwidth, uint16_t &screenheight);
|
||||
void apScreen();
|
||||
void drawLogo();
|
||||
void clearDsp();
|
||||
void centerText(const char* text, byte y, uint16_t fg, uint16_t bg);
|
||||
void rightText(const char* text, byte y, uint16_t fg, uint16_t bg);
|
||||
void set_TextSize(uint8_t s);
|
||||
void set_TextColor(uint16_t fg, uint16_t bg);
|
||||
void set_Cursor(int16_t x, int16_t y);
|
||||
void printText(const char* txt);
|
||||
void printClock(const char* timestr);
|
||||
void displayHeapForDebug();
|
||||
void drawVolumeBar(bool withNumber);
|
||||
char* utf8Rus(const char* str, bool uppercase);
|
||||
void drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg);
|
||||
void getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth);
|
||||
void clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg);
|
||||
void frameTitle(const char* str);
|
||||
void rssi(const char* str);
|
||||
void ip(const char* str);
|
||||
void drawPlaylist(uint16_t currentItem, char* currentItemText);
|
||||
private:
|
||||
uint16_t swidth, sheight;
|
||||
|
||||
};
|
||||
|
||||
extern DisplayDummy dsp;
|
||||
|
||||
/*
|
||||
* TFT COLORS
|
||||
*/
|
||||
#define BLACK 0x0000
|
||||
#define BLUE 0x001F
|
||||
#define RED 0xF800
|
||||
#define GREEN 0x07E0
|
||||
#define MAGENTA 0xF81F
|
||||
#define YELLOW 0xFFE0
|
||||
#define WHITE 0xFFFF
|
||||
#define GRAY 0x7BEF
|
||||
#define DARK_GRAY 0x2945
|
||||
#define LIGHT_GRAY 0xC618
|
||||
#define LIME 0x87E0
|
||||
#define AQUA 0x5D1C
|
||||
#define CYAN 0x07FF
|
||||
#define DARK_CYAN 0x03EF
|
||||
#define ORANGE 0xFCA0
|
||||
#define PINK 0xF97F
|
||||
#define BROWN 0x8200
|
||||
#define VIOLET 0x9199
|
||||
#define SILVER 0xA510
|
||||
#define GOLD 0xA508
|
||||
#define NAVY 0x000F
|
||||
#define MAROON 0x7800
|
||||
#define PURPLE 0x780F
|
||||
#define OLIVE 0x7BE0
|
||||
|
||||
#define TFT_BG BLACK
|
||||
#define TFT_FG WHITE
|
||||
#define TFT_LOGO 0xE68B // 224, 209, 92
|
||||
|
||||
#endif
|
||||
321
yoRadio/displayST7735.cpp
Normal file
321
yoRadio/displayST7735.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
#include "displayST7735.h"
|
||||
#include <SPI.h>
|
||||
#include "fonts/bootlogo.h"
|
||||
#include "player.h"
|
||||
#include "config.h"
|
||||
#include "network.h"
|
||||
|
||||
#define DTYPE INITR_BLACKTAB // 1.8' https://aliexpress.ru/item/1005002822797745.html
|
||||
/* If there is a noisy line on one side of the screen, then in Adafruit_ST7735.cpp:
|
||||
|
||||
// Black tab, change MADCTL color filter
|
||||
if ((options == INITR_BLACKTAB) || (options == INITR_MINI160x80)) {
|
||||
uint8_t data = 0xC0;
|
||||
sendCommand(ST77XX_MADCTL, &data, 1);
|
||||
_add this_ -> _colstart = 2;
|
||||
_add this_ -> _rowstart = 1;
|
||||
}
|
||||
|
||||
*/
|
||||
//#define DTYPE INITR_144GREENTAB // 1.44' https://aliexpress.ru/item/1005002822797745.html
|
||||
|
||||
class GFXClock {
|
||||
public:
|
||||
GFXClock() {};
|
||||
uint16_t init(Adafruit_ST7735 &tftd, const GFXfont *font, uint16_t fgcolor, uint16_t bgcolor ) {
|
||||
_dsp = &tftd;
|
||||
tftd.setFont(font);
|
||||
tftd.getTextBounds("88:88", 0, 0, &x, &y, &cwidth, &cheight);
|
||||
tftd.setFont();
|
||||
fg = fgcolor;
|
||||
bg = bgcolor;
|
||||
swidth = tftd.width();
|
||||
_canvas = new GFXcanvas1(swidth, cheight + 3);
|
||||
_canvas->setFont(font);
|
||||
_canvas->setTextWrap(false);
|
||||
_canvas->setTextColor(WHITE);
|
||||
uint16_t header = TFT_FRAMEWDT + 4 * TFT_LINEHGHT;
|
||||
uint16_t footer = TFT_FRAMEWDT * 2 + TFT_LINEHGHT + 5;
|
||||
clockY = header + (tftd.height() - header - footer) / 2 - cheight / 2;
|
||||
return cheight;
|
||||
}
|
||||
void print(const char* timestr) {
|
||||
_canvas->fillScreen(BLACK);
|
||||
_canvas->getTextBounds(timestr, 0, 0, &x, &y, &cwidth, &cheight);
|
||||
_canvas->setCursor((swidth - cwidth) / 2 - 4, cheight);
|
||||
_canvas->print(timestr);
|
||||
_dsp->drawBitmap(0, clockY , _canvas->getBuffer(), swidth, cheight + 3, fg, bg);
|
||||
}
|
||||
private:
|
||||
int16_t x, y;
|
||||
uint16_t cwidth, cheight, fg, bg, clockY, swidth;
|
||||
GFXcanvas1 *_canvas;
|
||||
Adafruit_ST7735 *_dsp;
|
||||
};
|
||||
|
||||
GFXClock gclock;
|
||||
|
||||
DisplayST7735::DisplayST7735(): Adafruit_ST7735(&SPI, TFT_CS, TFT_DC, TFT_RST) {
|
||||
|
||||
}
|
||||
|
||||
char* DisplayST7735::utf8Rus(const char* str, bool uppercase) {
|
||||
int index = 0;
|
||||
static char strn[BUFLEN];
|
||||
bool E = false;
|
||||
strlcpy(strn, str, BUFLEN);
|
||||
if (uppercase) {
|
||||
bool next = false;
|
||||
for (char *iter = strn; *iter != '\0'; ++iter)
|
||||
{
|
||||
if (E) {
|
||||
E = false;
|
||||
continue;
|
||||
}
|
||||
byte rus = (byte) * iter;
|
||||
if (rus == 208 && (byte) * (iter + 1) == 129) { // ёКостыли
|
||||
*iter = (char)209;
|
||||
*(iter + 1) = (char)145;
|
||||
E = true;
|
||||
continue;
|
||||
}
|
||||
if (rus == 209 && (byte) * (iter + 1) == 145) {
|
||||
*iter = (char)209;
|
||||
*(iter + 1) = (char)145;
|
||||
E = true;
|
||||
continue;
|
||||
}
|
||||
if (next) {
|
||||
if (rus >= 128 && rus <= 143) *iter = (char)(rus + 32);
|
||||
if (rus >= 176 && rus <= 191) *iter = (char)(rus - 32);
|
||||
next = false;
|
||||
}
|
||||
if (rus == 208) next = true;
|
||||
if (rus == 209) {
|
||||
*iter = (char)208;
|
||||
next = true;
|
||||
}
|
||||
*iter = toupper(*iter);
|
||||
}
|
||||
}
|
||||
while (strn[index])
|
||||
{
|
||||
if (strn[index] >= 0xBF)
|
||||
{
|
||||
switch (strn[index]) {
|
||||
case 0xD0: {
|
||||
if (strn[index + 1] == 0x81) {
|
||||
strn[index] = 0xA8;
|
||||
break;
|
||||
}
|
||||
if (strn[index + 1] >= 0x90 && strn[index + 1] <= 0xBF) strn[index] = strn[index + 1] + 0x30;
|
||||
break;
|
||||
}
|
||||
case 0xD1: {
|
||||
if (strn[index + 1] == 0x91) {
|
||||
//strn[index] = 0xB7;
|
||||
strn[index] = 0xB8;
|
||||
break;
|
||||
}
|
||||
if (strn[index + 1] >= 0x80 && strn[index + 1] <= 0x8F) strn[index] = strn[index + 1] + 0x70;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int sind = index + 2;
|
||||
while (strn[sind]) {
|
||||
strn[sind - 1] = strn[sind];
|
||||
sind++;
|
||||
}
|
||||
strn[sind - 1] = 0;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return strn;
|
||||
}
|
||||
|
||||
void DisplayST7735::apScreen() {
|
||||
setTextSize(1);
|
||||
setTextColor(TFT_FG, TFT_BG);
|
||||
setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + 2 * TFT_LINEHGHT);
|
||||
print("AP NAME: ");
|
||||
print(apSsid);
|
||||
setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + 3 * TFT_LINEHGHT);
|
||||
print("PASSWORD: ");
|
||||
print(apPassword);
|
||||
setTextColor(SILVER, TFT_BG);
|
||||
setCursor(TFT_FRAMEWDT, 107);
|
||||
print("SETTINGS PAGE ON: ");
|
||||
setCursor(TFT_FRAMEWDT, 117);
|
||||
print("http://");
|
||||
print(WiFi.softAPIP().toString().c_str());
|
||||
print("/");
|
||||
}
|
||||
|
||||
void DisplayST7735::initD(uint16_t &screenwidth, uint16_t &screenheight) {
|
||||
initR(DTYPE);
|
||||
cp437(true);
|
||||
fillScreen(TFT_BG);
|
||||
setRotation(TFT_ROTATE);
|
||||
setTextWrap(false);
|
||||
screenwidth = width();
|
||||
screenheight = height();
|
||||
swidth = screenwidth;
|
||||
sheight = screenheight;
|
||||
gclock.init(dsp, &DS_DIGI28pt7b, TFT_LOGO, BLACK);
|
||||
}
|
||||
|
||||
void DisplayST7735::drawLogo() {
|
||||
drawRGBBitmap((swidth - 99) / 2, 18, bootlogo2, 99, 64);
|
||||
}
|
||||
|
||||
// http://greekgeeks.net/#maker-tools_convertColor
|
||||
#define CLR_ITEM1 0x52AA
|
||||
#define CLR_ITEM2 0x39C7
|
||||
#define CLR_ITEM3 0x18E3
|
||||
|
||||
void DisplayST7735::drawPlaylist(uint16_t currentItem, char* currentItemText) {
|
||||
for (byte i = 0; i < PLMITEMS; i++) {
|
||||
plMenu[i][0] = '\0';
|
||||
}
|
||||
config.fillPlMenu(plMenu, currentItem - 3, PLMITEMS);
|
||||
setTextSize(2);
|
||||
int yStart = (sheight / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
|
||||
fillRect(0, (sheight / 2 - PLMITEMHEIGHT / 2) - 1, swidth, PLMITEMHEIGHT + 2, TFT_LOGO);
|
||||
for (byte i = 0; i < PLMITEMS; i++) {
|
||||
if (abs(i - 3) == 3) setTextColor(CLR_ITEM3, TFT_BG);
|
||||
if (abs(i - 3) == 2) setTextColor(CLR_ITEM2, TFT_BG);
|
||||
if (abs(i - 3) == 1) setTextColor(CLR_ITEM1, TFT_BG);
|
||||
if (i == 3) {
|
||||
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
|
||||
} else {
|
||||
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
|
||||
print(utf8Rus(plMenu[i], true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayST7735::clearDsp() {
|
||||
fillScreen(TFT_BG);
|
||||
}
|
||||
|
||||
void DisplayST7735::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
|
||||
fillRect(0, texttop, TFT_FRAMEWDT, textheight, bg);
|
||||
fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg);
|
||||
}
|
||||
|
||||
void DisplayST7735::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) {
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
setTextSize(textsize);
|
||||
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
|
||||
tWidth = w;
|
||||
tHeight = h;
|
||||
getTextBounds(separator, 0, 0, &x1, &y1, &w, &h);
|
||||
sWidth = w;
|
||||
}
|
||||
|
||||
void DisplayST7735::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
|
||||
fillRect(0, texttop, swidth, textheight, bg);
|
||||
}
|
||||
|
||||
void DisplayST7735::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
const char* txt = text;
|
||||
getTextBounds(txt, 0, 0, &x1, &y1, &w, &h);
|
||||
setTextColor(fg);
|
||||
setCursor((swidth - w) / 2, y);
|
||||
fillRect(0, y, swidth, h, bg);
|
||||
print(txt);
|
||||
}
|
||||
|
||||
void DisplayST7735::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
|
||||
setTextColor(fg);
|
||||
setCursor(swidth - w - TFT_FRAMEWDT, y);
|
||||
fillRect(swidth - w - TFT_FRAMEWDT, y, w, h, bg);
|
||||
print(text);
|
||||
}
|
||||
|
||||
void DisplayST7735::displayHeapForDebug() {
|
||||
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT * 2 - 2;
|
||||
setTextSize(1);
|
||||
setTextColor(DARK_GRAY, TFT_BG);
|
||||
setCursor(TFT_FRAMEWDT, vTop);
|
||||
fillRect(TFT_FRAMEWDT, vTop, swidth - TFT_FRAMEWDT / 2, 7, TFT_BG);
|
||||
print(ESP.getFreeHeap());
|
||||
print(" / ");
|
||||
print(ESP.getMaxAllocHeap());
|
||||
|
||||
// audio buffer;
|
||||
fillRect(0, sheight - 2, swidth, 2, TFT_BG);
|
||||
int astored = player.inBufferFilled();
|
||||
int afree = player.inBufferFree();
|
||||
int aprcnt = 100 * astored / (astored + afree);
|
||||
byte sbw = map(aprcnt, 0, 100 , 0, swidth);
|
||||
fillRect(0, sheight - 2, sbw, 2, SILVER);
|
||||
}
|
||||
|
||||
void DisplayST7735::printClock(const char* timestr) {
|
||||
gclock.print(timestr);
|
||||
}
|
||||
|
||||
void DisplayST7735::drawVolumeBar(bool withNumber) {
|
||||
int16_t vTop = sheight - TFT_FRAMEWDT * 2;
|
||||
int16_t vWidth = swidth - TFT_FRAMEWDT - 4;
|
||||
uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth - 2);
|
||||
fillRect(TFT_FRAMEWDT, vTop - 2, vWidth, 6, TFT_BG);
|
||||
drawRect(TFT_FRAMEWDT, vTop - 2, vWidth, 6, TFT_LOGO);
|
||||
fillRect(TFT_FRAMEWDT + 1, vTop - 1, ww, 5, TFT_LOGO);
|
||||
if (withNumber) {
|
||||
setTextSize(1);
|
||||
setTextColor(TFT_FG);
|
||||
setFont(&DS_DIGI28pt7b);
|
||||
char volstr[4];
|
||||
uint16_t wv, hv;
|
||||
int16_t x1, y1;
|
||||
sprintf(volstr, "%d", config.store.volume);
|
||||
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
|
||||
fillRect(TFT_FRAMEWDT, 48, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG);
|
||||
setCursor((swidth - wv) / 2, 48 + hv);
|
||||
print(volstr);
|
||||
setFont();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayST7735::frameTitle(const char* str) {
|
||||
setTextSize(2);
|
||||
centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG);
|
||||
}
|
||||
|
||||
void DisplayST7735::rssi(const char* str) {
|
||||
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
|
||||
setTextSize(1);
|
||||
rightText(str, vTop, SILVER, TFT_BG);
|
||||
}
|
||||
|
||||
void DisplayST7735::ip(const char* str) {
|
||||
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
|
||||
setTextSize(1);
|
||||
setTextColor(SILVER, TFT_BG);
|
||||
setCursor(4, vTop);
|
||||
print(str);
|
||||
}
|
||||
|
||||
void DisplayST7735::set_TextSize(uint8_t s) {
|
||||
setTextSize(s);
|
||||
}
|
||||
|
||||
void DisplayST7735::set_TextColor(uint16_t fg, uint16_t bg) {
|
||||
setTextColor(fg, bg);
|
||||
}
|
||||
|
||||
void DisplayST7735::set_Cursor(int16_t x, int16_t y) {
|
||||
setCursor(x, y);
|
||||
}
|
||||
|
||||
void DisplayST7735::printText(const char* txt) {
|
||||
print(txt);
|
||||
}
|
||||
83
yoRadio/displayST7735.h
Normal file
83
yoRadio/displayST7735.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef displayST7735_h
|
||||
#define displayST7735_h
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_ST7735.h>
|
||||
#include "options.h"
|
||||
#include "fonts/DS_DIGI28pt7b.h"
|
||||
|
||||
#define TFT_ROTATE 3
|
||||
#define TFT_LINEHGHT 10
|
||||
#define TFT_FRAMEWDT 4
|
||||
|
||||
#define PLMITEMS 7
|
||||
#define PLMITEMLENGHT 40
|
||||
#define PLMITEMHEIGHT 22
|
||||
|
||||
class DisplayST7735: public Adafruit_ST7735 {
|
||||
public:
|
||||
DisplayST7735();
|
||||
char plMenu[PLMITEMS][PLMITEMLENGHT];
|
||||
uint16_t clockY;
|
||||
void initD(uint16_t &screenwidth, uint16_t &screenheight);
|
||||
void apScreen();
|
||||
void drawLogo();
|
||||
void clearDsp();
|
||||
void centerText(const char* text, byte y, uint16_t fg, uint16_t bg);
|
||||
void rightText(const char* text, byte y, uint16_t fg, uint16_t bg);
|
||||
void set_TextSize(uint8_t s);
|
||||
void set_TextColor(uint16_t fg, uint16_t bg);
|
||||
void set_Cursor(int16_t x, int16_t y);
|
||||
void printText(const char* txt);
|
||||
void printClock(const char* timestr);
|
||||
void displayHeapForDebug();
|
||||
void drawVolumeBar(bool withNumber);
|
||||
char* utf8Rus(const char* str, bool uppercase);
|
||||
void drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg);
|
||||
void getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth);
|
||||
void clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg);
|
||||
void frameTitle(const char* str);
|
||||
void rssi(const char* str);
|
||||
void ip(const char* str);
|
||||
void drawPlaylist(uint16_t currentItem, char* currentItemText);
|
||||
private:
|
||||
uint16_t swidth, sheight;
|
||||
|
||||
};
|
||||
|
||||
extern DisplayST7735 dsp;
|
||||
|
||||
/*
|
||||
* TFT COLORS
|
||||
*/
|
||||
#define BLACK 0x0000
|
||||
#define BLUE 0x001F
|
||||
#define RED 0xF800
|
||||
#define GREEN 0x07E0
|
||||
#define MAGENTA 0xF81F
|
||||
#define YELLOW 0xFFE0
|
||||
#define WHITE 0xFFFF
|
||||
#define GRAY 0x7BEF
|
||||
#define DARK_GRAY 0x2945
|
||||
#define LIGHT_GRAY 0xC618
|
||||
#define LIME 0x87E0
|
||||
#define AQUA 0x5D1C
|
||||
#define CYAN 0x07FF
|
||||
#define DARK_CYAN 0x03EF
|
||||
#define ORANGE 0xFCA0
|
||||
#define PINK 0xF97F
|
||||
#define BROWN 0x8200
|
||||
#define VIOLET 0x9199
|
||||
#define SILVER 0xA510
|
||||
#define GOLD 0xA508
|
||||
#define NAVY 0x000F
|
||||
#define MAROON 0x7800
|
||||
#define PURPLE 0x780F
|
||||
#define OLIVE 0x7BE0
|
||||
|
||||
#define TFT_BG BLACK
|
||||
#define TFT_FG WHITE
|
||||
#define TFT_LOGO 0xE68B // 224, 209, 92
|
||||
|
||||
#endif
|
||||
109
yoRadio/fonts/DS_DIGI28pt7b.h
Normal file
109
yoRadio/fonts/DS_DIGI28pt7b.h
Normal file
@@ -0,0 +1,109 @@
|
||||
const uint8_t DS_DIGI28pt7bBitmaps[] PROGMEM = {
|
||||
0x13, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x20, 0x27, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xF7, 0x31, 0x00, 0xFF, 0xFF, 0x7F, 0xFF, 0xF5, 0xFF,
|
||||
0xFF, 0x77, 0xFF, 0xF7, 0xDF, 0xFF, 0x7F, 0x00, 0x07, 0xF8, 0x00, 0x3F,
|
||||
0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00,
|
||||
0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFC,
|
||||
0x00, 0x07, 0x40, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x05, 0xC0, 0x00,
|
||||
0x7F, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0,
|
||||
0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07,
|
||||
0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFD, 0xFF, 0xF7, 0xDF, 0xFF, 0xDD, 0xFF,
|
||||
0xFF, 0x5F, 0xFF, 0xFC, 0x00, 0x01, 0x37, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xF7, 0x20, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x31, 0x00, 0x7F,
|
||||
0xFF, 0xF1, 0xFF, 0xFF, 0x47, 0xFF, 0xF6, 0x1F, 0xFF, 0x70, 0x00, 0x07,
|
||||
0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00,
|
||||
0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x3C,
|
||||
0x00, 0x01, 0xE0, 0x00, 0x07, 0x0F, 0xFF, 0x90, 0xFF, 0xFE, 0x17, 0xFF,
|
||||
0xF1, 0xDF, 0xFF, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E,
|
||||
0x00, 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00,
|
||||
0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1D, 0xFF, 0xF0, 0xDF,
|
||||
0xFF, 0xC5, 0xFF, 0xFF, 0x1F, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xE7, 0xFF,
|
||||
0xFD, 0x3F, 0xFF, 0xB1, 0xFF, 0xF7, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00,
|
||||
0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0,
|
||||
0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x07,
|
||||
0x0F, 0xFF, 0x21, 0xFF, 0xF8, 0x1F, 0xFF, 0xA0, 0xFF, 0xF7, 0x00, 0x00,
|
||||
0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00,
|
||||
0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00,
|
||||
0x00, 0xF1, 0xFF, 0xF7, 0x3F, 0xFF, 0xB7, 0xFF, 0xFD, 0xFF, 0xFF, 0xE0,
|
||||
0x00, 0x00, 0x04, 0x00, 0x00, 0x70, 0x00, 0x07, 0xC0, 0x00, 0x7F, 0x00,
|
||||
0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F,
|
||||
0x80, 0x03, 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00,
|
||||
0x3F, 0xC0, 0x01, 0xFC, 0x00, 0x07, 0x4F, 0xFF, 0x90, 0xFF, 0xFE, 0x07,
|
||||
0xFF, 0xF4, 0x1F, 0xFF, 0x70, 0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01,
|
||||
0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00,
|
||||
0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x07,
|
||||
0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x7F, 0xFF, 0xF5, 0xFF, 0xFF, 0x37,
|
||||
0xFF, 0xF1, 0xDF, 0xFF, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00,
|
||||
0x1E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0,
|
||||
0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1C, 0x00, 0x00,
|
||||
0x4F, 0xFF, 0x80, 0xFF, 0xFE, 0x07, 0xFF, 0xF4, 0x1F, 0xFF, 0x70, 0x00,
|
||||
0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78,
|
||||
0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00,
|
||||
0x3C, 0x00, 0x01, 0xE1, 0xFF, 0xF7, 0x1F, 0xFF, 0xD9, 0xFF, 0xFF, 0x5F,
|
||||
0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xF5, 0xFF, 0xFF, 0x37, 0xFF, 0xF1, 0xDF,
|
||||
0xFF, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00,
|
||||
0xF0, 0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x00,
|
||||
0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1C, 0x00, 0x00, 0x4F, 0xFF, 0x80,
|
||||
0xFF, 0xFE, 0x17, 0xFF, 0xF5, 0xDF, 0xFF, 0x7F, 0x00, 0x07, 0xF8, 0x00,
|
||||
0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03, 0xFC,
|
||||
0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01,
|
||||
0xFD, 0xFF, 0xF7, 0xDF, 0xFF, 0xDD, 0xFF, 0xFF, 0x5F, 0xFF, 0xFC, 0x00,
|
||||
0xFF, 0xFF, 0xE7, 0xFF, 0xFD, 0x3F, 0xFF, 0xB1, 0xFF, 0xF7, 0x00, 0x00,
|
||||
0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00,
|
||||
0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00,
|
||||
0x00, 0xF0, 0x00, 0x07, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20,
|
||||
0x00, 0x07, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F,
|
||||
0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00,
|
||||
0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x00, 0x00, 0x30, 0x00,
|
||||
0x01, 0x7F, 0xFF, 0xF5, 0xFF, 0xFF, 0x77, 0xFF, 0xF7, 0xDF, 0xFF, 0x7F,
|
||||
0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00,
|
||||
0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8,
|
||||
0x00, 0x3F, 0xC0, 0x01, 0xFC, 0x00, 0x07, 0x4F, 0xFF, 0x90, 0xFF, 0xFE,
|
||||
0x17, 0xFF, 0xF5, 0xDF, 0xFF, 0x7F, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0,
|
||||
0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1F,
|
||||
0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFD, 0xFF,
|
||||
0xF7, 0xDF, 0xFF, 0xDD, 0xFF, 0xFF, 0x5F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF,
|
||||
0xF5, 0xFF, 0xFF, 0x77, 0xFF, 0xF7, 0xDF, 0xFF, 0x7F, 0x00, 0x07, 0xF8,
|
||||
0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03,
|
||||
0xFC, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0,
|
||||
0x01, 0xFC, 0x00, 0x07, 0x4F, 0xFF, 0x90, 0xFF, 0xFE, 0x07, 0xFF, 0xF4,
|
||||
0x1F, 0xFF, 0x70, 0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00,
|
||||
0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xF0,
|
||||
0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE1, 0xFF, 0xF7, 0x1F, 0xFF,
|
||||
0xD9, 0xFF, 0xFF, 0x5F, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
const GFXglyph DS_DIGI28pt7bGlyphs[] PROGMEM = {
|
||||
{ 0, 0, 0, 12, 0, 1 }, // 0x20 ' '
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x21 '!'
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x22 '"'
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x23 '#'
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x24 '$'
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x25 '%'
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x26 '&'
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x27 '''
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x28 '('
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x29 ')'
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x2A '*'
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x2B '+'
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x2C ','
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x2D '-'
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x2E '.'
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x2F '/'
|
||||
{ 20, 21, 35, 27, 3, -34 }, // 0x30 '0'
|
||||
{ 113, 4, 35, 14, 5, -34 }, // 0x31 '1'
|
||||
{ 131, 21, 35, 27, 3, -34 }, // 0x32 '2'
|
||||
{ 224, 20, 35, 27, 4, -34 }, // 0x33 '3'
|
||||
{ 312, 21, 34, 27, 3, -34 }, // 0x34 '4'
|
||||
{ 402, 21, 35, 27, 3, -34 }, // 0x35 '5'
|
||||
{ 495, 21, 35, 27, 3, -34 }, // 0x36 '6'
|
||||
{ 588, 20, 34, 27, 4, -34 }, // 0x37 '7'
|
||||
{ 673, 21, 35, 27, 3, -34 }, // 0x38 '8'
|
||||
{ 766, 21, 35, 27, 3, -34 }, // 0x39 '9'
|
||||
{ 859, 4, 29, 12, 4, -28 } // 0x3A ':'
|
||||
};
|
||||
|
||||
const GFXfont DS_DIGI28pt7b PROGMEM = {
|
||||
(uint8_t *)DS_DIGI28pt7bBitmaps,
|
||||
(GFXglyph *)DS_DIGI28pt7bGlyphs, 0x20, 0x3A, 55 };
|
||||
157
yoRadio/fonts/bootlogo.h
Normal file
157
yoRadio/fonts/bootlogo.h
Normal file
@@ -0,0 +1,157 @@
|
||||
#ifndef bootlogo_h
|
||||
#define bootlogo_h
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* generated by lcd-image-converter rev.030b30d from 2019-03-17 01:38:34 +0500
|
||||
* image
|
||||
* filename: unsaved
|
||||
* name: bootlogo
|
||||
*
|
||||
* preset name: Color R5G6B5
|
||||
* data block size: 16 bit(s), uint16_t
|
||||
* RLE compression enabled: no
|
||||
* conversion type: Color, not_used not_used
|
||||
* split to rows: yes
|
||||
* bits per pixel: 16
|
||||
*
|
||||
* preprocess:
|
||||
* main scan direction: top_to_bottom
|
||||
* line scan direction: forward
|
||||
* inverse: no
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint16_t bootlogo2[6336] PROGMEM = {
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙░▒▓▓▓▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓▓▓▓▓▓░∙∙∙∙∙∙∙∙∙▓▓▓▓▓▒▒▒▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▓▓▓▓▓▓▓▓▓▓░∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▒▒▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙▒▓▓▓▓▓▓▓▓▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▒▒▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙░▓▓▓▓▓▓▓▓▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▓▒▒▒▓▓▓▓▓▓∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▓▓▒▒▒▓▓▓░∙∙∙∙∙∙∙∙∙▒▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙░∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒▓▓▓▓▓▓▓▓▓▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙░∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙░░░∙∙░▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙∙░∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙░░▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙∙░░∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙▒░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙░░░∙∙∙∙∙░∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙░∙∙∙▓▓░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙∙░░∙∙∙∙∙∙░∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙░∙░█▓▓▒░░▒▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙∙░░∙∙∙∙░░░∙∙∙∙∙░∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙∙∙∙░∙∙█▓▓▓▒░░▒▒▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓∙∙∙∙∙∙∙∙░░∙∙∙∙∙░░∙∙∙∙░░░∙∙∙∙∙∙░∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙∙░∙∙▓▓▓▓▓▒░░▒▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒∙∙∙∙∙∙∙░░∙∙∙∙░░░∙∙∙∙∙░░∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙░░∙▓▓▓▓▓▓░░░▒▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░∙∙∙∙∙∙░∙∙∙∙∙░░∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙░∙░█▓▓▓▓▓░░▒▒▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒░░∙∙∙∙∙░░∙∙∙∙░░∙∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙░░∙▓▓▓▓▓▓▒░░▒▓▓▓▓▓▓▓▒▒▒▓▓▓▒▒▒▒▒░▒▒▒▒░░▒∙∙∙∙░░∙∙∙∙░░░∙∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙░∙░█████▓░░▒▒▓▓▓█░∙∙∙∙∙∙∙▒▓▓▒▒░▒▒▒▒░░░░▒∙∙∙░░∙∙∙∙░░∙∙∙∙∙∙∙∙░░∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙░░∙▓█████▒░▒▒▓▓▓▓∙∙∙∙∙∙∙∙∙∙░▓▒░▒▒▒▒▒░░░░▓░∙░░∙∙∙∙∙░░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙░░∙░█████▓░▒▒▓▓▓█∙∙∙∙∙∙░∙∙∙∙∙░░▒▒▒▒▒░░░░▒▒░∙░░∙∙∙∙∙░░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙░░∙▒█████▒▒▒▒▓▓█░∙∙∙∙∙░∙∙∙∙∙░░▒▒▒▒▓▒░░░░▒▒░░░∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙░░░∙▓████▒▒▓▓▓██▓∙░∙∙∙░░∙∙∙∙∙░░░▒▓▓▓░░░░▒▒▒▒░░∙∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙░░∙∙███▓▒▒▓▓▓██▓▒∙∙∙∙∙░∙∙∙∙∙░░∙∙▓▓▓▒░░░▒▒▒▒▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙░░░∙░▒▒▒▓▓▓▓▓██▓▒░∙∙∙∙░░∙∙∙∙░░░∙∙▓▓▓▒░░░▒▒▒▒▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙░░∙∙▓▓▓▓▓▓▓███▓░▓▒▒▒▒░░░░▒▒░░░░░░▓▓▒▒░▒▒▒▒▒▒▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙░░░∙∙▓▓▓▓▓▓███▓░▒▒▓██▒░▒▒▓▓▓▓░░▒▒▓▓▒▒░░▒▒▒▒▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙░░∙∙∙▓▓▓▓████▓░░▒▓▓█▓░░▒▓▓▓▓▓░░▒▓▓▓▒▒░▒▒▒▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙░░░∙∙∙▓▓▓▓▓▓█▓░░▒▒▓██▒░▒▒▓█▒▓▓░▒▒▓▓▒▓░░▒▒▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙░░∙∙∙∙▓▓▓▓▓▓▓▒░░▒▓▓█▓░░▒▓█▓▓▓▓░▒▓▓▓▓▒░▒▒▒▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙∙░░∙∙∙∙▓▓▓▓▓▓▒░░▒▒▓██▒░░▒▓▓▓▓██▒▒▒▓▓▓▒░▒▒▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙∙░░∙∙∙∙∙▓▓▓▓▓▓▒░░▒▓▓█▓▒░▒▒▓▓▓▓███▓▓▓▓▒░▒▒▓▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙∙∙░░∙∙∙∙∙▓▓▓▓▓▓▒░▒▒▓▓▓▓▒░▒▒▓▓▓██████▓▓▒░▒▒▓▓▓▓▓▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙∙∙░░∙∙∙∙∙∙▓▓▓▓▓▓▒░▒▒▓▓▓▓▓░▒▓▓▓███████▓█▒▒▓▓██████▓█∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙∙∙∙▓▓▓▓▓▓▒░▒▒▓▒▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙∙∙∙▒▓▒▓▓▓▒░▒▒▒▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙∙∙∙∙░▓▒▒▒▓▒▒▒▒▒▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙▓▒▒▒▒▒▒▒▒▒▓▓▓∙∙∙∙∙∙∙∙∙░∙∙∙░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙▓▒▒▒▒▒▒▒▒▓▓▓▓▓∙∙∙∙∙∙∙░∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓∙∙∙∙∙░∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▓▒▒▒▒▒▒▒▒▒▓▓▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙█∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓░░∙∙∙∙░∙∙∙∙░░▒▓███░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙░░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓░▓▓▓▓▒▓▓▓█████████▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙░░░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▓▒▒▓▓▓▓▓▓▓▓▓████∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙░░░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓██∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙░░░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓█∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙░░░░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒▒▒▒▒▒▒▒▒▒▒▒▒░░▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓█░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
// ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░▒▒▒▒▒▒▒▒▒▒▒▒░░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3183, 0xace9, 0xde4a, 0xe68a, 0xde6a, 0xd609, 0x9427, 0x2101, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0861, 0x5a84, 0xb4e7, 0xcd46, 0xc546, 0xc526, 0xac66, 0x5202, 0x0840, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9c68, 0xde4a, 0xde4a, 0xde4a, 0xde4a, 0xde4a, 0xde2a, 0xde2a, 0xde4a, 0x7345, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0840, 0xc568, 0xcd67, 0xc547, 0xc527, 0xbd06, 0xbce6, 0xbce6, 0xbce6, 0xac85, 0x0840, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7346, 0xde29, 0xd5e9, 0xd60a, 0xd62a, 0xde2a, 0xde4a, 0xde4a, 0xde2a, 0xd62a, 0xde6a, 0x4a23, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1081, 0xcdc9, 0xcda8, 0xcd68, 0xc567, 0xc547, 0xc527, 0xc527, 0xbd06, 0xbce6, 0xbce6, 0xb4c6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0861, 0xd5c9, 0xd5c9, 0xd5c9, 0xd5e9, 0xd5ea, 0xd60a, 0xd62a, 0xde2a, 0xde4a, 0xde4a, 0xde2a, 0xd609, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7b86, 0xde09, 0xcda8, 0xcda8, 0xcd88, 0xcd68, 0xc567, 0xc547, 0xc527, 0xc527, 0xbd06, 0xc546, 0x7324, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8387, 0xd5e8, 0xcd88, 0xcda9, 0xd5c9, 0xd5c9, 0xd5e9, 0xd60a, 0xd60a, 0xd62a, 0xde4a, 0xde4a, 0xe68a, 0x3982, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xcda9, 0xd5e9, 0xd5e9, 0xcdc9, 0xcda8, 0xcda8, 0xcd88, 0xc568, 0xc567, 0xc547, 0xc527, 0xbd07, 0xaca5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xac87, 0xcd88, 0xcd68, 0xcd88, 0xcda9, 0xcda9, 0xd5c9, 0xd5c9, 0xd5e9, 0xd60a, 0xd60a, 0xd62a, 0xe68a, 0x8be6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xde2a, 0xd609, 0xd5e9, 0xd5e9, 0xd5c9, 0xcdc9, 0xcda8, 0xcda8, 0xcd88, 0xcd68, 0xc547, 0xc547, 0xcd67, 0x0820, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb4c6, 0xc567, 0xc548, 0xcd68, 0xcd68, 0xcd88, 0xcda9, 0xcda9, 0xd5c9, 0xd5e9, 0xd5e9, 0xd60a, 0xde4a, 0x8c07, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xde4a, 0xd609, 0xd609, 0xd5e9, 0xd5e9, 0xd5e9, 0xd5e9, 0xcdc9, 0xcda8, 0xcda8, 0xcd88, 0xc567, 0xcd88, 0x0820, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xa468, 0xc547, 0xc527, 0xc547, 0xc548, 0xcd68, 0xcd68, 0xcd88, 0xcda9, 0xcda9, 0xd5c9, 0xd5e9, 0xde4a, 0x83a6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xde4a, 0xd62a, 0xd62a, 0xd609, 0xd609, 0xd5e9, 0xd5e9, 0xd5e9, 0xd5c9, 0xcdc8, 0xcda8, 0xcd88, 0xcda8, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6b06, 0xcd67, 0xbd07, 0xbd07, 0xc527, 0xc547, 0xc548, 0xcd68, 0xcd88, 0xcd88, 0xcda9, 0xcda9, 0xde49, 0x18a0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc5aa, 0xde4a, 0xd62a, 0xd62a, 0xd60a, 0xd609, 0xd609, 0xd5e9, 0xd5e9, 0xd5e9, 0xd5c9, 0xcdc8, 0xace7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xcd88, 0xbce6, 0xbce6, 0xbd07, 0xbd27, 0xc527, 0xc547, 0xc548, 0xcd68, 0xcd88, 0xcda8, 0xbd48, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7346, 0xe6ab, 0xde4a, 0xde2a, 0xd62a, 0xd62a, 0xd60a, 0xd609, 0xd609, 0xd5e9, 0xd5e9, 0xde49, 0x62e4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4a03, 0xcd86, 0xb4c6, 0xb4c6, 0xbce7, 0xbd07, 0xbd27, 0xc527, 0xc548, 0xcd68, 0xe629, 0x3162, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xd60c, 0xe68a, 0xde4a, 0xde4a, 0xde4a, 0xd62a, 0xd62a, 0xd60a, 0xd609, 0xde4a, 0xb508, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5a85, 0xcd67, 0xc526, 0xbce6, 0xb4c6, 0xbce7, 0xc527, 0xcda7, 0xc588, 0x41c3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xace9, 0xe68b, 0xde8a, 0xde4a, 0xde4a, 0xde4a, 0xde6a, 0xe68a, 0x9c67, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9407, 0xcd87, 0xd5a7, 0xd5c7, 0xcda8, 0x7325, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2943, 0xcdca, 0xe6cb, 0xe6cb, 0xeecb, 0xc5aa, 0x2922, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x2000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1800, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2800, 0x8000, 0x8800, 0x9800, 0x7800, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2800, 0x8000, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7000, 0xb000, 0x2000, 0x0000, 0x1800, 0xe000, 0x1000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x2800, 0x3800, 0x4800, 0x4800, 0x4800, 0x8000, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x2963, 0x5264, 0x8c06, 0xcdc9, 0xcdc8, 0xcda7, 0xcd87, 0xcda7, 0xd5c8, 0xd5e8, 0xde09, 0xcdca, 0x8be6, 0x5aa4, 0x2963, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0xa800, 0xd000, 0x1800, 0x0000, 0x0000, 0x1000, 0xe800, 0x1800, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x5800, 0xa000, 0xa800, 0x8000, 0x6000, 0x5800, 0x8800, 0xd000, 0xe000, 0xc000, 0x3000, 0x0000, 0x4a25, 0xde2a, 0xde07, 0xc546, 0xb4c5, 0xaca5, 0xac85, 0xac85, 0xac85, 0xac65, 0xac85, 0xaca5, 0xb4c6, 0xb4c6, 0xbd07, 0xcd87, 0xe648, 0xde4a, 0x4204, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x4000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0xc000, 0xe800, 0x2000, 0x0000, 0x0000, 0x0000, 0x2800, 0xd000, 0x0800, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x5800, 0x7800, 0x7000, 0x4000, 0x1000, 0x0000, 0x0000, 0x0000, 0x2000, 0x9800, 0x1000, 0x2800, 0xb000, 0xe800, 0xd365, 0xcda7, 0xbd06, 0xb4c6, 0xb4a6, 0xb4a5, 0xb4a5, 0xb4a5, 0xac85, 0xac85, 0xac85, 0xac65, 0xac65, 0xac85, 0xaca5, 0xb4a6, 0xb4c6, 0xb4e6, 0xc547, 0xd5e8, 0xc5a9, 0x5a85, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xa800, 0xe800, 0x6000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0xf800, 0x5800, 0x0000, 0x0000, 0x0000, 0x0000, 0x6000, 0x8000, 0x0000, 0x0000, 0x5000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x0000, 0x0000, 0x0800, 0x5800, 0x8800, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0xc000, 0x1800, 0x0000, 0x0020, 0xb4a9, 0xf8a0, 0xf040, 0xbb84, 0xb4a6, 0xbcc6, 0xbcc6, 0xb4c6, 0xb4c6, 0xb4a6, 0xb4a5, 0xb4a5, 0xac85, 0xac85, 0xac85, 0xac65, 0xac65, 0xac85, 0xaca5, 0xb4a6, 0xb4c6, 0xbce6, 0xbd07, 0xde49, 0x9c88, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6800, 0xf800, 0xd800, 0x1000, 0x0000, 0x0000, 0x0000, 0x9800, 0xf800, 0xb800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xa000, 0x2000, 0x0000, 0x4800, 0x2000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x8000, 0x8800, 0x7800, 0x7800, 0x8800, 0xa000, 0x7000, 0x2000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xa800, 0x4800, 0x0000, 0x3184, 0xde4b, 0xd5c8, 0xd962, 0xf800, 0xd1c2, 0xa486, 0xbce6, 0xbce6, 0xbce6, 0xbcc6, 0xb4c6, 0xb4c6, 0xb4a5, 0xb4a5, 0xb4a5, 0xac85, 0xac85, 0xac85, 0xac65, 0xac85, 0xac85, 0xb4a6, 0xb4c6, 0xb4c6, 0xbce6, 0xcda8, 0xcdc9, 0x0860, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1800, 0x8800, 0x5800, 0x0000, 0x0000, 0x0000, 0x1000, 0xe800, 0xf800, 0x5800, 0x0000, 0x0000, 0x0000, 0x5800, 0xf800, 0xf000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2800, 0x9000, 0x0000, 0x5000, 0x3800, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x4000, 0x9000, 0x6800, 0x1000, 0x0000, 0x0000, 0x3000, 0x8000, 0x5800, 0xa800, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9000, 0xb000, 0x0000, 0x39c4, 0xef0c, 0xd609, 0xcda8, 0xd243, 0xf800, 0xe8a1, 0xa405, 0xbce7, 0xbd27, 0xbd06, 0xbce6, 0xbcc6, 0xbcc6, 0xb4c6, 0xb4c6, 0xb4a5, 0xb4a5, 0xaca5, 0xac85, 0xac85, 0xac85, 0xac65, 0xac85, 0xac85, 0xb4a6, 0xb4c6, 0xb4c6, 0xc547, 0xe68b, 0x0860, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2800, 0xf800, 0xe800, 0x1000, 0x0000, 0x0000, 0x0000, 0x7800, 0xf800, 0xb000, 0x0000, 0x0000, 0x0000, 0x2800, 0xf000, 0xf800, 0xa000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xa800, 0x3000, 0x5800, 0x4800, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2800, 0x9000, 0x3800, 0x0800, 0x0000, 0x0000, 0x0000, 0x7000, 0x4000, 0x0800, 0x0000, 0xa800, 0x7000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0xe800, 0x2000, 0x3184, 0xef0c, 0xde2a, 0xd609, 0xd5e9, 0xda63, 0xf800, 0xf060, 0x9bc5, 0xbd07, 0xc547, 0xc527, 0xc507, 0xbd06, 0xbce6, 0xbcc6, 0xbcc6, 0xb4c6, 0xb4c6, 0xb4a5, 0xb4a5, 0xaca5, 0xac85, 0xac85, 0xac65, 0xac65, 0xac85, 0xac85, 0xb4a6, 0xb4c6, 0xc547, 0xc5a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc000, 0xf800, 0x8000, 0x0000, 0x0000, 0x0000, 0x1800, 0xf000, 0xf800, 0x3800, 0x0000, 0x0000, 0x1000, 0xa800, 0xf800, 0xf800, 0x4800, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0xc800, 0x8000, 0x2800, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4800, 0x7000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1800, 0x7000, 0x2000, 0x0000, 0x0000, 0x0000, 0x8800, 0xb800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1800, 0xe800, 0x7800, 0x0881, 0xdeac, 0xde6b, 0xde4a, 0xd62a, 0xd609, 0xd9e3, 0xf800, 0xf080, 0x9bc6, 0xbd27, 0xc568, 0xc567, 0xc547, 0xc527, 0xbd06, 0xbd06, 0xbce6, 0xbcc6, 0xbcc6, 0xb4c6, 0xb4a6, 0xb4a5, 0xb4a5, 0xac85, 0xac85, 0xac85, 0xac85, 0xac65, 0xac85, 0xac85, 0xb4a6, 0xcd87, 0x8c07, 0x2000, 0x7800, 0x0800, 0x0000, 0x0000, 0x0000, 0x7000, 0xd000, 0xe800, 0x2000, 0x0000, 0x0000, 0x0000, 0xb800, 0xf800, 0x9800, 0x0000, 0x0000, 0x0000, 0x6000, 0x7000, 0xf800, 0xd800, 0x0800, 0x0000, 0x0000, 0x0000, 0x0800, 0x9000, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6800, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, 0x2800, 0x7000, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x7000, 0xd800, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0xe800, 0x1000, 0xb58b, 0xe68b, 0xde6b, 0xde6b, 0xde4a, 0xd60a, 0xe921, 0xf800, 0xe142, 0x9426, 0xbd68, 0xcda8, 0xcd88, 0xc568, 0xc547, 0xc547, 0xc527, 0xbd07, 0xbce6, 0xbce6, 0xbcc6, 0xbcc6, 0xb4c6, 0xb4a6, 0xb4a5, 0xb4a5, 0xac85, 0xac85, 0xac85, 0xac85, 0xac65, 0xac85, 0xac85, 0xeaa3, 0xf800, 0x6000, 0x0000, 0x0000, 0x0000, 0x4000, 0x8000, 0xf800, 0x7000, 0x0000, 0x0000, 0x0000, 0x4800, 0xf800, 0xe000, 0x2000, 0x0000, 0x0000, 0x4800, 0x2800, 0x8800, 0xf800, 0x9000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9000, 0x1800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6800, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, 0x3800, 0x5800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7800, 0xe000, 0x1000, 0x0000, 0x0000, 0x0000, 0x5000, 0xf800, 0x8000, 0x5ac6, 0xeeec, 0xe68b, 0xe6ab, 0xe68b, 0xde8b, 0xddc9, 0xf040, 0xf800, 0xba84, 0x9467, 0xcda9, 0xd5e9, 0xcdc9, 0xcda8, 0xcda8, 0xd5c8, 0xd5c8, 0xd5c8, 0xcd87, 0xc547, 0xbce6, 0xbcc6, 0xbcc6, 0xb4c6, 0xb4c6, 0xb4a5, 0xb4a5, 0xb465, 0xcaa3, 0xd262, 0xc242, 0xb3a4, 0xb364, 0xf800, 0xf0a1, 0x1000, 0x0000, 0x0000, 0x3000, 0x6800, 0xf000, 0xd000, 0x0800, 0x0000, 0x0000, 0x1000, 0xd800, 0xf800, 0x8000, 0x0000, 0x0000, 0x1000, 0x4800, 0x0000, 0x8800, 0xf800, 0x5000, 0x0000, 0x0000, 0x0000, 0x7000, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6800, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x5800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9000, 0xd800, 0x0800, 0x0000, 0x0000, 0x1800, 0xe800, 0xe000, 0x2040, 0xce4c, 0xe6ac, 0xe6cc, 0xe6cc, 0xe6ab, 0xe6cc, 0xd3a6, 0xf800, 0xf080, 0x8ba6, 0xb508, 0xd609, 0xd609, 0xd609, 0xe6aa, 0xeecb, 0xcdeb, 0x9469, 0x7367, 0xace9, 0xd60b, 0xe669, 0xcd87, 0xbce6, 0xbce6, 0xbcc6, 0xb4c6, 0xbbe4, 0xe8e1, 0xc303, 0x9be4, 0x9b44, 0xbaa3, 0xc961, 0xf800, 0xd282, 0x10a1, 0x0000, 0x0800, 0x5000, 0xa000, 0xf800, 0x6800, 0x0000, 0x0000, 0x0000, 0xa000, 0xf800, 0xe000, 0x1800, 0x0000, 0x0000, 0x7800, 0x0800, 0x0000, 0x6800, 0xf800, 0x4800, 0x0000, 0x0800, 0x8800, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x5000, 0x4800, 0x0000, 0x0000, 0x0000, 0x0000, 0x3800, 0x5800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb800, 0xc000, 0x0000, 0x0000, 0x0000, 0x9000, 0xf800, 0x7800, 0x5ae6, 0xe6cc, 0xeeed, 0xef0c, 0xeeec, 0xeecc, 0xde6b, 0xf081, 0xf800, 0xc2c4, 0x9c67, 0xcdc9, 0xde4a, 0xe68b, 0xeeec, 0x5264, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x20e2, 0x7b86, 0xde29, 0xc506, 0xbd06, 0xd283, 0xf080, 0xb344, 0x93c5, 0x9c05, 0xaba4, 0xc1e2, 0xf800, 0xf040, 0x8b03, 0xa4a6, 0x0000, 0x6000, 0x6000, 0xf800, 0xc800, 0x1000, 0x0000, 0x0000, 0x4000, 0xe800, 0xf800, 0x7000, 0x0000, 0x0000, 0x5800, 0x1800, 0x0000, 0x0000, 0x1800, 0xe000, 0xa800, 0x7800, 0x7800, 0x2000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x1000, 0x6000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2800, 0x6800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0xe800, 0x8800, 0x0000, 0x0000, 0x2800, 0xf800, 0xf000, 0x2000, 0xdeac, 0xef0d, 0xf72d, 0xef2d, 0xef0d, 0xef0c, 0xe3a6, 0xf800, 0xe1e3, 0x9427, 0xbd69, 0xde4b, 0xe6ab, 0xd62b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x62e6, 0xe628, 0xda02, 0xf820, 0xb344, 0x8ba4, 0xa425, 0xb4a6, 0xbac3, 0xf020, 0xf800, 0xb9c2, 0x7303, 0xc566, 0x78e1, 0x3800, 0xd000, 0xf800, 0x4800, 0x0000, 0x0000, 0x1800, 0x9000, 0xd800, 0xd800, 0x1800, 0x0000, 0x3800, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, 0x2800, 0x6800, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x8000, 0x0800, 0x0000, 0x0000, 0x0000, 0x1000, 0x7000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4800, 0xf800, 0x4000, 0x0000, 0x0000, 0xc000, 0xf800, 0x9000, 0x5aa5, 0xef2d, 0xf72d, 0xf74e, 0xf74d, 0xf74d, 0xe62b, 0xf040, 0xf142, 0xa427, 0xad29, 0xde4b, 0xe6ac, 0xf72d, 0x0860, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x9800, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0xc122, 0xf800, 0xc263, 0x8b85, 0xa445, 0xb4c6, 0xb4c6, 0xe0a0, 0xf800, 0xe860, 0x7aa3, 0x8ba4, 0xbce5, 0x7921, 0x3800, 0xf800, 0xc000, 0x0800, 0x0000, 0x0000, 0x7800, 0x2000, 0xe800, 0x9000, 0x0000, 0x3800, 0x4800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x4800, 0x2000, 0x0000, 0x0000, 0x0000, 0x0800, 0x8000, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc000, 0xd000, 0x0800, 0x0000, 0x4000, 0xf800, 0xe800, 0x2000, 0x9cc9, 0xeeed, 0xf72d, 0xf74d, 0xf76e, 0xf6cc, 0xf102, 0xf162, 0xbca8, 0xad29, 0xd64b, 0xe6cc, 0xef2d, 0x4204, 0x0000, 0x1800, 0x0000, 0x0000, 0x8800, 0xf800, 0x5000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9800, 0xf800, 0xf242, 0x8bc5, 0x9c46, 0xbce7, 0xbd07, 0xc344, 0xf800, 0xf800, 0xb982, 0x7b24, 0xa445, 0xc2c3, 0x7b24, 0xa000, 0xf800, 0x4800, 0x0000, 0x0000, 0x7000, 0x3000, 0x1000, 0xe000, 0x6000, 0x4800, 0x6000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x7000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5800, 0x2000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0xf800, 0x7800, 0x0000, 0x0000, 0xc000, 0xf800, 0xa000, 0x0000, 0xe6ad, 0xef0d, 0xf72d, 0xf74d, 0xeeed, 0xf183, 0xf284, 0xbd29, 0xb56a, 0xd66c, 0xef0d, 0xef0d, 0xde8c, 0x5820, 0x9000, 0x7000, 0x6000, 0x2000, 0xf000, 0xc800, 0x0800, 0x0000, 0x0000, 0x0000, 0x7000, 0xf800, 0xe000, 0x8ae5, 0xacc6, 0xbd07, 0xc547, 0xc527, 0xe8e1, 0xf800, 0xf040, 0x8ac3, 0x93e5, 0xbb44, 0xbbe5, 0xaca6, 0xd800, 0xb800, 0x0000, 0x0000, 0x5800, 0x3800, 0x0000, 0x0000, 0x7000, 0xb800, 0x4800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x7000, 0x2800, 0x0000, 0x0000, 0x0000, 0x3000, 0x6800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9800, 0xe800, 0x2000, 0x0000, 0x5000, 0xf800, 0xf800, 0x3800, 0x28c2, 0xef0d, 0xef0d, 0xeeed, 0xf509, 0xf203, 0xebe7, 0xc5aa, 0xbdaa, 0xdeac, 0xef2d, 0xf74d, 0xee2b, 0xe263, 0x5800, 0x0000, 0x0000, 0x7000, 0x8800, 0xf800, 0x5800, 0x0000, 0x0000, 0x0000, 0x4800, 0xf800, 0xf800, 0x8000, 0x1081, 0xd5e8, 0xcd88, 0xcda8, 0xcb85, 0xf040, 0xf800, 0xb9e2, 0x7b64, 0xb3e5, 0xcb04, 0xac65, 0xbcc6, 0xe000, 0x6800, 0x0000, 0x5000, 0x5800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x1000, 0x6800, 0x0000, 0x0000, 0x0000, 0x0000, 0x7800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2800, 0xf800, 0x7800, 0x0000, 0x0800, 0xc800, 0xf800, 0xb000, 0x0000, 0xb9e4, 0xf345, 0xf366, 0xf224, 0xec07, 0xddeb, 0xc5aa, 0xce0b, 0xe6cc, 0xef2d, 0xf76e, 0xed6a, 0xf8e1, 0x8a44, 0x0000, 0x0000, 0x0000, 0x9000, 0xf000, 0xb800, 0x0800, 0x0000, 0x0000, 0x0800, 0xd800, 0xf800, 0xe000, 0x1000, 0x0000, 0xde4b, 0xcdc9, 0xcd07, 0xda03, 0xf040, 0xf060, 0x8304, 0x9be5, 0xd263, 0xb4a6, 0xac66, 0xcd66, 0xc000, 0x8800, 0x8000, 0x4800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x3000, 0x2000, 0x0000, 0x0000, 0x0000, 0x4000, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc800, 0xd000, 0x0800, 0x0000, 0x3000, 0xf800, 0xf000, 0x4000, 0x0000, 0xcdcb, 0xde0b, 0xd58a, 0xd60b, 0xce2b, 0xce0b, 0xde6c, 0xeeec, 0xef0d, 0xf72d, 0xec88, 0xf840, 0xf4e8, 0x83e7, 0x7ba7, 0x83e7, 0x8b66, 0xf000, 0xf820, 0x9a63, 0x6b25, 0x7ba6, 0x7ba6, 0xb9c2, 0xf0a1, 0xf800, 0xb9c2, 0x5ae4, 0x7345, 0xe6ab, 0xd60a, 0xdb45, 0xd223, 0xf800, 0xc1e3, 0x93c5, 0xcae4, 0xbca6, 0xaca6, 0xb4e6, 0xd5a7, 0x48e1, 0x6800, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x4000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5800, 0xf800, 0x4800, 0x0000, 0x0000, 0xa800, 0xf800, 0xc000, 0x0000, 0x0000, 0xe6ac, 0xe68c, 0xde6b, 0xde6c, 0xe68c, 0xe6cc, 0xeeed, 0xef0d, 0xef2d, 0xed09, 0xf800, 0xea24, 0xad09, 0xce0b, 0xef0d, 0xf76e, 0xe326, 0xf800, 0xe922, 0x9c68, 0xc5ea, 0xe6cc, 0xe4a8, 0xdd89, 0xe860, 0xf840, 0xa365, 0xad08, 0xd62a, 0xde4a, 0xd3e6, 0xd3c6, 0xf020, 0xf080, 0x9b65, 0xc263, 0xcc86, 0xb4e7, 0xbd07, 0xc547, 0xd5a7, 0x7324, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2000, 0xf000, 0x8000, 0x0000, 0x0000, 0x3000, 0xf800, 0xf800, 0x5000, 0x0000, 0x0000, 0xeecc, 0xe68b, 0xe68c, 0xe6ac, 0xeecc, 0xeeec, 0xeeec, 0xef0d, 0xed09, 0xf820, 0xf8e1, 0xb488, 0xbd8a, 0xe6cc, 0xf72d, 0xee4c, 0xf040, 0xf800, 0xc386, 0xad49, 0xdeac, 0xe5ea, 0xec68, 0xcd69, 0xf800, 0xe8e1, 0x9c47, 0xc5ca, 0xde8b, 0xdcc8, 0xe427, 0xd305, 0xf800, 0xc264, 0xc263, 0xcc67, 0xbd48, 0xbd48, 0xc588, 0xcd88, 0xd5c8, 0x8bc6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x6800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0xd000, 0xb000, 0x0800, 0x0000, 0x0000, 0x9800, 0xf800, 0xc000, 0x0800, 0x0000, 0x10a1, 0xeecb, 0xde4b, 0xe66b, 0xe68b, 0xe6ac, 0xeecc, 0xeecc, 0xe62b, 0xf0c1, 0xf800, 0xcb05, 0xa4c9, 0xd64c, 0xef0d, 0xf72d, 0xeb46, 0xf800, 0xe983, 0x9ca8, 0xce2b, 0xeecc, 0xeb86, 0xde8c, 0xd58a, 0xf000, 0xda03, 0xa4e8, 0xd64b, 0xe549, 0xe386, 0xcdca, 0xe901, 0xf840, 0xd1e3, 0xc3e6, 0xbd48, 0xc589, 0xcdc9, 0xcdc9, 0xcda8, 0xd5e8, 0x9406, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x6000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xa800, 0xc000, 0x1800, 0x0000, 0x0000, 0x1000, 0xe800, 0xf800, 0x5000, 0x0000, 0x0000, 0x18c2, 0xe68a, 0xde0a, 0xde2a, 0xde4b, 0xe66b, 0xe68b, 0xe66b, 0xe962, 0xf800, 0xf0c1, 0x9c27, 0xc5aa, 0xe6cc, 0xef0d, 0xe60b, 0xf020, 0xf800, 0xbb46, 0xad49, 0xe70d, 0xec88, 0xee4c, 0xde8c, 0xe68c, 0xe8a1, 0xe224, 0xc58a, 0xe427, 0xec07, 0xd60a, 0xcc68, 0xf800, 0xe922, 0xa3a6, 0xad08, 0xc5a9, 0xd60a, 0xd60a, 0xd609, 0xd5e9, 0xde29, 0x9427, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x5800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9800, 0xc800, 0x1800, 0x0000, 0x0000, 0x0000, 0x8000, 0xf800, 0xc000, 0x0800, 0x0000, 0x0000, 0x18e2, 0xe66a, 0xd5ea, 0xd60a, 0xde2a, 0xde2a, 0xde6b, 0xe3a6, 0xf800, 0xf800, 0xcac5, 0xa4c9, 0xde4b, 0xeeec, 0xeecc, 0xe9a3, 0xf800, 0xf0e1, 0xa448, 0xce0b, 0xe4e9, 0xed29, 0xde8c, 0xe6cd, 0xef2d, 0xe3a7, 0xe902, 0xea44, 0xe447, 0xd62b, 0xce0a, 0xe1a3, 0xf820, 0xb3c6, 0x9ca8, 0xcdca, 0xde4a, 0xde4a, 0xde4a, 0xd62a, 0xd60a, 0xde4a, 0x9427, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x6000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xa000, 0xa800, 0x1000, 0x0000, 0x0000, 0x0000, 0x1800, 0xf000, 0xf800, 0x3800, 0x0000, 0x0000, 0x0000, 0x18c2, 0xe669, 0xcda9, 0xcdc9, 0xd5ea, 0xd60a, 0xd60a, 0xda23, 0xf800, 0xf0a1, 0x9bc6, 0xbd69, 0xe68c, 0xeeed, 0xe488, 0xdaa5, 0xf800, 0xcae5, 0xa509, 0xe5aa, 0xed09, 0xde8c, 0xdeac, 0xef2d, 0xf74d, 0xeeed, 0xcdcb, 0xbd6a, 0xcdeb, 0xde6b, 0xe325, 0xf800, 0xe1a3, 0x9468, 0xc5ca, 0xde6b, 0xe68b, 0xde6b, 0xde6a, 0xde4a, 0xde4a, 0xe68a, 0x8c06, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x7000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2000, 0xa800, 0x8000, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x7800, 0xf800, 0x9000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0861, 0xde29, 0xc588, 0xcd89, 0xcda9, 0xd5c9, 0xd60a, 0xda23, 0xf800, 0xd1a3, 0x9427, 0xcdca, 0xe68b, 0xe509, 0xe468, 0xcb46, 0xf840, 0xac27, 0xcc68, 0xec07, 0xde6c, 0xde6c, 0xeeed, 0xf72d, 0xf72d, 0xf72d, 0xef0d, 0xe6ed, 0xe68c, 0xec68, 0xe326, 0xf800, 0xbb66, 0xad09, 0xde6b, 0xe6ac, 0xe6ab, 0xe68b, 0xe68b, 0xde6b, 0xde6a, 0xe6ab, 0x7345, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x6000, 0x4000, 0x0000, 0x0000, 0x0800, 0x7000, 0xb000, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0xe800, 0xe800, 0x2000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xde09, 0xc548, 0xc568, 0xc588, 0xcda9, 0xcdc9, 0xd9c3, 0xf800, 0xb284, 0xa4a8, 0xd5ea, 0xddaa, 0xe407, 0xde6b, 0xd529, 0xf0c1, 0xea64, 0xec27, 0xe68c, 0xde8c, 0xeeed, 0xf72d, 0xf74d, 0xf74e, 0xf74e, 0xf74e, 0xf72d, 0xf447, 0xeeed, 0xe962, 0xf1c3, 0xb529, 0xd64b, 0xef0d, 0xef0d, 0xef0c, 0xeeec, 0xeeec, 0xeecc, 0xe6cb, 0xef0c, 0x20e1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4800, 0x6000, 0x6800, 0x6800, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9800, 0xf800, 0x5800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xd5e9, 0xc527, 0xbd27, 0xc548, 0xc568, 0xc588, 0xda03, 0xf800, 0xa305, 0xace8, 0xd508, 0xdb86, 0xe66b, 0x62e5, 0x4a24, 0x5162, 0x5142, 0x4a04, 0x4a24, 0x4a44, 0x5265, 0x5265, 0x5265, 0x5265, 0x5265, 0x6265, 0x8962, 0x5224, 0x7a04, 0xf000, 0x6962, 0x39e3, 0x4a44, 0x5265, 0x5265, 0x5265, 0x5265, 0x5265, 0x5265, 0x5265, 0x5265, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2800, 0xf800, 0xa000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xacea, 0xbd26, 0xbce7, 0xbd07, 0xbd27, 0xc548, 0xcb04, 0xf800, 0xbaa4, 0xcbe6, 0xdb05, 0xc589, 0xd60a, 0x7366, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1800, 0x7800, 0x0800, 0x0000, 0xb000, 0xa800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0xd000, 0x1800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5aa6, 0xc526, 0xb4c6, 0xb4e6, 0xbce7, 0xbd07, 0xbca7, 0xca23, 0xd1a2, 0xcb45, 0xb4c8, 0xbd28, 0xcdc9, 0xb528, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1800, 0x7000, 0x0000, 0x0000, 0x2800, 0xe800, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5800, 0xf000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc567, 0xac85, 0xaca5, 0xb4c6, 0xb4e6, 0xb4e7, 0xacc7, 0x9446, 0x9c66, 0xacc7, 0xc548, 0xcd89, 0xe68b, 0x2101, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1800, 0x9000, 0x0800, 0x0000, 0x0000, 0x9800, 0x7800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1800, 0xe000, 0x5000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc567, 0xa444, 0xa465, 0xac85, 0xaca6, 0xb4c6, 0xb4c6, 0xb4c6, 0xb4e7, 0xbd27, 0xc548, 0xc568, 0xcdc9, 0xe68b, 0x0840, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x9000, 0x1000, 0x0000, 0x0000, 0x3000, 0xd000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0x7000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8385, 0xac84, 0xa424, 0xa445, 0xa465, 0xac85, 0xaca6, 0xb4c6, 0xb4e6, 0xbd07, 0xbd27, 0xc548, 0xc548, 0xd5e9, 0xc5a9, 0x18c1, 0x0000, 0x0000, 0x0000, 0x0800, 0xa800, 0x3000, 0x0000, 0x0000, 0x0000, 0xb000, 0x4800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9000, 0x8000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4a25, 0xc524, 0x9c04, 0xa424, 0xa444, 0xa445, 0xac65, 0xac85, 0xaca6, 0xb4c6, 0xb4e6, 0xbd07, 0xbd27, 0xc548, 0xcda8, 0xe6aa, 0x5a85, 0x0000, 0x0000, 0x7800, 0x4800, 0x0000, 0x0000, 0x0000, 0x3800, 0x8000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x31a4, 0xe6ce, 0x10a1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6800, 0x7000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc569, 0x9c04, 0x9be4, 0x9c04, 0xa424, 0xa444, 0xa465, 0xac65, 0xac85, 0xaca6, 0xb4c6, 0xb4e7, 0xbd07, 0xbd27, 0xc568, 0xde09, 0xcdea, 0x8a23, 0xc8a1, 0x0800, 0x0000, 0x0000, 0x0800, 0xa800, 0x0800, 0x0000, 0x0000, 0x0020, 0x41e4, 0x6b26, 0x8408, 0xbd8b, 0xf74e, 0xf74e, 0xf76e, 0x62e5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x5800, 0xe800, 0xc800, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6000, 0x6800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2102, 0xb4a4, 0x93c3, 0x93e4, 0x9be4, 0x9c04, 0xa424, 0xa444, 0xa465, 0xac85, 0xac85, 0xb4a6, 0xb4c6, 0xb4e7, 0xbd07, 0xbd27, 0xc507, 0xe8e1, 0xdcc7, 0xcdc9, 0xe66a, 0xe68b, 0xeb25, 0xd509, 0xcdca, 0xe68b, 0xef0d, 0xf72d, 0xf74d, 0xf72d, 0xef0d, 0xeeec, 0xeeec, 0xeeed, 0xf72d, 0xbd8b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x2000, 0xf000, 0xf800, 0xf800, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x7800, 0x4000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xa466, 0x9c03, 0x93a3, 0x93c3, 0x9be4, 0x9c04, 0x9c04, 0xa424, 0xa445, 0xa465, 0xac85, 0xac86, 0xb4a6, 0xb4e6, 0xbd07, 0xbbe5, 0xe901, 0x9c26, 0xaca7, 0xc5a9, 0xd3a5, 0xdbc6, 0xb528, 0xc589, 0xd60a, 0xde4a, 0xde4b, 0xde6b, 0xe68b, 0xe6ac, 0xeecc, 0xeecc, 0xeeec, 0xef0d, 0xef2e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x3800, 0xf800, 0xf800, 0xd800, 0x0800, 0x0000, 0x0000, 0x0000, 0x2800, 0x7800, 0x1800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x18e2, 0xbce5, 0x93a3, 0x93a3, 0x93a3, 0x93c3, 0x9be4, 0x9c04, 0x9c24, 0xa424, 0xa445, 0xa465, 0xac85, 0xaca5, 0xb4c6, 0xbac3, 0xe162, 0x93e5, 0xb4c7, 0xcbe6, 0xdae4, 0xb4e8, 0xb4e8, 0xcd89, 0xd5e9, 0xd60a, 0xde2a, 0xde2a, 0xde4b, 0xe66b, 0xe68b, 0xe6ac, 0xeecc, 0xeecc, 0xf76e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x2800, 0xf000, 0xf800, 0xe000, 0x3000, 0x1800, 0x5800, 0x8000, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x39a3, 0xb463, 0x8b63, 0x8b83, 0x93a3, 0x93a3, 0x93c3, 0x9be4, 0x9c04, 0x9c24, 0xa424, 0xa445, 0xa465, 0xaca5, 0xbaa3, 0xe8c1, 0xa364, 0xca83, 0xcb44, 0xaca7, 0xacc7, 0xbd48, 0xcd89, 0xcda9, 0xcdc9, 0xd5ea, 0xd60a, 0xde2a, 0xde2b, 0xde4b, 0xe66b, 0xe68b, 0xe6ac, 0xf74e, 0x2962, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x6800, 0xd000, 0xe000, 0xc800, 0x9800, 0x4800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7305, 0xac23, 0x8b62, 0x8b63, 0x8b83, 0x93a3, 0x93c3, 0x93c3, 0x9be4, 0x9c04, 0x9c24, 0xa444, 0xa445, 0xa3e4, 0xc981, 0xd161, 0xb344, 0xa466, 0xa466, 0xb4e7, 0xbd27, 0xc568, 0xc568, 0xcd89, 0xcda9, 0xd5c9, 0xd5ea, 0xd60a, 0xde2a, 0xde4b, 0xde4b, 0xe66b, 0xef0c, 0x5284, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0800, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5a64, 0xac23, 0x8b42, 0x8b42, 0x8b83, 0x8b83, 0x93a3, 0x93c3, 0x93c4, 0x9be4, 0x9c04, 0xa424, 0xa424, 0x93e4, 0x8ba4, 0x93e5, 0xa445, 0xaca6, 0xb4e6, 0xbd07, 0xbd27, 0xc548, 0xc568, 0xc588, 0xcda9, 0xcda9, 0xd5c9, 0xd5ea, 0xd60a, 0xde2a, 0xde4b, 0xe6ab, 0x7366, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4a24, 0xac44, 0x9382, 0x8342, 0x8b62, 0x8b83, 0x8b83, 0x93a3, 0x93c3, 0x9be4, 0x9be4, 0x9c04, 0x9c04, 0x9c04, 0xa425, 0xa465, 0xac85, 0xaca6, 0xb4c6, 0xb4e7, 0xbd07, 0xbd27, 0xc548, 0xc568, 0xcd88, 0xcda9, 0xcda9, 0xd5c9, 0xd5ea, 0xd60a, 0xde4b, 0x9447, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0841, 0xa466, 0x9be2, 0x8b42, 0x8342, 0x8b62, 0x8b83, 0x9383, 0x93a3, 0x93c3, 0x9be4, 0x9be4, 0x9c04, 0xa424, 0xa444, 0xa465, 0xac85, 0xac85, 0xb4a6, 0xb4c6, 0xb4e7, 0xbd07, 0xbd27, 0xc548, 0xc568, 0xcd88, 0xcda9, 0xcdc9, 0xd5e9, 0xd60a, 0xacc8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3982, 0x93c5, 0x9be3, 0x9382, 0x8342, 0x8b63, 0x8b83, 0x9383, 0x93a3, 0x93c3, 0x9be4, 0x9c04, 0x9c04, 0xa424, 0xa444, 0xa465, 0xac85, 0xaca5, 0xb4a6, 0xb4c6, 0xbce7, 0xbd07, 0xbd27, 0xc548, 0xc568, 0xd5c9, 0xde29, 0xde2a, 0x7325, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2942, 0x93c5, 0x9be3, 0x9bc3, 0x9382, 0x8b83, 0x9383, 0x93a3, 0x93a3, 0x93c3, 0x9be4, 0x9c04, 0x9c04, 0xa424, 0xa444, 0xa465, 0xac85, 0xaca5, 0xb4e6, 0xbd06, 0xc547, 0xcd88, 0xc587, 0xacc7, 0x4a03, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1081, 0x2901, 0x4a02, 0x7b44, 0x9be5, 0xa403, 0xac23, 0xac23, 0xac43, 0xac64, 0xb484, 0xb4a4, 0xb4a4, 0xb4a5, 0xac86, 0xa446, 0x6ae4, 0x5223, 0x2921, 0x18c1, 0x0820, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
|
||||
};
|
||||
|
||||
#endif
|
||||
273
yoRadio/fonts/glcdfont.c
Normal file
273
yoRadio/fonts/glcdfont.c
Normal file
@@ -0,0 +1,273 @@
|
||||
#ifndef FONT5X7_H
|
||||
#define FONT5X7_H
|
||||
|
||||
#ifdef __AVR__
|
||||
#include <avr/io.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <pgmspace.h>
|
||||
#else
|
||||
#define PROGMEM
|
||||
#endif
|
||||
|
||||
// Standard ASCII 5x7 font
|
||||
|
||||
static const unsigned char font[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3E, 0x55, 0x51, 0x55, 0x3E,
|
||||
0x3E, 0x6B, 0x6F, 0x6B, 0x3E,
|
||||
0x0C, 0x1E, 0x3C, 0x1E, 0x0C,
|
||||
0x08, 0x1C, 0x3E, 0x1C, 0x08,
|
||||
0x1C, 0x4A, 0x7F, 0x4A, 0x1C,
|
||||
0x18, 0x5C, 0x7F, 0x5C, 0x18,
|
||||
0x00, 0x1C, 0x1C, 0x1C, 0x00,
|
||||
0x7F, 0x63, 0x63, 0x63, 0x7F,
|
||||
0x00, 0x1C, 0x14, 0x1C, 0x00,
|
||||
0x7F, 0x63, 0x6B, 0x63, 0x7F,
|
||||
0x30, 0x48, 0x4D, 0x33, 0x07,
|
||||
0x06, 0x29, 0x79, 0x29, 0x06,
|
||||
0x20, 0x50, 0x3F, 0x02, 0x0C,
|
||||
0x60, 0x7F, 0x05, 0x35, 0x3F,
|
||||
0x2A, 0x1C, 0x77, 0x1C, 0x2A,
|
||||
0x00, 0x7F, 0x3E, 0x1C, 0x08,
|
||||
0x08, 0x1C, 0x3E, 0x7F, 0x00,
|
||||
0x14, 0x22, 0x7F, 0x22, 0x14,
|
||||
0x00, 0x5F, 0x00, 0x5F, 0x00,
|
||||
0x06, 0x09, 0x7F, 0x01, 0x7F,
|
||||
0x4A, 0x55, 0x55, 0x55, 0x29,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x54, 0x62, 0x7F, 0x62, 0x54,
|
||||
0x08, 0x04, 0x7E, 0x04, 0x08,
|
||||
0x08, 0x10, 0x3F, 0x10, 0x08,
|
||||
0x08, 0x08, 0x2A, 0x1C, 0x08,
|
||||
0x08, 0x1C, 0x2A, 0x08, 0x08,
|
||||
0x1C, 0x10, 0x10, 0x10, 0x10,
|
||||
0x1C, 0x3E, 0x08, 0x3E, 0x1C,
|
||||
0x30, 0x3C, 0x3F, 0x3C, 0x30,
|
||||
0x06, 0x1E, 0x7E, 0x1E, 0x06,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x5F, 0x00, 0x00,
|
||||
0x00, 0x07, 0x00, 0x07, 0x00,
|
||||
0x14, 0x7F, 0x14, 0x7F, 0x14,
|
||||
0x24, 0x2A, 0x7F, 0x2A, 0x12,
|
||||
0x23, 0x13, 0x08, 0x64, 0x62,
|
||||
0x36, 0x49, 0x56, 0x20, 0x50,
|
||||
0x00, 0x00, 0x07, 0x00, 0x00,
|
||||
0x00, 0x1C, 0x22, 0x41, 0x00,
|
||||
0x00, 0x41, 0x22, 0x1C, 0x00,
|
||||
0x14, 0x08, 0x3E, 0x08, 0x14,
|
||||
0x08, 0x08, 0x3E, 0x08, 0x08,
|
||||
0x00, 0xA0, 0x60, 0x00, 0x00,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x00, 0x60, 0x60, 0x00, 0x00,
|
||||
0x20, 0x10, 0x08, 0x04, 0x02,
|
||||
0x3E, 0x51, 0x49, 0x45, 0x3E,
|
||||
0x44, 0x42, 0x7F, 0x40, 0x40,
|
||||
0x42, 0x61, 0x51, 0x49, 0x46,
|
||||
0x21, 0x41, 0x45, 0x4B, 0x31,
|
||||
0x18, 0x14, 0x12, 0x7F, 0x10,
|
||||
0x27, 0x45, 0x45, 0x45, 0x39,
|
||||
0x3C, 0x4A, 0x49, 0x49, 0x30,
|
||||
0x01, 0x71, 0x09, 0x05, 0x03,
|
||||
0x36, 0x49, 0x49, 0x49, 0x36,
|
||||
0x06, 0x49, 0x49, 0x29, 0x1E,
|
||||
0x00, 0x6C, 0x6C, 0x00, 0x00,
|
||||
0x00, 0xAC, 0x6C, 0x00, 0x00,
|
||||
0x08, 0x14, 0x22, 0x41, 0x00,
|
||||
0x14, 0x14, 0x14, 0x14, 0x14,
|
||||
0x00, 0x41, 0x22, 0x14, 0x08,
|
||||
0x02, 0x01, 0x51, 0x09, 0x06,
|
||||
0x3E, 0x41, 0x5D, 0x55, 0x5E,
|
||||
0x7C, 0x12, 0x11, 0x12, 0x7C,
|
||||
0x7F, 0x49, 0x49, 0x49, 0x36,
|
||||
0x3E, 0x41, 0x41, 0x41, 0x22,
|
||||
0x7F, 0x41, 0x41, 0x22, 0x1C,
|
||||
0x7F, 0x49, 0x49, 0x49, 0x41,
|
||||
0x7F, 0x09, 0x09, 0x09, 0x01,
|
||||
0x3E, 0x41, 0x49, 0x49, 0x7A,
|
||||
0x7F, 0x08, 0x08, 0x08, 0x7F,
|
||||
0x00, 0x41, 0x7F, 0x41, 0x00,
|
||||
0x20, 0x40, 0x41, 0x3F, 0x01,
|
||||
0x7F, 0x08, 0x14, 0x22, 0x41,
|
||||
0x7F, 0x40, 0x40, 0x40, 0x60,
|
||||
0x7F, 0x02, 0x0C, 0x02, 0x7F,
|
||||
0x7F, 0x04, 0x08, 0x10, 0x7F,
|
||||
0x3E, 0x41, 0x41, 0x41, 0x3E,
|
||||
0x7F, 0x09, 0x09, 0x09, 0x06,
|
||||
0x3E, 0x41, 0x51, 0x21, 0x5E,
|
||||
0x7F, 0x09, 0x19, 0x29, 0x46,
|
||||
0x46, 0x49, 0x49, 0x49, 0x31,
|
||||
0x03, 0x01, 0x7F, 0x01, 0x03,
|
||||
0x3F, 0x40, 0x40, 0x40, 0x3F,
|
||||
0x1F, 0x20, 0x40, 0x20, 0x1F,
|
||||
0x3F, 0x40, 0x3C, 0x40, 0x3F,
|
||||
0x63, 0x14, 0x08, 0x14, 0x63,
|
||||
0x07, 0x08, 0x70, 0x08, 0x07,
|
||||
0x61, 0x51, 0x49, 0x45, 0x43,
|
||||
0x00, 0x7F, 0x41, 0x41, 0x00,
|
||||
0x02, 0x04, 0x08, 0x10, 0x20,
|
||||
0x00, 0x41, 0x41, 0x7F, 0x00,
|
||||
0x04, 0x02, 0x01, 0x02, 0x04,
|
||||
0x40, 0x40, 0x40, 0x40, 0x40,
|
||||
0x00, 0x01, 0x02, 0x04, 0x00,
|
||||
0x20, 0x54, 0x54, 0x54, 0x78,
|
||||
0x7F, 0x48, 0x44, 0x44, 0x38,
|
||||
0x38, 0x44, 0x44, 0x44, 0x48,
|
||||
0x38, 0x44, 0x44, 0x48, 0x7F,
|
||||
0x38, 0x54, 0x54, 0x54, 0x18,
|
||||
0x08, 0x7E, 0x09, 0x01, 0x02,
|
||||
0x08, 0x54, 0x54, 0x58, 0x3C,
|
||||
0x7F, 0x08, 0x04, 0x04, 0x78,
|
||||
0x00, 0x44, 0x7D, 0x40, 0x00,
|
||||
0x20, 0x40, 0x44, 0x3D, 0x00,
|
||||
0x7F, 0x10, 0x10, 0x28, 0x44,
|
||||
0x00, 0x41, 0x7F, 0x40, 0x00,
|
||||
0x7C, 0x04, 0x78, 0x04, 0x78,
|
||||
0x7C, 0x08, 0x04, 0x04, 0x78,
|
||||
0x38, 0x44, 0x44, 0x44, 0x38,
|
||||
0x7C, 0x14, 0x14, 0x14, 0x08,
|
||||
0x08, 0x14, 0x14, 0x0C, 0x7C,
|
||||
0x7C, 0x08, 0x04, 0x04, 0x08,
|
||||
0x48, 0x54, 0x54, 0x54, 0x24,
|
||||
0x04, 0x3F, 0x44, 0x40, 0x20,
|
||||
0x3C, 0x40, 0x40, 0x20, 0x7C,
|
||||
0x1C, 0x20, 0x40, 0x20, 0x1C,
|
||||
0x3C, 0x40, 0x38, 0x40, 0x3C,
|
||||
0x44, 0x28, 0x10, 0x28, 0x44,
|
||||
0x0C, 0x50, 0x50, 0x50, 0x3C,
|
||||
0x44, 0x64, 0x54, 0x4C, 0x44,
|
||||
0x00, 0x08, 0x36, 0x41, 0x00,
|
||||
0x00, 0x00, 0x7F, 0x00, 0x00,
|
||||
0x00, 0x41, 0x36, 0x08, 0x00,
|
||||
0x02, 0x01, 0x02, 0x04, 0x02,
|
||||
0x70, 0x48, 0x44, 0x48, 0x70,
|
||||
0x00, 0x0E, 0x11, 0x0E, 0x00,
|
||||
0x00, 0x12, 0x1F, 0x10, 0x00,
|
||||
0x00, 0x12, 0x19, 0x16, 0x00,
|
||||
0x00, 0x11, 0x15, 0x0B, 0x00,
|
||||
0x00, 0x07, 0x04, 0x1F, 0x00,
|
||||
0x00, 0x17, 0x15, 0x09, 0x00,
|
||||
0x00, 0x0E, 0x15, 0x09, 0x00,
|
||||
0x00, 0x01, 0x1D, 0x03, 0x00,
|
||||
0x00, 0x0A, 0x15, 0x0A, 0x00,
|
||||
0x00, 0x12, 0x15, 0x0E, 0x00,
|
||||
0x00, 0x04, 0x04, 0x04, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x3E, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3E, 0x3E, 0x00, 0x00, 0x00,
|
||||
0x3E, 0x3E, 0x00, 0x3E, 0x00,
|
||||
0x3E, 0x3E, 0x00, 0x3E, 0x3E,
|
||||
0x58, 0x64, 0x04, 0x64, 0x58,
|
||||
0x7F, 0x3E, 0x1C, 0x08, 0x7F,
|
||||
0x7F, 0x08, 0x1C, 0x3E, 0x7F,
|
||||
0x7F, 0x7F, 0x00, 0x7F, 0x7F,
|
||||
0x08, 0x3E, 0x22, 0x22, 0x22,
|
||||
0x22, 0x22, 0x22, 0x3E, 0x08,
|
||||
0x40, 0x00, 0x40, 0x00, 0x40,
|
||||
0x60, 0x00, 0x40, 0x00, 0x40,
|
||||
0x60, 0x00, 0x70, 0x00, 0x40,
|
||||
0x60, 0x00, 0x70, 0x00, 0x78,
|
||||
0x7C, 0x00, 0x40, 0x00, 0x40,
|
||||
0x7C, 0x00, 0x7E, 0x00, 0x40,
|
||||
0x7C, 0x00, 0x7E, 0x00, 0x7F,
|
||||
0x1C, 0x77, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x7F, 0x00,
|
||||
0x1C, 0x77, 0x41, 0x5D, 0x5D,
|
||||
0x41, 0x41, 0x41, 0x5D, 0x5D,
|
||||
0x5D, 0x5D, 0x41, 0x5D, 0x5D,
|
||||
0x5D, 0x5D, 0x41, 0x7F, 0x00,
|
||||
0x22, 0x1C, 0x14, 0x1C, 0x22,
|
||||
0x00, 0x08, 0x1C, 0x08, 0x00,
|
||||
0x00, 0x00, 0x77, 0x00, 0x00,
|
||||
0x46, 0x5D, 0x55, 0x5D, 0x31,
|
||||
0x7C, 0x55, 0x54, 0x55, 0x44,
|
||||
0x08, 0x08, 0x2A, 0x08, 0x08,
|
||||
0x00, 0x14, 0x08, 0x14, 0x00,
|
||||
0x08, 0x14, 0x22, 0x08, 0x14,
|
||||
0x7F, 0x41, 0x71, 0x31, 0x1F,
|
||||
0x03, 0x05, 0x7F, 0x05, 0x03,
|
||||
0x22, 0x14, 0x7F, 0x55, 0x22,
|
||||
0x02, 0x55, 0x7D, 0x05, 0x02,
|
||||
0x06, 0x09, 0x09, 0x06, 0x00,
|
||||
0x44, 0x44, 0x5F, 0x44, 0x44,
|
||||
0x1C, 0x14, 0x1C, 0x22, 0x7F,
|
||||
0x20, 0x3E, 0x61, 0x3E, 0x20,
|
||||
0x20, 0x50, 0x3F, 0x02, 0x0C,
|
||||
0x80, 0x7C, 0x20, 0x3C, 0x40,
|
||||
0x44, 0x3C, 0x04, 0x7C, 0x44,
|
||||
0x00, 0x00, 0x08, 0x00, 0x00,
|
||||
0x38, 0x55, 0x54, 0x55, 0x18,
|
||||
0x7E, 0x08, 0x10, 0x7F, 0x01,
|
||||
0x08, 0x10, 0x08, 0x04, 0x02,
|
||||
0x14, 0x08, 0x22, 0x14, 0x08,
|
||||
0x0E, 0x06, 0x0A, 0x10, 0x20,
|
||||
0x20, 0x10, 0x0A, 0x06, 0x0E,
|
||||
0x38, 0x30, 0x28, 0x04, 0x02,
|
||||
0x02, 0x04, 0x28, 0x30, 0x38,
|
||||
0x7E, 0x11, 0x11, 0x11, 0x7E,
|
||||
0x7F, 0x49, 0x49, 0x49, 0x31,
|
||||
0x7F, 0x49, 0x49, 0x49, 0x36,
|
||||
0x7F, 0x01, 0x01, 0x01, 0x03,
|
||||
0xC0, 0x7F, 0x41, 0x7F, 0xC0,
|
||||
0x7F, 0x49, 0x49, 0x49, 0x41,
|
||||
0x77, 0x08, 0x7F, 0x08, 0x77,
|
||||
0x41, 0x49, 0x49, 0x49, 0x36,
|
||||
0x7F, 0x10, 0x08, 0x04, 0x7F,
|
||||
0x7C, 0x21, 0x12, 0x09, 0x7C,
|
||||
0x7F, 0x08, 0x14, 0x22, 0x41,
|
||||
0x40, 0x3E, 0x01, 0x01, 0x7F,
|
||||
0x7F, 0x02, 0x0C, 0x02, 0x7F,
|
||||
0x7F, 0x08, 0x08, 0x08, 0x7F,
|
||||
0x3E, 0x41, 0x41, 0x41, 0x3E,
|
||||
0x7F, 0x01, 0x01, 0x01, 0x7F,
|
||||
0x7F, 0x09, 0x09, 0x09, 0x06,
|
||||
0x3E, 0x41, 0x41, 0x41, 0x22,
|
||||
0x01, 0x01, 0x7F, 0x01, 0x01,
|
||||
0x07, 0x48, 0x48, 0x48, 0x3F,
|
||||
0x0E, 0x11, 0x7F, 0x11, 0x0E,
|
||||
0x63, 0x14, 0x08, 0x14, 0x63,
|
||||
0x7F, 0x40, 0x40, 0x7F, 0xC0,
|
||||
0x07, 0x08, 0x08, 0x08, 0x7F,
|
||||
0x7F, 0x40, 0x7F, 0x40, 0x7F,
|
||||
0x7F, 0x40, 0x7F, 0x40, 0xFF,
|
||||
0x01, 0x7F, 0x48, 0x48, 0x30,
|
||||
0x7F, 0x48, 0x48, 0x30, 0x7F,
|
||||
0x7F, 0x48, 0x48, 0x48, 0x30,
|
||||
0x22, 0x41, 0x49, 0x49, 0x3E,
|
||||
0x7F, 0x08, 0x3E, 0x41, 0x3E,
|
||||
0x46, 0x29, 0x19, 0x09, 0x7F,
|
||||
0x20, 0x54, 0x54, 0x54, 0x78,
|
||||
0x3C, 0x4A, 0x4A, 0x49, 0x31,
|
||||
0x7C, 0x54, 0x54, 0x54, 0x28,
|
||||
0x7C, 0x04, 0x04, 0x04, 0x0C,
|
||||
0xC0, 0x78, 0x44, 0x7C, 0xC0,
|
||||
0x38, 0x54, 0x54, 0x54, 0x18,
|
||||
0x6C, 0x10, 0x7C, 0x10, 0x6C,
|
||||
0x44, 0x54, 0x54, 0x54, 0x28,
|
||||
0x7C, 0x20, 0x10, 0x08, 0x7C,
|
||||
0x7C, 0x40, 0x26, 0x10, 0x7C,
|
||||
0x7C, 0x10, 0x10, 0x28, 0x44,
|
||||
0x40, 0x38, 0x04, 0x04, 0x7C,
|
||||
0x7C, 0x08, 0x10, 0x08, 0x7C,
|
||||
0x7C, 0x10, 0x10, 0x10, 0x7C,
|
||||
0x38, 0x44, 0x44, 0x44, 0x38,
|
||||
0x7C, 0x04, 0x04, 0x04, 0x7C,
|
||||
0x7C, 0x14, 0x14, 0x14, 0x08,
|
||||
0x38, 0x44, 0x44, 0x44, 0x48,
|
||||
0x04, 0x04, 0x7C, 0x04, 0x04,
|
||||
0x0C, 0x50, 0x50, 0x50, 0x3C,
|
||||
0x18, 0x24, 0xFC, 0x24, 0x18,
|
||||
0x44, 0x28, 0x10, 0x28, 0x44,
|
||||
0x7C, 0x40, 0x40, 0x7C, 0xC0,
|
||||
0x0C, 0x10, 0x10, 0x10, 0x7C,
|
||||
0x7C, 0x40, 0x7C, 0x40, 0x7C,
|
||||
0x7C, 0x40, 0x7C, 0x40, 0xFC,
|
||||
0x04, 0x7C, 0x50, 0x50, 0x20,
|
||||
0x7C, 0x50, 0x50, 0x20, 0x7C,
|
||||
0x7C, 0x50, 0x50, 0x50, 0x20,
|
||||
0x28, 0x44, 0x54, 0x54, 0x38,
|
||||
0x7C, 0x10, 0x38, 0x44, 0x38,
|
||||
0x48, 0x34, 0x14, 0x14, 0x7C
|
||||
};
|
||||
#endif // FONT5X7_H
|
||||
384
yoRadio/netserver.cpp
Normal file
384
yoRadio/netserver.cpp
Normal file
@@ -0,0 +1,384 @@
|
||||
#include "netserver.h"
|
||||
#include <SPIFFS.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "player.h"
|
||||
#include "display.h"
|
||||
#include "options.h"
|
||||
#include "network.h"
|
||||
|
||||
NetServer netserver;
|
||||
|
||||
AsyncWebServer webserver(80);
|
||||
AsyncWebSocket websocket("/ws");
|
||||
AsyncUDP udp;
|
||||
|
||||
String processor(const String& var);
|
||||
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
|
||||
void handleHTTPPost(AsyncWebServerRequest * request);
|
||||
|
||||
byte ssidCount;
|
||||
|
||||
bool NetServer::begin() {
|
||||
importRequest = false;
|
||||
webserver.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
|
||||
ssidCount = 0;
|
||||
request->send(SPIFFS, "/www/index.html", String(), false, processor);
|
||||
});
|
||||
webserver.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=31536000");
|
||||
webserver.on("/", HTTP_POST, [](AsyncWebServerRequest * request) {
|
||||
handleHTTPPost(request);
|
||||
});
|
||||
webserver.on(PLAYLIST_PATH, HTTP_GET, [](AsyncWebServerRequest * request) {
|
||||
request->send(SPIFFS, PLAYLIST_PATH, "application/octet-stream");
|
||||
});
|
||||
webserver.on(INDEX_PATH, HTTP_GET, [](AsyncWebServerRequest * request) {
|
||||
request->send(SPIFFS, INDEX_PATH, "application/octet-stream");
|
||||
});
|
||||
webserver.on("/upload", HTTP_POST, [](AsyncWebServerRequest * request) {
|
||||
//request->send(200);
|
||||
}, handleUpload);
|
||||
webserver.begin();
|
||||
websocket.onEvent(onWsEvent);
|
||||
webserver.addHandler(&websocket);
|
||||
|
||||
//echo -n "helle?" | socat - udp-datagram:255.255.255.255:44490,broadcast
|
||||
if (udp.listen(44490)) {
|
||||
udp.onPacket([](AsyncUDPPacket packet) {
|
||||
if(strcmp((char*)packet.data(),"helle?")==0)
|
||||
packet.println(WiFi.localIP());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void NetServer::loop() {
|
||||
websocket.cleanupClients();
|
||||
if (playlistrequest > 0) {
|
||||
requestOnChange(PLAYLIST, playlistrequest);
|
||||
playlistrequest = 0;
|
||||
}
|
||||
if (importRequest) {
|
||||
if (importPlaylist()) {
|
||||
requestOnChange(PLAYLIST, 0);
|
||||
}
|
||||
importRequest = false;
|
||||
}
|
||||
yield();
|
||||
}
|
||||
|
||||
void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len) {
|
||||
AwsFrameInfo *info = (AwsFrameInfo*)arg;
|
||||
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
|
||||
data[len] = 0;
|
||||
char cmd[15], val[15];
|
||||
if (config.parseWsCommand((const char*)data, cmd, val, 15)) {
|
||||
if (strcmp(cmd, "volume") == 0) {
|
||||
byte v = atoi(val);
|
||||
player.setVol(v, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetServer::setRSSI(int val) {
|
||||
rssi = val;
|
||||
requestOnChange(NRSSI, 0);
|
||||
}
|
||||
|
||||
void NetServer::getPlaylist(uint8_t clientId) {
|
||||
String dataString = "";
|
||||
File file = SPIFFS.open(PLAYLIST_PATH, "r");
|
||||
if (!file || file.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
char sName[BUFLEN], sUrl[BUFLEN], pOvol[30];
|
||||
int sOvol;
|
||||
while (file.available()) {
|
||||
String line = file.readStringUntil('\n');
|
||||
if (config.parseCSV(line.c_str(), sName, sUrl, sOvol)) {
|
||||
sprintf(pOvol, "%d", sOvol);
|
||||
dataString += "{\"name\":\"" + String(sName) + "\",\"url\":\"" + String(sUrl) + "\",\"ovol\":" + String(pOvol) + "},";
|
||||
}
|
||||
}
|
||||
if (dataString.length() > 0) {
|
||||
if (clientId == 0) {
|
||||
websocket.textAll("{\"file\": [" + dataString.substring(0, dataString.length() - 1) + "]}");
|
||||
} else {
|
||||
websocket.text(clientId, "{\"file\": [" + dataString.substring(0, dataString.length() - 1) + "]}");
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool NetServer::savePlaylist(const char* post) {
|
||||
File file = SPIFFS.open(PLAYLIST_PATH, "w");
|
||||
if (!file) {
|
||||
return false;
|
||||
} else {
|
||||
file.print(post);
|
||||
file.close();
|
||||
netserver.requestOnChange(PLAYLISTSAVED, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool NetServer::importPlaylist() {
|
||||
File tempfile = SPIFFS.open(TMP_PATH, "r");
|
||||
if (!tempfile) {
|
||||
return false;
|
||||
}
|
||||
char sName[BUFLEN], sUrl[BUFLEN];
|
||||
int sOvol;
|
||||
String line = tempfile.readStringUntil('\n');
|
||||
if (config.parseCSV(line.c_str(), sName, sUrl, sOvol)) {
|
||||
File playlistfile = SPIFFS.open(PLAYLIST_PATH, "w");
|
||||
playlistfile.println(line);
|
||||
while (tempfile.available()) {
|
||||
line = tempfile.readStringUntil('\n');
|
||||
if (config.parseCSV(line.c_str(), sName, sUrl, sOvol)) {
|
||||
playlistfile.println(line);
|
||||
}
|
||||
}
|
||||
playlistfile.close();
|
||||
tempfile.close();
|
||||
SPIFFS.remove(TMP_PATH);
|
||||
requestOnChange(PLAYLISTSAVED, 0);
|
||||
return true;
|
||||
}
|
||||
if (config.parseJSON(line.c_str(), sName, sUrl, sOvol)) {
|
||||
File playlistfile = SPIFFS.open(PLAYLIST_PATH, "w");
|
||||
String wline = String(sName) + "\t" + String(sUrl) + "\t" + String(sOvol);
|
||||
playlistfile.println(wline);
|
||||
while (tempfile.available()) {
|
||||
line = tempfile.readStringUntil('\n');
|
||||
if (config.parseJSON(line.c_str(), sName, sUrl, sOvol)) {
|
||||
wline = String(sName) + "\t" + String(sUrl) + "\t" + String(sOvol);
|
||||
playlistfile.println(wline);
|
||||
}
|
||||
}
|
||||
playlistfile.close();
|
||||
tempfile.close();
|
||||
SPIFFS.remove(TMP_PATH);
|
||||
requestOnChange(PLAYLISTSAVED, 0);
|
||||
return true;
|
||||
}
|
||||
tempfile.close();
|
||||
SPIFFS.remove(TMP_PATH);
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetServer::requestOnChange(requestType_e request, uint8_t clientId) {
|
||||
char buf[BUFLEN + 50] = { 0 };
|
||||
switch (request) {
|
||||
case PLAYLIST: {
|
||||
getPlaylist(clientId);
|
||||
break;
|
||||
}
|
||||
case PLAYLISTSAVED: {
|
||||
config.indexPlaylist();
|
||||
config.initPlaylist();
|
||||
getPlaylist(clientId);
|
||||
break;
|
||||
}
|
||||
case STATION: {
|
||||
sprintf (buf, "{\"nameset\": \"%s\"}", config.station.name);
|
||||
requestOnChange(ITEM, clientId);
|
||||
break;
|
||||
}
|
||||
case ITEM: {
|
||||
sprintf (buf, "{\"current\": %d}", config.store.lastStation);
|
||||
break;
|
||||
}
|
||||
case TITLE: {
|
||||
sprintf (buf, "{\"meta\": \"%s\"}", config.station.title);
|
||||
break;
|
||||
}
|
||||
case VOLUME: {
|
||||
sprintf (buf, "{\"vol\": %d}", config.store.volume);
|
||||
break;
|
||||
}
|
||||
case NRSSI: {
|
||||
sprintf (buf, "{\"rssi\": %d}", rssi);
|
||||
break;
|
||||
}
|
||||
case BITRATE: {
|
||||
sprintf (buf, "{\"bitrate\": %d}", config.station.bitrate);
|
||||
break;
|
||||
}
|
||||
case MODE: {
|
||||
sprintf (buf, "{\"mode\": \"%s\"}", player.mode == PLAYING ? "playing" : "stopped");
|
||||
break;
|
||||
}
|
||||
case EQUALIZER: {
|
||||
sprintf (buf, "{\"bass\": %d, \"middle\": %d, \"trebble\": %d}", config.store.bass, config.store.middle, config.store.trebble);
|
||||
break;
|
||||
}
|
||||
case BALANCE: {
|
||||
sprintf (buf, "{\"balance\": %d}", config.store.balance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (strlen(buf) > 0) {
|
||||
if (clientId == 0) {
|
||||
websocket.textAll(buf);
|
||||
} else {
|
||||
websocket.text(clientId, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String processor(const String& var) { // %Templates%
|
||||
if (var == "VERSION") {
|
||||
return VERSION;
|
||||
}
|
||||
if (var == "SSID") {
|
||||
ssidCount++;
|
||||
return String(config.ssids[ssidCount - 1].ssid);
|
||||
}
|
||||
if (var == "PASS") {
|
||||
return String(config.ssids[ssidCount - 1].password);
|
||||
}
|
||||
if (var == "APMODE") {
|
||||
return network.status == CONNECTED ? "" : " style=\"display: none!important\"";
|
||||
}
|
||||
if (var == "NOTAPMODE") {
|
||||
return network.status == CONNECTED ? " hidden" : "";
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||
if (!index) {
|
||||
request->_tempFile = SPIFFS.open(TMP_PATH , "w");
|
||||
}
|
||||
if (len) {
|
||||
request->_tempFile.write(data, len);
|
||||
//TODO check index+len size
|
||||
}
|
||||
if (final) {
|
||||
request->_tempFile.close();
|
||||
netserver.importRequest = true;
|
||||
request->send(200);
|
||||
}
|
||||
}
|
||||
|
||||
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
|
||||
switch (type) {
|
||||
case WS_EVT_CONNECT:
|
||||
Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
|
||||
netserver.requestOnChange(STATION, client->id());
|
||||
netserver.requestOnChange(TITLE, client->id());
|
||||
netserver.requestOnChange(VOLUME, client->id());
|
||||
netserver.requestOnChange(EQUALIZER, client->id());
|
||||
netserver.requestOnChange(BALANCE, client->id());
|
||||
netserver.requestOnChange(BITRATE, client->id());
|
||||
netserver.requestOnChange(MODE, client->id());
|
||||
netserver.playlistrequest = client->id();
|
||||
break;
|
||||
case WS_EVT_DISCONNECT:
|
||||
Serial.printf("WebSocket client #%u disconnected\n", client->id());
|
||||
break;
|
||||
case WS_EVT_DATA:
|
||||
netserver.onWsMessage(arg, data, len);
|
||||
break;
|
||||
case WS_EVT_PONG:
|
||||
case WS_EVT_ERROR:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void handleHTTPPost(AsyncWebServerRequest * request) {
|
||||
if (request->hasParam("wifisettings", true)) {
|
||||
AsyncWebParameter* p = request->getParam("wifisettings", true);
|
||||
if (p->value() != "") {
|
||||
config.saveWifi(p->value().c_str());
|
||||
}
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
if (request->hasParam("playlist", true)) {
|
||||
AsyncWebParameter* p = request->getParam("playlist", true);
|
||||
netserver.savePlaylist(p->value().c_str());
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
if (network.status != CONNECTED) {
|
||||
request->send(404);
|
||||
return;
|
||||
}
|
||||
if (request->hasParam("start", true)) {
|
||||
player.request.station = config.store.lastStation;
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
if (request->hasParam("stop", true)) {
|
||||
player.mode = STOPPED;
|
||||
display.title("[stopped]");
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
if (request->hasParam("prev", true)) {
|
||||
player.prev();
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
if (request->hasParam("next", true)) {
|
||||
player.next();
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
if (request->hasParam("volm", true)) {
|
||||
player.stepVol(false);
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
if (request->hasParam("volp", true)) {
|
||||
player.stepVol(true);
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
if (request->hasParam("vol", true)) {
|
||||
AsyncWebParameter* p = request->getParam("vol", true);
|
||||
int v = atoi(p->value().c_str());
|
||||
if (v < 0) v = 0;
|
||||
if (v > 254) v = 254;
|
||||
player.setVol(v, false);
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
if (request->hasParam("trebble", true)) {
|
||||
AsyncWebParameter* pt = request->getParam("trebble", true);
|
||||
AsyncWebParameter* pm = request->getParam("middle", true);
|
||||
AsyncWebParameter* pb = request->getParam("bass", true);
|
||||
int t = atoi(pt->value().c_str());
|
||||
int m = atoi(pm->value().c_str());
|
||||
int b = atoi(pb->value().c_str());
|
||||
//setTone(int8_t gainLowPass, int8_t gainBandPass, int8_t gainHighPass)
|
||||
player.setTone(b, m, t);
|
||||
config.setTone(b, m, t);
|
||||
netserver.requestOnChange(EQUALIZER, 0);
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
if (request->hasParam("ballance", true)) {
|
||||
AsyncWebParameter* p = request->getParam("ballance", true);
|
||||
int b = atoi(p->value().c_str());
|
||||
player.setBalance(b);
|
||||
config.setBalance(b);
|
||||
netserver.requestOnChange(BALANCE, 0);
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
if (request->hasParam("playstation", true)) {
|
||||
AsyncWebParameter* p = request->getParam("playstation", true);
|
||||
int id = atoi(p->value().c_str());
|
||||
if (id < 1) id = 1;
|
||||
if (id > config.store.countStation) id = config.store.countStation;
|
||||
player.request.station = id;
|
||||
player.request.doSave = true;
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
|
||||
request->send(404);
|
||||
}
|
||||
31
yoRadio/netserver.h
Normal file
31
yoRadio/netserver.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef netserver_h
|
||||
#define netserver_h
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "AsyncUDP.h"
|
||||
|
||||
enum requestType_e { PLAYLIST, STATION, ITEM, TITLE, VOLUME, NRSSI, BITRATE, MODE, EQUALIZER, BALANCE, PLAYLISTSAVED };
|
||||
|
||||
class NetServer {
|
||||
public:
|
||||
uint8_t playlistrequest; // ClientId want the playlist
|
||||
bool importRequest;
|
||||
public:
|
||||
NetServer() {};
|
||||
bool begin();
|
||||
void loop();
|
||||
void requestOnChange(requestType_e request, uint8_t clientId);
|
||||
void setRSSI(int val);
|
||||
void onWsMessage(void *arg, uint8_t *data, size_t len);
|
||||
bool savePlaylist(const char* post);
|
||||
private:
|
||||
requestType_e request;
|
||||
int rssi;
|
||||
void getPlaylist(uint8_t clientId);
|
||||
bool importPlaylist();
|
||||
};
|
||||
|
||||
extern NetServer netserver;
|
||||
|
||||
#endif
|
||||
55
yoRadio/network.cpp
Normal file
55
yoRadio/network.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "network.h"
|
||||
#include "WiFi.h"
|
||||
#include "display.h"
|
||||
#include "options.h"
|
||||
|
||||
Network network;
|
||||
|
||||
void Network::begin() {
|
||||
config.initNetwork();
|
||||
if (config.ssidsCount == 0) {
|
||||
raiseSoftAP();
|
||||
return;
|
||||
}
|
||||
byte ls = (config.store.lastSSID == 0 || config.store.lastSSID > config.ssidsCount) ? 0 : config.store.lastSSID - 1;
|
||||
byte startedls = ls;
|
||||
byte errcnt = 0;
|
||||
WiFi.mode(WIFI_STA);
|
||||
char buf[40] = { 0 };
|
||||
while (true) {
|
||||
Serial.printf("Attempt to connect to %s\n", config.ssids[ls].ssid);
|
||||
snprintf(buf, sizeof(buf) - 1, "ATTEMPT TO %s", config.ssids[ls].ssid);
|
||||
display.bootString(buf, 110);
|
||||
WiFi.begin(config.ssids[ls].ssid, config.ssids[ls].password);
|
||||
strcpy(buf, ".");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
|
||||
strcat(buf, ".");
|
||||
display.bootString(buf, 90);
|
||||
errcnt++;
|
||||
if (errcnt > 16) {
|
||||
errcnt = 0;
|
||||
ls++;
|
||||
if (ls > config.ssidsCount - 1) ls = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(WiFi.status() != WL_CONNECTED && ls==startedls){
|
||||
raiseSoftAP();
|
||||
return;
|
||||
}
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
config.setLastSSID(ls + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
status = CONNECTED;
|
||||
}
|
||||
|
||||
void Network::raiseSoftAP() {
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP(apSsid, apPassword);
|
||||
status = SOFT_AP;
|
||||
}
|
||||
21
yoRadio/network.h
Normal file
21
yoRadio/network.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef network_h
|
||||
#define network_h
|
||||
|
||||
#define apSsid "yoRadioAP"
|
||||
#define apPassword "12345987"
|
||||
|
||||
enum n_Status_e { CONNECTED, SOFT_AP, FAILED };
|
||||
|
||||
class Network {
|
||||
public:
|
||||
n_Status_e status;
|
||||
public:
|
||||
Network() {};
|
||||
void begin();
|
||||
private:
|
||||
void raiseSoftAP();
|
||||
};
|
||||
|
||||
extern Network network;
|
||||
|
||||
#endif
|
||||
47
yoRadio/options.h
Normal file
47
yoRadio/options.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef options_h
|
||||
#define options_h
|
||||
|
||||
#define VERSION "0.4.170"
|
||||
|
||||
/*
|
||||
* TFT DISPLAY
|
||||
*/
|
||||
/**************
|
||||
* GND | GND *
|
||||
* VCC | +5v *
|
||||
* SCL | D18 *
|
||||
* SDA | D23 *
|
||||
* ************
|
||||
*/
|
||||
#define TFT_CS 5
|
||||
#define TFT_RST 15 // Or set to -1 and connect to Arduino RESET pin
|
||||
//#define TFT_RST -1 // we use the seesaw for resetting to save a pin
|
||||
#define TFT_DC 4
|
||||
|
||||
/*
|
||||
* I2S DAC
|
||||
*/
|
||||
#define I2S_DOUT 27 // DIN connection
|
||||
#define I2S_BCLK 26 // BCLK Bit clock
|
||||
#define I2S_LRC 25 // WSEL Left Right Clock
|
||||
|
||||
/*
|
||||
* ENCODER
|
||||
*/
|
||||
#define ENC_BTNL 13
|
||||
#define ENC_BTNB 12
|
||||
#define ENC_BTNR 14
|
||||
|
||||
/*
|
||||
* BUTTONS
|
||||
*/
|
||||
#define BTN_LEFT 32
|
||||
#define BTN_CENTER 12
|
||||
#define BTN_RIGHT 33
|
||||
|
||||
/*
|
||||
* ESP DEVBOARD
|
||||
*/
|
||||
#define LED_BUILTIN 2
|
||||
|
||||
#endif
|
||||
128
yoRadio/player.cpp
Normal file
128
yoRadio/player.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "player.h"
|
||||
#include "config.h"
|
||||
#include "telnet.h"
|
||||
#include "display.h"
|
||||
#include "options.h"
|
||||
#include "netserver.h"
|
||||
|
||||
Player player;
|
||||
|
||||
void Player::init() {
|
||||
setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
setVolume(0);
|
||||
mode = STOPPED;
|
||||
requesToStart = true;
|
||||
setBalance(config.store.balance);
|
||||
setTone(config.store.bass, config.store.middle, config.store.trebble);
|
||||
zeroRequest();
|
||||
}
|
||||
|
||||
void Player::stopInfo() {
|
||||
config.setSmartStart(0);
|
||||
telnet.info();
|
||||
netserver.requestOnChange(MODE, 0);
|
||||
requesToStart = true;
|
||||
}
|
||||
|
||||
void Player::loop() {
|
||||
if (mode == PLAYING) {
|
||||
Audio::loop();
|
||||
} else {
|
||||
if (isRunning()) {
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
stopSong();
|
||||
stopInfo();
|
||||
}
|
||||
}
|
||||
if (request.station > 0) {
|
||||
if (request.doSave) {
|
||||
config.setLastStation(request.station);
|
||||
}
|
||||
play(request.station);
|
||||
zeroRequest();
|
||||
}
|
||||
if (request.volume >= 0) {
|
||||
config.setVolume(request.volume, request.doSave);
|
||||
display.volume();
|
||||
telnet.printf("##CLI.VOL#: %d\n", config.store.volume);
|
||||
Audio::setVolume(volToI2S(request.volume));
|
||||
zeroRequest();
|
||||
}
|
||||
yield();
|
||||
}
|
||||
|
||||
void Player::zeroRequest() {
|
||||
request.station = 0;
|
||||
request.volume = -1;
|
||||
request.doSave = false;
|
||||
}
|
||||
|
||||
void Player::play(byte stationId) {
|
||||
stopSong();
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
display.title("[connecting]");
|
||||
telnet.printf("##CLI.META#: %s\n", config.station.title);
|
||||
config.loadStation(stationId);
|
||||
setVol(config.store.volume, true);
|
||||
display.station();
|
||||
telnet.printf("##CLI.NAMESET#: %d %s\n", config.store.lastStation, config.station.name);
|
||||
if (connecttohost(config.station.url)) {
|
||||
mode = PLAYING;
|
||||
config.setSmartStart(1);
|
||||
netserver.requestOnChange(MODE, 0);
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
requesToStart = true;
|
||||
}else{
|
||||
Serial.println("Some Unknown Bug...");
|
||||
}
|
||||
zeroRequest();
|
||||
}
|
||||
|
||||
void Player::prev() {
|
||||
if (config.store.lastStation == 1) config.store.lastStation = config.store.countStation; else config.store.lastStation--;
|
||||
request.station = config.store.lastStation;
|
||||
request.doSave = true;
|
||||
}
|
||||
|
||||
void Player::next() {
|
||||
if (config.store.lastStation == config.store.countStation) config.store.lastStation = 1; else config.store.lastStation++;
|
||||
request.station = config.store.lastStation;
|
||||
request.doSave = true;
|
||||
}
|
||||
|
||||
void Player::toggle() {
|
||||
if (mode == PLAYING) {
|
||||
mode = STOPPED;
|
||||
display.title("[stopped]");
|
||||
} else {
|
||||
request.station = config.store.lastStation;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::stepVol(bool up) {
|
||||
if (up) {
|
||||
if (config.store.volume < 254) {
|
||||
setVol(config.store.volume + 1, false);
|
||||
}
|
||||
} else {
|
||||
if (config.store.volume > 0) {
|
||||
setVol(config.store.volume - 1, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte Player::volToI2S(byte volume) {
|
||||
int vol = map(volume, 0, 254 - config.station.ovol * 2 , 0, 254);
|
||||
if (vol > 254) vol = 254;
|
||||
if (vol < 0) vol = 0;
|
||||
return vol;
|
||||
}
|
||||
|
||||
void Player::setVol(byte volume, bool inside) {
|
||||
if (inside) {
|
||||
setVolume(volToI2S(volume));
|
||||
} else {
|
||||
request.volume = volume;
|
||||
request.doSave = true;
|
||||
}
|
||||
}
|
||||
36
yoRadio/player.h
Normal file
36
yoRadio/player.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef player_h
|
||||
#define player_h
|
||||
|
||||
#include "src/audioI2S/AudioEx.h"
|
||||
|
||||
enum audioMode_e { PLAYING, STOPPED };
|
||||
|
||||
struct audiorequest_t
|
||||
{
|
||||
uint16_t station;
|
||||
int volume;
|
||||
bool doSave;
|
||||
};
|
||||
|
||||
class Player: public Audio {
|
||||
public:
|
||||
audioMode_e mode;
|
||||
audiorequest_t request;
|
||||
bool requesToStart;
|
||||
public:
|
||||
void init();
|
||||
void loop();
|
||||
void zeroRequest();
|
||||
void play(byte stationId);
|
||||
void prev();
|
||||
void next();
|
||||
void toggle();
|
||||
void stepVol(bool up);
|
||||
void setVol(byte volume, bool inside);
|
||||
byte volToI2S(byte volume);
|
||||
void stopInfo();
|
||||
};
|
||||
|
||||
extern Player player;
|
||||
|
||||
#endif
|
||||
4538
yoRadio/src/audioI2S/Audio.cpp
Normal file
4538
yoRadio/src/audioI2S/Audio.cpp
Normal file
File diff suppressed because it is too large
Load Diff
480
yoRadio/src/audioI2S/AudioEx.h
Normal file
480
yoRadio/src/audioI2S/AudioEx.h
Normal file
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* Audio.h
|
||||
*
|
||||
* Created on: Oct 26,2018
|
||||
* Updated on: Jan 05,2022
|
||||
* Author: Wolle (schreibfaul1)
|
||||
*/
|
||||
|
||||
//#define SDFATFS_USED // activate for SdFat
|
||||
|
||||
|
||||
#pragma once
|
||||
#pragma GCC optimize ("Ofast")
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <libb64/cencode.h>
|
||||
#include <SPI.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
#include <driver/i2s.h>
|
||||
|
||||
#ifdef SDFATFS_USED
|
||||
#include <SdFat.h> // https://github.com/greiman/SdFat
|
||||
#else
|
||||
#include <SD.h>
|
||||
#include <SD_MMC.h>
|
||||
#include <SPIFFS.h>
|
||||
#include <FS.h>
|
||||
#include <FFat.h>
|
||||
#endif // SDFATFS_USED
|
||||
|
||||
#define AUDIOBUFFER_MULTIPLIER 13
|
||||
|
||||
#ifdef SDFATFS_USED
|
||||
typedef File32 File;
|
||||
|
||||
namespace fs {
|
||||
class FS : public SdFat {
|
||||
public:
|
||||
bool begin(SdCsPin_t csPin = SS, uint32_t maxSck = SD_SCK_MHZ(25)) { return SdFat::begin(csPin, maxSck); }
|
||||
};
|
||||
|
||||
class SDFATFS : public fs::FS {
|
||||
public:
|
||||
// sdcard_type_t cardType();
|
||||
uint64_t cardSize() {
|
||||
return totalBytes();
|
||||
}
|
||||
uint64_t usedBytes() {
|
||||
// set SdFatConfig MAINTAIN_FREE_CLUSTER_COUNT non-zero. Then only the first call will take time.
|
||||
return (uint64_t)(clusterCount() - freeClusterCount()) * (uint64_t)bytesPerCluster();
|
||||
}
|
||||
uint64_t totalBytes() {
|
||||
return (uint64_t)clusterCount() * (uint64_t)bytesPerCluster();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
extern fs::SDFATFS SD_SDFAT;
|
||||
|
||||
using namespace fs;
|
||||
#define SD SD_SDFAT
|
||||
#endif //SDFATFS_USED
|
||||
|
||||
|
||||
|
||||
|
||||
extern __attribute__((weak)) void audio_info(const char*);
|
||||
extern __attribute__((weak)) void audio_id3data(const char*); //ID3 metadata
|
||||
extern __attribute__((weak)) void audio_id3image(File& file, const size_t pos, const size_t size); //ID3 metadata image
|
||||
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_bitrate(const char*);
|
||||
extern __attribute__((weak)) void audio_commercial(const char*);
|
||||
extern __attribute__((weak)) void audio_icyurl(const char*);
|
||||
extern __attribute__((weak)) void audio_icydescription(const char*);
|
||||
extern __attribute__((weak)) void audio_lasthost(const char*);
|
||||
extern __attribute__((weak)) void audio_eof_speech(const char*);
|
||||
extern __attribute__((weak)) void audio_eof_stream(const char*); // The webstream comes to an end
|
||||
extern __attribute__((weak)) void audio_process_extern(int16_t* buff, uint16_t len, bool *continueI2S); // record audiodata or send via BT
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
class AudioBuffer {
|
||||
// AudioBuffer will be allocated in PSRAM, If PSRAM not available or has not enough space AudioBuffer will be
|
||||
// allocated in FlashRAM with reduced size
|
||||
//
|
||||
// m_buffer m_readPtr m_writePtr m_endPtr
|
||||
// | |<------dataLength------->|<------ writeSpace ----->|
|
||||
// ▼ ▼ ▼ ▼
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// | <--m_buffSize--> | <--m_resBuffSize --> |
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// |<-----freeSpace------->| |<------freeSpace-------->|
|
||||
//
|
||||
//
|
||||
//
|
||||
// if the space between m_readPtr and buffend < m_resBuffSize copy data from the beginning to resBuff
|
||||
// so that the mp3/aac/flac frame is always completed
|
||||
//
|
||||
// m_buffer m_writePtr m_readPtr m_endPtr
|
||||
// | |<-------writeSpace------>|<--dataLength-->|
|
||||
// ▼ ▼ ▼ ▼
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// | <--m_buffSize--> | <--m_resBuffSize --> |
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// |<--- ------dataLength-- ------>|<-------freeSpace------->|
|
||||
//
|
||||
//
|
||||
|
||||
public:
|
||||
AudioBuffer(size_t maxBlockSize = 0); // constructor
|
||||
~AudioBuffer(); // frees the buffer
|
||||
size_t init(); // set default values
|
||||
void changeMaxBlockSize(uint16_t mbs); // is default 1600 for mp3 and aac, set 16384 for FLAC
|
||||
uint16_t getMaxBlockSize(); // returns maxBlockSize
|
||||
size_t freeSpace(); // number of free bytes to overwrite
|
||||
size_t writeSpace(); // space fom writepointer to bufferend
|
||||
size_t bufferFilled(); // returns the number of filled bytes
|
||||
void bytesWritten(size_t bw); // update writepointer
|
||||
void bytesWasRead(size_t br); // update readpointer
|
||||
uint8_t* getWritePtr(); // returns the current writepointer
|
||||
uint8_t* getReadPtr(); // returns the current readpointer
|
||||
uint32_t getWritePos(); // write position relative to the beginning
|
||||
uint32_t getReadPos(); // read position relative to the beginning
|
||||
void resetBuffer(); // restore defaults
|
||||
|
||||
protected:
|
||||
const size_t m_buffSizePSRAM = 300000; // most webstreams limit the advance to 100...300Kbytes
|
||||
//const size_t m_buffSizeRAM = 1600 * 5 * AUDIOBUFFER_MULTIPLIER;
|
||||
const size_t m_buffSizeRAM = 1600 * AUDIOBUFFER_MULTIPLIER;
|
||||
size_t m_buffSize = 0;
|
||||
size_t m_freeSpace = 0;
|
||||
size_t m_writeSpace = 0;
|
||||
size_t m_dataLength = 0;
|
||||
//size_t m_resBuffSizeRAM = 1600 * AUDIOBUFFER_MULTIPLIER; // reserved buffspace, >= one mp3 frame
|
||||
size_t m_resBuffSizeRAM = 1600; // reserved buffspace, >= one mp3 frame
|
||||
size_t m_resBuffSizePSRAM = 4096 * 4; // reserved buffspace, >= one flac frame
|
||||
size_t m_maxBlockSize = 1600;
|
||||
uint8_t* m_buffer = NULL;
|
||||
uint8_t* m_writePtr = NULL;
|
||||
uint8_t* m_readPtr = NULL;
|
||||
uint8_t* m_endPtr = NULL;
|
||||
bool m_f_start = true;
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
class Audio : private AudioBuffer{
|
||||
|
||||
AudioBuffer InBuff; // instance of input buffer
|
||||
|
||||
public:
|
||||
Audio(bool internalDAC = false, i2s_dac_mode_t channelEnabled = I2S_DAC_CHANNEL_LEFT_EN); // #99
|
||||
~Audio();
|
||||
bool connecttohost(const char* host, const char* user = "", const char* pwd = "");
|
||||
bool connecttospeech(const char* speech, const char* lang);
|
||||
bool connecttoFS(fs::FS &fs, const char* path);
|
||||
bool connecttoSD(const char* path);
|
||||
bool setFileLoop(bool input);//TEST loop
|
||||
bool setAudioPlayPosition(uint16_t sec);
|
||||
bool setFilePos(uint32_t pos);
|
||||
bool audioFileSeek(const float speed);
|
||||
bool setTimeOffset(int sec);
|
||||
bool setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t DIN=I2S_PIN_NO_CHANGE);
|
||||
bool pauseResume();
|
||||
bool isRunning() {return m_f_running;}
|
||||
void loop();
|
||||
void stopSong();
|
||||
void forceMono(bool m);
|
||||
void setBalance(int8_t bal = 0);
|
||||
void setVolume(uint8_t vol);
|
||||
uint8_t getVolume();
|
||||
|
||||
uint32_t getAudioDataStartPos();
|
||||
uint32_t getFileSize();
|
||||
uint32_t getFilePos();
|
||||
uint32_t getSampleRate();
|
||||
uint8_t getBitsPerSample();
|
||||
uint8_t getChannels();
|
||||
uint32_t getBitRate();
|
||||
uint32_t getAudioFileDuration();
|
||||
uint32_t getAudioCurrentTime();
|
||||
uint32_t getTotalPlayingTime();
|
||||
|
||||
esp_err_t i2s_mclk_pin_select(const uint8_t pin);
|
||||
uint32_t inBufferFilled(); // returns the number of stored bytes in the inputbuffer
|
||||
uint32_t inBufferFree(); // returns the number of free bytes in the inputbuffer
|
||||
void setTone(int8_t gainLowPass, int8_t gainBandPass, int8_t gainHighPass);
|
||||
[[deprecated]]void setInternalDAC(bool internalDAC = true, i2s_dac_mode_t channelEnabled = I2S_DAC_CHANNEL_LEFT_EN);
|
||||
void setI2SCommFMT_LSB(bool commFMT);
|
||||
|
||||
private:
|
||||
void UTF8toASCII(char* str);
|
||||
bool latinToUTF8(char* buff, size_t bufflen);
|
||||
void httpPrint(const char* url);
|
||||
void setDefaults(); // free buffers and set defaults
|
||||
void initInBuff();
|
||||
void processLocalFile();
|
||||
void processWebStream();
|
||||
void processPlayListData();
|
||||
void processM3U8entries(uint8_t nrOfEntries = 0, uint32_t seqNr = 0, uint8_t pos = 0, uint16_t targetDuration = 0);
|
||||
bool STfromEXTINF(char* str);
|
||||
void showCodecParams();
|
||||
int findNextSync(uint8_t* data, size_t len);
|
||||
int sendBytes(uint8_t* data, size_t len);
|
||||
void compute_audioCurrentTime(int bd);
|
||||
void printDecodeError(int r);
|
||||
void showID3Tag(const char* tag, const char* val);
|
||||
void unicode2utf8(char* buff, uint32_t len);
|
||||
int read_WAV_Header(uint8_t* data, size_t len);
|
||||
int read_FLAC_Header(uint8_t *data, size_t len);
|
||||
int read_MP3_Header(uint8_t* data, size_t len);
|
||||
int read_M4A_Header(uint8_t* data, size_t len);
|
||||
int read_OGG_Header(uint8_t *data, size_t len);
|
||||
bool setSampleRate(uint32_t hz);
|
||||
bool setBitsPerSample(int bits);
|
||||
bool setChannels(int channels);
|
||||
bool setBitrate(int br);
|
||||
bool playChunk();
|
||||
bool playSample(int16_t sample[2]) ;
|
||||
bool playI2Sremains();
|
||||
int32_t Gain(int16_t s[2]);
|
||||
bool fill_InputBuf();
|
||||
void showstreamtitle(const char* ml);
|
||||
bool parseContentType(const char* ct);
|
||||
void processAudioHeaderData();
|
||||
bool readMetadata(uint8_t b, bool first = false);
|
||||
esp_err_t I2Sstart(uint8_t i2s_num);
|
||||
esp_err_t I2Sstop(uint8_t i2s_num);
|
||||
void urlencode(char* buff, uint16_t buffLen, bool spacesOnly = false);
|
||||
int16_t* IIR_filterChain0(int16_t iir_in[2], bool clear = false);
|
||||
int16_t* IIR_filterChain1(int16_t* iir_in, bool clear = false);
|
||||
int16_t* IIR_filterChain2(int16_t* iir_in, bool clear = false);
|
||||
inline void setDatamode(uint8_t dm){m_datamode=dm;}
|
||||
inline uint8_t getDatamode(){return m_datamode;}
|
||||
inline uint32_t streamavail() {if(m_f_ssl==false) return client.available(); else return clientsecure.available();}
|
||||
void IIR_calculateCoefficients(int8_t G1, int8_t G2, int8_t G3);
|
||||
|
||||
// implement several function with respect to the index of string
|
||||
void trim(char *s) {
|
||||
//fb trim in place
|
||||
char *pe;
|
||||
char *p = s;
|
||||
while ( isspace(*p) ) p++; //left
|
||||
pe = p; //right
|
||||
while ( *pe != '\0' ) pe++;
|
||||
do {
|
||||
pe--;
|
||||
} while ( (pe > p) && isspace(*pe) );
|
||||
if (p == s) {
|
||||
*++pe = '\0';
|
||||
} else { //move
|
||||
while ( p <= pe ) *s++ = *p++;
|
||||
*s = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
bool startsWith (const char* base, const char* str) {
|
||||
//fb
|
||||
char c;
|
||||
while ( (c = *str++) != '\0' )
|
||||
if (c != *base++) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool endsWith (const char* base, const char* str) {
|
||||
//fb
|
||||
int slen = strlen(str) - 1;
|
||||
const char *p = base + strlen(base) - 1;
|
||||
while(p > base && isspace(*p)) p--; // rtrim
|
||||
p -= slen;
|
||||
if (p < base) return false;
|
||||
return (strncmp(p, str, slen) == 0);
|
||||
}
|
||||
|
||||
int indexOf (const char* base, const char* str, int startIndex) {
|
||||
//fb
|
||||
const char *p = base;
|
||||
for (; startIndex > 0; startIndex--)
|
||||
if (*p++ == '\0') return -1;
|
||||
char* pos = strstr(p, str);
|
||||
if (pos == nullptr) return -1;
|
||||
return pos - base;
|
||||
}
|
||||
|
||||
int indexOf (const char* base, char ch, int startIndex) {
|
||||
//fb
|
||||
const char *p = base;
|
||||
for (; startIndex > 0; startIndex--)
|
||||
if (*p++ == '\0') return -1;
|
||||
char *pos = strchr(p, ch);
|
||||
if (pos == nullptr) return -1;
|
||||
return pos - base;
|
||||
}
|
||||
|
||||
int lastIndexOf(const char* haystack, const char* needle) {
|
||||
//fb
|
||||
int nlen = strlen(needle);
|
||||
if (nlen == 0) return -1;
|
||||
const char *p = haystack - nlen + strlen(haystack);
|
||||
while (p >= haystack) {
|
||||
int i = 0;
|
||||
while (needle[i] == p[i])
|
||||
if (++i == nlen) return p - haystack;
|
||||
p--;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int lastIndexOf(const char* haystack, const char needle) {
|
||||
//fb
|
||||
const char *p = strrchr(haystack, needle);
|
||||
return (p ? p - haystack : -1);
|
||||
}
|
||||
|
||||
int specialIndexOf (uint8_t* base, const char* str, int baselen, bool exact = false){
|
||||
int result; // seek for str in buffer or in header up to baselen, not nullterninated
|
||||
if (strlen(str) > baselen) return -1; // if exact == true seekstr in buffer must have "\0" at the end
|
||||
for (int i = 0; i < baselen - strlen(str); i++){
|
||||
result = i;
|
||||
for (int j = 0; j < strlen(str) + exact; j++){
|
||||
if (*(base + i + j) != *(str + j)){
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result >= 0) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
size_t bigEndian(uint8_t* base, uint8_t numBytes, uint8_t shiftLeft = 8){
|
||||
size_t result = 0;
|
||||
if(numBytes < 1 or numBytes > 4) return 0;
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
result += *(base + i) << (numBytes -i - 1) * shiftLeft;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
bool b64encode(const char* source, uint16_t sourceLength, char* dest){
|
||||
size_t size = base64_encode_expected_len(sourceLength) + 1;
|
||||
char * buffer = (char *) malloc(size);
|
||||
if(buffer) {
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
int len = base64_encode_block(&source[0], sourceLength, &buffer[0], &_state);
|
||||
len = base64_encode_blockend((buffer + len), &_state);
|
||||
memcpy(dest, buffer, strlen(buffer));
|
||||
dest[strlen(buffer)] = '\0';
|
||||
free(buffer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
size_t urlencode_expected_len(const char* source){
|
||||
size_t expectedLen = strlen(source);
|
||||
for(int i = 0; i < strlen(source); i++) {
|
||||
if(isalnum(source[i])){;}
|
||||
else expectedLen += 2;
|
||||
}
|
||||
return expectedLen;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 };
|
||||
enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 };
|
||||
enum : int { CODEC_NONE, CODEC_WAV, CODEC_MP3, CODEC_AAC, CODEC_M4A, CODEC_FLAC, CODEC_OGG,
|
||||
CODEC_OGG_FLAC, CODEC_OGG_OPUS};
|
||||
enum : int { FORMAT_NONE = 0, FORMAT_M3U = 1, FORMAT_PLS = 2, FORMAT_ASX = 3, FORMAT_M3U8 = 4};
|
||||
enum : int { AUDIO_NONE, AUDIO_HEADER, AUDIO_DATA,
|
||||
AUDIO_PLAYLISTINIT, AUDIO_PLAYLISTHEADER, AUDIO_PLAYLISTDATA};
|
||||
enum : int { FLAC_BEGIN = 0, FLAC_MAGIC = 1, FLAC_MBH =2, FLAC_SINFO = 3, FLAC_PADDING = 4, FLAC_APP = 5,
|
||||
FLAC_SEEK = 6, FLAC_VORBIS = 7, FLAC_CUESHEET = 8, FLAC_PICTURE = 9, FLAC_OKAY = 100};
|
||||
enum : int { M4A_BEGIN = 0, M4A_FTYP = 1, M4A_CHK = 2, M4A_MOOV = 3, M4A_FREE = 4, M4A_TRAK = 5, M4A_MDAT = 6,
|
||||
M4A_ILST = 7, M4A_MP4A = 8, M4A_AMRDY = 99, M4A_OKAY = 100};
|
||||
enum : int { OGG_BEGIN = 0, OGG_MAGIC = 1, OGG_HEADER = 2, OGG_FIRST = 3, OGG_AMRDY = 99, OGG_OKAY = 100};
|
||||
typedef enum { LEFTCHANNEL=0, RIGHTCHANNEL=1 } SampleIndex;
|
||||
typedef enum { LOWSHELF = 0, PEAKEQ = 1, HIFGSHELF =2 } FilterType;
|
||||
|
||||
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
|
||||
|
||||
typedef struct _filter{
|
||||
float a0;
|
||||
float a1;
|
||||
float a2;
|
||||
float b1;
|
||||
float b2;
|
||||
} filter_t;
|
||||
|
||||
File audiofile; // @suppress("Abstract class cannot be instantiated")
|
||||
WiFiClient client; // @suppress("Abstract class cannot be instantiated")
|
||||
WiFiClientSecure clientsecure; // @suppress("Abstract class cannot be instantiated")
|
||||
WiFiUDP udpclient; // @suppress("Abstract class cannot be instantiated")
|
||||
i2s_config_t m_i2s_config; // stores values for I2S driver
|
||||
i2s_pin_config_t m_pin_config;
|
||||
|
||||
const size_t m_frameSizeWav = 1600;
|
||||
const size_t m_frameSizeMP3 = 1600;
|
||||
const size_t m_frameSizeAAC = 1600;
|
||||
const size_t m_frameSizeFLAC = 4096 * 4;
|
||||
|
||||
char chbuf[512 + 128]; // must be greater than m_lastHost #254
|
||||
char m_lastHost[512]; // Store the last URL to a webstream
|
||||
char* m_playlistBuff = NULL; // stores playlistdata
|
||||
const uint16_t m_plsBuffEntryLen = 256; // length of each entry in playlistBuff
|
||||
filter_t m_filter[3]; // digital filters
|
||||
int m_LFcount = 0; // Detection of end of header
|
||||
uint32_t m_sampleRate=16000;
|
||||
uint32_t m_bitRate=0; // current bitrate given fom decoder
|
||||
uint32_t m_avr_bitrate = 0; // average bitrate, median computed by VBR
|
||||
int m_readbytes=0; // bytes read
|
||||
int m_metalen=0; // Number of bytes in metadata
|
||||
int m_controlCounter = 0; // Status within readID3data() and readWaveHeader()
|
||||
int8_t m_balance = 0; // -16 (mute left) ... +16 (mute right)
|
||||
uint8_t m_vol=64; // volume
|
||||
uint8_t m_bitsPerSample = 16; // bitsPerSample
|
||||
uint8_t m_channels=2;
|
||||
uint8_t m_i2s_num = I2S_NUM_0; // I2S_NUM_0 or I2S_NUM_1
|
||||
uint8_t m_playlistFormat = 0; // M3U, PLS, ASX
|
||||
uint8_t m_m3u8codec = CODEC_NONE; // M4A
|
||||
uint8_t m_codec = CODEC_NONE; //
|
||||
uint8_t m_filterType[2]; // lowpass, highpass
|
||||
int16_t m_outBuff[2048*2]; // Interleaved L/R
|
||||
int16_t m_validSamples = 0;
|
||||
int16_t m_curSample = 0;
|
||||
uint16_t m_st_remember = 0; // Save hash from the last streamtitle
|
||||
uint16_t m_datamode = 0; // Statemaschine
|
||||
uint8_t m_flacBitsPerSample = 0; // bps should be 16
|
||||
uint8_t m_flacNumChannels = 0; // can be read out in the FLAC file header
|
||||
uint32_t m_flacSampleRate = 0; // can be read out in the FLAC file header
|
||||
uint16_t m_flacMaxFrameSize = 0; // can be read out in the FLAC file header
|
||||
uint16_t m_flacMaxBlockSize = 0; // can be read out in the FLAC file header
|
||||
uint32_t m_flacTotalSamplesInStream = 0; // can be read out in the FLAC file header
|
||||
uint32_t m_metaint = 0; // Number of databytes between metadata
|
||||
uint32_t m_chunkcount = 0 ; // Counter for chunked transfer
|
||||
uint32_t m_t0 = 0; // store millis(), is needed for a small delay
|
||||
uint32_t m_contentlength = 0; // Stores the length if the stream comes from fileserver
|
||||
uint32_t m_bytesNotDecoded = 0; // pictures or something else that comes with the stream
|
||||
uint32_t m_PlayingStartTime = 0; // Stores the milliseconds after the start of the audio
|
||||
bool m_f_swm = true; // Stream without metadata
|
||||
bool m_f_unsync = false; // set within ID3 tag but not used
|
||||
bool m_f_exthdr = false; // ID3 extended header
|
||||
bool m_f_localfile = false ; // Play from local mp3-file
|
||||
bool m_f_webstream = false ; // Play from URL
|
||||
bool m_f_ssl = false;
|
||||
bool m_f_running = false;
|
||||
bool m_f_firstCall = false; // InitSequence for processWebstream and processLokalFile
|
||||
bool m_f_ctseen = false; // First line of header seen or not
|
||||
bool m_f_chunked = false ; // Station provides chunked transfer
|
||||
bool m_f_firstmetabyte = false; // True if first metabyte (counter)
|
||||
bool m_f_playing = false; // valid mp3 stream recognized
|
||||
bool m_f_webfile = false; // assume it's a radiostream, not a podcast
|
||||
bool m_f_tts = false; // text to speech
|
||||
bool m_f_psram = false; // set if PSRAM is availabe
|
||||
bool m_f_loop = false; // Set if audio file should loop
|
||||
bool m_f_forceMono = false; // if true stereo -> mono
|
||||
bool m_f_internalDAC = false; // false: output vis I2S, true output via internal DAC
|
||||
bool m_f_rtsp = false; // set if RTSP is used (m3u8 stream)
|
||||
bool m_f_m3u8data = false; // used in processM3U8entries
|
||||
bool m_f_Log = true; // if m3u8: log is cancelled
|
||||
bool m_f_continue = false; // next m3u8 chunk is available
|
||||
bool m_f_initInbuffOnce = false; // init InBuff only once
|
||||
i2s_dac_mode_t m_f_channelEnabled = I2S_DAC_CHANNEL_LEFT_EN; // internal DAC on GPIO26 for M5StickC/Plus
|
||||
uint32_t m_audioFileDuration = 0;
|
||||
float m_audioCurrentTime = 0;
|
||||
uint32_t m_audioDataStart = 0; // in bytes
|
||||
size_t m_audioDataSize = 0; //
|
||||
float m_filterBuff[3][2][2][2]; // IIR filters memory for Audio DSP
|
||||
size_t m_i2s_bytesWritten = 0; // set in i2s_write() but not used
|
||||
size_t m_file_size = 0; // size of the file
|
||||
uint16_t m_filterFrequency[2];
|
||||
int8_t m_gain0 = 0; // cut or boost filters (EQ)
|
||||
int8_t m_gain1 = 0;
|
||||
int8_t m_gain2 = 0;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
10224
yoRadio/src/audioI2S/aac_decoder/aac_decoder.cpp
Normal file
10224
yoRadio/src/audioI2S/aac_decoder/aac_decoder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
583
yoRadio/src/audioI2S/aac_decoder/aac_decoder.h
Normal file
583
yoRadio/src/audioI2S/aac_decoder/aac_decoder.h
Normal file
@@ -0,0 +1,583 @@
|
||||
// based on helix aac decoder
|
||||
#pragma once
|
||||
//#pragma GCC optimize ("O3")
|
||||
//#pragma GCC diagnostic ignored "-Wnarrowing"
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define AAC_ENABLE_MPEG4
|
||||
//#define AAC_ENABLE_SBR // needs additional 60KB Heap,
|
||||
|
||||
#define ASSERT(x) /* do nothing */
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) std::max(a,b)
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) std::min(a,b)
|
||||
#endif
|
||||
|
||||
|
||||
/* AAC file format */
|
||||
enum {
|
||||
AAC_FF_Unknown = 0, /* should be 0 on init */
|
||||
AAC_FF_ADTS = 1,
|
||||
AAC_FF_ADIF = 2,
|
||||
AAC_FF_RAW = 3
|
||||
};
|
||||
|
||||
/* syntactic element type */
|
||||
enum {
|
||||
AAC_ID_INVALID = -1,
|
||||
AAC_ID_SCE = 0,
|
||||
AAC_ID_CPE = 1,
|
||||
AAC_ID_CCE = 2,
|
||||
AAC_ID_LFE = 3,
|
||||
AAC_ID_DSE = 4,
|
||||
AAC_ID_PCE = 5,
|
||||
AAC_ID_FIL = 6,
|
||||
AAC_ID_END = 7
|
||||
};
|
||||
|
||||
enum {
|
||||
ERR_AAC_NONE = 0,
|
||||
ERR_AAC_INDATA_UNDERFLOW = -1,
|
||||
ERR_AAC_NULL_POINTER = -2,
|
||||
ERR_AAC_INVALID_ADTS_HEADER = -3,
|
||||
ERR_AAC_INVALID_ADIF_HEADER = -4,
|
||||
ERR_AAC_INVALID_FRAME = -5,
|
||||
ERR_AAC_MPEG4_UNSUPPORTED = -6,
|
||||
ERR_AAC_CHANNEL_MAP = -7,
|
||||
ERR_AAC_SYNTAX_ELEMENT = -8,
|
||||
ERR_AAC_DEQUANT = -9,
|
||||
ERR_AAC_STEREO_PROCESS = -10,
|
||||
ERR_AAC_PNS = -11,
|
||||
ERR_AAC_SHORT_BLOCK_DEINT = -12,
|
||||
ERR_AAC_TNS = -13,
|
||||
ERR_AAC_IMDCT = -14,
|
||||
ERR_AAC_NCHANS_TOO_HIGH = -15,
|
||||
ERR_AAC_SBR_INIT = -16,
|
||||
ERR_AAC_SBR_BITSTREAM = -17,
|
||||
ERR_AAC_SBR_DATA = -18,
|
||||
ERR_AAC_SBR_PCM_FORMAT = -19,
|
||||
ERR_AAC_SBR_NCHANS_TOO_HIGH = -20,
|
||||
ERR_AAC_SBR_SINGLERATE_UNSUPPORTED = -21,
|
||||
ERR_AAC_RAWBLOCK_PARAMS = -22,
|
||||
ERR_AAC_UNKNOWN = -9999
|
||||
};
|
||||
|
||||
enum {
|
||||
SBR_GRID_FIXFIX = 0,
|
||||
SBR_GRID_FIXVAR = 1,
|
||||
SBR_GRID_VARFIX = 2,
|
||||
SBR_GRID_VARVAR = 3
|
||||
};
|
||||
|
||||
enum {
|
||||
HuffTabSBR_tEnv15 = 0,
|
||||
HuffTabSBR_fEnv15 = 1,
|
||||
HuffTabSBR_tEnv15b = 2,
|
||||
HuffTabSBR_fEnv15b = 3,
|
||||
HuffTabSBR_tEnv30 = 4,
|
||||
HuffTabSBR_fEnv30 = 5,
|
||||
HuffTabSBR_tEnv30b = 6,
|
||||
HuffTabSBR_fEnv30b = 7,
|
||||
HuffTabSBR_tNoise30 = 8,
|
||||
HuffTabSBR_fNoise30 = 5,
|
||||
HuffTabSBR_tNoise30b = 9,
|
||||
HuffTabSBR_fNoise30b = 7
|
||||
};
|
||||
|
||||
typedef struct _AACDecInfo_t {
|
||||
/* raw decoded data, before rounding to 16-bit PCM (for postprocessing such as SBR) */
|
||||
void *rawSampleBuf[2];
|
||||
int rawSampleBytes;
|
||||
int rawSampleFBits;
|
||||
/* fill data (can be used for processing SBR or other extensions) */
|
||||
uint8_t *fillBuf;
|
||||
int fillCount;
|
||||
int fillExtType;
|
||||
int prevBlockID; /* block information */
|
||||
int currBlockID;
|
||||
int currInstTag;
|
||||
int sbDeinterleaveReqd[2]; // [MAX_NCHANS_ELEM]
|
||||
int adtsBlocksLeft;
|
||||
int bitRate; /* user-accessible info */
|
||||
int nChans;
|
||||
int sampRate;
|
||||
float compressionRatio;
|
||||
int id; /* 0: MPEG-4, 1: MPEG2 */
|
||||
int profile; /* 0: Main profile, 1: LowComplexity (LC), 2: ScalableSamplingRate (SSR), 3: reserved */
|
||||
int format;
|
||||
int sbrEnabled;
|
||||
int tnsUsed;
|
||||
int pnsUsed;
|
||||
int frameCount;
|
||||
} AACDecInfo_t;
|
||||
|
||||
|
||||
typedef struct _aac_BitStreamInfo_t {
|
||||
uint8_t *bytePtr;
|
||||
unsigned int iCache;
|
||||
int cachedBits;
|
||||
int nBytes;
|
||||
} aac_BitStreamInfo_t;
|
||||
|
||||
typedef union _U64 {
|
||||
int64_t w64;
|
||||
struct {
|
||||
unsigned int lo32;
|
||||
signed int hi32;
|
||||
} r;
|
||||
} U64;
|
||||
|
||||
typedef struct _AACFrameInfo_t {
|
||||
int bitRate;
|
||||
int nChans;
|
||||
int sampRateCore;
|
||||
int sampRateOut;
|
||||
int bitsPerSample;
|
||||
int outputSamps;
|
||||
int profile;
|
||||
int tnsUsed;
|
||||
int pnsUsed;
|
||||
} AACFrameInfo_t;
|
||||
|
||||
typedef struct _HuffInfo_t {
|
||||
int maxBits; /* number of bits in longest codeword */
|
||||
uint8_t count[20]; /* count[MAX_HUFF_BITS] = number of codes with length i+1 bits */
|
||||
int offset; /* offset into symbol table */
|
||||
} HuffInfo_t;
|
||||
|
||||
typedef struct _PulseInfo_t {
|
||||
uint8_t pulseDataPresent;
|
||||
uint8_t numPulse;
|
||||
uint8_t startSFB;
|
||||
uint8_t offset[4]; // [MAX_PULSES]
|
||||
uint8_t amp[4]; // [MAX_PULSES]
|
||||
} PulseInfo_t;
|
||||
|
||||
typedef struct _TNSInfo_t {
|
||||
uint8_t tnsDataPresent;
|
||||
uint8_t numFilt[8]; // [MAX_TNS_FILTERS] max 1 filter each for 8 short windows, or 3 filters for 1 long window
|
||||
uint8_t coefRes[8]; // [MAX_TNS_FILTERS]
|
||||
uint8_t length[8]; // [MAX_TNS_FILTERS]
|
||||
uint8_t order[8]; // [MAX_TNS_FILTERS]
|
||||
uint8_t dir[8]; // [MAX_TNS_FILTERS]
|
||||
int8_t coef[60]; // [MAX_TNS_COEFS] max 3 filters * 20 coefs for 1 long window,
|
||||
// or 1 filter * 7 coefs for each of 8 short windows
|
||||
} TNSInfo_t;
|
||||
|
||||
typedef struct _GainControlInfo_t {
|
||||
uint8_t gainControlDataPresent;
|
||||
uint8_t maxBand;
|
||||
uint8_t adjNum[3][8]; // [MAX_GAIN_BANDS][MAX_GAIN_WIN]
|
||||
uint8_t alevCode[3][8][7]; // [MAX_GAIN_BANDS][MAX_GAIN_WIN][MAX_GAIN_ADJUST]
|
||||
uint8_t alocCode[3][8][7]; // [MAX_GAIN_BANDS][MAX_GAIN_WIN][MAX_GAIN_ADJUST]
|
||||
} GainControlInfo_t;
|
||||
|
||||
typedef struct _ICSInfo_t {
|
||||
uint8_t icsResBit;
|
||||
uint8_t winSequence;
|
||||
uint8_t winShape;
|
||||
uint8_t maxSFB;
|
||||
uint8_t sfGroup;
|
||||
uint8_t predictorDataPresent;
|
||||
uint8_t predictorReset;
|
||||
uint8_t predictorResetGroupNum;
|
||||
uint8_t predictionUsed[41]; // [MAX_PRED_SFB]
|
||||
uint8_t numWinGroup;
|
||||
uint8_t winGroupLen[8]; // [MAX_WIN_GROUPS]
|
||||
} ICSInfo_t;
|
||||
|
||||
typedef struct _ADTSHeader_t {
|
||||
/* fixed */
|
||||
uint8_t id; /* MPEG bit - should be 1 */
|
||||
uint8_t layer; /* MPEG layer - should be 0 */
|
||||
uint8_t protectBit; /* 0 = CRC word follows, 1 = no CRC word */
|
||||
uint8_t profile; /* 0 = main, 1 = LC, 2 = SSR, 3 = reserved */
|
||||
uint8_t sampRateIdx; /* sample rate index range = [0, 11] */
|
||||
uint8_t privateBit; /* ignore */
|
||||
uint8_t channelConfig; /* 0 = implicit, >0 = use default table */
|
||||
uint8_t origCopy; /* 0 = copy, 1 = original */
|
||||
uint8_t home; /* ignore */
|
||||
/* variable */
|
||||
uint8_t copyBit; /* 1 bit of the 72-bit copyright ID (transmitted as 1 bit per frame) */
|
||||
uint8_t copyStart; /* 1 = this bit starts the 72-bit ID, 0 = it does not */
|
||||
int frameLength; /* length of frame */
|
||||
int bufferFull; /* number of 32-bit words left in enc buffer, 0x7FF = VBR */
|
||||
uint8_t numRawDataBlocks; /* number of raw data blocks in frame */
|
||||
/* CRC */
|
||||
int crcCheckWord; /* 16-bit CRC check word (present if protectBit == 0) */
|
||||
} ADTSHeader_t;
|
||||
|
||||
typedef struct _ADIFHeader_t {
|
||||
uint8_t copyBit; /* 0 = no copyright ID, 1 = 72-bit copyright ID follows immediately */
|
||||
uint8_t origCopy; /* 0 = copy, 1 = original */
|
||||
uint8_t home; /* ignore */
|
||||
uint8_t bsType; /* bitstream type: 0 = CBR, 1 = VBR */
|
||||
int bitRate; /* bitRate: CBR = bits/sec, VBR = peak bits/frame, 0 = unknown */
|
||||
uint8_t numPCE; /* number of program config elements (max = 16) */
|
||||
int bufferFull; /* bits left in bit reservoir */
|
||||
uint8_t copyID[9]; /* [ADIF_COPYID_SIZE] optional 72-bit copyright ID */
|
||||
} ADIFHeader_t;
|
||||
|
||||
/* sizeof(ProgConfigElement_t) = 82 bytes (if KEEP_PCE_COMMENTS not defined) */
|
||||
typedef struct _ProgConfigElement_t {
|
||||
uint8_t elemInstTag; /* element instance tag */
|
||||
uint8_t profile; /* 0 = main, 1 = LC, 2 = SSR, 3 = reserved */
|
||||
uint8_t sampRateIdx; /* sample rate index range = [0, 11] */
|
||||
uint8_t numFCE; /* number of front channel elements (max = 15) */
|
||||
uint8_t numSCE; /* number of side channel elements (max = 15) */
|
||||
uint8_t numBCE; /* number of back channel elements (max = 15) */
|
||||
uint8_t numLCE; /* number of LFE channel elements (max = 3) */
|
||||
uint8_t numADE; /* number of associated data elements (max = 7) */
|
||||
uint8_t numCCE; /* number of valid channel coupling elements (max = 15) */
|
||||
uint8_t monoMixdown; /* mono mixdown: bit 4 = present flag, bits 3-0 = element number */
|
||||
uint8_t stereoMixdown; /* stereo mixdown: bit 4 = present flag, bits 3-0 = element number */
|
||||
uint8_t matrixMixdown; /* bit 4 = present flag, bit 3 = unused,bits 2-1 = index, bit 0 = pseudo-surround enable */
|
||||
uint8_t fce[15]; /* [MAX_NUM_FCE] front element channel pair: bit 4 = SCE/CPE flag, bits 3-0 = inst tag */
|
||||
uint8_t sce[15]; /* [MAX_NUM_SCE] side element channel pair: bit 4 = SCE/CPE flag, bits 3-0 = inst tag */
|
||||
uint8_t bce[15]; /* [MAX_NUM_BCE] back element channel pair: bit 4 = SCE/CPE flag, bits 3-0 = inst tag */
|
||||
uint8_t lce[3]; /* [MAX_NUM_LCE] instance tag for LFE elements */
|
||||
uint8_t ade[7]; /* [MAX_NUM_ADE] instance tag for ADE elements */
|
||||
uint8_t cce[15]; /* [MAX_NUM_BCE] channel coupling elements: bit 4 = switching flag, bits 3-0 = inst tag */
|
||||
} ProgConfigElement_t;
|
||||
|
||||
typedef struct _SBRHeader {
|
||||
int count;
|
||||
|
||||
uint8_t ampRes;
|
||||
uint8_t startFreq;
|
||||
uint8_t stopFreq;
|
||||
uint8_t crossOverBand;
|
||||
uint8_t resBitsHdr;
|
||||
uint8_t hdrExtra1;
|
||||
uint8_t hdrExtra2;
|
||||
|
||||
uint8_t freqScale;
|
||||
uint8_t alterScale;
|
||||
uint8_t noiseBands;
|
||||
|
||||
uint8_t limiterBands;
|
||||
uint8_t limiterGains;
|
||||
uint8_t interpFreq;
|
||||
uint8_t smoothMode;
|
||||
} SBRHeader;
|
||||
|
||||
/* need one SBRGrid per channel, updated every frame */
|
||||
typedef struct _SBRGrid {
|
||||
uint8_t frameClass;
|
||||
uint8_t ampResFrame;
|
||||
uint8_t pointer;
|
||||
|
||||
uint8_t numEnv; /* L_E */
|
||||
uint8_t envTimeBorder[5 + 1]; // [MAX_NUM_ENV+1] /* t_E */
|
||||
uint8_t freqRes[5]; // [MAX_NUM_ENV]/* r */
|
||||
uint8_t numNoiseFloors; /* L_Q */
|
||||
uint8_t noiseTimeBorder[2 + 1]; // [MAX_NUM_NOISE_FLOORS+1] /* t_Q */
|
||||
|
||||
uint8_t numEnvPrev;
|
||||
uint8_t numNoiseFloorsPrev;
|
||||
uint8_t freqResPrev;
|
||||
} SBRGrid;
|
||||
|
||||
/* need one SBRFreq per element (SCE/CPE/LFE), updated only on header reset */
|
||||
typedef struct _SBRFreq {
|
||||
int kStart; /* k_x */
|
||||
int nMaster;
|
||||
int nHigh;
|
||||
int nLow;
|
||||
int nLimiter; /* N_l */
|
||||
int numQMFBands; /* M */
|
||||
int numNoiseFloorBands; /* Nq */
|
||||
int kStartPrev;
|
||||
int numQMFBandsPrev;
|
||||
uint8_t freqMaster[48 + 1]; // [MAX_QMF_BANDS + 1] /* not necessary to save this after derived tables are generated */
|
||||
uint8_t freqHigh[48 + 1]; // [MAX_QMF_BANDS + 1]
|
||||
uint8_t freqLow[48 / 2 + 1]; // [MAX_QMF_BANDS / 2 + 1] /* nLow = nHigh - (nHigh >> 1) */
|
||||
uint8_t freqNoise[5 + 1]; // [MAX_NUM_NOISE_FLOOR_BANDS+1]
|
||||
uint8_t freqLimiter[48 / 2 + 5];// [MAX_QMF_BANDS / 2 + MAX_NUM_PATCHES] /* max (intermediate) size = nLow + numPatches - 1 */
|
||||
|
||||
uint8_t numPatches;
|
||||
uint8_t patchNumSubbands[5 + 1]; // [MAX_NUM_PATCHES + 1]
|
||||
uint8_t patchStartSubband[5 + 1]; // [MAX_NUM_PATCHES + 1]
|
||||
} SBRFreq;
|
||||
|
||||
typedef struct _SBRChan {
|
||||
int reset;
|
||||
uint8_t deltaFlagEnv[5]; // [MAX_NUM_ENV]
|
||||
uint8_t deltaFlagNoise[2]; // [MAX_NUM_NOISE_FLOORS]
|
||||
int8_t envDataQuant[5][48]; // [MAX_NUM_ENV][MAX_QMF_BANDS] /* range = [0, 127] */
|
||||
int8_t noiseDataQuant[2][5]; // [MAX_NUM_NOISE_FLOORS][MAX_NUM_NOISE_FLOOR_BANDS]
|
||||
|
||||
uint8_t invfMode[2][5]; // [2][MAX_NUM_NOISE_FLOOR_BANDS] /* invfMode[0/1][band] = prev/curr */
|
||||
int chirpFact[5]; // [MAX_NUM_NOISE_FLOOR_BANDS] /* bwArray */
|
||||
uint8_t addHarmonicFlag[2]; /* addHarmonicFlag[0/1] = prev/curr */
|
||||
uint8_t addHarmonic[2][64]; /* addHarmonic[0/1][band] = prev/curr */
|
||||
|
||||
int gbMask[2]; /* gbMask[0/1] = XBuf[0-31]/XBuf[32-39] */
|
||||
int8_t laPrev;
|
||||
|
||||
int noiseTabIndex;
|
||||
int sinIndex;
|
||||
int gainNoiseIndex;
|
||||
int gTemp[5][48]; // [MAX_NUM_SMOOTH_COEFS][MAX_QMF_BANDS]
|
||||
int qTemp[5][48]; // [MAX_NUM_SMOOTH_COEFS][MAX_QMF_BANDS]
|
||||
|
||||
} SBRChan;
|
||||
|
||||
|
||||
/* state info struct for baseline (MPEG-4 LC) decoding */
|
||||
typedef struct _PSInfoBase_t {
|
||||
int dataCount;
|
||||
uint8_t dataBuf[510]; // [DATA_BUF_SIZE]
|
||||
int fillCount;
|
||||
uint8_t fillBuf[269]; //[FILL_BUF_SIZE]
|
||||
/* state information which is the same throughout whole frame */
|
||||
int nChans;
|
||||
int useImpChanMap;
|
||||
int sampRateIdx;
|
||||
/* state information which can be overwritten by subsequent elements within frame */
|
||||
ICSInfo_t icsInfo[2]; // [MAX_NCHANS_ELEM]
|
||||
int commonWin;
|
||||
short scaleFactors[2][15*8]; // [MAX_NCHANS_ELEM][MAX_SF_BANDS]
|
||||
uint8_t sfbCodeBook[2][15*8]; // [MAX_NCHANS_ELEM][MAX_SF_BANDS]
|
||||
int msMaskPresent;
|
||||
uint8_t msMaskBits[(15 * 8 + 7) >> 3]; // [MAX_MS_MASK_BYTES]
|
||||
int pnsUsed[2]; // [MAX_NCHANS_ELEM]
|
||||
int pnsLastVal;
|
||||
int intensityUsed[2]; // [MAX_NCHANS_ELEM]
|
||||
// PulseInfo_t pulseInfo[2]; // [MAX_NCHANS_ELEM]
|
||||
TNSInfo_t tnsInfo[2]; // [MAX_NCHANS_ELEM]
|
||||
int tnsLPCBuf[20]; // [MAX_TNS_ORDER]
|
||||
int tnsWorkBuf[20]; //[MAX_TNS_ORDER]
|
||||
GainControlInfo_t gainControlInfo[2]; // [MAX_NCHANS_ELEM]
|
||||
int gbCurrent[2]; // [MAX_NCHANS_ELEM]
|
||||
int coef[2][1024]; // [MAX_NCHANS_ELEM][AAC_MAX_NSAMPS]
|
||||
#ifdef AAC_ENABLE_SBR
|
||||
int sbrWorkBuf[2][1024]; // [MAX_NCHANS_ELEM][AAC_MAX_NSAMPS];
|
||||
#endif
|
||||
/* state information which must be saved for each element and used in next frame */
|
||||
int overlap[2][1024]; // [AAC_MAX_NCHANS][AAC_MAX_NSAMPS]
|
||||
int prevWinShape[2]; // [AAC_MAX_NCHANS]
|
||||
} PSInfoBase_t;
|
||||
|
||||
typedef struct _PSInfoSBR {
|
||||
/* save for entire file */
|
||||
int frameCount;
|
||||
int sampRateIdx;
|
||||
|
||||
/* state info that must be saved for each channel */
|
||||
SBRHeader sbrHdr[2];
|
||||
SBRGrid sbrGrid[2];
|
||||
SBRFreq sbrFreq[2];
|
||||
SBRChan sbrChan[2];
|
||||
|
||||
/* temp variables, no need to save between blocks */
|
||||
uint8_t dataExtra;
|
||||
uint8_t resBitsData;
|
||||
uint8_t extendedDataPresent;
|
||||
int extendedDataSize;
|
||||
|
||||
int8_t envDataDequantScale[2][5]; // [MAX_NCHANS_ELEM][MAX_NUM_ENV
|
||||
int envDataDequant[2][5][48]; // [MAX_NCHANS_ELEM][MAX_NUM_ENV][MAX_QMF_BANDS
|
||||
int noiseDataDequant[2][2][5]; // [MAX_NCHANS_ELEM][MAX_NUM_NOISE_FLOORS][MAX_NUM_NOISE_FLOOR_BANDS]
|
||||
|
||||
int eCurr[48]; // [MAX_QMF_BANDS]
|
||||
uint8_t eCurrExp[48]; // [MAX_QMF_BANDS]
|
||||
uint8_t eCurrExpMax;
|
||||
int8_t la;
|
||||
|
||||
int crcCheckWord;
|
||||
int couplingFlag;
|
||||
int envBand;
|
||||
int eOMGainMax;
|
||||
int gainMax;
|
||||
int gainMaxFBits;
|
||||
int noiseFloorBand;
|
||||
int qp1Inv;
|
||||
int qqp1Inv;
|
||||
int sMapped;
|
||||
int sBand;
|
||||
int highBand;
|
||||
|
||||
int sumEOrigMapped;
|
||||
int sumECurrGLim;
|
||||
int sumSM;
|
||||
int sumQM;
|
||||
int gLimBoost[48];
|
||||
int qmLimBoost[48];
|
||||
int smBoost[48];
|
||||
|
||||
int smBuf[48];
|
||||
int qmLimBuf[48];
|
||||
int gLimBuf[48];
|
||||
int gLimFbits[48];
|
||||
|
||||
int gFiltLast[48];
|
||||
int qFiltLast[48];
|
||||
|
||||
/* large buffers */
|
||||
int delayIdxQMFA[2]; // [AAC_MAX_NCHANS]
|
||||
int delayQMFA[2][10 * 32]; // [AAC_MAX_NCHANS][DELAY_SAMPS_QMFA]
|
||||
int delayIdxQMFS[2]; // [AAC_MAX_NCHANS]
|
||||
int delayQMFS[2][10 * 128]; // [AAC_MAX_NCHANS][DELAY_SAMPS_QMFS]
|
||||
int XBufDelay[2][8][64][2]; // [AAC_MAX_NCHANS][HF_GEN][64][2]
|
||||
int XBuf[32+8][64][2];
|
||||
} PSInfoSBR_t;
|
||||
|
||||
bool AACDecoder_AllocateBuffers(void);
|
||||
int AACFlushCodec();
|
||||
void AACDecoder_FreeBuffers(void);
|
||||
bool AACDecoder_IsInit(void);
|
||||
int AACFindSyncWord(uint8_t *buf, int nBytes);
|
||||
int AACSetRawBlockParams(int copyLast, int nChans, int sampRateCore, int profile);
|
||||
int AACDecode(uint8_t *inbuf, int *bytesLeft, short *outbuf);
|
||||
int AACGetSampRate();
|
||||
int AACGetChannels();
|
||||
int AACGetID(); // 0-MPEG4, 1-MPEG2
|
||||
uint8_t AACGetProfile(); // 0-Main, 1-LC, 2-SSR, 3-reserved
|
||||
uint8_t AACGetFormat(); // 0-unknown 1-ADTS 2-ADIF, 3-RAW
|
||||
int AACGetBitsPerSample();
|
||||
int AACGetBitrate();
|
||||
int AACGetOutputSamps();
|
||||
int AACGetBitrate();
|
||||
void DecodeLPCCoefs(int order, int res, int8_t *filtCoef, int *a, int *b);
|
||||
int FilterRegion(int size, int dir, int order, int *audioCoef, int *a, int *hist);
|
||||
int TNSFilter(int ch);
|
||||
int DecodeSingleChannelElement();
|
||||
int DecodeChannelPairElement();
|
||||
int DecodeLFEChannelElement();
|
||||
int DecodeDataStreamElement();
|
||||
int DecodeProgramConfigElement(uint8_t idx);
|
||||
int DecodeFillElement();
|
||||
int DecodeNextElement(uint8_t **buf, int *bitOffset, int *bitsAvail);
|
||||
void PreMultiply(int tabidx, int *zbuf1);
|
||||
void PostMultiply(int tabidx, int *fft1);
|
||||
void PreMultiplyRescale(int tabidx, int *zbuf1, int es);
|
||||
void PostMultiplyRescale(int tabidx, int *fft1, int es);
|
||||
void DCT4(int tabidx, int *coef, int gb);
|
||||
void BitReverse(int *inout, int tabidx);
|
||||
void R4FirstPass(int *x, int bg);
|
||||
void R8FirstPass(int *x, int bg);
|
||||
void R4Core(int *x, int bg, int gp, int *wtab);
|
||||
void R4FFT(int tabidx, int *x);
|
||||
void UnpackZeros(int nVals, int *coef);
|
||||
void UnpackQuads(int cb, int nVals, int *coef);
|
||||
void UnpackPairsNoEsc(int cb, int nVals, int *coef);
|
||||
void UnpackPairsEsc(int cb, int nVals, int *coef);
|
||||
void DecodeSpectrumLong(int ch);
|
||||
void DecodeSpectrumShort(int ch);
|
||||
void DecWindowOverlap(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapLongStart(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapLongStop(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapShort(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev);
|
||||
int IMDCT(int ch, int chOut, short *outbuf);
|
||||
void DecodeICSInfo(ICSInfo_t *icsInfo, int sampRateIdx);
|
||||
void DecodeSectionData(int winSequence, int numWinGrp, int maxSFB, uint8_t *sfbCodeBook);
|
||||
int DecodeOneScaleFactor();
|
||||
void DecodeScaleFactors(int numWinGrp, int maxSFB, int globalGain, uint8_t *sfbCodeBook, short *scaleFactors);
|
||||
void DecodePulseInfo(uint8_t ch);
|
||||
void DecodeTNSInfo(int winSequence, TNSInfo_t *ti, int8_t *tnsCoef);
|
||||
void DecodeGainControlInfo(int winSequence, GainControlInfo_t *gi);
|
||||
void DecodeICS(int ch);
|
||||
int DecodeNoiselessData(uint8_t **buf, int *bitOffset, int *bitsAvail, int ch);
|
||||
int DecodeHuffmanScalar(const signed short *huffTab, const HuffInfo_t *huffTabInfo, unsigned int bitBuf, int32_t *val);
|
||||
int UnpackADTSHeader(uint8_t **buf, int *bitOffset, int *bitsAvail);
|
||||
int GetADTSChannelMapping(uint8_t *buf, int bitOffset, int bitsAvail);
|
||||
int GetNumChannelsADIF(int nPCE);
|
||||
int GetSampleRateIdxADIF(int nPCE);
|
||||
int UnpackADIFHeader(uint8_t **buf, int *bitOffset, int *bitsAvail);
|
||||
int SetRawBlockParams(int copyLast, int nChans, int sampRate, int profile);
|
||||
int PrepareRawBlock();
|
||||
int DequantBlock(int *inbuf, int nSamps, int scale);
|
||||
int AACDequantize(int ch);
|
||||
int DeinterleaveShortBlocks(int ch);
|
||||
unsigned int Get32BitVal(unsigned int *last);
|
||||
int InvRootR(int r);
|
||||
int ScaleNoiseVector(int *coef, int nVals, int sf);
|
||||
void GenerateNoiseVector(int *coef, int *last, int nVals);
|
||||
void CopyNoiseVector(int *coefL, int *coefR, int nVals);
|
||||
int PNS(int ch);
|
||||
int GetSampRateIdx(int sampRate);
|
||||
void StereoProcessGroup(int *coefL, int *coefR, const uint16_t *sfbTab, int msMaskPres, uint8_t *msMaskPtr,
|
||||
int msMaskOffset, int maxSFB, uint8_t *cbRight, short *sfRight, int *gbCurrent);
|
||||
int StereoProcess();
|
||||
int RatioPowInv(int a, int b, int c);
|
||||
int SqrtFix(int q, int fBitsIn, int *fBitsOut);
|
||||
int InvRNormalized(int r);
|
||||
void BitReverse32(int *inout);
|
||||
void R8FirstPass32(int *r0);
|
||||
void R4Core32(int *r0);
|
||||
void FFT32C(int *x);
|
||||
void CVKernel1(int *XBuf, int *accBuf);
|
||||
void CVKernel2(int *XBuf, int *accBuf);
|
||||
void SetBitstreamPointer(int nBytes, uint8_t *buf);
|
||||
inline void RefillBitstreamCache();
|
||||
unsigned int GetBits(int nBits);
|
||||
unsigned int GetBitsNoAdvance(int nBits);
|
||||
void AdvanceBitstream(int nBits);
|
||||
int CalcBitsUsed(uint8_t *startBuf, int startOffset);
|
||||
void ByteAlignBitstream();
|
||||
// SBR
|
||||
void InitSBRState();
|
||||
int DecodeSBRBitstream(int chBase);
|
||||
int DecodeSBRData(int chBase, short *outbuf);
|
||||
int FlushCodecSBR();
|
||||
void BubbleSort(uint8_t *v, int nItems);
|
||||
uint8_t VMin(uint8_t *v, int nItems);
|
||||
uint8_t VMax(uint8_t *v, int nItems);
|
||||
int CalcFreqMasterScaleZero(uint8_t *freqMaster, int alterScale, int k0, int k2);
|
||||
int CalcFreqMaster(uint8_t *freqMaster, int freqScale, int alterScale, int k0, int k2);
|
||||
int CalcFreqHigh(uint8_t *freqHigh, uint8_t *freqMaster, int nMaster, int crossOverBand);
|
||||
int CalcFreqLow(uint8_t *freqLow, uint8_t *freqHigh, int nHigh);
|
||||
int CalcFreqNoise(uint8_t *freqNoise, uint8_t *freqLow, int nLow, int kStart, int k2, int noiseBands);
|
||||
int BuildPatches(uint8_t *patchNumSubbands, uint8_t *patchStartSubband, uint8_t *freqMaster, int nMaster, int k0,
|
||||
int kStart, int numQMFBands, int sampRateIdx);
|
||||
int FindFreq(uint8_t *freq, int nFreq, uint8_t val);
|
||||
void RemoveFreq(uint8_t *freq, int nFreq, int removeIdx);
|
||||
int CalcFreqLimiter(uint8_t *freqLimiter, uint8_t *patchNumSubbands, uint8_t *freqLow, int nLow, int kStart,
|
||||
int limiterBands, int numPatches);
|
||||
int CalcFreqTables(SBRHeader *sbrHdr, SBRFreq *sbrFreq, int sampRateIdx);
|
||||
void EstimateEnvelope(SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, int env);
|
||||
int GetSMapped(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int env, int band, int la);
|
||||
void CalcMaxGain(SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, int ch, int env, int lim, int fbitsDQ);
|
||||
void CalcNoiseDivFactors(int q, int *qp1Inv, int *qqp1Inv);
|
||||
void CalcComponentGains(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch, int env, int lim, int fbitsDQ);
|
||||
void ApplyBoost(SBRFreq *sbrFreq, int lim, int fbitsDQ);
|
||||
void CalcGain(SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch, int env);
|
||||
void MapHF(SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int env, int hfReset);
|
||||
void AdjustHighFreq(SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch);
|
||||
int CalcCovariance1(int *XBuf, int *p01reN, int *p01imN, int *p12reN, int *p12imN, int *p11reN, int *p22reN);
|
||||
int CalcCovariance2(int *XBuf, int *p02reN, int *p02imN);
|
||||
void CalcLPCoefs(int *XBuf, int *a0re, int *a0im, int *a1re, int *a1im, int gb);
|
||||
void GenerateHighFreq(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch);
|
||||
int DecodeHuffmanScalar(const signed int *huffTab, const HuffInfo_t *huffTabInfo, unsigned int bitBuf, signed int *val);
|
||||
int DecodeOneSymbol(int huffTabIndex);
|
||||
int DequantizeEnvelope(int nBands, int ampRes, int8_t *envQuant, int *envDequant);
|
||||
void DequantizeNoise(int nBands, int8_t *noiseQuant, int *noiseDequant);
|
||||
void DecodeSBREnvelope(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch);
|
||||
void DecodeSBRNoise(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch);
|
||||
void UncoupleSBREnvelope(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChanR);
|
||||
void UncoupleSBRNoise(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChanR);
|
||||
void DecWindowOverlapNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapLongStartNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapLongStopNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev);
|
||||
void DecWindowOverlapShortNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev);
|
||||
void PreMultiply64(int *zbuf1);
|
||||
void PostMultiply64(int *fft1, int nSampsOut);
|
||||
void QMFAnalysisConv(int *cTab, int *delay, int dIdx, int *uBuf);
|
||||
int QMFAnalysis(int *inbuf, int *delay, int *XBuf, int fBitsIn, int *delayIdx, int qmfaBands);
|
||||
void QMFSynthesisConv(int *cPtr, int *delay, int dIdx, short *outbuf, int nChans);
|
||||
void QMFSynthesis(int *inbuf, int *delay, int *delayIdx, int qmfsBands, short *outbuf, int nChans);
|
||||
int UnpackSBRHeader(SBRHeader *sbrHdr);
|
||||
void UnpackSBRGrid(SBRHeader *sbrHdr, SBRGrid *sbrGrid);
|
||||
void UnpackDeltaTimeFreq(int numEnv, uint8_t *deltaFlagEnv, int numNoiseFloors, uint8_t *deltaFlagNoise);
|
||||
void UnpackInverseFilterMode(int numNoiseFloorBands, uint8_t *mode);
|
||||
void UnpackSinusoids(int nHigh, int addHarmonicFlag, uint8_t *addHarmonic);
|
||||
void CopyCouplingGrid(SBRGrid *sbrGridLeft, SBRGrid *sbrGridRight);
|
||||
void CopyCouplingInverseFilterMode(int numNoiseFloorBands, uint8_t *modeLeft, uint8_t *modeRight);
|
||||
void UnpackSBRSingleChannel(int chBase);
|
||||
void UnpackSBRChannelPair(int chBase);
|
||||
556
yoRadio/src/audioI2S/flac_decoder/flac_decoder.cpp
Normal file
556
yoRadio/src/audioI2S/flac_decoder/flac_decoder.cpp
Normal file
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* flac_decoder.cpp
|
||||
* Java source code from https://www.nayuki.io/page/simple-flac-implementation
|
||||
* adapted to ESP32
|
||||
*
|
||||
* Created on: Jul 03,2020
|
||||
* Updated on: Jul 03,2021
|
||||
*
|
||||
* Author: Wolle
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include "flac_decoder.h"
|
||||
#include "vector"
|
||||
using namespace std;
|
||||
|
||||
|
||||
FLACFrameHeader_t *FLACFrameHeader;
|
||||
FLACMetadataBlock_t *FLACMetadataBlock;
|
||||
FLACsubFramesBuff_t *FLACsubFramesBuff;
|
||||
|
||||
vector<int32_t>coefs;
|
||||
const uint16_t outBuffSize = 2048;
|
||||
uint16_t m_blockSize=0;
|
||||
uint16_t m_blockSizeLeft = 0;
|
||||
uint16_t m_validSamples = 0;
|
||||
uint8_t m_status = 0;
|
||||
uint8_t* m_inptr;
|
||||
int16_t m_bytesAvail;
|
||||
int16_t m_bytesDecoded = 0;
|
||||
float m_compressionRatio = 0;
|
||||
uint16_t m_rIndex=0;
|
||||
uint64_t m_bitBuffer = 0;
|
||||
uint8_t m_bitBufferLen = 0;
|
||||
bool m_f_OggS_found = false;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// FLAC INI SECTION
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool FLACDecoder_AllocateBuffers(void){
|
||||
if(psramFound()) {
|
||||
// PSRAM found, Buffer will be allocated in PSRAM
|
||||
if(!FLACFrameHeader) {FLACFrameHeader = (FLACFrameHeader_t*) ps_malloc(sizeof(FLACFrameHeader_t));}
|
||||
if(!FLACMetadataBlock) {FLACMetadataBlock = (FLACMetadataBlock_t*) ps_malloc(sizeof(FLACMetadataBlock_t));}
|
||||
if(!FLACsubFramesBuff) {FLACsubFramesBuff = (FLACsubFramesBuff_t*) ps_malloc(sizeof(FLACsubFramesBuff_t));}
|
||||
}
|
||||
else {
|
||||
if(!FLACFrameHeader) {FLACFrameHeader = (FLACFrameHeader_t*) malloc(sizeof(FLACFrameHeader_t));}
|
||||
if(!FLACMetadataBlock) {FLACMetadataBlock = (FLACMetadataBlock_t*) malloc(sizeof(FLACMetadataBlock_t));}
|
||||
if(!FLACsubFramesBuff) {FLACsubFramesBuff = (FLACsubFramesBuff_t*) malloc(sizeof(FLACsubFramesBuff_t));}
|
||||
}
|
||||
if(!FLACFrameHeader || !FLACMetadataBlock || !FLACsubFramesBuff ){
|
||||
log_e("not enough memory to allocate flacdecoder buffers");
|
||||
return false;
|
||||
}
|
||||
FLACDecoder_ClearBuffer();
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void FLACDecoder_ClearBuffer(){
|
||||
memset(FLACFrameHeader, 0, sizeof(FLACFrameHeader_t));
|
||||
memset(FLACMetadataBlock, 0, sizeof(FLACMetadataBlock_t));
|
||||
memset(FLACsubFramesBuff, 0, sizeof(FLACsubFramesBuff_t));
|
||||
m_status = DECODE_FRAME;
|
||||
return;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void FLACDecoder_FreeBuffers(){
|
||||
if(FLACFrameHeader) {free(FLACFrameHeader); FLACFrameHeader = NULL;}
|
||||
if(FLACMetadataBlock) {free(FLACMetadataBlock); FLACMetadataBlock = NULL;}
|
||||
if(FLACsubFramesBuff) {free(FLACsubFramesBuff); FLACsubFramesBuff = NULL;}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// B I T R E A D E R
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint32_t readUint(uint8_t nBits){
|
||||
while (m_bitBufferLen < nBits){
|
||||
uint8_t temp = *(m_inptr + m_rIndex);
|
||||
m_rIndex++;
|
||||
m_bytesAvail--;
|
||||
if(m_bytesAvail < 0) { log_i("error in bitreader"); }
|
||||
m_bitBuffer = (m_bitBuffer << 8) | temp;
|
||||
m_bitBufferLen += 8;
|
||||
}
|
||||
m_bitBufferLen -= nBits;
|
||||
uint32_t result = m_bitBuffer >> m_bitBufferLen;
|
||||
if (nBits < 32)
|
||||
result &= (1 << nBits) - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t readSignedInt(int nBits){
|
||||
int32_t temp = readUint(nBits) << (32 - nBits);
|
||||
temp = temp >> (32 - nBits); // The C++ compiler uses the sign bit to fill vacated bit positions
|
||||
return temp;
|
||||
}
|
||||
|
||||
int64_t readRiceSignedInt(uint8_t param){
|
||||
long val = 0;
|
||||
while (readUint(1) == 0)
|
||||
val++;
|
||||
val = (val << param) | readUint(param);
|
||||
return (val >> 1) ^ -(val & 1);
|
||||
}
|
||||
|
||||
void alignToByte() {
|
||||
m_bitBufferLen -= m_bitBufferLen % 8;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// F L A C - D E C O D E R
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void FLACSetRawBlockParams(uint8_t Chans, uint32_t SampRate, uint8_t BPS, uint32_t tsis, uint32_t AuDaLength){
|
||||
FLACMetadataBlock->numChannels = Chans;
|
||||
FLACMetadataBlock->sampleRate = SampRate;
|
||||
FLACMetadataBlock->bitsPerSample = BPS;
|
||||
FLACMetadataBlock->totalSamples = tsis; // total samples in stream
|
||||
FLACMetadataBlock->audioDataLength = AuDaLength;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void FLACDecoderReset(){ // set var to default
|
||||
m_status = DECODE_FRAME;
|
||||
m_bitBuffer = 0;
|
||||
m_bitBufferLen = 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int FLACFindSyncWord(unsigned char *buf, int nBytes) {
|
||||
int i;
|
||||
|
||||
/* find byte-aligned syncword - need 13 matching bits */
|
||||
for (i = 0; i < nBytes - 1; i++) {
|
||||
if ((buf[i + 0] & 0xFF) == 0xFF && (buf[i + 1] & 0xF8) == 0xF8) {
|
||||
FLACDecoderReset();
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int FLACFindOggSyncWord(unsigned char *buf, int nBytes){
|
||||
int i;
|
||||
|
||||
/* find byte-aligned syncword - need 13 matching bits */
|
||||
for (i = 0; i < nBytes - 1; i++) {
|
||||
if ((buf[i + 0] & 0xFF) == 0xFF && (buf[i + 1] & 0xF8) == 0xF8) {
|
||||
FLACDecoderReset();
|
||||
log_i("FLAC sync found");
|
||||
return i;
|
||||
}
|
||||
}
|
||||
/* find byte-aligned OGG Magic - OggS */
|
||||
for (i = 0; i < nBytes - 1; i++) {
|
||||
if ((buf[i + 0] == 'O') && (buf[i + 1] == 'g') && (buf[i + 2] == 'g') && (buf[i + 3] == 'S')) {
|
||||
FLACDecoderReset();
|
||||
log_i("OggS found");
|
||||
m_f_OggS_found = true;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int FLACparseOggHeader(unsigned char *buf){
|
||||
uint8_t i = 0;
|
||||
uint8_t ssv = *(buf + i); // stream_structure_version
|
||||
(void)ssv;
|
||||
i++;
|
||||
uint8_t htf = *(buf + i); // header_type_flag
|
||||
(void)htf;
|
||||
i++;
|
||||
uint32_t tmp = 0; // absolute granule position
|
||||
for (int j = 0; j < 4; j++) {
|
||||
tmp += *(buf + j + i) << (4 -j - 1) * 8;
|
||||
}
|
||||
i += 4;
|
||||
uint64_t agp = (uint64_t) tmp << 32;
|
||||
for (int j = 0; j < 4; j++) {
|
||||
agp += *(buf + j + i) << (4 -j - 1) * 8;
|
||||
}
|
||||
i += 4;
|
||||
uint32_t ssnr = 0; // stream serial number
|
||||
for (int j = 0; j < 4; j++) {
|
||||
ssnr += *(buf + j + i) << (4 -j - 1) * 8;
|
||||
}
|
||||
i += 4;
|
||||
uint32_t psnr = 0; // page sequence no
|
||||
for (int j = 0; j < 4; j++) {
|
||||
psnr += *(buf + j + i) << (4 -j - 1) * 8;
|
||||
}
|
||||
i += 4;
|
||||
uint32_t pchk = 0; // page checksum
|
||||
for (int j = 0; j < 4; j++) {
|
||||
pchk += *(buf + j + i) << (4 -j - 1) * 8;
|
||||
}
|
||||
i += 4;
|
||||
uint8_t psegm = *(buf + i);
|
||||
i++;
|
||||
uint8_t psegmBuff[256];
|
||||
uint32_t pageLen = 0;
|
||||
for(uint8_t j = 0; j < psegm; j++){
|
||||
psegmBuff[j] = *(buf + i);
|
||||
pageLen += psegmBuff[j];
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t FLACDecode(uint8_t *inbuf, int *bytesLeft, short *outbuf){
|
||||
|
||||
if(m_f_OggS_found == true){
|
||||
m_f_OggS_found = false;
|
||||
*bytesLeft -= FLACparseOggHeader(inbuf);
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
|
||||
if(m_status != OUT_SAMPLES){
|
||||
m_rIndex = 0;
|
||||
m_bytesAvail = (*bytesLeft);
|
||||
m_inptr = inbuf;
|
||||
}
|
||||
|
||||
if(m_status == DECODE_FRAME){ // Read a ton of header fields, and ignore most of them
|
||||
|
||||
if ((inbuf[0] == 'O') && (inbuf[1] == 'g') && (inbuf[2] == 'g') && (inbuf[3] == 'S')){
|
||||
*bytesLeft -= 4;
|
||||
m_f_OggS_found = true;
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
|
||||
uint32_t temp = readUint(8);
|
||||
uint16_t sync = temp << 6 |readUint(6);
|
||||
if (sync != 0x3FFE){
|
||||
log_i("Sync code expected 0x3FFE but received %X", sync);
|
||||
return ERR_FLAC_SYNC_CODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
readUint(1);
|
||||
FLACFrameHeader->blockingStrategy = readUint(1);
|
||||
FLACFrameHeader->blockSizeCode = readUint(4);
|
||||
FLACFrameHeader->sampleRateCode = readUint(4);
|
||||
FLACFrameHeader->chanAsgn = readUint(4);
|
||||
FLACFrameHeader->sampleSizeCode = readUint(3);
|
||||
|
||||
if(!FLACMetadataBlock->numChannels){
|
||||
if(FLACFrameHeader->chanAsgn == 0) FLACMetadataBlock->numChannels = 1;
|
||||
if(FLACFrameHeader->chanAsgn == 1) FLACMetadataBlock->numChannels = 2;
|
||||
if(FLACFrameHeader->chanAsgn > 7) FLACMetadataBlock->numChannels = 2;
|
||||
}
|
||||
if(FLACMetadataBlock->numChannels < 1) return ERR_FLAC_UNKNOWN_CHANNEL_ASSIGNMENT;
|
||||
|
||||
if(!FLACMetadataBlock->bitsPerSample){
|
||||
if(FLACFrameHeader->sampleSizeCode == 1) FLACMetadataBlock->bitsPerSample = 8;
|
||||
if(FLACFrameHeader->sampleSizeCode == 2) FLACMetadataBlock->bitsPerSample = 12;
|
||||
if(FLACFrameHeader->sampleSizeCode == 4) FLACMetadataBlock->bitsPerSample = 16;
|
||||
if(FLACFrameHeader->sampleSizeCode == 5) FLACMetadataBlock->bitsPerSample = 20;
|
||||
if(FLACFrameHeader->sampleSizeCode == 6) FLACMetadataBlock->bitsPerSample = 24;
|
||||
}
|
||||
if(FLACMetadataBlock->bitsPerSample > 16) return ERR_FLAC_BITS_PER_SAMPLE_TOO_BIG;
|
||||
if(FLACMetadataBlock->bitsPerSample < 8 ) return ERR_FLAG_BITS_PER_SAMPLE_UNKNOWN;
|
||||
|
||||
if(!FLACMetadataBlock->sampleRate){
|
||||
if(FLACFrameHeader->sampleRateCode == 1) FLACMetadataBlock->sampleRate = 88200;
|
||||
if(FLACFrameHeader->sampleRateCode == 2) FLACMetadataBlock->sampleRate = 176400;
|
||||
if(FLACFrameHeader->sampleRateCode == 3) FLACMetadataBlock->sampleRate = 192000;
|
||||
if(FLACFrameHeader->sampleRateCode == 4) FLACMetadataBlock->sampleRate = 8000;
|
||||
if(FLACFrameHeader->sampleRateCode == 5) FLACMetadataBlock->sampleRate = 16000;
|
||||
if(FLACFrameHeader->sampleRateCode == 6) FLACMetadataBlock->sampleRate = 22050;
|
||||
if(FLACFrameHeader->sampleRateCode == 7) FLACMetadataBlock->sampleRate = 24000;
|
||||
if(FLACFrameHeader->sampleRateCode == 8) FLACMetadataBlock->sampleRate = 32000;
|
||||
if(FLACFrameHeader->sampleRateCode == 9) FLACMetadataBlock->sampleRate = 44100;
|
||||
if(FLACFrameHeader->sampleRateCode == 10) FLACMetadataBlock->sampleRate = 48000;
|
||||
if(FLACFrameHeader->sampleRateCode == 11) FLACMetadataBlock->sampleRate = 96000;
|
||||
}
|
||||
|
||||
readUint(1);
|
||||
temp = (readUint(8) << 24);
|
||||
temp = ~temp;
|
||||
|
||||
uint32_t shift = 0x80000000; // Number of leading zeros
|
||||
int8_t count = 0;
|
||||
for(int i=0; i<32; i++){
|
||||
if((temp & shift) == 0) {count++; shift >>= 1;}
|
||||
else break;
|
||||
}
|
||||
count--;
|
||||
for (int i = 0; i < count; i++) readUint(8);
|
||||
m_blockSize = 0;
|
||||
|
||||
if (FLACFrameHeader->blockSizeCode == 1)
|
||||
m_blockSize = 192;
|
||||
else if (2 <= FLACFrameHeader->blockSizeCode && FLACFrameHeader->blockSizeCode <= 5)
|
||||
m_blockSize = 576 << (FLACFrameHeader->blockSizeCode - 2);
|
||||
else if (FLACFrameHeader->blockSizeCode == 6)
|
||||
m_blockSize = readUint(8) + 1;
|
||||
else if (FLACFrameHeader->blockSizeCode == 7)
|
||||
m_blockSize = readUint(16) + 1;
|
||||
else if (8 <= FLACFrameHeader->blockSizeCode && FLACFrameHeader->blockSizeCode <= 15)
|
||||
m_blockSize = 256 << (FLACFrameHeader->blockSizeCode - 8);
|
||||
else{
|
||||
return ERR_FLAC_RESERVED_BLOCKSIZE_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if(m_blockSize > 8192){
|
||||
log_e("Error: blockSize too big");
|
||||
return ERR_FLAC_BLOCKSIZE_TOO_BIG;
|
||||
}
|
||||
|
||||
if(FLACFrameHeader->sampleRateCode == 12)
|
||||
readUint(8);
|
||||
else if (FLACFrameHeader->sampleRateCode == 13 || FLACFrameHeader->sampleRateCode == 14){
|
||||
readUint(16);
|
||||
}
|
||||
readUint(8);
|
||||
m_status = DECODE_SUBFRAMES;
|
||||
*bytesLeft = m_bytesAvail;
|
||||
m_blockSizeLeft = m_blockSize;
|
||||
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
|
||||
if(m_status == DECODE_SUBFRAMES){
|
||||
|
||||
// Decode each channel's subframe, then skip footer
|
||||
int ret = decodeSubframes();
|
||||
if(ret != 0) return ret;
|
||||
m_status = OUT_SAMPLES;
|
||||
}
|
||||
|
||||
if(m_status == OUT_SAMPLES){ // Write the decoded samples
|
||||
// blocksize can be much greater than outbuff, so we can't stuff all in once
|
||||
// therefore we need often more than one loop (split outputblock into pieces)
|
||||
uint16_t blockSize;
|
||||
static uint16_t offset = 0;
|
||||
if(m_blockSize < outBuffSize + offset) blockSize = m_blockSize - offset;
|
||||
else blockSize = outBuffSize;
|
||||
|
||||
|
||||
for (int i = 0; i < blockSize; i++) {
|
||||
for (int j = 0; j < FLACMetadataBlock->numChannels; j++) {
|
||||
int val = FLACsubFramesBuff->samplesBuffer[j][i + offset];
|
||||
if (FLACMetadataBlock->bitsPerSample == 8) val += 128;
|
||||
outbuf[2*i+j] = val;
|
||||
}
|
||||
}
|
||||
|
||||
m_validSamples = blockSize * FLACMetadataBlock->numChannels;
|
||||
offset += blockSize;
|
||||
|
||||
if(offset != m_blockSize) return GIVE_NEXT_LOOP;
|
||||
offset = 0;
|
||||
if(offset > m_blockSize) { log_e("offset has a wrong value"); }
|
||||
}
|
||||
|
||||
alignToByte();
|
||||
readUint(16);
|
||||
m_bytesDecoded = *bytesLeft - m_bytesAvail;
|
||||
// log_i("m_bytesDecoded %i", m_bytesDecoded);
|
||||
// m_compressionRatio = (float)m_bytesDecoded / (float)m_blockSize * FLACMetadataBlock->numChannels * (16/8);
|
||||
// log_i("m_compressionRatio % f", m_compressionRatio);
|
||||
*bytesLeft = m_bytesAvail;
|
||||
m_status = DECODE_FRAME;
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint16_t FLACGetOutputSamps(){
|
||||
int vs = m_validSamples;
|
||||
m_validSamples=0;
|
||||
return vs;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint64_t FLACGetTotoalSamplesInStream(){
|
||||
return FLACMetadataBlock->totalSamples;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint8_t FLACGetBitsPerSample(){
|
||||
return FLACMetadataBlock->bitsPerSample;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint8_t FLACGetChannels(){
|
||||
return FLACMetadataBlock->numChannels;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint32_t FLACGetSampRate(){
|
||||
return FLACMetadataBlock->sampleRate;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint32_t FLACGetBitRate(){
|
||||
if(FLACMetadataBlock->totalSamples){
|
||||
float BitsPerSamp = (float)FLACMetadataBlock->audioDataLength / (float)FLACMetadataBlock->totalSamples * 8;
|
||||
return ((uint32_t)BitsPerSamp * FLACMetadataBlock->sampleRate);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint32_t FLACGetAudioFileDuration() {
|
||||
if(FLACGetSampRate()){
|
||||
uint32_t afd = FLACGetTotoalSamplesInStream()/ FLACGetSampRate(); // AudioFileDuration
|
||||
return afd;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t decodeSubframes(){
|
||||
if(FLACFrameHeader->chanAsgn <= 7) {
|
||||
for (int ch = 0; ch < FLACMetadataBlock->numChannels; ch++)
|
||||
decodeSubframe(FLACMetadataBlock->bitsPerSample, ch);
|
||||
}
|
||||
else if (8 <= FLACFrameHeader->chanAsgn && FLACFrameHeader->chanAsgn <= 10) {
|
||||
decodeSubframe(FLACMetadataBlock->bitsPerSample + (FLACFrameHeader->chanAsgn == 9 ? 1 : 0), 0);
|
||||
decodeSubframe(FLACMetadataBlock->bitsPerSample + (FLACFrameHeader->chanAsgn == 9 ? 0 : 1), 1);
|
||||
if(FLACFrameHeader->chanAsgn == 8) {
|
||||
for (int i = 0; i < m_blockSize; i++)
|
||||
FLACsubFramesBuff->samplesBuffer[1][i] = (
|
||||
FLACsubFramesBuff->samplesBuffer[0][i] -
|
||||
FLACsubFramesBuff->samplesBuffer[1][i]);
|
||||
}
|
||||
else if (FLACFrameHeader->chanAsgn == 9) {
|
||||
for (int i = 0; i < m_blockSize; i++)
|
||||
FLACsubFramesBuff->samplesBuffer[0][i] += FLACsubFramesBuff->samplesBuffer[1][i];
|
||||
}
|
||||
else if (FLACFrameHeader->chanAsgn == 10) {
|
||||
for (int i = 0; i < m_blockSize; i++) {
|
||||
long side = FLACsubFramesBuff->samplesBuffer[1][i];
|
||||
long right = FLACsubFramesBuff->samplesBuffer[0][i] - (side >> 1);
|
||||
FLACsubFramesBuff->samplesBuffer[1][i] = right;
|
||||
FLACsubFramesBuff->samplesBuffer[0][i] = right + side;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_e("unknown channel assignment");
|
||||
return ERR_FLAC_UNKNOWN_CHANNEL_ASSIGNMENT;
|
||||
}
|
||||
}
|
||||
else{
|
||||
log_e("Reserved channel assignment");
|
||||
return ERR_FLAC_RESERVED_CHANNEL_ASSIGNMENT;
|
||||
}
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t decodeSubframe(uint8_t sampleDepth, uint8_t ch) {
|
||||
int8_t ret = 0;
|
||||
readUint(1);
|
||||
uint8_t type = readUint(6);
|
||||
int shift = readUint(1);
|
||||
if (shift == 1) {
|
||||
while (readUint(1) == 0)
|
||||
shift++;
|
||||
}
|
||||
sampleDepth -= shift;
|
||||
|
||||
if(type == 0){ // Constant coding
|
||||
int16_t s= readSignedInt(sampleDepth);
|
||||
for(int i=0; i < m_blockSize; i++){
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] = s;
|
||||
}
|
||||
}
|
||||
else if (type == 1) { // Verbatim coding
|
||||
for (int i = 0; i < m_blockSize; i++)
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] = readSignedInt(sampleDepth);
|
||||
}
|
||||
else if (8 <= type && type <= 12){
|
||||
ret = decodeFixedPredictionSubframe(type - 8, sampleDepth, ch);
|
||||
if(ret) return ret;
|
||||
}
|
||||
else if (32 <= type && type <= 63){
|
||||
ret = decodeLinearPredictiveCodingSubframe(type - 31, sampleDepth, ch);
|
||||
if(ret) return ret;
|
||||
}
|
||||
else{
|
||||
return ERR_FLAC_RESERVED_SUB_TYPE;
|
||||
}
|
||||
if(shift>0){
|
||||
for (int i = 0; i < m_blockSize; i++){
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] <<= shift;
|
||||
}
|
||||
}
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t decodeFixedPredictionSubframe(uint8_t predOrder, uint8_t sampleDepth, uint8_t ch) {
|
||||
uint8_t ret = 0;
|
||||
for(uint8_t i = 0; i < predOrder; i++)
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] = readSignedInt(sampleDepth);
|
||||
ret = decodeResiduals(predOrder, ch);
|
||||
if(ret) return ret;
|
||||
coefs.clear();
|
||||
if(predOrder == 0) coefs.resize(0);
|
||||
if(predOrder == 1) coefs.push_back(1); // FIXED_PREDICTION_COEFFICIENTS
|
||||
if(predOrder == 2){coefs.push_back(2); coefs.push_back(-1);}
|
||||
if(predOrder == 3){coefs.push_back(3); coefs.push_back(-3); coefs.push_back(1);}
|
||||
if(predOrder == 4){coefs.push_back(4); coefs.push_back(-6); coefs.push_back(4); coefs.push_back(-1);}
|
||||
if(predOrder > 4) return ERR_FLAC_PREORDER_TOO_BIG; // Error: preorder > 4"
|
||||
restoreLinearPrediction(ch, 0);
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t decodeLinearPredictiveCodingSubframe(int lpcOrder, int sampleDepth, uint8_t ch){
|
||||
int8_t ret = 0;
|
||||
for (int i = 0; i < lpcOrder; i++)
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] = readSignedInt(sampleDepth);
|
||||
int precision = readUint(4) + 1;
|
||||
int shift = readSignedInt(5);
|
||||
coefs.resize(0);
|
||||
for (uint8_t i = 0; i < lpcOrder; i++)
|
||||
coefs.push_back(readSignedInt(precision));
|
||||
ret = decodeResiduals(lpcOrder, ch);
|
||||
if(ret) return ret;
|
||||
restoreLinearPrediction(ch, shift);
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
int8_t decodeResiduals(uint8_t warmup, uint8_t ch) {
|
||||
|
||||
int method = readUint(2);
|
||||
if (method >= 2)
|
||||
return ERR_FLAC_RESERVED_RESIDUAL_CODING; // Reserved residual coding method
|
||||
uint8_t paramBits = method == 0 ? 4 : 5;
|
||||
int escapeParam = (method == 0 ? 0xF : 0x1F);
|
||||
int partitionOrder = readUint(4);
|
||||
|
||||
int numPartitions = 1 << partitionOrder;
|
||||
if (m_blockSize % numPartitions != 0)
|
||||
return ERR_FLAC_WRONG_RICE_PARTITION_NR; //Error: Block size not divisible by number of Rice partitions
|
||||
int partitionSize = m_blockSize/ numPartitions;
|
||||
|
||||
for (int i = 0; i < numPartitions; i++) {
|
||||
int start = i * partitionSize + (i == 0 ? warmup : 0);
|
||||
int end = (i + 1) * partitionSize;
|
||||
|
||||
int param = readUint(paramBits);
|
||||
if (param < escapeParam) {
|
||||
for (int j = start; j < end; j++){
|
||||
FLACsubFramesBuff->samplesBuffer[ch][j] = readRiceSignedInt(param);
|
||||
}
|
||||
} else {
|
||||
int numBits = readUint(5);
|
||||
for (int j = start; j < end; j++){
|
||||
FLACsubFramesBuff->samplesBuffer[ch][j] = readSignedInt(numBits);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ERR_FLAC_NONE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void restoreLinearPrediction(uint8_t ch, uint8_t shift) {
|
||||
|
||||
for (int i = coefs.size(); i < m_blockSize; i++) {
|
||||
int32_t sum = 0;
|
||||
for (int j = 0; j < coefs.size(); j++){
|
||||
sum += FLACsubFramesBuff->samplesBuffer[ch][i - 1 - j] * coefs[j];
|
||||
}
|
||||
FLACsubFramesBuff->samplesBuffer[ch][i] += (sum >> shift);
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
175
yoRadio/src/audioI2S/flac_decoder/flac_decoder.h
Normal file
175
yoRadio/src/audioI2S/flac_decoder/flac_decoder.h
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* flac_decoder.h
|
||||
*
|
||||
* Created on: Jul 03,2020
|
||||
* Updated on: Apr 27,2021
|
||||
*
|
||||
* Author: wolle
|
||||
*
|
||||
* Restrictions:
|
||||
* blocksize must not exceed 8192
|
||||
* bits per sample must be 8 or 16
|
||||
* num Channels must be 1 or 2
|
||||
*
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#pragma GCC optimize ("Ofast")
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define MAX_CHANNELS 2
|
||||
#define MAX_BLOCKSIZE 8192
|
||||
#define APLL_DISABLE 0
|
||||
#define EXTERNAL_I2S 0
|
||||
|
||||
|
||||
typedef struct FLACsubFramesBuff_t{
|
||||
int32_t samplesBuffer[MAX_CHANNELS][MAX_BLOCKSIZE];
|
||||
}FLACsubframesBuffer_t;
|
||||
|
||||
enum : uint8_t {FLACDECODER_INIT, FLACDECODER_READ_IN, FLACDECODER_WRITE_OUT};
|
||||
enum : uint8_t {DECODE_FRAME, DECODE_SUBFRAMES, OUT_SAMPLES};
|
||||
enum : int8_t {GIVE_NEXT_LOOP = +1,
|
||||
ERR_FLAC_NONE = 0,
|
||||
ERR_FLAC_BLOCKSIZE_TOO_BIG = -1,
|
||||
ERR_FLAC_RESERVED_BLOCKSIZE_UNSUPPORTED = -2,
|
||||
ERR_FLAC_SYNC_CODE_NOT_FOUND = -3,
|
||||
ERR_FLAC_UNKNOWN_CHANNEL_ASSIGNMENT = -4,
|
||||
ERR_FLAC_RESERVED_CHANNEL_ASSIGNMENT = -5,
|
||||
ERR_FLAC_RESERVED_SUB_TYPE = -6,
|
||||
ERR_FLAC_PREORDER_TOO_BIG = -7,
|
||||
ERR_FLAC_RESERVED_RESIDUAL_CODING = -8,
|
||||
ERR_FLAC_WRONG_RICE_PARTITION_NR = -9,
|
||||
ERR_FLAC_BITS_PER_SAMPLE_TOO_BIG = -10,
|
||||
ERR_FLAG_BITS_PER_SAMPLE_UNKNOWN = 11};
|
||||
|
||||
typedef struct FLACMetadataBlock_t{
|
||||
// METADATA_BLOCK_STREAMINFO
|
||||
uint16_t minblocksize; // The minimum block size (in samples) used in the stream.
|
||||
//----------------------------------------------------------------------------------------
|
||||
// The maximum block size (in samples) used in the stream.
|
||||
uint16_t maxblocksize; // (Minimum blocksize == maximum blocksize) implies a fixed-blocksize stream.
|
||||
//----------------------------------------------------------------------------------------
|
||||
// The minimum frame size (in bytes) used in the stream.
|
||||
uint32_t minframesize; // May be 0 to imply the value is not known.
|
||||
//----------------------------------------------------------------------------------------
|
||||
// The maximum frame size (in bytes) used in the stream.
|
||||
uint32_t maxframesize; // May be 0 to imply the value is not known.
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Sample rate in Hz. Though 20 bits are available,
|
||||
// the maximum sample rate is limited by the structure of frame headers to 655350Hz.
|
||||
uint32_t sampleRate; // Also, a value of 0 is invalid.
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Number of channels FLAC supports from 1 to 8 channels
|
||||
uint8_t numChannels; // 000 : 1 channel .... 111 : 8 channels
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Sample size in bits:
|
||||
// 000 : get from STREAMINFO metadata block
|
||||
// 001 : 8 bits per sample
|
||||
// 010 : 12 bits per sample
|
||||
// 011 : reserved
|
||||
// 100 : 16 bits per sample
|
||||
// 101 : 20 bits per sample
|
||||
// 110 : 24 bits per sample
|
||||
uint8_t bitsPerSample; // 111 : reserved
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Total samples in stream. 'Samples' means inter-channel sample,
|
||||
// i.e. one second of 44.1Khz audio will have 44100 samples regardless of the number
|
||||
uint64_t totalSamples; // of channels. A value of zero here means the number of total samples is unknown.
|
||||
//----------------------------------------------------------------------------------------
|
||||
uint32_t audioDataLength;// is not the filelength, is only the length of the audio datablock in bytes
|
||||
|
||||
|
||||
|
||||
}FLACMetadataBlock_t;
|
||||
|
||||
|
||||
typedef struct FLACFrameHeader_t {
|
||||
// 0 : fixed-blocksize stream; frame header encodes the frame number
|
||||
uint8_t blockingStrategy; // 1 : variable-blocksize stream; frame header encodes the sample number
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Block size in inter-channel samples:
|
||||
// 0000 : reserved
|
||||
// 0001 : 192 samples
|
||||
// 0010-0101 : 576 * (2^(n-2)) samples, i.e. 576/1152/2304/4608
|
||||
// 0110 : get 8 bit (blocksize-1) from end of header
|
||||
// 0111 : get 16 bit (blocksize-1) from end of header
|
||||
uint8_t blockSizeCode; // 1000-1111 : 256 * (2^(n-8)) samples, i.e. 256/512/1024/2048/4096/8192/16384/32768
|
||||
//----------------------------------------------------------------------------------------
|
||||
// 0000 : get from STREAMINFO metadata block
|
||||
// 0001 : 88.2kHz
|
||||
// 0010 : 176.4kHz
|
||||
// 0011 : 192kHz
|
||||
// 0100 : 8kHz
|
||||
// 0101 : 16kHz
|
||||
// 0110 : 22.05kHz
|
||||
// 0111 : 24kHz
|
||||
// 1000 : 32kHz
|
||||
// 1001 : 44.1kHz
|
||||
// 1010 : 48kHz
|
||||
// 1011 : 96kHz
|
||||
// 1100 : get 8 bit sample rate (in kHz) from end of header
|
||||
// 1101 : get 16 bit sample rate (in Hz) from end of header
|
||||
// 1110 : get 16 bit sample rate (in tens of Hz) from end of header
|
||||
uint8_t sampleRateCode; // 1111 : invalid, to prevent sync-fooling string of 1s
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Channel assignment
|
||||
// 0000 1 channel: mono
|
||||
// 0001 2 channels: left, right
|
||||
// 0010 3 channels
|
||||
// 0011 4 channels
|
||||
// 0100 5 channels
|
||||
// 0101 6 channels
|
||||
// 0110 7 channels
|
||||
// 0111 8 channels
|
||||
// 1000 : left/side stereo: channel 0 is the left channel, channel 1 is the side(difference) channel
|
||||
// 1001 : right/side stereo: channel 0 is the side(difference) channel, channel 1 is the right channel
|
||||
// 1010 : mid/side stereo: channel 0 is the mid(average) channel, channel 1 is the side(difference) channel
|
||||
uint8_t chanAsgn; // 1011-1111 : reserved
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Sample size in bits:
|
||||
// 000 : get from STREAMINFO metadata block
|
||||
// 001 : 8 bits per sample
|
||||
// 010 : 12 bits per sample
|
||||
// 011 : reserved
|
||||
// 100 : 16 bits per sample
|
||||
// 101 : 20 bits per sample
|
||||
// 110 : 24 bits per sample
|
||||
uint8_t sampleSizeCode; // 111 : reserved
|
||||
//----------------------------------------------------------------------------------------
|
||||
uint32_t totalSamples; // totalSamplesInStream
|
||||
//----------------------------------------------------------------------------------------
|
||||
uint32_t bitrate; // bitrate
|
||||
|
||||
|
||||
}FLACFrameHeader_t;
|
||||
|
||||
int FLACFindSyncWord(unsigned char *buf, int nBytes);
|
||||
int FLACFindOggSyncWord(unsigned char *buf, int nBytes);
|
||||
int FLACparseOggHeader(unsigned char *buf);
|
||||
bool FLACDecoder_AllocateBuffers(void);
|
||||
void FLACDecoder_ClearBuffer();
|
||||
void FLACDecoder_FreeBuffers();
|
||||
void FLACSetRawBlockParams(uint8_t Chans, uint32_t SampRate, uint8_t BPS, uint32_t tsis, uint32_t AuDaLength);
|
||||
void FLACDecoderReset();
|
||||
int8_t FLACDecode(uint8_t *inbuf, int *bytesLeft, short *outbuf);
|
||||
uint16_t FLACGetOutputSamps();
|
||||
uint64_t FLACGetTotoalSamplesInStream();
|
||||
uint8_t FLACGetBitsPerSample();
|
||||
uint8_t FLACGetChannels();
|
||||
uint32_t FLACGetSampRate();
|
||||
uint32_t FLACGetBitRate();
|
||||
uint32_t FLACGetAudioFileDuration();
|
||||
uint32_t readUint(uint8_t nBits);
|
||||
int32_t readSignedInt(int nBits);
|
||||
int64_t readRiceSignedInt(uint8_t param);
|
||||
void alignToByte();
|
||||
int8_t decodeSubframes();
|
||||
int8_t decodeSubframe(uint8_t sampleDepth, uint8_t ch);
|
||||
int8_t decodeFixedPredictionSubframe(uint8_t predOrder, uint8_t sampleDepth, uint8_t ch);
|
||||
int8_t decodeLinearPredictiveCodingSubframe(int lpcOrder, int sampleDepth, uint8_t ch);
|
||||
int8_t decodeResiduals(uint8_t warmup, uint8_t ch);
|
||||
void restoreLinearPrediction(uint8_t ch, uint8_t shift);
|
||||
|
||||
|
||||
3822
yoRadio/src/audioI2S/mp3_decoder/mp3_decoder.cpp
Normal file
3822
yoRadio/src/audioI2S/mp3_decoder/mp3_decoder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
513
yoRadio/src/audioI2S/mp3_decoder/mp3_decoder.h
Normal file
513
yoRadio/src/audioI2S/mp3_decoder/mp3_decoder.h
Normal file
@@ -0,0 +1,513 @@
|
||||
// based om helix mp3 decoder
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "assert.h"
|
||||
|
||||
static const uint8_t m_HUFF_PAIRTABS =32;
|
||||
static const uint8_t m_BLOCK_SIZE =18;
|
||||
static const uint8_t m_NBANDS =32;
|
||||
static const uint8_t m_MAX_REORDER_SAMPS =(192-126)*3; // largest critical band for short blocks (see sfBandTable)
|
||||
static const uint16_t m_VBUF_LENGTH =17*2* m_NBANDS; // for double-sized vbuf FIFO
|
||||
static const uint8_t m_MAX_SCFBD =4; // max scalefactor bands per channel
|
||||
static const uint16_t m_MAINBUF_SIZE =1940;
|
||||
static const uint8_t m_MAX_NGRAN =2; // max granules
|
||||
static const uint8_t m_MAX_NCHAN =2; // max channels
|
||||
static const uint16_t m_MAX_NSAMP =576; // max samples per channel, per granule
|
||||
|
||||
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;
|
||||
|
||||
|
||||
|
||||
|
||||
/* 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 */
|
||||
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
|
||||
*/
|
||||
const unsigned int poly43lo[5] PROGMEM = { 0x29a0bda9, 0xb02e4828, 0x5957aa1b, 0x236c498d, 0xff581859 };
|
||||
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
|
||||
bool MP3Decoder_AllocateBuffers(void);
|
||||
void MP3Decoder_FreeBuffers();
|
||||
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 MP3Decoder_ClearBuffer(void);
|
||||
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 MP3Dequantize( 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 uint64_t xSAR64(uint64_t x, int n){return x >> n;}
|
||||
inline int FASTABS(int x){ return __builtin_abs(x);} //xtensa has a fast abs instruction //fb
|
||||
#define CLZ(x) __builtin_clz(x) //fb
|
||||
269
yoRadio/telnet.cpp
Normal file
269
yoRadio/telnet.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include <stdarg.h>
|
||||
#include "WiFi.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "telnet.h"
|
||||
#include "player.h"
|
||||
#include "display.h"
|
||||
|
||||
Telnet telnet;
|
||||
|
||||
bool Telnet::_isIPSet(IPAddress ip) {
|
||||
return ip.toString() == "0.0.0.0";
|
||||
}
|
||||
|
||||
bool Telnet::begin() {
|
||||
if (WiFi.status() == WL_CONNECTED || _isIPSet(WiFi.softAPIP())) {
|
||||
server.begin();
|
||||
server.setNoDelay(true);
|
||||
Serial.printf("Ready! Use 'telnet %s 23' to connect\n", WiFi.localIP().toString().c_str());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Telnet::stop() {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
void Telnet::emptyClientStream(WiFiClient client) {
|
||||
client.flush();
|
||||
delay(50);
|
||||
while (client.available()) {
|
||||
client.read();
|
||||
}
|
||||
}
|
||||
|
||||
void Telnet::cleanupClients() {
|
||||
for (int i = 0; i < MAX_TLN_CLIENTS; i++) {
|
||||
if (!clients[i].connected()) {
|
||||
if (clients[i]) {
|
||||
Serial.printf("Client [%d] is %s\n", i, clients[i].connected() ? "connected" : "disconnected");
|
||||
clients[i].stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Telnet::loop() {
|
||||
uint8_t i;
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
if (server.hasClient()) {
|
||||
for (i = 0; i < MAX_TLN_CLIENTS; i++) {
|
||||
if (!clients[i] || !clients[i].connected()) {
|
||||
if (clients[i]) {
|
||||
clients[i].stop();
|
||||
}
|
||||
clients[i] = server.available();
|
||||
if (!clients[i]) Serial.println("available broken");
|
||||
on_connect(clients[i].remoteIP().toString().c_str(), i);
|
||||
clients[i].setNoDelay(true);
|
||||
emptyClientStream(clients[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= MAX_TLN_CLIENTS) {
|
||||
server.available().stop();
|
||||
}
|
||||
}
|
||||
for (i = 0; i < MAX_TLN_CLIENTS; i++) {
|
||||
if (clients[i] && clients[i].connected() && clients[i].available()) {
|
||||
String inputstr = clients[i].readStringUntil('\n');
|
||||
inputstr.trim();
|
||||
on_input(inputstr.c_str(), i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < MAX_TLN_CLIENTS; i++) {
|
||||
if (clients[i]) {
|
||||
clients[i].stop();
|
||||
}
|
||||
}
|
||||
delay(1000);
|
||||
}
|
||||
yield();
|
||||
}
|
||||
|
||||
void Telnet::print(const char *buf) {
|
||||
for (int id = 0; id < MAX_TLN_CLIENTS; id++) {
|
||||
if (clients[id] && clients[id].connected()) {
|
||||
print(id, buf);
|
||||
}
|
||||
}
|
||||
Serial.print(buf);
|
||||
}
|
||||
|
||||
void Telnet::print(byte id, const char *buf) {
|
||||
if (clients[id] && clients[id].connected()) {
|
||||
clients[id].print(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void Telnet::printf(const char *format, ...) {
|
||||
char buf[MAX_PRINTF_LEN];
|
||||
va_list args;
|
||||
va_start (args, format );
|
||||
vsnprintf(buf, MAX_PRINTF_LEN, format, args);
|
||||
va_end (args);
|
||||
for (int id = 0; id < MAX_TLN_CLIENTS; id++) {
|
||||
if (clients[id] && clients[id].connected()) {
|
||||
clients[id].print(buf);
|
||||
}
|
||||
}
|
||||
Serial.print(buf);
|
||||
}
|
||||
|
||||
void Telnet::printf(byte id, const char *format, ...) {
|
||||
if (clients[id] && clients[id].connected()) {
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
char *szBuffer = 0;
|
||||
const size_t nBufferLength = vsnprintf(szBuffer, 0, format, argptr) + 1;
|
||||
if (nBufferLength == 1) return;
|
||||
szBuffer = (char *) malloc(nBufferLength);
|
||||
if (! szBuffer) return;
|
||||
vsnprintf(szBuffer, nBufferLength, format, argptr);
|
||||
va_end(argptr);
|
||||
clients[id].print(szBuffer);
|
||||
free(szBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void Telnet::on_connect(const char* str, byte clientId) {
|
||||
Serial.printf("Telnet: [%d] %s connected\n", clientId, str);
|
||||
print(clientId, "\nWelcome to ёRadio!\n(Use ^] + q to disconnect.)\n> ");
|
||||
}
|
||||
|
||||
void Telnet::info() {
|
||||
byte volume;
|
||||
telnet.printf("##CLI.INFO#\n");
|
||||
char timeStringBuff[50];
|
||||
strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S+03:00", &display.timeinfo);
|
||||
telnet.printf("##SYS.DATE#: %s\n", timeStringBuff); //TODO timezone offset
|
||||
telnet.printf("##CLI.NAMESET#: %d %s\n", config.store.lastStation, config.station.name);
|
||||
if (player.mode == PLAYING) {
|
||||
telnet.printf("##CLI.META#: %s\n", config.station.title);
|
||||
}
|
||||
telnet.printf("##CLI.VOL#: %d\n", config.store.volume);
|
||||
if (player.mode == PLAYING) {
|
||||
telnet.printf("##CLI.PLAYING#\n");
|
||||
} else {
|
||||
telnet.printf("##CLI.STOPPED#\n");
|
||||
}
|
||||
telnet.printf("> ");
|
||||
}
|
||||
|
||||
void Telnet::on_input(const char* str, byte clientId) {
|
||||
if (strlen(str) == 0) return;
|
||||
if (strcmp(str, "cli.prev") == 0 || strcmp(str, "prev") == 0) {
|
||||
player.prev();
|
||||
return;
|
||||
}
|
||||
if (strcmp(str, "cli.next") == 0 || strcmp(str, "next") == 0) {
|
||||
player.next();
|
||||
return;
|
||||
}
|
||||
if (strcmp(str, "cli.toggle") == 0 || strcmp(str, "toggle") == 0) {
|
||||
player.toggle();
|
||||
return;
|
||||
}
|
||||
if (strcmp(str, "cli.stop") == 0 || strcmp(str, "stop") == 0) {
|
||||
player.mode = STOPPED;
|
||||
display.title("[stopped]");
|
||||
info();
|
||||
return;
|
||||
}
|
||||
if (strcmp(str, "cli.start") == 0 || strcmp(str, "start") == 0 || strcmp(str, "cli.play") == 0 || strcmp(str, "play") == 0) {
|
||||
player.play(config.store.lastStation);
|
||||
return;
|
||||
}
|
||||
if (strcmp(str, "sys.boot") == 0 || strcmp(str, "boot") == 0 || strcmp(str, "reboot") == 0) {
|
||||
ESP.restart();
|
||||
return;
|
||||
}
|
||||
if (strcmp(str, "cli.vol") == 0 || strcmp(str, "vol") == 0) {
|
||||
printf(clientId, "##CLI.VOL#: %d\n", config.store.volume);
|
||||
return;
|
||||
}
|
||||
if (strcmp(str, "sys.date") == 0) {
|
||||
char timeStringBuff[50];
|
||||
strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S+03:00", &display.timeinfo);
|
||||
telnet.printf("##SYS.DATE#: %s\n", timeStringBuff); //TODO timezone offset
|
||||
return;
|
||||
}
|
||||
int volume;
|
||||
if (sscanf(str, "vol(%d)", &volume) == 1 || sscanf(str, "cli.vol(\"%d\")", &volume) == 1 || sscanf(str, "vol %d", &volume) == 1) {
|
||||
if (volume < 0) volume = 0;
|
||||
if (volume > 254) volume = 254;
|
||||
player.setVol(volume, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(str, "cli.audioinfo") == 0 || strcmp(str, "audioinfo") == 0) {
|
||||
printf(clientId, "##CLI.AUDIOINFO#: %d\n", config.store.audioinfo>0);
|
||||
return;
|
||||
}
|
||||
byte ainfo;
|
||||
if (sscanf(str, "audioinfo(%d)", &ainfo) == 1 || sscanf(str, "cli.audioinfo(\"%d\")", &ainfo) == 1 || sscanf(str, "audioinfo %d", &ainfo) == 1) {
|
||||
config.store.audioinfo = ainfo>0;
|
||||
config.save();
|
||||
return;
|
||||
}
|
||||
if (strcmp(str, "cli.smartstart") == 0 || strcmp(str, "smartstart") == 0) {
|
||||
printf(clientId, "##CLI.SMARTSTART#: %d\n", config.store.smartstart);
|
||||
return;
|
||||
}
|
||||
byte sstart;
|
||||
if (sscanf(str, "smartstart(%d)", &sstart) == 1 || sscanf(str, "cli.smartstart(\"%d\")", &sstart) == 1 || sscanf(str, "smartstart %d", &sstart) == 1) {
|
||||
config.store.smartstart = sstart;
|
||||
config.save();
|
||||
return;
|
||||
}
|
||||
if (strcmp(str, "cli.list") == 0 || strcmp(str, "list") == 0) {
|
||||
printf(clientId, "#CLI.LIST#\n");
|
||||
File file = SPIFFS.open(PLAYLIST_PATH, "r");
|
||||
if (!file || file.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
char sName[BUFLEN], sUrl[BUFLEN];
|
||||
int sOvol;
|
||||
byte c = 1;
|
||||
while (file.available()) {
|
||||
if (config.parseCSV(file.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) {
|
||||
printf(clientId, "#CLI.LISTNUM#: %*d: %s, %s\n", 3, c, sName, sUrl);
|
||||
c++;
|
||||
}
|
||||
}
|
||||
printf(clientId, "##CLI.LIST#\n");
|
||||
printf(clientId, "> ");
|
||||
return;
|
||||
}
|
||||
if (strcmp(str, "cli.info") == 0 || strcmp(str, "info") == 0) {
|
||||
printf(clientId, "##CLI.INFO#\n");
|
||||
char timeStringBuff[50];
|
||||
strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S+03:00", &display.timeinfo);
|
||||
printf(clientId, "##SYS.DATE#: %s\n", timeStringBuff); //TODO timezone offset
|
||||
printf(clientId, "##CLI.NAMESET#: %d %s\n", config.store.lastStation, config.station.name);
|
||||
if (player.mode == PLAYING) {
|
||||
printf(clientId, "##CLI.META#: %s\n", config.station.title);
|
||||
}
|
||||
printf(clientId, "##CLI.VOL#: %d\n", config.store.volume);
|
||||
if (player.mode == PLAYING) {
|
||||
printf(clientId, "##CLI.PLAYING#\n");
|
||||
} else {
|
||||
printf(clientId, "##CLI.STOPPED#\n");
|
||||
}
|
||||
printf(clientId, "> ");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t sb;
|
||||
if (sscanf(str, "play(%d)", &sb) == 1 || sscanf(str, "cli.play(\"%d\")", &sb) == 1 || sscanf(str, "play %d", &sb) == 1 ) {
|
||||
if (sb < 1) sb = 1;
|
||||
if (sb >= config.store.countStation) sb = config.store.countStation;
|
||||
player.play(sb);
|
||||
return;
|
||||
}
|
||||
telnet.printf(clientId, "unknown command: %s\n> ", str);
|
||||
}
|
||||
33
yoRadio/telnet.h
Normal file
33
yoRadio/telnet.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef telnet_h
|
||||
#define telnet_h
|
||||
|
||||
#include <WiFi.h>
|
||||
|
||||
#define MAX_TLN_CLIENTS 5
|
||||
#define MAX_PRINTF_LEN BUFLEN+50
|
||||
|
||||
class Telnet {
|
||||
public:
|
||||
Telnet() {};
|
||||
bool begin();
|
||||
void loop();
|
||||
void stop();
|
||||
void print(byte id, const char *buf);
|
||||
void print(const char *buf);
|
||||
void printf(byte id, const char *format, ...);
|
||||
void printf(const char *format, ...);
|
||||
void cleanupClients();
|
||||
void info();
|
||||
protected:
|
||||
WiFiServer server = WiFiServer(23);
|
||||
WiFiClient clients[MAX_TLN_CLIENTS];
|
||||
void emptyClientStream(WiFiClient client);
|
||||
void on_connect(const char* str, byte clientId);
|
||||
void on_input(const char* str, byte clientId);
|
||||
private:
|
||||
bool _isIPSet(IPAddress ip);
|
||||
};
|
||||
|
||||
extern Telnet telnet;
|
||||
|
||||
#endif
|
||||
42
yoRadio/yoRadio.ino
Normal file
42
yoRadio/yoRadio.ino
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "options.h"
|
||||
#include "config.h"
|
||||
#include "telnet.h"
|
||||
#include "player.h"
|
||||
#include "display.h"
|
||||
#include "player.h"
|
||||
#include "network.h"
|
||||
#include "netserver.h"
|
||||
#include "controls.h"
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
config.init();
|
||||
display.init();
|
||||
player.init();
|
||||
network.begin();
|
||||
if (network.status != CONNECTED) {
|
||||
netserver.begin();
|
||||
display.start();
|
||||
return;
|
||||
}
|
||||
initControls();
|
||||
netserver.begin();
|
||||
telnet.begin();
|
||||
player.setVol(config.store.volume, true);
|
||||
display.start();
|
||||
if(config.store.smartstart==1) player.play(config.store.lastStation);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (network.status == CONNECTED) {
|
||||
telnet.loop();
|
||||
player.loop();
|
||||
loopControls();
|
||||
}
|
||||
display.loop();
|
||||
netserver.loop();
|
||||
}
|
||||
Reference in New Issue
Block a user