From a81a46edfe3451fb07148ca67c7076a999492fef Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Sat, 1 Aug 2026 06:24:45 +0000 Subject: [PATCH 1/3] Report a recording as pending from the moment it is requested isRecording() gated the pending length check on a flag that only the recording task sets, and only once it has been scheduled and found work to do. record() stores the request and returns without touching it, so between the two the function answers 0 while a request is pending. The usual idiom if (M5.Mic.record(...)) { while (M5.Mic.isRecording()) { delay(1); } } therefore falls straight through on the first call after an idle period, and the buffer is read while it still holds the previous contents or a partially written frame. begin() waits the same way before restarting the task at a new sample rate. The lengths alone already answer the question: record() fills one in before notifying the task, and the task zeroes it only after writing the last sample. Drop the flag from the condition, and with it the member, whose only reader this was. end() now clears the requests, so one left unfinished by a stopped task cannot keep reporting a recording. Marking the function volatile keeps the reads in a polling loop from being optimized away, as the speaker side already does. The speaker side reaches the same guarantee the other way around, by setting its channel bits in the calling context before notifying its task, and re-checking them after clearing. --- src/utility/Mic_Class.cpp | 7 ++++--- src/utility/Mic_Class.hpp | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utility/Mic_Class.cpp b/src/utility/Mic_Class.cpp index f5cf740..f19e996 100644 --- a/src/utility/Mic_Class.cpp +++ b/src/utility/Mic_Class.cpp @@ -584,7 +584,6 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) { dst_remain = current_rec->length; if (dst_remain == 0) { - self->_is_recording = false; ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); src_idx = ~0u; src_len = 0; @@ -593,7 +592,6 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) { continue; } } - self->_is_recording = true; for (;;) { @@ -702,7 +700,6 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) { } } } - self->_is_recording = false; _i2s_stop(self->_cfg.i2s_port); self->_task_handle = nullptr; @@ -758,6 +755,10 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) { do { vTaskDelay(1); } while (_task_handle); } + // an unfinished request would otherwise keep isRecording() reporting a recording. + _rec_info[0] = recording_info_t(); + _rec_info[1] = recording_info_t(); + if (_cb_set_enabled) { _cb_set_enabled(_cb_set_enabled_args, false); } _i2s_driver_uninstall(_cfg.i2s_port); } diff --git a/src/utility/Mic_Class.hpp b/src/utility/Mic_Class.hpp index b784410..8af605a 100644 --- a/src/utility/Mic_Class.hpp +++ b/src/utility/Mic_Class.hpp @@ -114,7 +114,7 @@ namespace m5 /// now in recording or not. /// @return 0=not recording / 1=recording (There's room in the queue) / 2=recording (There's no room in the queue.) - size_t isRecording(void) const { return _is_recording ? ((bool)_rec_info[0].length) + ((bool)_rec_info[1].length) : 0; } + size_t isRecording(void) const volatile { return ((bool)_rec_info[0].length) + ((bool)_rec_info[1].length); } /// set recording sampling rate. /// @param sample_rate the sampling rate (Hz) @@ -186,7 +186,6 @@ namespace m5 int32_t _offset = 0; volatile bool _task_running = false; - volatile bool _is_recording = false; #if defined (SDL_h_) SDL_Thread* _task_handle = nullptr; #else From b5f52c5ec44bb0e612cdc7639ee18e7fcc27fcee Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Sat, 1 Aug 2026 06:24:45 +0000 Subject: [PATCH 2/3] Fail begin() when the recording task cannot be created The result of the task creation was discarded, so a failure left the class believing it was running with no task to serve it: begin() and record() both reported success, and the request stayed queued forever. Report the failure instead, which turns it into the false that record() is already documented to return, and take the half-finished start back down so a failed begin() leaves nothing configured behind it. --- src/utility/Mic_Class.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/utility/Mic_Class.cpp b/src/utility/Mic_Class.cpp index f19e996..60432bb 100644 --- a/src/utility/Mic_Class.cpp +++ b/src/utility/Mic_Class.cpp @@ -733,13 +733,16 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) { #if portNUM_PROCESSORS > 1 if (_cfg.task_pinned_core < portNUM_PROCESSORS) { - xTaskCreatePinnedToCore(mic_task, "mic_task", stack_size, this, _cfg.task_priority, &_task_handle, _cfg.task_pinned_core); + res = (pdPASS == xTaskCreatePinnedToCore(mic_task, "mic_task", stack_size, this, _cfg.task_priority, &_task_handle, _cfg.task_pinned_core)); } else #endif { - xTaskCreate(mic_task, "mic_task", stack_size, this, _cfg.task_priority, &_task_handle); + res = (pdPASS == xTaskCreate(mic_task, "mic_task", stack_size, this, _cfg.task_priority, &_task_handle)); } + // end() takes the driver and the callback back down; it still sees the + // class as running, which is what lets it do that. + if (!res) { end(); } } return res; From 7917eba448822e7296b2008782126d5ef27e9ec7 Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Sat, 1 Aug 2026 06:24:45 +0000 Subject: [PATCH 3/3] Fail begin() when the sound task cannot be created The result of the task creation was discarded here as well, so a failure left the class reporting that it had started although nothing would ever serve the queue. Report it, and take the half-finished start back down the same way. Playback itself already survives a failure: _play_raw() returns before queueing when there is no task handle, so nothing is left pending and isPlaying() keeps answering false. What this corrects is the answer begin() gives, and the driver it used to leave configured behind it. --- src/utility/Speaker_Class.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/utility/Speaker_Class.cpp b/src/utility/Speaker_Class.cpp index bc5c951..49d4727 100644 --- a/src/utility/Speaker_Class.cpp +++ b/src/utility/Speaker_Class.cpp @@ -930,20 +930,24 @@ namespace m5 _task_running = true; #if defined (SDL_h_) _task_handle = SDL_CreateThread(reinterpret_cast(spk_task), "spk_task", this); + res = (_task_handle != nullptr); #else size_t stack_size = 1280 + (_cfg.dma_buf_len * sizeof(uint32_t)); #if portNUM_PROCESSORS > 1 if (_cfg.task_pinned_core < portNUM_PROCESSORS) { - xTaskCreatePinnedToCore(spk_task, "spk_task", stack_size, this, _cfg.task_priority, &_task_handle, _cfg.task_pinned_core); + res = (pdPASS == xTaskCreatePinnedToCore(spk_task, "spk_task", stack_size, this, _cfg.task_priority, &_task_handle, _cfg.task_pinned_core)); } else #endif { - xTaskCreate(spk_task, "spk_task", stack_size, this, _cfg.task_priority, &_task_handle); + res = (pdPASS == xTaskCreate(spk_task, "spk_task", stack_size, this, _cfg.task_priority, &_task_handle)); } #endif + // end() takes the driver and the callback back down; it still sees the + // class as running, which is what lets it do that. + if (!res) { end(); } } return res;