mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-16 15:26:29 +00:00
Audio refactoring and fixes (#19594)
This commit is contained in:
parent
0f6b59f67e
commit
31e56903c5
@ -215,6 +215,7 @@ be_extern_native_class(AudioGeneratorWAV);
|
||||
be_extern_native_class(AudioGeneratorMP3);
|
||||
be_extern_native_class(AudioFileSourceFS);
|
||||
be_extern_native_class(AudioOpusDecoder);
|
||||
be_extern_native_class(AudioInputI2S);
|
||||
be_extern_native_class(md5);
|
||||
be_extern_native_class(udp);
|
||||
be_extern_native_class(webclient);
|
||||
@ -304,6 +305,7 @@ BERRY_LOCAL bclass_array be_class_table = {
|
||||
&be_native_class(AudioFileSourceFS),
|
||||
#endif // USE_UFILESYS
|
||||
&be_native_class(AudioOpusDecoder),
|
||||
&be_native_class(AudioInputI2S),
|
||||
#endif // USE_I2S_AUDIO_BERRY
|
||||
#if defined(USE_BERRY_INT64) || defined(USE_MATTER_DEVICE)
|
||||
&be_native_class(int64),
|
||||
|
@ -86,6 +86,17 @@ BE_FUNC_CTYPE_DECLARE(be_audio_output_consume_silence, "i", ".");
|
||||
extern int i2s_output_i2s_set_lsb_justified(void* out, bbool lsbJustified);
|
||||
BE_FUNC_CTYPE_DECLARE(i2s_output_i2s_set_lsb_justified, "b", ".b");
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Audio Input I2S
|
||||
|
||||
// AudioInputI2S.init() -> instance
|
||||
extern void* be_audio_input_i2s_init(void);
|
||||
BE_FUNC_CTYPE_DECLARE(be_audio_input_i2s_init, "+.p", "");
|
||||
|
||||
// AudioInputI2S.deinit()-> void
|
||||
extern void* be_audio_input_i2s_deinit(void* instance);
|
||||
BE_FUNC_CTYPE_DECLARE(be_audio_input_i2s_deinit, "", ".");
|
||||
|
||||
|
||||
#include "be_fixed_be_class_AudioOutputI2S.h"
|
||||
#include "be_fixed_be_class_AudioGenerator.h"
|
||||
@ -93,6 +104,7 @@ BE_FUNC_CTYPE_DECLARE(i2s_output_i2s_set_lsb_justified, "b", ".b");
|
||||
#include "be_fixed_be_class_AudioGeneratorMP3.h"
|
||||
#include "be_fixed_be_class_AudioFileSource.h"
|
||||
#include "be_fixed_be_class_AudioFileSourceFS.h"
|
||||
#include "be_fixed_be_class_AudioInputI2S.h"
|
||||
|
||||
/* @const_object_info_begin
|
||||
|
||||
@ -146,6 +158,12 @@ class be_class_AudioFileSourceFS (scope: global, name: AudioFileSourceFS, super:
|
||||
deinit, func(i2s_file_source_fs_deinit)
|
||||
}
|
||||
|
||||
class be_class_AudioInputI2S (scope: global, name: AudioInputI2S, strings: weak) {
|
||||
.p, var
|
||||
init, ctype_func(be_audio_input_i2s_init)
|
||||
deinit, ctype_func(be_audio_input_i2s_deinit)
|
||||
}
|
||||
|
||||
@const_object_info_end */
|
||||
|
||||
#endif // USE_I2S_AUDIO_BERRY
|
||||
|
@ -42,6 +42,28 @@
|
||||
* Driver Settings in memory
|
||||
\*********************************************************************************************/
|
||||
|
||||
// I2S communication mode
|
||||
enum : int8_t {
|
||||
I2S_MODE_STD = 0, // I2S mode standard
|
||||
I2S_MODE_PDM = 1, // I2S mode PDM
|
||||
I2S_MODE_TDM = 2, // I2S mode TDM
|
||||
I2S_MODE_DAC = 3, // Using internal DAC - only available on ESP32
|
||||
};
|
||||
|
||||
// I2S slot mask (left, right, both)
|
||||
enum : int8_t {
|
||||
I2S_SLOT_LEFT = 1, // left
|
||||
I2S_SLOT_RIGHT = 2, // right
|
||||
I2S_SLOT_BOTH = 3, // both
|
||||
};
|
||||
|
||||
// I2S slot configuration
|
||||
enum : int8_t {
|
||||
I2S_SLOT_MSB = 0, // MSB
|
||||
I2S_SLOT_PCM = 1, // PCM
|
||||
I2S_SLOT_PHILIPS = 2, // Philips
|
||||
};
|
||||
|
||||
typedef struct{
|
||||
struct{
|
||||
uint8_t version = 0; // B00
|
||||
@ -53,11 +75,11 @@ typedef struct{
|
||||
uint32_t spare01; // B04-07
|
||||
} sys;
|
||||
struct {
|
||||
uint8_t mode = 0; // B00 STD = 0, PDM = 1, TDM = 2
|
||||
uint8_t mode = I2S_MODE_STD; // B00 - I2S mode standard, PDM, TDM, DAC
|
||||
bool apll = 1; // B01 - will be ignored on unsupported SOC's
|
||||
bool channels = 0; // B02 - 1 = mono, 2 = stereo
|
||||
uint8_t channels = 2; // B02 - 1 = mono, 2 = stereo
|
||||
uint8_t codec = 0; // B03 - S3 box only, unused for now
|
||||
uint8_t slot_config = 0; // B04 - slot configuration MSB = 0, PCM = 1, PHILIPS = 2
|
||||
uint8_t slot_config = I2S_SLOT_MSB;// B04 - slot configuration MSB = 0, PCM = 1, PHILIPS = 2
|
||||
uint8_t volume = 10; // B05
|
||||
bool mclk_inv = 0; // B06 - invert mclk
|
||||
bool bclk_inv = 0; // B07 - invert bclk
|
||||
@ -65,11 +87,10 @@ typedef struct{
|
||||
uint8_t spare[7]; // B09-0F
|
||||
} tx;
|
||||
struct {
|
||||
uint32_t sample_rate = 32000; // B00-03
|
||||
uint32_t sample_rate = 16000; // B00-03
|
||||
uint8_t gain = 30; // B04
|
||||
uint8_t mode = 0; // B05 - STD = 0, PDM = 1, TDM = 2
|
||||
|
||||
uint8_t slot_mask = 1; // B06 - left = 1 /right = 2 /both = 3
|
||||
uint8_t mode = I2S_MODE_STD; // B05 - I2S mode standard, PDM, TDM, DAC
|
||||
uint8_t slot_mask = I2S_SLOT_LEFT;// B06 - slot mask
|
||||
uint8_t slot_mode = 0; // B07 - mono/stereo - 1 is added for both
|
||||
uint8_t codec = 0; // B08 - unused for now
|
||||
uint8_t mp3_encoder = 1; // B09 - will be ignored without PS-RAM
|
||||
@ -106,10 +127,7 @@ struct AUDIO_I2S_t {
|
||||
TaskHandle_t mp3_task_handle;
|
||||
TaskHandle_t mic_task_handle;
|
||||
|
||||
uint32_t mic_size;
|
||||
uint8_t *mic_buff;
|
||||
char mic_path[32];
|
||||
File fwp;
|
||||
uint8_t mic_stop;
|
||||
int8_t mic_error;
|
||||
bool use_stream = false;
|
||||
@ -132,8 +150,6 @@ struct AUDIO_I2S_t {
|
||||
|
||||
} audio_i2s;
|
||||
|
||||
enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 };
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Class for outputting sound as endpoint for ESP8266Audio library
|
||||
\*********************************************************************************************/
|
||||
@ -172,7 +188,7 @@ public:
|
||||
}
|
||||
|
||||
virtual bool SetRate(int hz) {
|
||||
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: SetRate: %i"), hz);
|
||||
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: SetRate: %i was %i on=%i"), hz, this->hertz, _i2s_on);
|
||||
if (hz == (int) this->hertz) { return true; }
|
||||
this->hertz = hz;
|
||||
if (_i2s_on) {
|
||||
@ -229,7 +245,7 @@ protected:
|
||||
bool _i2s_on = false; // is I2S audio active
|
||||
// local copy of useful settings for audio
|
||||
// TX
|
||||
uint8_t _tx_mode = EXTERNAL_I2S; // EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2
|
||||
uint8_t _tx_mode = I2S_MODE_STD; // EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2
|
||||
bool _tx_enabled = false; // true = enabled, false = disabled
|
||||
uint8_t _tx_channels = 2; // true = mono, false = stereo
|
||||
i2s_chan_handle_t _tx_chan; // I2S channel handle, automatically computed
|
||||
@ -255,10 +271,10 @@ void TasmotaAudioOutputI2S::loadSettings(void) {
|
||||
_i2s_on = false;
|
||||
bps = I2S_DATA_BIT_WIDTH_16BIT;
|
||||
_tx_channels = audio_i2s.Settings->tx.channels;
|
||||
if (_tx_channels == 0) { _tx_channels = 1; } // if zero channel default to mono
|
||||
if (_tx_channels == 0) { _tx_channels = 2; } // if zero channel default to stereo
|
||||
if (_tx_channels > 2) { _tx_channels = 2; } // if > 2 channels default to stereo
|
||||
channels = (_tx_channels == 1) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO;
|
||||
_tx_mode = EXTERNAL_I2S;
|
||||
_tx_mode = I2S_MODE_STD;
|
||||
_tx_enabled = false;
|
||||
|
||||
_gpio_mclk = (gpio_num_t) Pin(GPIO_I2S_MCLK);
|
||||
@ -292,6 +308,7 @@ bool TasmotaAudioOutputI2S::stop() {
|
||||
if (audio_i2s.Settings->sys.duplex == 0 && audio_i2s.Settings->sys.rx == 1) {
|
||||
i2s_del_channel(_tx_chan);
|
||||
_i2s_on = false;
|
||||
AddLog(LOG_LEVEL_INFO, "I2S: stop: I2S channel disabled");
|
||||
}
|
||||
_tx_enabled = false;
|
||||
return true;
|
||||
@ -322,7 +339,7 @@ int32_t TasmotaAudioOutputI2S::consumeSamples(int16_t *samples, size_t count) {
|
||||
right = (((int16_t)(right & 0xff)) - 128) << 8;
|
||||
}
|
||||
|
||||
if (_tx_mode == INTERNAL_DAC) {
|
||||
if (_tx_mode == I2S_MODE_DAC) {
|
||||
left = Amplify(left) + 0x8000;
|
||||
right = Amplify(right) + 0x8000;
|
||||
} else {
|
||||
@ -377,14 +394,14 @@ bool TasmotaAudioOutputI2S::startI2SChannel(void) {
|
||||
};
|
||||
|
||||
// change configuration if we are using PCM or PHILIPS
|
||||
if (audio_i2s.Settings->tx.slot_config == 1) { // PCM
|
||||
if (audio_i2s.Settings->tx.slot_config == I2S_SLOT_PCM) { // PCM
|
||||
tx_std_cfg.slot_cfg = I2S_STD_PCM_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t)bps, (i2s_slot_mode_t)channels);
|
||||
} else if (audio_i2s.Settings->tx.slot_config == 2) { // PHILIPS
|
||||
} else if (audio_i2s.Settings->tx.slot_config == I2S_SLOT_PHILIPS) { // PHILIPS
|
||||
tx_std_cfg.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t)bps, (i2s_slot_mode_t)channels);
|
||||
}
|
||||
|
||||
_i2s_on = (i2s_channel_init_std_mode(_tx_chan, &tx_std_cfg) == 0);
|
||||
AddLog(LOG_LEVEL_DEBUG, "I2S: TX channel with %i bit width on %i channels initialized ok=%i", bps, channels, _i2s_on);
|
||||
AddLog(LOG_LEVEL_DEBUG, "I2S: TX channel with %i bits width on %i channels initialized i2s_on=%i", bps, channels, _i2s_on);
|
||||
|
||||
if (audio_i2s.Settings->sys.duplex == 1) {
|
||||
i2s_channel_init_std_mode(audio_i2s.rx_handle, &tx_std_cfg);
|
||||
@ -403,7 +420,7 @@ int TasmotaAudioOutputI2S::updateClockConfig(void) {
|
||||
#endif
|
||||
int result = i2s_channel_reconfig_std_clock(_tx_chan, &clk_cfg );
|
||||
if (_tx_enabled) { i2s_channel_enable(_tx_chan); }
|
||||
AddLog(LOG_LEVEL_DEBUG, "I2S: Updating clock config, result=%i", result);
|
||||
AddLog(LOG_LEVEL_DEBUG, "I2S: Updating clock config");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -100,31 +100,35 @@ void (* const I2SAudio_Command[])(void) PROGMEM = {
|
||||
* microphone related functions
|
||||
\*********************************************************************************************/
|
||||
|
||||
uint32_t I2sMicInit(uint8_t enable) {
|
||||
uint32_t I2sMicInit(void) {
|
||||
esp_err_t err = ESP_OK;
|
||||
i2s_slot_mode_t slot_mode = (audio_i2s.Settings->rx.slot_mode == 0) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO;
|
||||
gpio_num_t _CLK;
|
||||
gpio_num_t clk_gpio;
|
||||
|
||||
if(audio_i2s.Settings->sys.duplex == 1 && audio_i2s.rx_handle != nullptr){
|
||||
i2s_slot_mode_t slot_mode = (audio_i2s.Settings->rx.slot_mode == 0) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO;
|
||||
AddLog(LOG_LEVEL_DEBUG, "I2S: mic init rx_channels=%i", slot_mode);
|
||||
|
||||
if (audio_i2s.Settings->sys.duplex == 1 && audio_i2s.rx_handle){
|
||||
return 0; // no need to en- or disable when in full duplex mode and already initialized
|
||||
}
|
||||
|
||||
if (audio_i2s.rx_handle == nullptr){
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
|
||||
err = i2s_new_channel(&chan_cfg, NULL, &audio_i2s.rx_handle);
|
||||
AddLog(LOG_LEVEL_DEBUG, "I2S: mic init i2s_new_channel err=%i", err);
|
||||
switch (audio_i2s.Settings->rx.mode){
|
||||
case 1:
|
||||
case I2S_MODE_PDM:
|
||||
{
|
||||
_CLK = (gpio_num_t)Pin(GPIO_I2S_WS,1); //legacy setting for Core2, might be wrong
|
||||
if(_CLK == -1){
|
||||
_CLK = (gpio_num_t)Pin(GPIO_I2S_WS); //fallback to other port, might be wrong
|
||||
clk_gpio = (gpio_num_t)Pin(GPIO_I2S_WS,1); //legacy setting for Core2, might be wrong
|
||||
if (clk_gpio == -1){
|
||||
clk_gpio = (gpio_num_t)Pin(GPIO_I2S_WS); //fallback to other port, might be wrong
|
||||
}
|
||||
i2s_pdm_rx_config_t pdm_rx_cfg = {
|
||||
.clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(audio_i2s.Settings->rx.sample_rate),
|
||||
/* The default mono slot is the left slot (whose 'select pin' of the PDM microphone is pulled down) */
|
||||
.slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, slot_mode),
|
||||
.gpio_cfg = {
|
||||
.clk = _CLK,
|
||||
.clk = clk_gpio,
|
||||
.din = (gpio_num_t)Pin(GPIO_I2S_DIN),
|
||||
.invert_flags = {
|
||||
.clk_inv = false,
|
||||
@ -132,10 +136,11 @@ uint32_t I2sMicInit(uint8_t enable) {
|
||||
},
|
||||
};
|
||||
pdm_rx_cfg.slot_cfg.slot_mask = (i2s_pdm_slot_mask_t)audio_i2s.Settings->rx.slot_mask;
|
||||
err = i2s_channel_init_pdm_rx_mode(audio_i2s.rx_handle, &pdm_rx_cfg);}
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel in PDM mode, CLK: %i, DIN: %i, 16 bit width, %i channel(s), err code: %u"),_CLK, Pin(GPIO_I2S_DIN), slot_mode, err);
|
||||
err = i2s_channel_init_pdm_rx_mode(audio_i2s.rx_handle, &pdm_rx_cfg);
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel in PDM mode, CLK: %i, DIN: %i, 16 bit width, %i channel(s), err code: %u"),clk_gpio, Pin(GPIO_I2S_DIN), slot_mode, err);
|
||||
}
|
||||
break;
|
||||
default: // same as 0
|
||||
case I2S_MODE_STD:
|
||||
{
|
||||
i2s_std_config_t rx_std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(audio_i2s.Settings->rx.sample_rate),
|
||||
@ -154,25 +159,35 @@ uint32_t I2sMicInit(uint8_t enable) {
|
||||
},
|
||||
};
|
||||
rx_std_cfg.slot_cfg.slot_mask = (i2s_std_slot_mask_t)audio_i2s.Settings->rx.slot_mask;
|
||||
i2s_channel_init_std_mode(audio_i2s.rx_handle, &rx_std_cfg);}
|
||||
i2s_channel_init_std_mode(audio_i2s.rx_handle, &rx_std_cfg);
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel in standard mode with 16 bit width on %i channel(s) initialized"),slot_mode);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
AddLog(LOG_LEVEL_INFO, "I2S: invalid rx mode=%i", audio_i2s.Settings->rx.mode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if(enable == 0){
|
||||
int _err = i2s_channel_disable(audio_i2s.rx_handle);
|
||||
i2s_del_channel(audio_i2s.rx_handle);
|
||||
audio_i2s.rx_handle = nullptr;
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel disable: %i"),_err);
|
||||
}
|
||||
else{
|
||||
err = i2s_channel_enable(audio_i2s.rx_handle);
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel enable: %i"),err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void I2sMicDeinit(void) {
|
||||
esp_err_t err = ESP_OK;
|
||||
gpio_num_t clk_gpio;
|
||||
|
||||
if (!audio_i2s.rx_handle) { return; }
|
||||
|
||||
if (!audio_i2s.Settings->sys.duplex) { // if duplex mode, there is no mic channel - TODO check this
|
||||
int err = i2s_channel_disable(audio_i2s.rx_handle);
|
||||
i2s_del_channel(audio_i2s.rx_handle);
|
||||
audio_i2s.rx_handle = nullptr;
|
||||
AddLog(LOG_LEVEL_DEBUG, "I2S: RX channel disable: %i", err);
|
||||
}
|
||||
}
|
||||
|
||||
// micro to mp3 file or stream
|
||||
void I2sMicTask(void *arg){
|
||||
int8_t error = 0;
|
||||
@ -295,7 +310,7 @@ exit:
|
||||
audio_i2s.client.stop();
|
||||
}
|
||||
|
||||
I2sMicInit(0);
|
||||
I2sMicDeinit();
|
||||
audio_i2s.mic_stop = 0;
|
||||
audio_i2s.mic_error = error;
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %d"), error);
|
||||
@ -319,7 +334,7 @@ int32_t I2sRecordShine(char *path) {
|
||||
if (audio_i2s.use_stream) {
|
||||
stack = 8000;
|
||||
}
|
||||
I2sMicInit(1);
|
||||
I2sMicInit();
|
||||
|
||||
err = xTaskCreatePinnedToCore(I2sMicTask, "MIC", stack, NULL, 3, &audio_i2s.mic_task_handle, 1);
|
||||
|
||||
@ -342,40 +357,47 @@ void I2SAudioPower(bool power) {
|
||||
callBerryEventDispatcher(PSTR("audio"), PSTR("power"), power, nullptr, 0);
|
||||
}
|
||||
|
||||
void I2SSettingsLoad(bool erase) {
|
||||
|
||||
//
|
||||
// I2SSettingsLoad(erase:bool)
|
||||
//
|
||||
// Load settings from file system.
|
||||
// File is `/.drvset042`
|
||||
void I2SSettingsLoad(const char * config_filename, bool erase) {
|
||||
// allocate memory for settings
|
||||
audio_i2s.Settings = new tI2SSettings();
|
||||
if (!audio_i2s.Settings) {
|
||||
AddLog(LOG_LEVEL_ERROR, "I2S: ERROR memory allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config_filename) { return; } // if no filename, use defaults
|
||||
|
||||
#ifndef USE_UFILESYS
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("CFG: I2S use defaults as file system not enabled"));
|
||||
AddLog(LOG_LEVEL_INFO, "I2S: use defaults as file system not enabled");
|
||||
#else
|
||||
char filename[20];
|
||||
// Use for drivers:
|
||||
snprintf_P(filename, sizeof(filename), TASM_FILE_DRIVER, XDRV_42);
|
||||
if (erase) {
|
||||
TfsDeleteFile(filename); // Use defaults
|
||||
TfsDeleteFile(config_filename); // Use defaults
|
||||
}
|
||||
else if (TfsLoadFile(filename, (uint8_t*)audio_i2s.Settings, sizeof(tI2SSettings))) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("CFG: I2S loaded from file"));
|
||||
else if (TfsLoadFile(config_filename, (uint8_t*)audio_i2s.Settings, sizeof(tI2SSettings))) {
|
||||
AddLog(LOG_LEVEL_INFO, "I2S: config loaded from file '%s'", config_filename);
|
||||
}
|
||||
else {
|
||||
// File system not ready: No flash space reserved for file system
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: I2S use defaults as file system not ready or file not found"));
|
||||
I2SSettingsSave();
|
||||
AddLog(LOG_LEVEL_DEBUG, "I2S: use defaults as file system not ready or file not found");
|
||||
I2SSettingsSave(config_filename);
|
||||
}
|
||||
#endif // USE_UFILESYS
|
||||
}
|
||||
|
||||
void I2SSettingsSave(void) {
|
||||
void I2SSettingsSave(const char * config_filename) {
|
||||
#ifdef USE_UFILESYS
|
||||
char filename[20];
|
||||
// Use for drivers:
|
||||
snprintf_P(filename, sizeof(filename), TASM_FILE_DRIVER, XDRV_42);
|
||||
if (TfsSaveFile(filename, (const uint8_t*)audio_i2s.Settings, sizeof(tI2SSettings))) {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: I2S saved to file"));
|
||||
if (!config_filename) { return; } // if no filename, use defaults
|
||||
|
||||
if (TfsSaveFile(config_filename, (const uint8_t*)audio_i2s.Settings, sizeof(tI2SSettings))) {
|
||||
AddLog(LOG_LEVEL_DEBUG, "I2S: config saved to file '%s'", config_filename);
|
||||
} else {
|
||||
// File system not ready: No flash space reserved for file system
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: ERROR I2S file system not ready or unable to save file"));
|
||||
AddLog(LOG_LEVEL_DEBUG, "I2S: ERROR file system not ready or unable to save file");
|
||||
}
|
||||
#endif // USE_UFILESYS
|
||||
}
|
||||
@ -384,11 +406,17 @@ void I2SSettingsSave(void) {
|
||||
* Driver init
|
||||
\*********************************************************************************************/
|
||||
|
||||
//
|
||||
// I2sCheckCfg
|
||||
//
|
||||
// Multiple checks
|
||||
void I2sCheckCfg(void){
|
||||
bool useDuplexMode = ((Pin(GPIO_I2S_DIN) != -1) && (Pin(GPIO_I2S_DOUT) != -1)); // din and dout must be configured on port 0 for full duplex
|
||||
// din and dout must be configured on port 0 for full duplex
|
||||
bool useDuplexMode = ((Pin(GPIO_I2S_DIN) != -1) && (Pin(GPIO_I2S_DOUT) != -1));
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: DIN %i , DOUT %i"),Pin(GPIO_I2S_DIN),Pin(GPIO_I2S_DOUT) );
|
||||
|
||||
if (useDuplexMode){
|
||||
if(audio_i2s.Settings->rx.mode == 1 || audio_i2s.Settings->tx.mode == 1 ){
|
||||
if (audio_i2s.Settings->rx.mode == I2S_MODE_PDM || audio_i2s.Settings->tx.mode == I2S_MODE_PDM ){
|
||||
useDuplexMode = false;
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: PDM forbids full duplex mode"));
|
||||
}
|
||||
@ -399,7 +427,7 @@ void I2sCheckCfg(void){
|
||||
}
|
||||
if (Pin(GPIO_I2S_DIN) != -1 || Pin(GPIO_I2S_DIN, 1) != -1){ // micro could be port 0 or 1
|
||||
audio_i2s.Settings->sys.rx = 1;
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: I2S RX config = mode: %i, channels: %i, gain: %i, sample rate: %i"), audio_i2s.Settings->rx.mode, (uint8_t)(audio_i2s.Settings->rx.slot_mode + 1), audio_i2s.Settings->rx.gain, audio_i2s.Settings->rx.sample_rate);
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX config = mode: %i, channels: %i, gain: %i, sample rate: %i"), audio_i2s.Settings->rx.mode, (uint8_t)(audio_i2s.Settings->rx.slot_mode + 1), audio_i2s.Settings->rx.gain, audio_i2s.Settings->rx.sample_rate);
|
||||
}
|
||||
else{
|
||||
audio_i2s.Settings->sys.rx = 0;
|
||||
@ -412,22 +440,33 @@ void I2sCheckCfg(void){
|
||||
audio_i2s.Settings->sys.tx = 0;
|
||||
}
|
||||
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("I2S: init pins bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), Pin(GPIO_I2S_BCLK) , Pin(GPIO_I2S_WS), Pin(GPIO_I2S_DOUT), Pin(GPIO_I2S_MCLK), Pin(GPIO_I2S_DIN));
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("I2S: init pins bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d [tx=%i, rx=%i, duplex=%i]"),
|
||||
Pin(GPIO_I2S_BCLK) , Pin(GPIO_I2S_WS), Pin(GPIO_I2S_DOUT), Pin(GPIO_I2S_MCLK), Pin(GPIO_I2S_DIN),
|
||||
audio_i2s.Settings->sys.tx, audio_i2s.Settings->sys.tx, audio_i2s.Settings->sys.duplex);
|
||||
|
||||
}
|
||||
|
||||
bool I2sPinInit(void) {
|
||||
int result = 0;
|
||||
|
||||
//
|
||||
// I2sInit
|
||||
//
|
||||
// Initialize I2S driver for input and output
|
||||
void I2sInit(void) {
|
||||
// we need at least one pin configured
|
||||
if(Pin(GPIO_I2S_DIN) + Pin(GPIO_I2S_DIN,1) + Pin(GPIO_I2S_DOUT) + Pin(GPIO_I2S_DOUT,1) == -4){
|
||||
return false;
|
||||
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: no pin configured"));
|
||||
return;
|
||||
}
|
||||
|
||||
I2SSettingsLoad(false);
|
||||
I2SSettingsLoad("/.drvset042", false); // load configuration (no-erase)
|
||||
if (!audio_i2s.Settings) { return; } // fatal error, could not allocate memory for configuration
|
||||
|
||||
// check configuration is valid
|
||||
I2sCheckCfg();
|
||||
|
||||
// TODO
|
||||
int result = 0;
|
||||
if (audio_i2s.Settings->sys.rx == 1 && audio_i2s.Settings->sys.duplex == 0){
|
||||
result += I2sMicInit(0);
|
||||
I2sMicDeinit();
|
||||
}
|
||||
|
||||
if(audio_i2s.Settings->sys.tx == 1){
|
||||
@ -436,16 +475,6 @@ bool I2sPinInit(void) {
|
||||
result += err;
|
||||
}
|
||||
|
||||
return (result == 0);
|
||||
}
|
||||
|
||||
void I2sInit(void) {
|
||||
|
||||
if (I2sPinInit() == false) {
|
||||
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: no valid pin config"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(audio_i2s.out != nullptr){
|
||||
audio_i2s.out->SetGain(((float)audio_i2s.Settings->tx.volume / 100.0) * 4.0);
|
||||
audio_i2s.out->begin();
|
||||
@ -691,12 +720,12 @@ bool Xdrv42(uint32_t function) {
|
||||
bool result = false;
|
||||
|
||||
switch (function) {
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command);
|
||||
break;
|
||||
case FUNC_INIT:
|
||||
I2sInit();
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command);
|
||||
break;
|
||||
case FUNC_WEB_ADD_MAIN_BUTTON:
|
||||
//MP3ShowStream();
|
||||
break;
|
||||
|
@ -308,6 +308,19 @@ extern "C" {
|
||||
be_return_nil(vm);
|
||||
}
|
||||
#endif // USE_UFILESYS
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// AudioInputI2S.init() -> instance
|
||||
void* be_audio_input_i2s_init(void) {
|
||||
return audio_i2s.out; // return the singleton of TasmotaAudioOutputI2S which is already initialized
|
||||
}
|
||||
|
||||
// AudioInputI2S.deinit()-> void
|
||||
void be_audio_input_i2s_deinit(TasmotaAudioOutputI2S * out) {
|
||||
out->stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // USE_I2S_AUDIO_BERRY
|
||||
|
Loading…
x
Reference in New Issue
Block a user