Audio refactoring and fixes (#19594)

This commit is contained in:
s-hadinger 2023-09-25 23:31:32 +02:00 committed by GitHub
parent 0f6b59f67e
commit 31e56903c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 199 additions and 120 deletions

View File

@ -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),

View File

@ -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

View File

@ -42,34 +42,55 @@
* 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
// runtime options, will be saved but ignored on setting read
bool duplex = 0; // B01 - depends on GPIO setting and SOC caps, DIN and DOUT on same port in GPIO means -> try to use duplex if possible
bool tx = 0; // B02 - depends on GPIO setting
bool rx = 0; // B03 - depends on GPIO setting
uint32_t spare01; // B04-07
bool duplex = 0; // B01 - depends on GPIO setting and SOC caps, DIN and DOUT on same port in GPIO means -> try to use duplex if possible
bool tx = 0; // B02 - depends on GPIO setting
bool rx = 0; // B03 - depends on GPIO setting
uint32_t spare01; // B04-07
} sys;
struct {
uint8_t mode = 0; // B00 STD = 0, PDM = 1, TDM = 2
bool apll = 1; // B01 - will be ignored on unsupported SOC's
bool channels = 0; // 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 volume = 10; // B05
bool mclk_inv = 0; // B06 - invert mclk
bool bclk_inv = 0; // B07 - invert bclk
bool ws_inv = 0; // B08 - invert ws
uint8_t spare[7]; // B09-0F
uint8_t mode = I2S_MODE_STD; // B00 - I2S mode standard, PDM, TDM, DAC
bool apll = 1; // B01 - will be ignored on unsupported SOC's
uint8_t channels = 2; // B02 - 1 = mono, 2 = stereo
uint8_t codec = 0; // B03 - S3 box only, unused for now
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
bool ws_inv = 0; // B08 - invert ws
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;
}

View File

@ -100,77 +100,92 @@ 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){
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:
{
_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
case I2S_MODE_PDM:
{
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_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_gpio,
.din = (gpio_num_t)Pin(GPIO_I2S_DIN),
.invert_flags = {
.clk_inv = false,
.clk_inv = false,
},
},
};
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);
},
};
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_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),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, slot_mode),
.gpio_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(audio_i2s.Settings->rx.sample_rate),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, slot_mode),
.gpio_cfg = {
.mclk = (gpio_num_t)Pin(GPIO_I2S_MCLK),
.bclk = (gpio_num_t)Pin(GPIO_I2S_BCLK),
.ws = (gpio_num_t)Pin(GPIO_I2S_WS),
.dout = I2S_GPIO_UNUSED,
.din = (gpio_num_t)Pin(GPIO_I2S_DIN),
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
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;
}
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);
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, PSTR("I2S: RX channel disable: %i"),_err);
AddLog(LOG_LEVEL_DEBUG, "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;
}
// micro to mp3 file or stream
@ -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 (useDuplexMode){
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"));
}
@ -397,37 +425,48 @@ void I2sCheckCfg(void){
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: will try to use full duplex mode"));
}
}
if(Pin(GPIO_I2S_DIN) != -1 || Pin(GPIO_I2S_DIN,1) != -1){ // micro could be port 0 or 1
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;
audio_i2s.Settings->rx.mp3_encoder = 0; // do not allocate buffer
}
if(Pin(GPIO_I2S_DOUT) != -1){ // output is only supported on port 0
if (Pin(GPIO_I2S_DOUT) != -1){ // output is only supported on port 0
audio_i2s.Settings->sys.tx = 1;
}
else{
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();
if(audio_i2s.Settings->sys.rx == 1 && audio_i2s.Settings->sys.duplex == 0){
result += I2sMicInit(0);
// TODO
int result = 0;
if (audio_i2s.Settings->sys.rx == 1 && audio_i2s.Settings->sys.duplex == 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;

View File

@ -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