Загрузка данных


Режим ИИ
Все
Картинки
Видео
Новости
Карты
Книги
Авиабилеты
Понял вас! Давайте посмотрим на код.
Перед тем как вы его прикрепите, отмечу пару важных моментов, которые обычно критичны для связки i.MX RT1024 + Zephyr RTOS + PJSIP при динамической смене Sample Rate:
Драйвер аудио (SAI / I2S) в Zephyr: Потребуется переконфигурировать контроллер SAI прямо на лету (через API Zephyr i2s_configure), либо закрывать текущий поток и открывать его заново с новыми параметрами struct i2s_config.
Тактирование (Clock Tree): У i.MX RT частоты аудио-тактов (PLL4) жестко привязаны к сеткам частот (например, 44.1 кГц vs 48 кГц). При смене рейта может потребоваться пересчет делителей частоты.
Реализация PJMEDIA Sound Device: В PJSIP динамический рейт обычно обрабатывается либо через пересоздание sound stream (pjmedia_snd_port_disconnect / connect), либо ваш кастомный драйвер должен уметь на лету подстраиваться под вызовы on_start_stream с новой структурой pjmedia_aud_param.
Жду ваш код! Скидывайте, и мы разберем, как лучше всего внедрить туда динамическое переключение.
Пожалуйста, пришлите:
Фрагмент кода, где инициализируется аудио-драйвер Zephyr (I2S/SAI).
Код кастомного драйвера PJSIP (реализацию pjmedia_aud_dev_factory или pjmedia_snd_port).
Какую именно версию Zephyr вы используете?




