Skip to content

Concepts First

  • Codec: converts between analog audio and digital PCM — recording converts mic analog signals to digital PCM, playback converts digital PCM to speaker analog signals. BL616/BL618 embed an audio codec (xcodec).
  • PCM: the raw digital audio format recording each sample's amplitude; the example uses 16 kHz, 16-bit, mono PCM.
  • Sample rate / bit depth: samples per second (16K = 16000/s) and bits per sample (16-bit). Mic and speaker must agree.
  • DMA transfer: audio data volume is large, so the example reads/writes asynchronously via DMA channels (xcodec_output_write_async / xcodec_input_read_async) instead of busy-waiting.

Example Overview

This page is based on the codec example in the official Bouffalo SDK (examples/audio/codec), which demonstrates the built-in audio codec of Ai-M6x:

  • codec_speaker: initializes the xcodec output channel and plays a built-in 16K/16-bit/mono PCM test tone via DMA;
  • codec_mic: initializes the input channel, captures microphone data, and prints statistics;
  • codec_loop: loopback test (the README notes loopback is still being improved);
  • codec_eq: audio-processing parameter debugging (EQ/gain/limiter);
  • The example uses the xcodec driver API (xcodec_init / xcodec_output_open / xcodec_output_write_async, etc.).
  • Sibling examples (examples/audio/): player (player, see "Audio Player"), minialsa (ALSA-style interface).

Note

Audio pins and PA configuration must match your board (see the README's multimedia_port.c adaptation notes: g_pa_delay_2, CONFIG_AUDIO_PA_PIN, mic/speaker input/output pins). When using an external ES8388, set CONFIG_CODEC_USE_ES8388=1.

Operation Steps

1
Enter the Example Directory

This page needs a board with a microphone/speaker. Open a terminal and enter the SDK audio codec example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):

cd examples/audio/codec
2
Build the Project

Run the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:

make CHIP=bl616 BOARD=bl616dk
3
Flash the Firmware

