feat: Use BOOT button to enter audio testing state when Wi-Fi configuring

This commit is contained in:
Terrence
2025-06-13 19:56:47 +08:00
parent dfad6a5b2c
commit bf125446b3
5 changed files with 62 additions and 1 deletions

View File

@@ -45,6 +45,7 @@ static const char* const STATE_STRINGS[] = {
"speaking", "speaking",
"upgrading", "upgrading",
"activating", "activating",
"audio_testing",
"fatal_error", "fatal_error",
"invalid_state" "invalid_state"
}; };
@@ -290,10 +291,31 @@ void Application::PlaySound(const std::string_view& sound) {
} }
} }
void Application::EnterAudioTestingMode() {
ESP_LOGI(TAG, "Entering audio testing mode");
ResetDecoder();
SetDeviceState(kDeviceStateAudioTesting);
}
void Application::ExitAudioTestingMode() {
ESP_LOGI(TAG, "Exiting audio testing mode");
SetDeviceState(kDeviceStateWifiConfiguring);
// Copy audio_testing_queue_ to audio_decode_queue_
std::lock_guard<std::mutex> lock(mutex_);
audio_decode_queue_ = std::move(audio_testing_queue_);
audio_decode_cv_.notify_all();
}
void Application::ToggleChatState() { void Application::ToggleChatState() {
if (device_state_ == kDeviceStateActivating) { if (device_state_ == kDeviceStateActivating) {
SetDeviceState(kDeviceStateIdle); SetDeviceState(kDeviceStateIdle);
return; return;
} else if (device_state_ == kDeviceStateWifiConfiguring) {
EnterAudioTestingMode();
return;
} else if (device_state_ == kDeviceStateAudioTesting) {
ExitAudioTestingMode();
return;
} }
if (!protocol_) { if (!protocol_) {
@@ -327,6 +349,9 @@ void Application::StartListening() {
if (device_state_ == kDeviceStateActivating) { if (device_state_ == kDeviceStateActivating) {
SetDeviceState(kDeviceStateIdle); SetDeviceState(kDeviceStateIdle);
return; return;
} else if (device_state_ == kDeviceStateWifiConfiguring) {
EnterAudioTestingMode();
return;
} }
if (!protocol_) { if (!protocol_) {
@@ -354,6 +379,11 @@ void Application::StartListening() {
} }
void Application::StopListening() { void Application::StopListening() {
if (device_state_ == kDeviceStateAudioTesting) {
ExitAudioTestingMode();
return;
}
const std::array<int, 3> valid_states = { const std::array<int, 3> valid_states = {
kDeviceStateListening, kDeviceStateListening,
kDeviceStateSpeaking, kDeviceStateSpeaking,
@@ -824,6 +854,28 @@ void Application::OnAudioOutput() {
} }
void Application::OnAudioInput() { void Application::OnAudioInput() {
if (device_state_ == kDeviceStateAudioTesting) {
if (audio_testing_queue_.size() >= AUDIO_TESTING_MAX_DURATION_MS / OPUS_FRAME_DURATION_MS) {
ExitAudioTestingMode();
return;
}
std::vector<int16_t> data;
int samples = OPUS_FRAME_DURATION_MS * 16000 / 1000;
if (ReadAudio(data, 16000, samples)) {
background_task_->Schedule([this, data = std::move(data)]() mutable {
opus_encoder_->Encode(std::move(data), [this](std::vector<uint8_t>&& opus) {
AudioStreamPacket packet;
packet.payload = std::move(opus);
packet.frame_duration = OPUS_FRAME_DURATION_MS;
packet.sample_rate = 16000;
std::lock_guard<std::mutex> lock(mutex_);
audio_testing_queue_.push_back(std::move(packet));
});
});
return;
}
}
if (wake_word_->IsDetectionRunning()) { if (wake_word_->IsDetectionRunning()) {
std::vector<int16_t> data; std::vector<int16_t> data;
int samples = wake_word_->GetFeedSize(); int samples = wake_word_->GetFeedSize();
@@ -834,6 +886,7 @@ void Application::OnAudioInput() {
} }
} }
} }
if (audio_processor_->IsRunning()) { if (audio_processor_->IsRunning()) {
std::vector<int16_t> data; std::vector<int16_t> data;
int samples = audio_processor_->GetFeedSize(); int samples = audio_processor_->GetFeedSize();

View File

@@ -44,11 +44,13 @@ enum DeviceState {
kDeviceStateSpeaking, kDeviceStateSpeaking,
kDeviceStateUpgrading, kDeviceStateUpgrading,
kDeviceStateActivating, kDeviceStateActivating,
kDeviceStateAudioTesting,
kDeviceStateFatalError kDeviceStateFatalError
}; };
#define OPUS_FRAME_DURATION_MS 60 #define OPUS_FRAME_DURATION_MS 60
#define MAX_AUDIO_PACKETS_IN_QUEUE (2400 / OPUS_FRAME_DURATION_MS) #define MAX_AUDIO_PACKETS_IN_QUEUE (2400 / OPUS_FRAME_DURATION_MS)
#define AUDIO_TESTING_MAX_DURATION_MS 10000
class Application { class Application {
public: public:
@@ -111,6 +113,7 @@ private:
std::list<AudioStreamPacket> audio_send_queue_; std::list<AudioStreamPacket> audio_send_queue_;
std::list<AudioStreamPacket> audio_decode_queue_; std::list<AudioStreamPacket> audio_decode_queue_;
std::condition_variable audio_decode_cv_; std::condition_variable audio_decode_cv_;
std::list<AudioStreamPacket> audio_testing_queue_;
// 新增用于维护音频包的timestamp队列 // 新增用于维护音频包的timestamp队列
std::list<uint32_t> timestamp_queue_; std::list<uint32_t> timestamp_queue_;
@@ -134,6 +137,8 @@ private:
void OnClockTimer(); void OnClockTimer();
void SetListeningMode(ListeningMode mode); void SetListeningMode(ListeningMode mode);
void AudioLoop(); void AudioLoop();
void EnterAudioTestingMode();
void ExitAudioTestingMode();
}; };
#endif // _APPLICATION_H_ #endif // _APPLICATION_H_

View File

@@ -205,7 +205,8 @@ void CircularStrip::OnStateChanged() {
SetAllColor(color); SetAllColor(color);
break; break;
} }
case kDeviceStateListening: { case kDeviceStateListening:
case kDeviceStateAudioTesting: {
StripColor color = { default_brightness_, low_brightness_, low_brightness_ }; StripColor color = { default_brightness_, low_brightness_, low_brightness_ };
SetAllColor(color); SetAllColor(color);
break; break;

View File

@@ -220,6 +220,7 @@ void GpioLed::OnStateChanged() {
TurnOn(); TurnOn();
break; break;
case kDeviceStateListening: case kDeviceStateListening:
case kDeviceStateAudioTesting:
if (app.IsVoiceDetected()) { if (app.IsVoiceDetected()) {
SetBrightness(HIGH_BRIGHTNESS); SetBrightness(HIGH_BRIGHTNESS);
} else { } else {

View File

@@ -136,6 +136,7 @@ void SingleLed::OnStateChanged() {
TurnOn(); TurnOn();
break; break;
case kDeviceStateListening: case kDeviceStateListening:
case kDeviceStateAudioTesting:
if (app.IsVoiceDetected()) { if (app.IsVoiceDetected()) {
SetColor(HIGH_BRIGHTNESS, 0, 0); SetColor(HIGH_BRIGHTNESS, 0, 0);
} else { } else {