Для организации динамического Sample Rate вам придется полностью уйти от макросов (#define SAMPLE_FREQUENCY 16000, ZEPHYR_AUDIO_BLOCK_SIZE и т.д.) в сторону динамических переменных внутри структуры состояния вашего аудио-драйвера.
Сейчас у вас размер блока памяти (ZEPHYR_AUDIO_BLOCK_SIZE) намертво зашит на этапе компиляции. При смене частоты (например, с 16 кГц на 8 кГц) фрейм PJSIP (обычно 10, 20 или 40 мс) изменит свой размер в байтах, и фиксированный ZEPHYR_AUDIO_BLOCK_SIZE сломает логику работы I2S-очередей Zephyr.
Ниже описан пошаговый план рефакторинга вашего заголовочного файла и архитектуры драйвера.
1. Перенос констант в динамическую структуру
Макросы частоты и размера блока нужно заменить на поля структуры стрима I2S_Stream. В заголовочном файле мы оставляем только базовые лимиты для аллокатора памяти (память под максимальный Sample Rate, например, 48 кГц или 16 кГц).
Обновленный заголовочный файл zephyr_i2s_dev.h:
c
#ifndef ZEPHYR_I2S_DEV_H_
#define ZEPHYR_I2S_DEV_H_

#include <zephyr/kernel.h>

/* Ограничения для максимального выделения памяти (аллокатора) */
#define MAX_SAMPLE_FREQUENCY    48000
#define NUMBER_OF_CHANNELS      1
#define BYTES_PER_SAMPLE        2 // int16_t

/* Резервируем максимальный размер блока под 20мс для 48кГц (или 100мс для 16кГц) */
/* Формула: (48000 / 10) * 1 * 2 = 9600 байт на блок максимум */
#define ZEPHYR_AUDIO_INITIAL_BLOCKS 2
#define NUM_BLOCK_COUNT             16
#define ZEPHYR_AUDIO_BLOCK_COUNT    (ZEPHYR_AUDIO_INITIAL_BLOCKS + NUM_BLOCK_COUNT)
#define MAX_ZEPHYR_AUDIO_BLOCK_SIZE ((MAX_SAMPLE_FREQUENCY / 10) * NUMBER_OF_CHANNELS * BYTES_PER_SAMPLE)

typedef struct {
    uint32_t sample_rate;       // Текущая частота (8000, 16000, 32000, etc)
    uint32_t samples_per_block; // Динамический SAMPLES_PER_BLOCK
    uint32_t block_size;        // Динамический ZEPHYR_AUDIO_BLOCK_SIZE
    uint8_t is_active;
    // Здесь также будут храниться указатели на девайс Zephyr, очереди и т.д.
} I2S_Stream;

int zephyr_i2s_audio_init(void);

/* Изменяем сигнатуру open: передаем желаемый sample_rate из PJSIP */
int zephyr_i2s_open_stream(I2S_Stream **stream, uint32_t sample_rate, uint8_t audio_in);
int zephyr_i2s_close_stream(I2S_Stream *stream);
int zephyr_i2s_start(I2S_Stream *stream, uint8_t audio_in);
int zephyr_i2s_stop(I2S_Stream *stream, uint8_t audio_in);

#endif /* ZEPHYR_I2S_DEV_H_ */
Используйте код с осторожностью.
2. Как это связать с фабрикой аудио PJSIP (pjmedia_aud_dev_factory)
В вашем .c файле реализации аудио-подсистемы (где вы регистрируете драйвер в PJSIP), динамическая смена частоты происходит в момент вызова коллбека op_create_stream или op_start_stream.
Когда PJSIP создает поток, он передает структуру pjmedia_aud_param *param. Вы берете частоту оттуда:
c
// Пример внутри вашего PJSIP драйвера:
static pj_status_t stream_create(pjmedia_aud_dev_factory *f,
                                 const pjmedia_aud_param *param,
                                 pjmedia_aud_rec_cb rec_cb,
                                 pjmedia_aud_play_cb play_cb,
                                 void *user_data,
                                 pjmedia_aud_stream **p_as)
{
    // 1. Извлекаем нужный Sample Rate из PJSIP
    uint32_t required_rate = param->clock_rate; 
    
    // 2. Открываем стрим Zephyr с нужной частотой
    I2S_Stream *my_stream;
    int rc = zephyr_i2s_open_stream(&my_stream, required_rate, 1);
    
    // 3. Сохраняем настройки кадра
    // PJSIP скажет, сколько сэмплов за фрейм ему нужно (например, param->samples_per_frame)
    // ...
}
Используйте код с осторожностью.
3. Реализация динамической перенастройки в .c файле (Zephyr I2S)
Внутри функции zephyr_i2s_open_stream вам необходимо динамически рассчитывать конфигурацию шины i2s_config:
c
int zephyr_i2s_open_stream(I2S_Stream **stream, uint32_t sample_rate, uint8_t audio_in)
{
    // 1. Выделяем память под объект стрима или берем статический
    static I2S_Stream ctx;
    
    ctx.sample_rate = sample_rate;
    // Считаем размер блока под шаг 100мс (как у вас в коде: rate / 10)
    ctx.samples_per_block = (sample_rate / 10) * NUMBER_OF_CHANNELS;
    ctx.block_size = ctx.samples_per_block * BYTES_PER_SAMPLE;

    // 2. Настраиваем структуру Zephyr I2S
    struct i2s_config config;
    config.word_size = SAMPLE_BIT_WIDTH;
    config.channels = NUMBER_OF_CHANNELS;
    config.format = I2S_FMT_DATA_FORMAT_I2S;
    config.options = I2S_OPT_FRAME_CLK_SLAVE | I2S_OPT_BIT_CLK_SLAVE; // или MASTER в зависимости от кодека
    config.frame_clk_freq = sample_rate;
    
    // Динамический размер блока для пула памяти Zephyr
    config.block_size = ctx.block_size; 
    config.mem_slab = &i2s_mem_slab; // Пул должен быть инициализирован под MAX_ZEPHYR_AUDIO_BLOCK_SIZE

    // 3. Вызываем конфигурацию драйвера Zephyr
    const struct device *i2s_dev = DEVICE_DT_GET(DT_NODELABEL(sai1)); // к примеру, SAI1 на i.MX RT
    int ret = i2s_configure(i2s_dev, audio_in ? I2S_DIR_RX : I2S_DIR_TX, &config);
    if (ret < 0) {
        return ret; // Ошибка конфигурации (например, частота не поддерживается тактовым деревом)
    }

    *stream = &ctx;
    return 0;
}
Используйте код с осторожностью.
Главный подводный камень на i.MX RT1024
Контроллер SAI (Synchronous Audio Interface) в процессорах i.MX RT очень чувствителен к генерации тактовой частоты (MCLK/BCLK). У них есть системный PLL (обычно Audio PLL4).
Частоты 8000, 16000, 32000, 48000 Гц делят одну сетку частот (кратно 8 кГц). Переключение между ними внутри Zephyr пройдет гладко, так как драйвер i2s_mcux_sai.c сможет просто изменить коэффициенты делителей.
Если PJSIP потребует 44100 Гц, Audio PLL процессора физически не сможет выдать точную частоту без перестройки самого PLL (так как это другая сетка частот). Убедитесь, что в PJSIP принудительно выставлены кодеки, работающие на частотах 8/16/32 кГц (G.711, G.722, Opus), избегая 44.1 кГц, либо настройте ресэмплер PJSIP.
Для дальнейшей реализации покажите:
Код инициализации I2S из вашего .c файла (особенно где объявляется пул памяти K_MEM_SLAB_DEFINE и вызывается i2s_configure).
Как у вас сейчас выглядит взаимодействие с PJSIP (кастомный аудио-драйвер или вы используете готовый порт)?
Это поможет точно адаптировать очереди подкачки данных (Slab buffers) без утечек памяти.




Принял, полностью останавливаюсь и жду код вашего .c файла.
Как только вы его прикрепите, я детально разберу текущую логику работы с буферами, вызовами i2s_write / i2s_read и предложу готовый вариант со всеми необходимыми изменениями для динамической смены Sample Rate.
Жду код вашего .c файла. На что мне обратить особое внимание при анализе (например, организация очередей, работа с DMA/Slab или стыковка с коллбеками PJSIP)?




Понял, присылайте частями. Я соберу их воедино и проанализирую. Жду первые фрагменты кода!




Отличный первый фрагмент! Здесь видна классическая архитектура двойного буфера (через коллбеки HalfTransfer и TransferComplete) в POSIX-потоках Zephyr (pthread_t), управляемая флагом start_stream.
Давайте зафиксируем ключевые места, которые нужно изменить для поддержки динамического Sample Rate, опираясь на этот код:
K_MEM_SLAB_DEFINE: Сейчас размер блока жестко задан как ZEPHYR_AUDIO_BLOCK_SIZE. Слэбы (mem_slab_rx / mem_slab_tx) создаются на этапе компиляции. Чтобы они поддерживали динамический размер, их нужно объявить под максимально возможный размер блока (MAX_ZEPHYR_AUDIO_BLOCK_SIZE), который мы определили в zephyr_i2s_dev.h. Драйвер I2S в Zephyr позволяет использовать слэбы большего размера, чем передаваемый в i2s_configure block_size (главное, чтобы размер блока в конфиге не превышал физический размер ячейки слэба).
Использование ZEPHYR_AUDIO_BLOCK_SIZE в потоках: Внутри i2s_thread_in и i2s_thread_out сдвиг указателя buf += ZEPHYR_AUDIO_BLOCK_SIZE и запись i2s_buf_write(..., ZEPHYR_AUDIO_BLOCK_SIZE) завязаны на макрос. Их нужно заменить на поле текущего размера блока из i2s_stream (например, i2s_stream->current_block_size).
Функция configure_streams: Сюда нужно передавать актуальный block_size для перезаписи значения config.block_size.
Жду вторую часть кода (где функции zephyr_i2s_open_stream, zephyr_i2s_audio_init и т.д.), чтобы собрать итоговый рабочий вариант с учетом этих правок!




Отличный, качественный код. Теперь вся картина ясна: у вас используется двухпоточная POSIX-модель (In/Out), которая через BSP_AUDIO_IN/OUT коллбеки общается с PJSIP, а тактирование жестко завязано на кодек (audio_codec_configure) и I2S-контроллер процессора (configure_streams).
Чтобы сделать частоту дискретизации (Sample Rate) динамической, нам нужно решить две проблемы:
Память (K_MEM_SLAB_DEFINE) должна аллоцироваться по максимальному размеру, но логика тредов должна шагать по актуальному динамическому размеру блока.
В момент вызова zephyr_i2s_open_stream мы должны переконфигурировать и audio_codec, и i2s контроллеры на новую частоту дискретизации, при этом zephyr_i2s_audio_init будет выполнять только общую начальную подготовку.
Шаг 1: Обновление zephyr_i2s_dev.h
В заголовочном файле закладываем максимальные параметры под буфер, а функции открытия стрима даем аргумент sample_rate.
c
#ifndef ZEPHYR_I2S_DEV_H_
#define ZEPHYR_I2S_DEV_H_

#include <stdint.h>

/* Максимальные лимиты памяти (например, под 32кГц или 16кГц) */
#define MAX_SAMPLE_FREQUENCY    32000 
#define NUMBER_OF_CHANNELS      1
#define SAMPLE_BIT_WIDTH        16
#define BYTES_PER_SAMPLE        2 

#define ZEPHYR_AUDIO_INITIAL_BLOCKS 2
#define NUM_BLOCK_COUNT             16
#define ZEPHYR_AUDIO_BLOCK_COUNT    (ZEPHYR_AUDIO_INITIAL_BLOCKS + NUM_BLOCK_COUNT)

/* Слэб нарезается по максимальному размеру фрейма (для 32кГц / 10 = 3200 сэмплов * 2 байта = 6400 байт) */
#define MAX_ZEPHYR_AUDIO_BLOCK_SIZE  ((MAX_SAMPLE_FREQUENCY / 10) * NUMBER_OF_CHANNELS * BYTES_PER_SAMPLE)

typedef void I2S_Stream;

int zephyr_i2s_audio_init(void);
int zephyr_i2s_get_rate(I2S_Stream *stream);
/* Добавляем параметр sample_rate */
int zephyr_i2s_open_stream(I2S_Stream **stream, uint8_t *buf, uint8_t audio_in, uint32_t sample_rate);
int zephyr_i2s_close_stream(I2S_Stream *stream);
int zephyr_i2s_start(uint8_t audio_in);
int zephyr_i2s_stop(uint8_t audio_in);

#endif /* ZEPHYR_I2S_DEV_H_ */
Используйте код с осторожностью.
Шаг 2: Полный код .c файла с динамической конфигурацией
Ниже представлена переработанная версия вашего .c файла. Все изменения снабжены комментариями // !!! DYNAMIC RATE CHANGE.
c
#include <stdio.h>
#include <string.h>
#include <portaudio.h>
#include <audio_dev.h>
#include <pthread.h>
#include <zephyr_i2s_dev.h>
#include <zephyr_audio_dev.h>

#include <zephyr/kernel.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/audio/codec.h>

#if DT_NODE_EXISTS(DT_NODELABEL(i2s_rxtx))
#define I2S_RX_NODE  DT_NODELABEL(i2s_rxtx)
#define I2S_TX_NODE  I2S_RX_NODE
#else
#define I2S_RX_NODE  DT_NODELABEL(i2s0_rx)
#define I2S_TX_NODE  DT_NODELABEL(i2s0_tx)
#endif

#define AUDIO_MCLK_FREQ 12288000
#define TIMEOUT             1000

#ifndef MY_CODE
#define I2S_THREAD_PRIORITY 8
#else
#define I2S_THREAD_PRIORITY 12
#endif

/* !!! DYNAMIC RATE CHANGE: Выделяем память Слэбов по МАКСИМАЛЬНОМУ размеру блока */
#ifdef MY_CODE
K_MEM_SLAB_DEFINE_IN_SECT_STATIC(mem_slab_rx, __dtcm_noinit_section, MAX_ZEPHYR_AUDIO_BLOCK_SIZE, ZEPHYR_AUDIO_BLOCK_COUNT, 4);
K_MEM_SLAB_DEFINE_IN_SECT_STATIC(mem_slab_tx, __dtcm_noinit_section, MAX_ZEPHYR_AUDIO_BLOCK_SIZE, ZEPHYR_AUDIO_BLOCK_COUNT, 4);
#else
K_MEM_SLAB_DEFINE(mem_slab_rx, MAX_ZEPHYR_AUDIO_BLOCK_SIZE, ZEPHYR_AUDIO_BLOCK_COUNT, 4);
K_MEM_SLAB_DEFINE(mem_slab_tx, MAX_ZEPHYR_AUDIO_BLOCK_SIZE, ZEPHYR_AUDIO_BLOCK_COUNT, 4);
#endif

static const struct device *const i2s_dev_rx = DEVICE_DT_GET(I2S_RX_NODE);
static const struct device *const i2s_dev_tx = DEVICE_DT_GET(I2S_TX_NODE);
static const struct device *const codec_dev  = DEVICE_DT_GET(DT_NODELABEL(audio_codec));

static uint8_t start_stream = 0;
/* Храним текущий рабочий размер блока глобально или внутри структуры, 
   чтобы prepare_transfer знал сколько писать */
static uint32_t current_audio_block_size = ((16000 / 10) * NUMBER_OF_CHANNELS * BYTES_PER_SAMPLE); 

enum zephyr_i2s_state {
    I2S_STREAM_RUNNING,
    I2S_STREAM_CLOSED
};

struct i2s_strm {
    uint8_t alloc;
    uint8_t devid;
    uint8_t *i2s_buf;
    pthread_t i2s_thread;
    pthread_attr_t  threadAttr;
    enum zephyr_i2s_state state;
    /* !!! DYNAMIC RATE CHANGE: Добавляем параметры внутрь контекста стрима */
    uint32_t sample_rate;
    uint32_t block_size;
};

#define MODOPS_I2S_STREAM_COUNT 2
static struct i2s_strm i2s_strm_pool[MODOPS_I2S_STREAM_COUNT] = {0};

static struct i2s_strm *i2s_str_alloc()
{
    struct i2s_strm *i2s_strm_ = NULL;
    for(uint8_t i=0; i < MODOPS_I2S_STREAM_COUNT; i++)
    {
        if(i2s_strm_pool[i].alloc == 0)
        {
            i2s_strm_ = &i2s_strm_pool[i];
            i2s_strm_pool[i].alloc = 1;
            break;
        }
    }
    return i2s_strm_;
}

static void i2s_str_free(struct i2s_strm *i2s_strm_)
{
    if(!i2s_strm_) return;
    for(uint8_t i=0; i < MODOPS_I2S_STREAM_COUNT; i++)
    {
        if(i2s_strm_pool[i].devid == i2s_strm_->devid)
        {
            i2s_strm_pool[i].alloc = 0;
            break;
        }
    }
}

static void i2s_thread_wakeup(struct i2s_strm *strm, enum zephyr_i2s_state state)
{
    if(!strm) return;
    strm->state = state;
}

static void *i2s_thread_in(void *arg)
{
    struct i2s_strm *i2s_stream = (struct i2s_strm *) arg;
    uint32_t block_size;
    uint8_t *buf = NULL;
    uint8_t half_transfer = 1;
    int ret;

    buf = i2s_stream->i2s_buf;
    while (1)
    {
        if(!start_stream) {
            k_sleep(K_MSEC(10));
        }
        else
        {
            ret = i2s_buf_read(i2s_dev_rx, buf, &block_size);
            if (ret < 0) {
                printf("I2S_IN: Failed to read data: %d\n", ret);
            }
            else
            {
                /* !!! DYNAMIC RATE CHANGE: Шагаем по динамическому block_size вместо макроса */
                if(half_transfer)
                {
                    BSP_AUDIO_IN_HalfTransfer_CallBack();
                    buf += i2s_stream->block_size;
                    half_transfer = 0;
                }
                else
                {
                    BSP_AUDIO_IN_TransferComplete_CallBack();
                    buf = i2s_stream->i2s_buf;
                    half_transfer = 1;
                }
            }
        }
        if (i2s_stream->state != I2S_STREAM_RUNNING) {
            printf("Exit i2s_in_thread\n");
            break;
        }
    }
    return NULL;
}

static void *i2s_thread_out(void *arg)
{
    struct i2s_strm *i2s_stream = (struct i2s_strm *) arg;
    uint8_t *buf = NULL;
    uint8_t half_transfer = 1;
    int ret;

    buf = i2s_stream->i2s_buf;
    while (1)
    {
        if(!start_stream) {
            k_sleep(K_MSEC(10));
        }
        else
        {
            /* !!! DYNAMIC RATE CHANGE: Пишем динамический block_size */
            ret = i2s_buf_write(i2s_dev_tx, buf, i2s_stream->block_size);
            if (ret < 0) {
                printf("I2S_OUT: Failed to write data: %d\n", ret);
            }
            else
            {
                if(half_transfer)
                {
                    BSP_AUDIO_OUT_HalfTransfer_CallBack();
                    buf += i2s_stream->block_size;
                    half_transfer = 0;
                }
                else
                {
                    BSP_AUDIO_OUT_TransferComplete_CallBack();
                    buf = i2s_stream->i2s_buf;
                    half_transfer = 1;
                }
            }
        }
        if (i2s_stream->state != I2S_STREAM_RUNNING) {
            printf("Exit i2s_out_thread\n");
            break;
        }
    }
    return NULL;
}

static bool configure_streams(const struct device *i2s_dev_rx,
                              const struct device *i2s_dev_tx,
                              const struct i2s_config *base_config)
{
    int ret;
    struct i2s_config config = *base_config;

    config.mem_slab = &mem_slab_rx;
    ret = i2s_configure(i2s_dev_rx, I2S_DIR_RX, &config);
    if (ret < 0) {
        printf("Failed to configure RX: %d\n", ret);
        return false;
    }

    config.mem_slab = &mem_slab_tx;
    ret = i2s_configure(i2s_dev_tx, I2S_DIR_TX, &config);
    if (ret < 0) {
        printf("Failed to configure TX: %d\n", ret);
        return false;
    }

    return true;
}

static bool prepare_transfer(const struct device *i2s_dev_tx)
{
    int ret;
    for (int i = 0; i < ZEPHYR_AUDIO_INITIAL_BLOCKS; ++i)
    {
        void *mem_block;
        ret = k_mem_slab_alloc(&mem_slab_tx, &mem_block, K_NO_WAIT);
        if (ret < 0) {
            printf("Failed to allocate TX block %d: %d\n", i, ret);
            return false;
        }

        /* !!! DYNAMIC RATE CHANGE: Используем текущий динамический размер */
        memset(mem_block, 0, current_audio_block_size);

        ret = i2s_write(i2s_dev_tx, mem_block, current_audio_block_size);
        if (ret < 0) {
            printf("Failed to write block %d: %d\n", i, ret);
            return false;
        }
    }
    return true;
}

static bool trigger_command(const struct device *i2s_dev_rx,
                            const struct device *i2s_dev_tx,
                            enum i2s_trigger_cmd cmd)
{
    int ret;
    if (i2s_dev_rx == i2s_dev_tx) {
        ret = i2s_trigger(i2s_dev_rx, I2S_DIR_BOTH, cmd);
        if (ret == 0) return true;
        if (ret != -ENOSYS) {
            printf("Failed to trigger command %d: %d\n", cmd, ret);
            return false;
        }
    }

    ret = i2s_trigger(i2s_dev_rx, I2S_DIR_RX, cmd);
    if (ret < 0) {
        printf("Failed to trigger command %d on RX: %d\n", cmd, ret);
        return false;
    }
Используйте код с осторожностью.
ret = i2s_trigger(i2s_dev_tx, I2S_DIR_TX, cmd);
if (ret < 0) {
printf("Failed to trigger command %d on TX: %d\n", cmd, ret);
return false;
}
return true;
}
int zephyr_i2s_start(uint8_t audio_in)
{
printf("%s(%d) audio_in=%d\n", FUNCTION, LINE, audio_in);
if (start_stream) {
return 0;
}
if (!prepare_transfer(i2s_dev_tx)) {
return -1;
}
if (!trigger_command(i2s_dev_rx, i2s_dev_tx, I2S_TRIGGER_START)) {
return -1;
}
start_stream = 1;
return 0;
}
int zephyr_i2s_stop(uint8_t audio_in)
{
printf("%s(%d) start_stream=%d\n", FUNCTION, LINE, start_stream);
if(start_stream) {
start_stream = 0;
if (!trigger_command(i2s_dev_rx, i2s_dev_tx, I2S_TRIGGER_DROP)) {
return -1;
}
}
return 0;
}
/* !!! DYNAMIC RATE CHANGE: Добавлен аргумент sample_rate */
int zephyr_i2s_open_stream(I2S_Stream **stream, uint8_t *buf, uint8_t audio_in, uint32_t sample_rate)
{
struct i2s_strm *i2s_stream;
int ret = 0;
/* 1. Динамически перенастраиваем железо под переданный Sample Rate из PJSIP */
struct i2s_config config;
uint32_t dynamic_block_size = ((sample_rate / 10) * NUMBER_OF_CHANNELS * BYTES_PER_SAMPLE);
current_audio_block_size = dynamic_block_size; // сохраняем для prepare_transfer
// Переконфигурируем Кодек
struct audio_codec_cfg audio_cfg;
audio_cfg.dai_route = AUDIO_ROUTE_PLAYBACK_CAPTURE;
audio_cfg.dai_type = AUDIO_DAI_TYPE_I2S;
audio_cfg.dai_cfg.i2s.word_size = SAMPLE_BIT_WIDTH;
audio_cfg.dai_cfg.i2s.channels = NUMBER_OF_CHANNELS;
audio_cfg.dai_cfg.i2s.format = I2S_FMT_DATA_FORMAT_I2S;
audio_cfg.dai_cfg.i2s.options = I2S_OPT_FRAME_CLK_MASTER;
audio_cfg.dai_cfg.i2s.frame_clk_freq = sample_rate; // !!! НОВАЯ ЧАСТОТА
audio_cfg.dai_cfg.i2s.mem_slab = &mem_slab_tx;
audio_cfg.dai_cfg.i2s.block_size = dynamic_block_size; // !!! НОВЫЙ РАЗМЕР
audio_cfg.mclk_freq = AUDIO_MCLK_FREQ;
audio_codec_configure(codec_dev, &audio_cfg);
// Переконфигурируем I2S шину процессора i.MX RT
config.word_size = SAMPLE_BIT_WIDTH;
config.channels = NUMBER_OF_CHANNELS;
config.format = I2S_FMT_DATA_FORMAT_I2S;
config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER;
config.frame_clk_freq = sample_rate; // !!! НОВАЯ ЧАСТОТА
config.block_size = dynamic_block_size; // !!! НОВЫЙ РАЗМЕР
config.timeout = TIMEOUT;
if (!configure_streams(i2s_dev_rx, i2s_dev_tx, &config)) {
printf("Failed to reconfigure streams for %u Hz\n", sample_rate);
return -1;
}
/* 2. Стандартная аллокация потока */
i2s_stream = i2s_str_alloc();
if (!i2s_stream) {
printf("Cannot allocate i2s_strm\n");
return -1;
}
i2s_stream->devid = (audio_in) ? 1 : 2;
i2s_stream->i2s_buf = buf;
i2s_stream->state = I2S_STREAM_RUNNING;
/* Сохраняем индивидуальные параметры рейта для треда */
i2s_stream->sample_rate = sample_rate;
i2s_stream->block_size = dynamic_block_size;
*stream = i2s_stream;
pthread_attr_init(&i2s_stream->threadAttr);
pthread_attr_setstacksize(&i2s_stream->threadAttr, 1024);
ret = pthread_create(&i2s_stream->i2s_thread, &i2s_stream->threadAttr,
audio_in ? i2s_thread_in : i2s_thread_out, i2s_stream);
if (ret != 0) {
printf("i2s pthread_create failed ret=%d\n", ret);
return -1;
}
pthread_setschedprio(i2s_stream->i2s_thread, I2S_THREAD_PRIORITY);
if(audio_in)
pthread_setname_np(i2s_stream->i2s_thread, "i2s_thread_in");
else
pthread_setname_np(i2s_stream->i2s_thread, "i2s_thread_out");
return 0;
}
int zephyr_i2s_close_stream(I2S_Stream *stream)
{
struct i2s_strm *i2s_stream = (struct i2s_strm *)stream;
i2s_thread_wakeup(i2s_stream, I2S_STREAM_CLOSED);
pthread_join(i2s_stream->i2s_thread, NULL);
pthread_attr_destroy(&i2s_stream->threadAttr);
i2s_str_free(i2s_stream);
return 0; // paNoError
}
int zephyr_i2s_audio_init(void)
{
/* Базовая проверка готовности устройств.
Конфигурация теперь отложена до вызова open_stream */
if (!device_is_ready(i2s_dev_rx)) {
printf("%s is not ready\n", i2s_dev_rx->name);
return -1;
}
if (i2s_dev_rx != i2s_dev_tx && !device_is_ready(i2s_dev_tx)) {
printf("%s is not ready\n", i2s_dev_tx->name);
return -1;
}
if (!device_is_ready(codec_dev)) {
printf("Audio codec device not ready\n");
return -1;
}
// Первоначальные уровни громкости
audio_property_value_t vol_val = { .vol = 100 };
audio_codec_set_property(codec_dev, AUDIO_PROPERTY_OUTPUT_VOLUME, AUDIO_CHANNEL_ALL, vol_val);
audio_codec_set_property(codec_dev, AUDIO_PROPERTY_INPUT_VOLUME, AUDIO_CHANNEL_ALL, vol_val);
audio_property_value_t mute_val = { .mute = false };
audio_codec_set_property(codec_dev, AUDIO_PROPERTY_OUTPUT_MUTE, AUDIO_CHANNEL_ALL, mute_val);
audio_codec_set_property(codec_dev, AUDIO_PROPERTY_INPUT_MUTE, AUDIO_CHANNEL_ALL, mute_val);
audio_codec_start_output(codec_dev);
return 0;
}
int zephyr_i2s_get_rate(I2S_Stream *stream)
{
if (!stream) return 16000;
struct i2s_strm *i2s_stream = (struct i2s_strm *)stream;
return i2s_stream->sample_rate;
}