i2s wav player (#22003)

* i2s wav player

* move framepointer
This commit is contained in:
gemu 2024-08-26 08:48:35 +02:00 committed by GitHub
parent 5c30d92627
commit c35c493809
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 207 additions and 5 deletions

View File

@ -106,6 +106,7 @@ public:
dim_cb dim_cbp = 0;
LVGL_PARAMS lvgl_param;
int8_t disp_bpp;
uint16_t *rgb_fb;
private:
void DrawCharAt(int16_t x, int16_t y, char ascii_char,int16_t colored);
inline void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, uint16_t color) __attribute__((always_inline));

View File

@ -396,7 +396,7 @@ class uDisplay : public Renderer {
#if ESP_IDF_VERSION_MAJOR < 5
esp_rgb_panel_t *_rgb_panel;
#endif //ESP_IDF_VERSION_MAJOR < 5
uint16_t *rgb_fb;
esp_lcd_i80_bus_handle_t _i80_bus = nullptr;

View File

@ -185,6 +185,12 @@ char *Get_esc_char(char *cp, char *esc_chr);
#endif
#endif // ESP32
#ifdef ESP32
#include "driver/i2s_std.h"
#include "driver/i2s_pdm.h"
#endif
#ifdef SCRIPT_FULL_OPTIONS
#undef USE_BUTTON_EVENT
@ -722,8 +728,15 @@ typedef struct {
uint16_t ufs_script_size;
#ifdef USE_PLAY_WAVE
#ifdef ESP32
i2s_chan_handle_t tx_handle;
#endif
#endif
} SCRIPT_MEM;
SCRIPT_MEM glob_script_mem;
@ -792,6 +805,7 @@ int32_t script_ow(uint8_t sel, uint32_t val);
int32_t script_logfile_write(char *path, char *payload, uint32_t size);
void script_sort_array(TS_FLOAT *array, uint16_t size);
uint32_t Touch_Status(int32_t sel);
int32_t play_wave(char *path);
void ScriptEverySecond(void) {
@ -4877,7 +4891,7 @@ char *Plugin_Query(uint8_t, uint8_t);
len++;
goto exit;
}
#if defined(ESP32) && (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH) || defined(USE_M5STACK_CORE2))
#if defined(ESP32) && defined(USE_I2S_AUDIO)
if (!strncmp_XP(lp, XPSTR("pl("), 3)) {
char path[SCRIPT_MAX_SBSIZE];
lp = GetStringArgument(lp + 3, OPER_EQU, path, 0);
@ -4952,6 +4966,14 @@ char *Plugin_Query(uint8_t, uint8_t);
goto exit;
}
#ifdef USE_PLAY_WAVE
if (!strncmp_XP(lp, XPSTR("pwav("), 5)) {
char str[SCRIPT_MAX_SBSIZE];
lp = GetStringArgument(lp + 5, OPER_EQU, str, 0);
fvar = play_wave(str);
goto nfuncexit;
}
#endif // USE_PLAY_WAVE
break;
case 'r':
@ -6621,6 +6643,169 @@ char *getop(char *lp, uint8_t *operand) {
return lp;
}
#ifdef USE_PLAY_WAVE
#ifdef ESP8266
#include <i2s.h>
#include <i2s_reg.h>
/*
i2S on ESP8266
dout = 3 (RX)
clk = 15 (D8)
ws = 2 (D4)
*/
#endif // ESP8266
// RIFF header
typedef struct {
uint32_t ChunkID; //"RIFF"
uint32_t ChunkSize; //"36 + sizeof(wav_data_t) + data"
uint32_t Format; // "WAV"
} wav_riff_t;
// FMT header
typedef struct {
uint32_t Subchunk1ID; //"fmt "
uint32_t Subchunk1Size; //16 (PCM)
uint16_t AudioFormat; // 1 'cause PCM
uint16_t NumChannels; // mono = 1; stereo = 2
uint32_t SampleRate; // 8000, 44100, etc.
uint32_t ByteRate; //== SampleRate * NumChannels * byte
uint16_t BlockAlign; //== NumChannels * bytePerSample
uint16_t BytesPerSample; //8 byte = 8, 16 byte = 16, etc.
} wav_fmt_t;
// Data header
typedef struct {
uint32_t Subchunk2ID; //"data"
uint32_t Subchunk2Size; //== NumSamples * NumChannels * bytePerSample/8
} wav_data_t;
// complete header
typedef struct {
wav_riff_t Riff;
wav_fmt_t Fmt;
wav_data_t Data;
} wav_header_t;
// we assume 1 channel with 8khz
int32_t play_wave(char *path) {
#ifdef ESP32
if (path[0] == 'i' && path[1] == ':') {
// get esp32 i2s pins
char *cp = &path[2];
uint8_t bck = strtol(cp, &cp, 10);
cp++;
uint8_t ws = strtol(cp, &cp, 10);
cp++;
uint8_t dout = strtol(cp, &cp, 10);
cp++;
uint8_t mode = strtol(cp, &cp, 10);
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
i2s_new_channel(&chan_cfg, &glob_script_mem.tx_handle, NULL);
i2s_std_slot_config_t slot_cfg;
switch (mode) {
case 0:
slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO);
break;
case 1:
slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO);
break;
default:
slot_cfg = I2S_STD_PCM_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO);
break;
}
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(8000),
.slot_cfg = slot_cfg,
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = (gpio_num_t)bck,
.ws = (gpio_num_t)ws,
.dout = (gpio_num_t)dout,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
/* Initialize the channel */
i2s_channel_init_std_mode(glob_script_mem.tx_handle, &std_cfg);
return 0;
}
#endif
File wf = ufsp->open(path, FS_FILE_READ);
if (!wf) {
return -1;
}
int16_t buffer[512];
uint32_t fsize = wf.size();
// check for RIFF
wf.readBytes((char*)buffer, sizeof(wav_header_t));
wav_header_t *wh = (wav_header_t *)buffer;
// 0x52494646
if (wh->Riff.ChunkID != 0x46464952 && wh->Fmt.NumChannels != 1) {
wf.close();
return -2;
}
fsize -= sizeof(wav_header_t);
#ifdef ESP8266
i2s_begin();
i2s_set_rate(wh->Fmt.SampleRate);
while (wf.position() < fsize) {
int numBytes = _min(sizeof(buffer), fsize - wf.position() - 1);
int bytesread = wf.readBytes((char*)buffer, numBytes);
if (!bytesread) {
break;
}
for (int i = 0; i < numBytes / 2; i++) {
i2s_write_sample(buffer[i]);
OsWatchLoop();
}
}
i2s_end();
#endif // ESP8266
#ifdef ESP32
i2s_channel_enable(glob_script_mem.tx_handle);
while (wf.position() < fsize) {
int numBytes = _min(sizeof(buffer), fsize - wf.position() - 1);
int bytesread = wf.readBytes((char*)buffer, numBytes);
if (!bytesread) {
break;
}
i2s_channel_write(glob_script_mem.tx_handle, buffer, numBytes, nullptr, 100);
OsWatchLoop();
}
i2s_channel_disable(glob_script_mem.tx_handle);
#endif // ESP32
wf.close();
return 0;
}
#endif // USE_PLAY_WAVE
#ifdef USE_SCRIPT_FATFS_EXT
#ifdef USE_UFILESYS
int32_t script_logfile_write(char *path, char *payload, uint32_t size) {
@ -8823,13 +9008,14 @@ bool Is_gpio_used(uint8_t gpiopin) {
}
void ScripterEvery100ms(void) {
static uint8_t xsns_index = 0;
if (bitRead(Settings->rule_enabled, 0) && (TasmotaGlobal.uptime > 4)) {
if (GetNextSensor()) {
//Run_Scripter(">T", 2, ResponseData());
if (glob_script_mem.teleperiod) Run_Scripter(glob_script_mem.teleperiod, 0, ResponseData());
}
}
if (bitRead(Settings->rule_enabled, 0)) {
if (glob_script_mem.fast_script) Run_Scripter1(glob_script_mem.fast_script, 0, 0);
}
@ -10648,9 +10834,10 @@ uint32_t fsize;
#define fileHeaderSize 14
#define infoHeaderSize 40
if (renderer && renderer->framebuffer) {
if (renderer && (renderer->framebuffer || renderer->rgb_fb)) {
uint8_t *bp = renderer->framebuffer;
uint8_t *lbuf = (uint8_t*)special_malloc(Settings->display_width * 3 + 2);
uint16_t *dwp = renderer->rgb_fb;
uint8_t *lbuf = (uint8_t*)special_malloc(Settings->display_width * 3 + 6);
memset(lbuf, 0, Settings->display_width * 3);
if (!lbuf) return -3;
uint8_t dmflg = 0;
@ -10728,6 +10915,19 @@ uint32_t fsize;
#endif
}
}
} else if (bpp == 16) {
// RGB displays have RAM display buffer only ESP32 S3
lbp = lbuf;
dwp = renderer->rgb_fb + ((Settings->display_height - lins - 1) * Settings->display_width);
for (uint32_t cols = 0; cols < Settings->display_width; cols++) {
uint16_t color = *dwp++;
if (renderer->lvgl_pars()->swap_color) {
color = (color >> 8) | (color << 8);
}
*lbp++ = (color &0x001f) << 3; // B (5 bit)
*lbp++ = (color &0x07e0) >> 3; // >> 5 G (6 bit)
*lbp++ = (color &0xf800) >> 8; // >> 10 R (5 bit)
}
} else {
// one bit
for (uint32_t cols = 0; cols < Settings->display_width; cols += 8) {
@ -10748,6 +10948,7 @@ uint32_t fsize;
}
}
client.write((const char*)lbuf, Settings->display_width * 3);
client.flush();
}
}
if (lbuf) free(lbuf);