Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2S: multiple fixes #21511

Merged
merged 1 commit into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions lib/lib_audio/ESP8266Audio/src/AudioGeneratorMP3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ bool AudioGeneratorMP3::GetOneSample(int16_t sample[2])
// If we're here, we have one decoded frame and sent 0 or more samples out
if (samplePtr < synth->pcm.length) {
sample[AudioOutput::LEFTCHANNEL ] = synth->pcm.samples[0][samplePtr];
sample[AudioOutput::RIGHTCHANNEL] = synth->pcm.samples[1][samplePtr];
if(lastChannels == 2) {
sample[AudioOutput::RIGHTCHANNEL] = synth->pcm.samples[1][samplePtr];
}
samplePtr++;
} else {
samplePtr = 0;
Expand All @@ -200,7 +202,9 @@ bool AudioGeneratorMP3::GetOneSample(int16_t sample[2])
}
// for IGNORE and CONTINUE, just play what we have now
sample[AudioOutput::LEFTCHANNEL ] = synth->pcm.samples[0][samplePtr];
sample[AudioOutput::RIGHTCHANNEL] = synth->pcm.samples[1][samplePtr];
if(lastChannels == 2) {
sample[AudioOutput::RIGHTCHANNEL] = synth->pcm.samples[1][samplePtr];
}
samplePtr++;
}
return true;
Expand Down
2 changes: 1 addition & 1 deletion lib/lib_audio/ESP8266Audio/src/AudioOutputMixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ bool AudioOutputMixer::loop()
}
}
if (avail) {
int16_t s[2];
int16_t s[2] = {0};
if (leftAccum[readPtr] > 32767) {
s[LEFTCHANNEL] = 32767;
} else if (leftAccum[readPtr] < -32767) {
Expand Down
20 changes: 20 additions & 0 deletions tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ public:
// Tx
virtual bool begin(void) { return beginTx(); }; // the name `begin()`is inherited from superclass, prefer `beginTx()` which is more explicit
virtual bool stop(void) { return stopTx(); }; // the name `stop()`is inherited from superclass, prefer `stopTx()` which is more explicit
virtual void flush(void); // makes sure that all stored DMA samples are consumed / played to prevent static noise after stop()
bool beginTx(void);
bool stopTx(void);
bool ConsumeSample(int16_t sample[2]);
Expand Down Expand Up @@ -322,6 +323,11 @@ bool TasmotaI2S::beginTx(void) {
} else
#endif // SOC_DAC_SUPPORTED
{
uint8_t zero_buffer[240] = {0};
size_t sz;
for(int i = 0;i < 6;i++){
i2s_channel_preload_data(_tx_handle, zero_buffer, sizeof(zero_buffer), &sz); // preload DMA buffer with silence
}
err = i2s_channel_enable(_tx_handle);
}
AddLog(LOG_LEVEL_INFO, "I2S: Tx i2s_channel_enable err=0x%04X", err);
Expand Down Expand Up @@ -363,6 +369,20 @@ bool TasmotaI2S::stopTx() {
return true;
}

void TasmotaI2S::flush()
{
int buffersize = 6 * 240;
int16_t samples[2] = {0x0, 0x0};
for (int i = 0; i < buffersize; i++)
{
while (!ConsumeSample(samples))
{
delay(1);
}
}
AddLog(LOG_LEVEL_INFO, "I2S: flush DMA TX buffer");
}

bool TasmotaI2S::delTxHandle(void) {
esp_err_t err = ESP_OK;
AddLog(LOG_LEVEL_DEBUG, "I2S: calling delTxHandle() tx_running:%i tx_handle:%p", _tx_running, _tx_handle);
Expand Down
104 changes: 61 additions & 43 deletions tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ exit:
audio_i2s.in->stopRx();
audio_i2s_mp3.mic_stop = 0;
audio_i2s_mp3.mic_error = error;
AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %d"), error);
AddLog(LOG_LEVEL_INFO, PSTR("I2S: mp3task result code: %d"), error);
audio_i2s_mp3.mic_task_handle = 0;
audio_i2s_mp3.recdur = 0;
audio_i2s_mp3.stream_active = 0;
Expand All @@ -484,6 +484,15 @@ exit:
int32_t I2sRecordShine(char *path) {
esp_err_t err = ESP_OK;

switch(audio_i2s.Settings->rx.sample_rate){
case 32000: case 48000: case 44100:
break; // supported
default:
AddLog(LOG_LEVEL_INFO, PSTR("I2S: unsupported sample rate for MP3 encoding: %d"), audio_i2s.Settings->rx.sample_rate);
return -1;
}
AddLog(LOG_LEVEL_INFO, PSTR("I2S: accepted sample rate for MP3 encoding: %d"), audio_i2s.Settings->rx.sample_rate);

#ifdef USE_I2S_MP3
if (audio_i2s_mp3.decoder || audio_i2s_mp3.mp3) return 0;
#endif
Expand Down Expand Up @@ -735,8 +744,8 @@ void I2sInit(void) {
}
if (init_tx_ok) { audio_i2s.out = i2s; }
if (init_rx_ok) { audio_i2s.in = i2s; }
audio_i2s.Settings->sys.tx = init_tx_ok;
audio_i2s.Settings->sys.rx = init_rx_ok;
audio_i2s.Settings->sys.tx |= init_tx_ok; // Do not set to zero id already configured on another channnel
audio_i2s.Settings->sys.rx |= init_rx_ok;
if (init_tx_ok && init_rx_ok) { audio_i2s.Settings->sys.duplex = true; }

// if intput and output are configured, don't proceed with other IS2 ports
Expand All @@ -750,11 +759,11 @@ void I2sInit(void) {
if (audio_i2s.out) { audio_i2s.out->setExclusive(exclusive); }
if (audio_i2s.in) { audio_i2s.in->setExclusive(exclusive); }

if(audio_i2s.out != nullptr){
audio_i2s.out->SetGain(((float)audio_i2s.Settings->tx.gain / 100.0) * 4.0);
audio_i2s.out->beginTx(); // TODO - useful?
audio_i2s.out->stopTx();
}
// if(audio_i2s.out != nullptr){
// audio_i2s.out->SetGain(((float)(audio_i2s.Settings->tx.gain + 1)/ 100.0));
// audio_i2s.out->beginTx(); // TODO - useful?
// audio_i2s.out->stopTx();
// }
#ifdef USE_I2S_MP3
audio_i2s_mp3.mp3ram = nullptr;
if (audio_i2s.Settings->sys.mp3_preallocate == 1){
Expand Down Expand Up @@ -784,11 +793,21 @@ int32_t I2SPrepareTx(void) {
delay(1);
}
}

if (audio_i2s_mp3.mic_task_handle) {
audio_i2s_mp3.mic_stop = 1;
while (audio_i2s_mp3.mic_stop) {
delay(1);
}
}

AddLog(LOG_LEVEL_DEBUG, "I2S: I2SPrepareTx out=%p", audio_i2s.out);
if (!audio_i2s.out) { return I2S_ERR_OUTPUT_NOT_CONFIGURED; }

if (!audio_i2s.out->beginTx()) { return I2S_ERR_TX_FAILED; }

audio_i2s.out->SetGain(((float)(audio_i2s.Settings->tx.gain + 1)/ 100.0));

return I2S_OK;
}

Expand All @@ -813,21 +832,20 @@ int32_t I2SPrepareRx(void) {

#if defined(USE_I2S_MP3) || defined(USE_I2S_WEBRADIO)
void I2sMp3Task(void *arg) {
while (1) {
while (audio_i2s_mp3.mp3->isRunning()) {
if (!audio_i2s_mp3.mp3->loop()) {
audio_i2s_mp3.mp3->stop();
mp3_delete();
audio_i2s.out->stop();
if (audio_i2s_mp3.mp3_task_handle) {
vTaskDelete(audio_i2s_mp3.mp3_task_handle);
audio_i2s_mp3.mp3_task_handle = 0;
}
//mp3_task_handle=nullptr;
}
vTaskDelay(pdMS_TO_TICKS(1));
audio_i2s_mp3.task_running = true;
while (audio_i2s_mp3.mp3->isRunning() && audio_i2s_mp3.task_running) {
if (!audio_i2s_mp3.mp3->loop()) {
audio_i2s_mp3.task_running == false;
}
vTaskDelay(pdMS_TO_TICKS(1));
}
audio_i2s.out->flush();
audio_i2s_mp3.mp3->stop();
I2sStopPlaying();
mp3_delete();
audio_i2s_mp3.mp3_task_handle = nullptr;
audio_i2s_mp3.task_has_ended = true;
vTaskDelete(NULL);
}
#endif // defined(USE_I2S_MP3) || defined(USE_I2S_WEBRADIO)

Expand All @@ -851,29 +869,17 @@ void I2sMp3WrTask(void *arg){
vTaskDelay(pdMS_TO_TICKS(1));
}
}
audio_i2s_mp3.decoder->stop();
audio_i2s_mp3.task_has_ended = true;
audio_i2s.out->flush();
I2sStopPlaying();
audio_i2s_mp3.mp3_task_handle = nullptr;
audio_i2s_mp3.task_has_ended = true;
vTaskDelete(NULL);
}
void I2SStopMP3Play(void) {

if (audio_i2s_mp3.decoder) {
audio_i2s_mp3.decoder->stop();
delete audio_i2s_mp3.decoder;
audio_i2s_mp3.decoder = NULL;
}

if (audio_i2s_mp3.mp3_task_handle) {
vTaskDelete(audio_i2s_mp3.mp3_task_handle);
audio_i2s_mp3.mp3_task_handle = nullptr;
}
}
#endif // USE_I2S_MP3

void I2sStopPlaying() {
#ifdef USE_I2S_MP3
I2SStopMP3Play();
#endif // USE_I2S_MP3

#ifdef USE_I2S_WEBRADIO
I2sWebRadioStopPlaying();
#endif
Expand Down Expand Up @@ -921,7 +927,12 @@ void mp3_delete(void) {
delete audio_i2s_mp3.id3;
delete audio_i2s_mp3.mp3;
audio_i2s_mp3.mp3=nullptr;
I2SAudioPower(false);

if (audio_i2s_mp3.decoder) {
audio_i2s_mp3.decoder->stop();
delete audio_i2s_mp3.decoder;
audio_i2s_mp3.decoder = NULL;
}
}
#endif // USE_I2S_MP3

Expand Down Expand Up @@ -959,7 +970,7 @@ void CmndI2SMic(void) {


void CmndI2SStop(void) {
if (!I2SPrepareTx()) {
if (I2SPrepareTx() != I2S_OK) {
ResponseCmndChar("I2S output not configured");
return;
}
Expand Down Expand Up @@ -1006,7 +1017,7 @@ void CmndI2SGain(void) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
if (audio_i2s.out) {
audio_i2s.Settings->tx.gain = XdrvMailbox.payload;
audio_i2s.out->SetGain(((float)(audio_i2s.Settings->tx.gain-2)/100.0)*4.0);
audio_i2s.out->SetGain(((float)(audio_i2s.Settings->tx.gain+1)/100.0));
}
}
ResponseCmndNumber(audio_i2s.Settings->tx.gain);
Expand Down Expand Up @@ -1050,8 +1061,12 @@ void CmndI2SMicRec(void) {
if (!strncmp(XdrvMailbox.data, "-?", 2)) {
Response_P("{\"I2SREC-duration\":%d}", audio_i2s_mp3.recdur);
} else {
I2sRecordShine(XdrvMailbox.data);
ResponseCmndChar(XdrvMailbox.data);
int err = I2sRecordShine(XdrvMailbox.data);
if(err == pdPASS){
ResponseCmndChar(XdrvMailbox.data);
} else {
ResponseCmndChar_P(PSTR("Did not launch recording task"));
}
}
} else {
if (audio_i2s_mp3.mic_task_handle) {
Expand All @@ -1062,6 +1077,9 @@ void CmndI2SMicRec(void) {
}
ResponseCmndChar_P(PSTR("Stopped"));
}
else {
ResponseCmndChar_P(PSTR("No running recording"));
}
}
}
else{
Expand Down
Loading