diff --git a/main/application.cc b/main/application.cc index 42c05469..fe0d4df4 100644 --- a/main/application.cc +++ b/main/application.cc @@ -45,6 +45,7 @@ static const char* const STATE_STRINGS[] = { "speaking", "upgrading", "activating", + "audio_testing", "fatal_error", "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 lock(mutex_); + audio_decode_queue_ = std::move(audio_testing_queue_); + audio_decode_cv_.notify_all(); +} + void Application::ToggleChatState() { if (device_state_ == kDeviceStateActivating) { SetDeviceState(kDeviceStateIdle); return; + } else if (device_state_ == kDeviceStateWifiConfiguring) { + EnterAudioTestingMode(); + return; + } else if (device_state_ == kDeviceStateAudioTesting) { + ExitAudioTestingMode(); + return; } if (!protocol_) { @@ -327,6 +349,9 @@ void Application::StartListening() { if (device_state_ == kDeviceStateActivating) { SetDeviceState(kDeviceStateIdle); return; + } else if (device_state_ == kDeviceStateWifiConfiguring) { + EnterAudioTestingMode(); + return; } if (!protocol_) { @@ -354,6 +379,11 @@ void Application::StartListening() { } void Application::StopListening() { + if (device_state_ == kDeviceStateAudioTesting) { + ExitAudioTestingMode(); + return; + } + const std::array valid_states = { kDeviceStateListening, kDeviceStateSpeaking, @@ -824,6 +854,28 @@ void Application::OnAudioOutput() { } 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 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&& opus) { + AudioStreamPacket packet; + packet.payload = std::move(opus); + packet.frame_duration = OPUS_FRAME_DURATION_MS; + packet.sample_rate = 16000; + std::lock_guard lock(mutex_); + audio_testing_queue_.push_back(std::move(packet)); + }); + }); + return; + } + } + if (wake_word_->IsDetectionRunning()) { std::vector data; int samples = wake_word_->GetFeedSize(); @@ -834,6 +886,7 @@ void Application::OnAudioInput() { } } } + if (audio_processor_->IsRunning()) { std::vector data; int samples = audio_processor_->GetFeedSize(); diff --git a/main/application.h b/main/application.h index 3a9b6fd1..82ab9a1b 100644 --- a/main/application.h +++ b/main/application.h @@ -44,11 +44,13 @@ enum DeviceState { kDeviceStateSpeaking, kDeviceStateUpgrading, kDeviceStateActivating, + kDeviceStateAudioTesting, kDeviceStateFatalError }; #define OPUS_FRAME_DURATION_MS 60 #define MAX_AUDIO_PACKETS_IN_QUEUE (2400 / OPUS_FRAME_DURATION_MS) +#define AUDIO_TESTING_MAX_DURATION_MS 10000 class Application { public: @@ -111,6 +113,7 @@ private: std::list audio_send_queue_; std::list audio_decode_queue_; std::condition_variable audio_decode_cv_; + std::list audio_testing_queue_; // 新增:用于维护音频包的timestamp队列 std::list timestamp_queue_; @@ -134,6 +137,8 @@ private: void OnClockTimer(); void SetListeningMode(ListeningMode mode); void AudioLoop(); + void EnterAudioTestingMode(); + void ExitAudioTestingMode(); }; #endif // _APPLICATION_H_ diff --git a/main/led/circular_strip.cc b/main/led/circular_strip.cc index 936a328d..c9f0439f 100644 --- a/main/led/circular_strip.cc +++ b/main/led/circular_strip.cc @@ -205,7 +205,8 @@ void CircularStrip::OnStateChanged() { SetAllColor(color); break; } - case kDeviceStateListening: { + case kDeviceStateListening: + case kDeviceStateAudioTesting: { StripColor color = { default_brightness_, low_brightness_, low_brightness_ }; SetAllColor(color); break; diff --git a/main/led/gpio_led.cc b/main/led/gpio_led.cc index 5089c6b8..e719adf2 100644 --- a/main/led/gpio_led.cc +++ b/main/led/gpio_led.cc @@ -220,6 +220,7 @@ void GpioLed::OnStateChanged() { TurnOn(); break; case kDeviceStateListening: + case kDeviceStateAudioTesting: if (app.IsVoiceDetected()) { SetBrightness(HIGH_BRIGHTNESS); } else { diff --git a/main/led/single_led.cc b/main/led/single_led.cc index f02d5981..e6e6fb60 100644 --- a/main/led/single_led.cc +++ b/main/led/single_led.cc @@ -136,6 +136,7 @@ void SingleLed::OnStateChanged() { TurnOn(); break; case kDeviceStateListening: + case kDeviceStateAudioTesting: if (app.IsVoiceDetected()) { SetColor(HIGH_BRIGHTNESS, 0, 0); } else {