325 lines
8.8 KiB
C++
325 lines
8.8 KiB
C++
#include "options.h"
|
|
#include "player.h"
|
|
#include "config.h"
|
|
#include "telnet.h"
|
|
#include "display.h"
|
|
#include "sdmanager.h"
|
|
#include "netserver.h"
|
|
|
|
Player player;
|
|
QueueHandle_t playerQueue;
|
|
|
|
#if VS1053_CS!=255 && !I2S_INTERNAL
|
|
#if VS_HSPI
|
|
Player::Player(): Audio(VS1053_CS, VS1053_DCS, VS1053_DREQ, &SPI2) {}
|
|
#else
|
|
Player::Player(): Audio(VS1053_CS, VS1053_DCS, VS1053_DREQ, &SPI) {}
|
|
#endif
|
|
void ResetChip(){
|
|
pinMode(VS1053_RST, OUTPUT);
|
|
digitalWrite(VS1053_RST, LOW);
|
|
delay(30);
|
|
digitalWrite(VS1053_RST, HIGH);
|
|
delay(100);
|
|
}
|
|
#else
|
|
#if !I2S_INTERNAL
|
|
Player::Player() {}
|
|
#else
|
|
Player::Player(): Audio(true, I2S_DAC_CHANNEL_BOTH_EN) {}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
void Player::init() {
|
|
Serial.print("##[BOOT]#\tplayer.init\t");
|
|
playerQueue=NULL;
|
|
_resumeFilePos = 0;
|
|
playerQueue = xQueueCreate( 5, sizeof( playerRequestParams_t ) );
|
|
setOutputPins(false);
|
|
delay(50);
|
|
memset(_plError, 0, PLERR_LN);
|
|
#ifdef MQTT_ROOT_TOPIC
|
|
memset(burl, 0, MQTT_BURL_SIZE);
|
|
#endif
|
|
if(MUTE_PIN!=255) pinMode(MUTE_PIN, OUTPUT);
|
|
#if I2S_DOUT!=255
|
|
#if !I2S_INTERNAL
|
|
setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
|
#endif
|
|
#else
|
|
SPI.begin();
|
|
if(VS1053_RST>0) ResetChip();
|
|
begin();
|
|
#endif
|
|
setBalance(config.store.balance);
|
|
setTone(config.store.bass, config.store.middle, config.store.trebble);
|
|
setVolume(0);
|
|
_status = STOPPED;
|
|
//setOutputPins(false);
|
|
_volTimer=false;
|
|
//randomSeed(analogRead(0));
|
|
#if PLAYER_FORCE_MONO
|
|
forceMono(true);
|
|
#endif
|
|
_loadVol(config.store.volume);
|
|
setConnectionTimeout(1700, 3700);
|
|
Serial.println("done");
|
|
}
|
|
|
|
void Player::sendCommand(playerRequestParams_t request){
|
|
if(playerQueue==NULL) return;
|
|
xQueueSend(playerQueue, &request, PLQ_SEND_DELAY);
|
|
}
|
|
|
|
void Player::resetQueue(){
|
|
if(playerQueue!=NULL) xQueueReset(playerQueue);
|
|
}
|
|
|
|
void Player::stopInfo() {
|
|
config.setSmartStart(0);
|
|
//telnet.info();
|
|
netserver.requestOnChange(MODE, 0);
|
|
}
|
|
|
|
void Player::setError(const char *e){
|
|
strlcpy(_plError, e, PLERR_LN);
|
|
if(hasError()) {
|
|
config.setTitle(_plError);
|
|
telnet.printf("##ERROR#:\t%s\n", e);
|
|
}
|
|
}
|
|
|
|
void Player::_stop(bool alreadyStopped){
|
|
log_i("%s called", __func__);
|
|
if(config.getMode()==PM_SDCARD && !alreadyStopped) config.sdResumePos = player.getFilePos();
|
|
_status = STOPPED;
|
|
setOutputPins(false);
|
|
if(!hasError()) config.setTitle((display.mode()==LOST || display.mode()==UPDATING)?"":const_PlStopped);
|
|
config.station.bitrate = 0;
|
|
config.setBitrateFormat(BF_UNCNOWN);
|
|
#ifdef USE_NEXTION
|
|
nextion.bitrate(config.station.bitrate);
|
|
#endif
|
|
netserver.requestOnChange(BITRATE, 0);
|
|
display.putRequest(DBITRATE);
|
|
display.putRequest(PSTOP);
|
|
setDefaults();
|
|
if(!alreadyStopped) stopSong();
|
|
if(!lockOutput) stopInfo();
|
|
if (player_on_stop_play) player_on_stop_play();
|
|
pm.on_stop_play();
|
|
}
|
|
|
|
void Player::initHeaders(const char *file) {
|
|
if(strlen(file)==0 || true) return; //TODO Read TAGs
|
|
connecttoFS(sdman,file);
|
|
eofHeader = false;
|
|
while(!eofHeader) Audio::loop();
|
|
//netserver.requestOnChange(SDPOS, 0);
|
|
setDefaults();
|
|
}
|
|
|
|
#ifndef PL_QUEUE_TICKS
|
|
#define PL_QUEUE_TICKS 0
|
|
#endif
|
|
#ifndef PL_QUEUE_TICKS_ST
|
|
#define PL_QUEUE_TICKS_ST 15
|
|
#endif
|
|
void Player::loop() {
|
|
if(playerQueue==NULL) return;
|
|
playerRequestParams_t requestP;
|
|
if(xQueueReceive(playerQueue, &requestP, isRunning()?PL_QUEUE_TICKS:PL_QUEUE_TICKS_ST)){
|
|
switch (requestP.type){
|
|
case PR_STOP: _stop(); break;
|
|
case PR_PLAY: {
|
|
if (requestP.payload>0) {
|
|
config.setLastStation((uint16_t)requestP.payload);
|
|
}
|
|
_play((uint16_t)abs(requestP.payload));
|
|
if (player_on_station_change) player_on_station_change();
|
|
pm.on_station_change();
|
|
break;
|
|
}
|
|
case PR_VOL: {
|
|
config.setVolume(requestP.payload);
|
|
Audio::setVolume(volToI2S(requestP.payload));
|
|
break;
|
|
}
|
|
#ifdef USE_SD
|
|
case PR_CHECKSD: {
|
|
if(config.getMode()==PM_SDCARD){
|
|
if(!sdman.cardPresent()){
|
|
sdman.stop();
|
|
config.changeMode(PM_WEB);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
case PR_VUTONUS:
|
|
if(config.vuThreshold>10) config.vuThreshold -=10;
|
|
default: break;
|
|
}
|
|
}
|
|
Audio::loop();
|
|
if(!isRunning() && _status==PLAYING) _stop(true);
|
|
if(_volTimer){
|
|
if((millis()-_volTicks)>3000){
|
|
config.saveVolume();
|
|
_volTimer=false;
|
|
}
|
|
}
|
|
#ifdef MQTT_ROOT_TOPIC
|
|
if(strlen(burl)>0){
|
|
browseUrl();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Player::setOutputPins(bool isPlaying) {
|
|
if(REAL_LEDBUILTIN!=255) digitalWrite(REAL_LEDBUILTIN, LED_INVERT?!isPlaying:isPlaying);
|
|
bool _ml = MUTE_LOCK?!MUTE_VAL:(isPlaying?!MUTE_VAL:MUTE_VAL);
|
|
if(MUTE_PIN!=255) digitalWrite(MUTE_PIN, _ml);
|
|
}
|
|
|
|
void Player::_play(uint16_t stationId) {
|
|
log_i("%s called, stationId=%d", __func__, stationId);
|
|
setError("");
|
|
remoteStationName = false;
|
|
config.setDspOn(1);
|
|
config.vuThreshold = 0;
|
|
//display.putRequest(PSTOP);
|
|
config.screensaverTicks=SCREENSAVERSTARTUPDELAY;
|
|
if(config.getMode()!=PM_SDCARD) {
|
|
display.putRequest(PSTOP);
|
|
}
|
|
setOutputPins(false);
|
|
//config.setTitle(config.getMode()==PM_WEB?const_PlConnect:"");
|
|
config.setTitle(config.getMode()==PM_WEB?const_PlConnect:"[next track]");
|
|
config.station.bitrate=0;
|
|
config.setBitrateFormat(BF_UNCNOWN);
|
|
config.loadStation(stationId);
|
|
_loadVol(config.store.volume);
|
|
display.putRequest(DBITRATE);
|
|
display.putRequest(NEWSTATION);
|
|
netserver.requestOnChange(STATION, 0);
|
|
netserver.loop();
|
|
netserver.loop();
|
|
config.setSmartStart(0);
|
|
bool isConnected = false;
|
|
if(config.getMode()==PM_SDCARD && SDC_CS!=255){
|
|
isConnected=connecttoFS(sdman,config.station.url,config.sdResumePos==0?_resumeFilePos:config.sdResumePos-player.sd_min);
|
|
}else {
|
|
config.saveValue(&config.store.play_mode, static_cast<uint8_t>(PM_WEB));
|
|
}
|
|
if(config.getMode()==PM_WEB) isConnected=connecttohost(config.station.url);
|
|
if(isConnected){
|
|
//if (config.store.play_mode==PM_WEB?connecttohost(config.station.url):connecttoFS(SD,config.station.url,config.sdResumePos==0?_resumeFilePos:config.sdResumePos-player.sd_min)) {
|
|
_status = PLAYING;
|
|
if(config.getMode()==PM_SDCARD) {
|
|
config.sdResumePos = 0;
|
|
config.saveValue(&config.store.lastSdStation, stationId);
|
|
}
|
|
//config.setTitle("");
|
|
config.setSmartStart(1);
|
|
netserver.requestOnChange(MODE, 0);
|
|
setOutputPins(true);
|
|
display.putRequest(NEWMODE, PLAYER);
|
|
display.putRequest(PSTART);
|
|
if (player_on_start_play) player_on_start_play();
|
|
pm.on_start_play();
|
|
}else{
|
|
telnet.printf("##ERROR#:\tError connecting to %s\n", config.station.url);
|
|
SET_PLAY_ERROR("Error connecting to %s", config.station.url);
|
|
_stop(true);
|
|
};
|
|
}
|
|
|
|
#ifdef MQTT_ROOT_TOPIC
|
|
void Player::browseUrl(){
|
|
setError("");
|
|
remoteStationName = true;
|
|
config.setDspOn(1);
|
|
resumeAfterUrl = _status==PLAYING;
|
|
display.putRequest(PSTOP);
|
|
// setDefaults();
|
|
setOutputPins(false);
|
|
config.setTitle(const_PlConnect);
|
|
if (connecttohost(burl)){
|
|
_status = PLAYING;
|
|
config.setTitle("");
|
|
netserver.requestOnChange(MODE, 0);
|
|
setOutputPins(true);
|
|
display.putRequest(PSTART);
|
|
if (player_on_start_play) player_on_start_play();
|
|
pm.on_start_play();
|
|
}else{
|
|
telnet.printf("##ERROR#:\tError connecting to %s\n", burl);
|
|
SET_PLAY_ERROR("Error connecting to %s", burl);
|
|
_stop(true);
|
|
}
|
|
memset(burl, 0, MQTT_BURL_SIZE);
|
|
}
|
|
#endif
|
|
|
|
void Player::prev() {
|
|
|
|
uint16_t lastStation = config.lastStation();
|
|
if(config.getMode()==PM_WEB || !config.store.sdsnuffle){
|
|
if (lastStation == 1) config.lastStation(config.store.countStation); else config.lastStation(lastStation-1);
|
|
}
|
|
sendCommand({PR_PLAY, config.lastStation()});
|
|
}
|
|
|
|
void Player::next() {
|
|
uint16_t lastStation = config.lastStation();
|
|
if(config.getMode()==PM_WEB || !config.store.sdsnuffle){
|
|
if (lastStation == config.store.countStation) config.lastStation(1); else config.lastStation(lastStation+1);
|
|
}else{
|
|
config.lastStation(random(1, config.store.countStation));
|
|
}
|
|
sendCommand({PR_PLAY, config.lastStation()});
|
|
}
|
|
|
|
void Player::toggle() {
|
|
if (_status == PLAYING) {
|
|
sendCommand({PR_STOP, 0});
|
|
} else {
|
|
sendCommand({PR_PLAY, config.lastStation()});
|
|
}
|
|
}
|
|
|
|
void Player::stepVol(bool up) {
|
|
if (up) {
|
|
if (config.store.volume <= 254 - config.store.volsteps) {
|
|
setVol(config.store.volume + config.store.volsteps);
|
|
}else{
|
|
setVol(254);
|
|
}
|
|
} else {
|
|
if (config.store.volume >= config.store.volsteps) {
|
|
setVol(config.store.volume - config.store.volsteps);
|
|
}else{
|
|
setVol(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t Player::volToI2S(uint8_t volume) {
|
|
int vol = map(volume, 0, 254 - config.station.ovol * 3 , 0, 254);
|
|
if (vol > 254) vol = 254;
|
|
if (vol < 0) vol = 0;
|
|
return vol;
|
|
}
|
|
|
|
void Player::_loadVol(uint8_t volume) {
|
|
setVolume(volToI2S(volume));
|
|
}
|
|
|
|
void Player::setVol(uint8_t volume) {
|
|
_volTicks = millis();
|
|
_volTimer = true;
|
|
player.sendCommand({PR_VOL, volume});
|
|
}
|