mirror of https://github.com/arendst/Tasmota.git
207 lines
4.4 KiB
C++
207 lines
4.4 KiB
C++
/*
|
|
xdrv_42_i2s_audio.ino - audio dac support for Tasmota
|
|
|
|
Copyright (C) 2020 Gerhard Mutz and Theo Arends
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#if defined(ESP32) && (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH))
|
|
|
|
#include "AudioFileSourcePROGMEM.h"
|
|
#include "AudioFileSourceID3.h"
|
|
#include "AudioGeneratorMP3.h"
|
|
#include "AudioOutputI2S.h"
|
|
#include <ESP8266SAM.h>
|
|
#include "AudioFileSourceFS.h"
|
|
|
|
// unfortunately tasks do not help very much,
|
|
// mp3 is extremely sensitive to interruptions
|
|
// also mp3 needs 240 Mhz CPU clock
|
|
#define MP3_TASK
|
|
|
|
|
|
#define EXTERNAL_DAC_PLAY 1
|
|
|
|
#define XDRV_42 42
|
|
|
|
AudioGeneratorMP3 *mp3 = nullptr;
|
|
AudioFileSourceFS *file;
|
|
AudioOutputI2S *out;
|
|
AudioFileSourceID3 *id3;
|
|
|
|
|
|
//! MAX98357A + INMP441 DOUBLE I2S BOARD
|
|
#ifdef ESP32
|
|
#undef TWATCH_DAC_IIS_BCK
|
|
#undef TWATCH_DAC_IIS_WS
|
|
#undef TWATCH_DAC_IIS_DOUT
|
|
#define TWATCH_DAC_IIS_BCK 26
|
|
#define TWATCH_DAC_IIS_WS 25
|
|
#define TWATCH_DAC_IIS_DOUT 33
|
|
#else
|
|
#undef TWATCH_DAC_IIS_BCK
|
|
#undef TWATCH_DAC_IIS_WS
|
|
#undef TWATCH_DAC_IIS_DOUT
|
|
#define TWATCH_DAC_IIS_BCK 15
|
|
#define TWATCH_DAC_IIS_WS 2
|
|
#define TWATCH_DAC_IIS_DOUT 3
|
|
#endif
|
|
|
|
|
|
uint8_t volume;
|
|
|
|
void I2S_Init(void) {
|
|
|
|
#if EXTERNAL_DAC_PLAY
|
|
out = new AudioOutputI2S();
|
|
out->SetPinout(TWATCH_DAC_IIS_BCK, TWATCH_DAC_IIS_WS, TWATCH_DAC_IIS_DOUT);
|
|
#else
|
|
out = new AudioOutputI2S(0, 1);
|
|
#endif
|
|
|
|
volume=50;
|
|
out->SetGain(((float)volume/100.0)*4.0);
|
|
out->stop();
|
|
}
|
|
|
|
|
|
#ifdef MP3_TASK
|
|
uint16_t mp3_task_timer;
|
|
TaskHandle_t mp3_task_h;
|
|
|
|
void mp3_task(void *arg) {
|
|
while (1) {
|
|
while (mp3->isRunning()) {
|
|
if (!mp3->loop()) {
|
|
mp3->stop();
|
|
mp3_delete();
|
|
vTaskDelete(mp3_task_h);
|
|
//mp3_task_h=nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // MP3_TASK
|
|
|
|
|
|
void Play_mp3(const char *path) {
|
|
#if defined(USE_SCRIPT) && defined(USE_SCRIPT_FATFS)
|
|
if (mp3) return;
|
|
|
|
#ifdef USE_TTGO_WATCH
|
|
TTGO_audio_power(true);
|
|
#endif
|
|
|
|
file = new AudioFileSourceFS(*fsp,path);
|
|
id3 = new AudioFileSourceID3(file);
|
|
mp3 = new AudioGeneratorMP3();
|
|
mp3->begin(id3, out);
|
|
|
|
#ifdef MP3_TASK
|
|
// esp32 use mp3 task
|
|
xTaskCreatePinnedToCore(mp3_task, "MP3", 8192, NULL, 3, &mp3_task_h, 1);
|
|
#else
|
|
// esp8266 must wait in loop
|
|
while (mp3->isRunning()) {
|
|
if (!mp3->loop()) {
|
|
mp3->stop();
|
|
mp3_delete();
|
|
break;
|
|
}
|
|
OsWatchLoop();
|
|
}
|
|
#endif // MP3_TASK
|
|
|
|
#endif // USE_SCRIPT
|
|
}
|
|
|
|
void mp3_delete(void) {
|
|
delete file;
|
|
delete id3;
|
|
delete mp3;
|
|
mp3=nullptr;
|
|
#ifdef USE_TTGO_WATCH
|
|
TTGO_audio_power(false);
|
|
#endif
|
|
}
|
|
|
|
void Say(char *text) {
|
|
|
|
#ifdef USE_TTGO_WATCH
|
|
TTGO_audio_power(true);
|
|
#endif
|
|
|
|
out->begin();
|
|
ESP8266SAM *sam = new ESP8266SAM;
|
|
sam->Say(out, text);
|
|
delete sam;
|
|
out->stop();
|
|
|
|
#ifdef USE_TTGO_WATCH
|
|
TTGO_audio_power(false);
|
|
#endif
|
|
}
|
|
|
|
const char kI2SAudio_Commands[] PROGMEM = "I2S|"
|
|
"Say|Play|Gain";
|
|
|
|
void (* const I2SAudio_Command[])(void) PROGMEM = {
|
|
&Cmd_Say,&Cmd_Play,&Cmd_Gain};
|
|
|
|
void Cmd_Play(void) {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
Play_mp3(XdrvMailbox.data);
|
|
}
|
|
ResponseCmndChar(XdrvMailbox.data);
|
|
}
|
|
|
|
void Cmd_Gain(void) {
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
|
|
if (out) {
|
|
volume=XdrvMailbox.payload;
|
|
out->SetGain(((float)(volume-2)/100.0)*4.0);
|
|
}
|
|
}
|
|
ResponseCmndNumber(volume);
|
|
}
|
|
|
|
void Cmd_Say(void) {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
Say(XdrvMailbox.data);
|
|
}
|
|
ResponseCmndChar(XdrvMailbox.data);
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* Interface
|
|
\*********************************************************************************************/
|
|
|
|
bool Xdrv42(uint8_t function) {
|
|
bool result = false;
|
|
|
|
switch (function) {
|
|
case FUNC_COMMAND:
|
|
result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command);
|
|
break;
|
|
case FUNC_INIT:
|
|
I2S_Init();
|
|
break;
|
|
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#endif // USE_I2S_AUDIO
|