Connect the board with a USB cable, hold the BOOT button (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash (replace the serial port with the one on your computer):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
Speaker Output Test

Open a serial tool (baud rate 2000000) and type at the bouffalolab /> prompt (the example plays a 16K 16-bit mono PCM test tone):

codec_speaker
5
Microphone Recording Test

Type codec_mic to start capturing microphone data and print sampling info (press Ctrl+C to stop):

codec_mic
6
Other Test Commands

Loopback test: codec_loop; EQ/gain debugging: codec_eq (with MSP_PEQ, MSP_GAIN, etc. subcommands).

Code Execution Flow

The complete speaker playback flow is shown below (mic capture is the same in reverse):

APIs Used by the Example

xcodec_init(&codec, 0)

Initializes the audio codec device.

Parameters:

  • codec: xcodec_dev_t device structure
  • id: device number (0)

Return: 0 on success; negative on failure

xcodec_output_open(&codec, &ch, 0)

Opens the output (playback) channel; sample rate, bit depth, and channels are configured in xcodec_ch_cfg.

Parameters:

  • codec: codec device
  • ch: output channel handle
  • id: channel number (0)

Return: 0 on success; negative on failure

xcodec_output_write_async(&ch, buf, len)

Asynchronously writes PCM data to the output channel (sent to the speaker via DMA); returns the bytes actually written, so call it in a loop until everything is written.

Parameters:

  • ch: output channel handle
  • buf: PCM data
  • len: bytes to write

Return: bytes actually written this call

xcodec_input_open / xcodec_input_read_async(&codec, &ch, 0) / (&ch, buf, len)

Opens the input (recording) channel and asynchronously reads mic PCM data; symmetric to the output interface.

Parameters:

  • codec / ch: device and input channel
  • buf / len: destination buffer and requested length

Return: open returns 0 on success; read_async returns bytes actually read

xcodec_uninit(&codec)

Releases codec resources; called after the test finishes.

Parameters:

  • codec: codec device

Return: none

Complete Code

The codec/main.c below matches the official example (examples/audio/codec) verbatim (the actual playback/recording logic lives in app/codec_speaker.c, app/codec_mic.c, etc.). Collapsed by default, click to expand:

📜 Click to expand codec/main.c full code
c
/****************************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"

#include <lwip/tcpip.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>

#include "bl_fw_api.h"
#include "fhost_api.h"
#include "wifi_mgmr_ext.h"
#include "wifi_mgmr.h"

#include "bflb_irq.h"
#include "bflb_uart.h"

#include "bl616_glb.h"
#include "rfparam_adapter.h"

#include "board.h"
#include "shell.h"

#define DBG_TAG "MAIN"
#include "log.h"
#include "async_event.h"

struct bflb_device_s *gpio;

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define WIFI_STACK_SIZE  (1536)
#define TASK_PRIORITY_FW (16)

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Private Data
 ****************************************************************************/

static struct bflb_device_s *uart0;


extern void shell_init_with_task(struct bflb_device_s *shell);
extern void wifi_event_handler(async_input_event_t ev, void *priv);

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/****************************************************************************
 * Functions
 ****************************************************************************/

void wifi_start_firmware_task(void *param)
{
    LOG_I("Starting wifi ...\r\n");

    /* set ble controller EM Size */

    GLB_Set_EM_Sel(GLB_WRAM160KB_EM0KB);

    if (0 != rfparam_init(NULL, NULL, NULL)) {
        LOG_I("PHY RF init failed!\r\n");
        vTaskDelete(NULL);
    }

    async_register_event_filter(EV_WIFI, wifi_event_handler, NULL);


    wifi_task_create();

    LOG_I("Starting fhost ...\r\n");
    fhost_init();

    vTaskDelete(NULL);
}

void wifi_event_handler(async_input_event_t ev, void *priv)
{
    uint32_t code = ev->code;

     switch (code) {
        case CODE_WIFI_ON_INIT_DONE: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_INIT_DONE\r\n", __func__);
            wifi_mgmr_task_start();
        } break;
        case CODE_WIFI_ON_MGMR_DONE: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_MGMR_DONE\r\n", __func__);
        } break;
        case CODE_WIFI_ON_SCAN_DONE: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_SCAN_DONE\r\n", __func__);
            wifi_mgmr_sta_scanlist();
        } break;
        case CODE_WIFI_ON_CONNECTED: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_CONNECTED\r\n", __func__);
            void mm_sec_keydump();
            mm_sec_keydump();
        } break;
        #ifdef CODE_WIFI_ON_GOT_IP_ABORT
        case CODE_WIFI_ON_GOT_IP_ABORT: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP_ABORT\r\n", __func__);
        } break;
        #endif
        #ifdef CODE_WIFI_ON_GOT_IP_TIMEOUT
        case CODE_WIFI_ON_GOT_IP_TIMEOUT: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP_TIMEOUT\r\n", __func__);
        } break;
        #endif
        case CODE_WIFI_ON_GOT_IP: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP\r\n", __func__);
        } break;
        case CODE_WIFI_ON_DISCONNECT: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_DISCONNECT\r\n", __func__);
        } break;
        case CODE_WIFI_ON_AP_STARTED: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STARTED\r\n", __func__);
        } break;
        case CODE_WIFI_ON_AP_STOPPED: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STOPPED\r\n", __func__);
        } break;
        case CODE_WIFI_ON_AP_STA_ADD: {
            LOG_I("[APP] [EVT] [AP] [ADD] %lld\r\n", xTaskGetTickCount());
        } break;
        case CODE_WIFI_ON_AP_STA_DEL: {
            LOG_I("[APP] [EVT] [AP] [DEL] %lld\r\n", xTaskGetTickCount());
        } break;
        default: {
            LOG_I("[APP] [EVT] Unknown code %u \r\n", code);
        }
    }
}

int main(void)
{
    board_init();

    uart0 = bflb_device_get_by_name("uart0");
    shell_init_with_task(uart0);

    tcpip_init(NULL, NULL);
    xTaskCreate(wifi_start_firmware_task, "wifi init", 1024, NULL, 10, NULL);
#if defined(CONFIG_CODEC_USE_I2S)
    extern msp_i2s_port_init(void);
    msp_i2s_port_init();
#endif
    vTaskStartScheduler();

    while (1) {
    }
}

FAQ

codec_speaker produces no sound

Check the speaker/PA wiring and the pin configuration in multimedia_port.c (CONFIG_AUDIO_PA_PIN, g_pa_delay_2); confirm the output pins (OUTPUT_POSITIVE/NEGATIVE_PIN) match your board, and do not mix built-in codec and external ES8388 configs.

codec_mic captures no data

Confirm the mic is wired to the input pins (INPUT_POSITIVE/NEGATIVE_PIN) and the config selects the codec actually used by the board (built-in or ES8388); set unused differential input pins to 255.

Sample rate does not match expectations

The example uses 16 kHz/16-bit/mono. For other sample rates, change the channel config (xcodec_ch_cfg) in codec_speaker.c / codec_mic.c, keeping within the built-in codec's supported range.

Have questions?

For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

Released under the MIT License. Build Time 2026-09-11 14:52:23