229 lines
5.0 KiB
C++
229 lines
5.0 KiB
C++
#include "audio.h"
|
|
|
|
#include "AudioTools.h"
|
|
#include "AudioTools/AudioLibs/AudioBoardStream.h"
|
|
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
|
|
#include "AudioTools/AudioCodecs/CodecOpus.h"
|
|
|
|
#include <SPI.h>
|
|
#include <SD.h>
|
|
#include <SnapClient.h>
|
|
|
|
#include "player.h"
|
|
#include "wifi.h"
|
|
|
|
bool audioMute = true;
|
|
|
|
bool audioMute2 = true; // keep last applied mute state because we cannot retrieve it from library
|
|
|
|
float audioVolume = 0.1;
|
|
|
|
int copyErrors = 0;
|
|
|
|
AudioBoardStream board(AudioKitEs8388V1);
|
|
|
|
File file;
|
|
|
|
ICYStream icy;
|
|
|
|
WiFiClient wifi;
|
|
|
|
OpusAudioDecoder opusDecoder;
|
|
|
|
SnapClient snap(wifi, board, opusDecoder);
|
|
|
|
MP3DecoderHelix mp3Decoder;
|
|
|
|
EncodedAudioStream mp3Stream(&board, &mp3Decoder);
|
|
|
|
StreamCopy mp3Copier;
|
|
|
|
bool running = false;
|
|
|
|
void audioStop() {
|
|
if (running) {
|
|
running = false;
|
|
Serial.println("[AUDIO ] STOP");
|
|
}
|
|
copyErrors = 0;
|
|
mp3Copier.end();
|
|
mp3Stream.end();
|
|
mp3Decoder.end();
|
|
opusDecoder.end();
|
|
icy.end();
|
|
file.close();
|
|
// snap.end(); // TODO
|
|
}
|
|
|
|
bool audioPlayInit(const Entry &entry) {
|
|
audioStop();
|
|
|
|
Serial.printf("[AUDIO ] [%s] Opening: %s\n", entry.type.c_str(), entry.url.c_str());
|
|
running = true;
|
|
|
|
if (entry.type == "ICY" || entry.type == "SNAP") {
|
|
if (!isWifiConnected()) {
|
|
Serial.printf("[AUDIO ] [%s] WiFi not connected.\n", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (entry.type == "SD") {
|
|
if (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)) {
|
|
Serial.printf("[AUDIO ] [%s] Failed to mount SD-card.\n", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool audioBeginMP3Stream(const Entry &entry, Stream &source) {
|
|
if (!mp3Decoder.begin()) {
|
|
Serial.printf("[AUDIO ] [%s] Failed to start MP3DecoderHelix.", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
|
|
if (!mp3Stream.begin()) {
|
|
Serial.printf("[AUDIO ] [%s] Failed to start EncodedAudioStream.", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
|
|
mp3Copier.begin(mp3Stream, source);
|
|
|
|
if (mp3Copier.copy() == 0) {
|
|
Serial.printf("[AUDIO ] [%s] Failed to copy initial data.", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
|
|
Serial.printf("[AUDIO ] [%s] Stream running.\n", entry.type.c_str());
|
|
return true;
|
|
}
|
|
|
|
bool audioPlayICY(const Entry &entry) {
|
|
if (!audioPlayInit(entry)) {
|
|
return false;
|
|
}
|
|
|
|
if (!icy.begin(entry.url.c_str())) {
|
|
Serial.printf("[AUDIO ] [%s] Failed to start ICYStream.\n", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
|
|
return audioBeginMP3Stream(entry, icy);
|
|
}
|
|
|
|
bool audioPlaySNAP(const Entry &entry) {
|
|
if (!audioPlayInit(entry)) {
|
|
return false;
|
|
}
|
|
|
|
IPAddress ip;
|
|
if (!WiFiClass::hostByName(entry.url.c_str(), ip)) {
|
|
Serial.printf("[AUDIO ] [%s] Failed to resolve host.\n", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
|
|
snap.setServerIP(ip);
|
|
if (!snap.begin()) {
|
|
Serial.printf("[AUDIO ] [%s] Failed to connect.\n", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
|
|
if (!opusDecoder.begin()) {
|
|
Serial.printf("[AUDIO ] [%s] Failed to start OpusAudioDecoder.\n", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
|
|
if (!snap.doLoop()) {
|
|
Serial.printf("[AUDIO ] [%s] Failed to copy initial data.\n", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
|
|
Serial.printf("[AUDIO ] [%s] Stream running.\n", entry.type.c_str());
|
|
return true;
|
|
}
|
|
|
|
bool audioPlaySD(const Entry &entry) {
|
|
if (!audioPlayInit(entry)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SD.exists(entry.url.c_str())) {
|
|
Serial.printf("[AUDIO ] [%s] File not found.\n", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
|
|
file = SD.open(entry.url.c_str(), FILE_READ);
|
|
if (!file) {
|
|
Serial.printf("[AUDIO ] [%s] Failed to open file.\n", entry.type.c_str());
|
|
audioStop();
|
|
return false;
|
|
}
|
|
|
|
return audioBeginMP3Stream(entry, file);
|
|
}
|
|
|
|
bool audioPlay(const Entry &entry) {
|
|
audioStop();
|
|
if (entry.type == "") {
|
|
return false;
|
|
}
|
|
if (entry.type == "ICY") {
|
|
return audioPlayICY(entry);
|
|
}
|
|
if (entry.type == "SD") {
|
|
return audioPlaySD(entry);
|
|
}
|
|
if (entry.type == "SNAP") {
|
|
return audioPlaySNAP(entry);
|
|
}
|
|
Serial.printf("[AUDIO ] Unknown type: %s\n", entry.type.c_str());
|
|
return false;
|
|
}
|
|
|
|
void audioSetup() {
|
|
AudioToolsLogger.setLogLevel(AudioToolsLogLevel::Error);
|
|
Serial.println("[AUDIO ] Initializing board...");
|
|
board.begin();
|
|
audioMute2 = !audioMute;
|
|
}
|
|
|
|
bool audioLoop() {
|
|
if (abs(board.volume() - audioVolume) >= 0.01f) {
|
|
Serial.printf("[AUDIO ] volume = %.2f\n", audioVolume);
|
|
board.setVolume(audioVolume);
|
|
stateBufferUpdateRequest();
|
|
}
|
|
if (audioMute2 != audioMute) {
|
|
audioMute2 = audioMute;
|
|
Serial.printf("[AUDIO ] mute = %s\n", audioMute ? "MUTED" : "no");
|
|
board.setMute(audioMute);
|
|
stateBufferUpdateRequest();
|
|
}
|
|
if (mp3Copier.copy() > 0) {
|
|
copyErrors = 0;
|
|
return true;
|
|
}
|
|
if (running) {
|
|
copyErrors++;
|
|
if (copyErrors < 100) {
|
|
return true;
|
|
}
|
|
Serial.println("[AUDIO ] Stream ended!");
|
|
audioStop();
|
|
}
|
|
return false;
|
|
}
|