esp32-sip-client-with-headset/main/audio/audio_router.c

402 lines
9.7 KiB
C

/**
* Audio Router - Verwaltet Audio-Quellen und -Routing
*
* USB hat Priorität über Bluetooth
* Automatischer Wechsel bei Verbindungsänderungen
*/
#include <string.h>
#include "audio_router.h"
#include "bluetooth/bt_manager.h"
#include "usb_audio/usb_audio_host.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
static const char* TAG = "AUDIO_RT";
// State
static bool s_initialized = false;
static audio_source_t s_active_source = AUDIO_SOURCE_NONE;
static bool s_call_active = false;
static uint8_t s_volume = 80;
static bool s_muted = false;
static audio_stats_t s_stats;
static SemaphoreHandle_t s_mutex = NULL;
// Callbacks
static audio_from_headset_callback_t s_input_callback = NULL;
static headset_button_callback_t s_button_callback = NULL;
static audio_source_change_callback_t s_source_change_callback = NULL;
// Forward Declarations
static void update_active_source(void);
static void bt_audio_data_handler(const uint8_t* data, size_t len);
static void bt_button_handler(bt_button_event_t event);
static void usb_audio_data_handler(const uint8_t* data, size_t len);
static void usb_button_handler(usb_button_event_t event);
static void usb_state_handler(usb_audio_state_t state);
// Callback von Bluetooth Manager
static void bt_audio_data_handler(const uint8_t* data, size_t len)
{
if (s_active_source != AUDIO_SOURCE_BLUETOOTH || !s_call_active) {
return;
}
s_stats.packets_received++;
// An SIP weiterleiten
if (s_input_callback) {
audio_format_t format = {
.sample_rate = 8000, // HFP Standard
.channels = 1,
.bits_per_sample = 16
};
s_input_callback(data, len, &format);
}
}
static void bt_button_handler(bt_button_event_t event)
{
headset_button_t button;
switch (event) {
case BT_BUTTON_ANSWER:
button = HEADSET_BUTTON_ANSWER;
break;
case BT_BUTTON_REJECT:
button = HEADSET_BUTTON_REJECT;
break;
case BT_BUTTON_HANGUP:
button = HEADSET_BUTTON_HANGUP;
break;
case BT_BUTTON_VOLUME_UP:
button = HEADSET_BUTTON_VOLUME_UP;
break;
case BT_BUTTON_VOLUME_DOWN:
button = HEADSET_BUTTON_VOLUME_DOWN;
break;
case BT_BUTTON_MUTE:
button = HEADSET_BUTTON_MUTE;
break;
default:
return;
}
if (s_button_callback) {
s_button_callback(button, AUDIO_SOURCE_BLUETOOTH);
}
}
// Callback von USB Audio
static void usb_audio_data_handler(const uint8_t* data, size_t len)
{
if (s_active_source != AUDIO_SOURCE_USB || !s_call_active) {
return;
}
s_stats.packets_received++;
// An SIP weiterleiten
if (s_input_callback) {
audio_format_t format = {
.sample_rate = 16000, // USB typisch
.channels = 1,
.bits_per_sample = 16
};
s_input_callback(data, len, &format);
}
}
static void usb_button_handler(usb_button_event_t event)
{
headset_button_t button;
switch (event) {
case USB_BUTTON_ANSWER:
button = HEADSET_BUTTON_ANSWER;
break;
case USB_BUTTON_HANGUP:
button = HEADSET_BUTTON_HANGUP;
break;
case USB_BUTTON_MUTE:
button = HEADSET_BUTTON_MUTE;
break;
case USB_BUTTON_VOLUME_UP:
button = HEADSET_BUTTON_VOLUME_UP;
break;
case USB_BUTTON_VOLUME_DOWN:
button = HEADSET_BUTTON_VOLUME_DOWN;
break;
default:
return;
}
if (s_button_callback) {
s_button_callback(button, AUDIO_SOURCE_USB);
}
}
static void usb_state_handler(usb_audio_state_t state)
{
ESP_LOGI(TAG, "USB Audio State: %d", state);
// Bei USB Verbindungsänderung Quelle neu evaluieren
update_active_source();
}
static void update_active_source(void)
{
audio_source_t new_source = AUDIO_SOURCE_NONE;
xSemaphoreTake(s_mutex, portMAX_DELAY);
// USB hat höchste Priorität
if (usb_audio_host_is_connected()) {
new_source = AUDIO_SOURCE_USB;
}
// Bluetooth als Fallback
else if (bt_manager_is_connected()) {
new_source = AUDIO_SOURCE_BLUETOOTH;
}
if (new_source != s_active_source) {
audio_source_t old_source = s_active_source;
s_active_source = new_source;
const char* sources[] = {"Keine", "USB", "Bluetooth"};
ESP_LOGI(TAG, "Audio-Quelle: %s -> %s", sources[old_source], sources[new_source]);
// Bei aktivem Anruf: Streaming anpassen
if (s_call_active) {
// Altes Audio stoppen
if (old_source == AUDIO_SOURCE_USB) {
usb_audio_host_stop_stream();
}
// Neues Audio starten
if (new_source == AUDIO_SOURCE_USB) {
usb_audio_host_start_stream();
}
}
xSemaphoreGive(s_mutex);
// Callback aufrufen
if (s_source_change_callback) {
s_source_change_callback(old_source, new_source);
}
} else {
xSemaphoreGive(s_mutex);
}
}
esp_err_t audio_router_init(void)
{
if (s_initialized) {
return ESP_OK;
}
ESP_LOGI(TAG, "Initialisiere Audio Router");
s_mutex = xSemaphoreCreateMutex();
if (!s_mutex) {
ESP_LOGE(TAG, "Mutex erstellen fehlgeschlagen");
return ESP_ERR_NO_MEM;
}
// Callbacks bei Audio-Quellen registrieren
bt_manager_register_audio_data_callback(bt_audio_data_handler);
bt_manager_register_button_callback(bt_button_handler);
usb_audio_host_register_data_callback(usb_audio_data_handler);
usb_audio_host_register_button_callback(usb_button_handler);
usb_audio_host_register_state_callback(usb_state_handler);
memset(&s_stats, 0, sizeof(s_stats));
s_initialized = true;
// Initial die Quelle bestimmen
update_active_source();
ESP_LOGI(TAG, "Audio Router initialisiert");
return ESP_OK;
}
esp_err_t audio_router_deinit(void)
{
if (!s_initialized) return ESP_OK;
ESP_LOGI(TAG, "Deinitalisiere Audio Router");
if (s_call_active) {
audio_router_stop_call();
}
if (s_mutex) {
vSemaphoreDelete(s_mutex);
s_mutex = NULL;
}
s_initialized = false;
return ESP_OK;
}
audio_source_t audio_router_get_active_source(void)
{
return s_active_source;
}
bool audio_router_is_source_available(audio_source_t source)
{
switch (source) {
case AUDIO_SOURCE_USB:
return usb_audio_host_is_connected();
case AUDIO_SOURCE_BLUETOOTH:
return bt_manager_is_connected();
default:
return false;
}
}
esp_err_t audio_router_start_call(void)
{
if (s_call_active) {
return ESP_OK;
}
ESP_LOGI(TAG, "Starte Audio für Anruf");
xSemaphoreTake(s_mutex, portMAX_DELAY);
s_call_active = true;
memset(&s_stats, 0, sizeof(s_stats));
// Audio-Streaming auf aktiver Quelle starten
if (s_active_source == AUDIO_SOURCE_USB) {
usb_audio_host_start_stream();
} else if (s_active_source == AUDIO_SOURCE_BLUETOOTH) {
// BT Audio wird automatisch über HFP gestartet
// bt_hfp_audio_connect() wird vom SIP-Client aufgerufen
}
xSemaphoreGive(s_mutex);
return ESP_OK;
}
esp_err_t audio_router_stop_call(void)
{
if (!s_call_active) {
return ESP_OK;
}
ESP_LOGI(TAG, "Stoppe Audio für Anruf");
xSemaphoreTake(s_mutex, portMAX_DELAY);
s_call_active = false;
// Audio-Streaming stoppen
if (s_active_source == AUDIO_SOURCE_USB) {
usb_audio_host_stop_stream();
}
xSemaphoreGive(s_mutex);
return ESP_OK;
}
esp_err_t audio_router_send_to_headset(const uint8_t* data, size_t len, const audio_format_t* format)
{
if (!s_call_active || s_active_source == AUDIO_SOURCE_NONE) {
return ESP_ERR_INVALID_STATE;
}
if (s_muted) {
return ESP_OK; // Muted - nichts senden
}
s_stats.packets_sent++;
// TODO: Lautstärke anwenden
// TODO: Format-Konvertierung wenn nötig
esp_err_t ret = ESP_OK;
if (s_active_source == AUDIO_SOURCE_USB) {
ret = usb_audio_host_send(data, len);
} else if (s_active_source == AUDIO_SOURCE_BLUETOOTH) {
ret = bt_manager_send_audio(data, len);
}
if (ret != ESP_OK) {
s_stats.underruns++;
}
return ret;
}
void audio_router_register_input_callback(audio_from_headset_callback_t callback)
{
s_input_callback = callback;
}
void audio_router_register_button_callback(headset_button_callback_t callback)
{
s_button_callback = callback;
}
void audio_router_register_source_change_callback(audio_source_change_callback_t callback)
{
s_source_change_callback = callback;
}
esp_err_t audio_router_set_volume(uint8_t volume)
{
if (volume > 100) volume = 100;
s_volume = volume;
ESP_LOGI(TAG, "Lautstärke: %d%%", volume);
// Lautstärke an aktive Quelle weitergeben
if (s_active_source == AUDIO_SOURCE_USB) {
usb_audio_host_set_volume(volume);
}
// BT: Volume wird über HFP AG gehandhabt
return ESP_OK;
}
uint8_t audio_router_get_volume(void)
{
return s_volume;
}
esp_err_t audio_router_set_mute(bool mute)
{
s_muted = mute;
ESP_LOGI(TAG, "Mute: %s", mute ? "an" : "aus");
if (s_active_source == AUDIO_SOURCE_USB) {
usb_audio_host_set_mute(mute);
}
return ESP_OK;
}
bool audio_router_is_muted(void)
{
return s_muted;
}
esp_err_t audio_router_get_stats(audio_stats_t* stats)
{
if (!stats) return ESP_ERR_INVALID_ARG;
memcpy(stats, &s_stats, sizeof(audio_stats_t));
return ESP_OK;
}