基于ALSA lib录音接口

接口文件 record_interface.h

/**
 * @file record_interface.h
 * @brief For the operation of record API
 */

#ifndef __record_INTERFACE_HEAD__
#define __record_INTERFACE_HEAD__

#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <time.h>
#include <locale.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "alsa/asoundlib.h"
#include <assert.h>

typedef unsigned char  uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int   uint32_t;

typedef struct {
    /**
     * @brief Alsa handle.
     */
    snd_pcm_t *handle;
    /**
     * @brief Alsa log Output redirection.
     */
    snd_output_t *log;
    /**
     * @brief Alsa internal audio cache buffer size.
     */
    snd_pcm_uframes_t buffer_size;
    /**
     * @brief Data format.
     */
    snd_pcm_format_t format;
    /**
     * @brief The number of channels.
     */
    uint16_t channels;
    /**
     * @brief The number of bit per sample point.
     */
    size_t bits_per_sample;
    /**
     * @brief The number of bit per frame.
     */
    size_t bits_per_frame;
    /**
     * @brief The amount of frame required to record.
     */
    snd_pcm_uframes_t chunk_size;
    /**
     * @brief The number of bytes required to be recorded is matched with the
     * chunk_size.
     */
    size_t chunk_bytes;
    /**
     * @brief Point to the recording data buffer, the size is chunk_bytes bytes,
     * have chunk_size frame.
     */
    uint8_t *data_buf;
} record_handle_t;

typedef struct {
    /*
     * Specifies data format.
     * SND_PCM_FORMAT_S16_LE;
     * SND_PCM_FORMAT_S8;
     * SND_PCM_FORMAT_UNKNOWN;
     * All references at sound/asound.h
     */
    snd_pcm_format_t format;
    /**
     * @brief Specifies the number of channels.
     */
    uint16_t channels;
    /**
     * @brief frequence of sample
     */
    uint32_t sample_rate;
} record_params_t;


/**
 * @brief Get the handle for the recording.
 *
 * @param record_params Recording parameters.
 *
 * @return Record handle on success otherwise NULL is returned.
 */
record_handle_t *mozart_get_record_handle(record_params_t record_params);

/**
 * @brief Get the recording data, the size is chunk_bytes bytes.
 *
 * @param record_handle
 *
 * @return Return a pointer to data on success otherwise NULL is returned.
 */
uint8_t *mozart_record_get_data(record_handle_t * record_handle);

/**
 * @brief Release record handle after call mozart_get_record_handle.
 *
 * @param record_handle
 */
void mozart_release_record_handle(record_handle_t * record_handle);

#endif /* __record_INTERFACE_HEAD__ */

实现文件 record.c

/**
 * @file record.c
 * @brief For the operation of record API
 */

#include "record_interface.h"

#define DEBUG(x,y...)   {printf("[ %s : %s : %d] ",__FILE__, __func__, __LINE__); printf(x,##y); printf("\n");}
#define ERROR(x,y...)   {printf("[ %s : %s : %d] ",__FILE__, __func__, __LINE__); printf(x,##y); printf("\n");}

static int record_set_params(record_handle_t * record_handle, record_params_t record_params)
{
    snd_pcm_hw_params_t *hwparams;
    uint32_t exact_rate;
    uint32_t buffer_time, period_time;

    /* Allocate the snd_pcm_hw_params_t structure on the stack. */
    snd_pcm_hw_params_alloca(&hwparams);

    /* Init hwparams with full configuration space */
    if (snd_pcm_hw_params_any(record_handle->handle, hwparams) < 0) {
        ERROR("Error snd_pcm_hw_params_any");
        goto ERR_SET_PARAMS;
    }

    if (snd_pcm_hw_params_set_access(record_handle->handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
        ERROR("Error snd_pcm_hw_params_set_access");
        goto ERR_SET_PARAMS;
    }

    if (snd_pcm_hw_params_set_format(record_handle->handle, hwparams, record_params.format) < 0) {
        ERROR("Error snd_pcm_hw_params_set_format");
        goto ERR_SET_PARAMS;
    }
    record_handle->format = record_params.format;

    /* Set number of channels */
    if (snd_pcm_hw_params_set_channels(record_handle->handle, hwparams, record_params.channels) < 0) {
        ERROR("Error snd_pcm_hw_params_set_channels");
        goto ERR_SET_PARAMS;
    }
    record_handle->channels = record_params.channels;

    /* Set sample rate. If the exact rate is not supported */
    /* by the hardware, use nearest possible rate.       */
    exact_rate = record_params.sample_rate;
    if (snd_pcm_hw_params_set_rate_near(record_handle->handle, hwparams, &exact_rate, 0) < 0) {
        ERROR("Error snd_pcm_hw_params_set_rate_near");
        goto ERR_SET_PARAMS;
    }
    if (record_params.sample_rate != exact_rate) {
        ERROR("The rate %d Hz is not supported by your hardware. ==> Using %d Hz instead.",
            record_params.sample_rate, exact_rate);
    }

    if (snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0) < 0) {
        ERROR("Error snd_pcm_hw_params_get_buffer_time_max");
        goto ERR_SET_PARAMS;
    }
    if (buffer_time > 500000) buffer_time = 500000;
    period_time = buffer_time / 4;

    if (snd_pcm_hw_params_set_buffer_time_near(record_handle->handle, hwparams, &buffer_time, 0) < 0) {
        ERROR("Error snd_pcm_hw_params_set_buffer_time_near");
        goto ERR_SET_PARAMS;
    }

    if (snd_pcm_hw_params_set_period_time_near(record_handle->handle, hwparams, &period_time, 0) < 0) {
        ERROR("Error snd_pcm_hw_params_set_period_time_near");
        goto ERR_SET_PARAMS;
    }

    /* Set hw params */
    if (snd_pcm_hw_params(record_handle->handle, hwparams) < 0) {
        ERROR("Error snd_pcm_hw_params(handle, params)");
        goto ERR_SET_PARAMS;
    }

    snd_pcm_hw_params_get_period_size(hwparams, &record_handle->chunk_size, 0);
    snd_pcm_hw_params_get_buffer_size(hwparams, &record_handle->buffer_size);
    if (record_handle->chunk_size == record_handle->buffer_size) {
        ERROR(("Can't use period equal to buffer size (%lu == %lu)"), record_handle->chunk_size, record_handle->buffer_size);
        goto ERR_SET_PARAMS;
    }

    //通过fomart获取每sample的bit数
    record_handle->bits_per_sample = snd_pcm_format_physical_width(record_params.format);

    //把frame定义为一个sample的数据,包括每个channel
    record_handle->bits_per_frame = record_handle->bits_per_sample * record_params.channels;

    //计算录chunk_size个frame需要的byte数
    record_handle->chunk_bytes = record_handle->chunk_size * record_handle->bits_per_frame / 8;

    //开辟录音buffer
    record_handle->data_buf = (uint8_t *)malloc(record_handle->chunk_bytes);
    if (!record_handle->data_buf) {
        ERROR("Error malloc: [data_buf]");
        goto ERR_SET_PARAMS;
    }

    return 0;

ERR_SET_PARAMS:
    return -1;
}

record_handle_t *mozart_get_record_handle(record_params_t record_params)
{
    char *devicename = "default";
    record_handle_t *record_handle;
    record_handle = (record_handle_t *)malloc(sizeof(record_handle_t));
    memset(record_handle, 0x0, sizeof(record_handle_t));

    if (snd_output_stdio_attach(&record_handle->log, stderr, 0) < 0) {
        ERROR("Error snd_output_stdio_attach");
        free(record_handle);
        return NULL;
    }

    if (snd_pcm_open(&(record_handle->handle), devicename, SND_PCM_STREAM_CAPTURE, 0) < 0) {
        ERROR("Error snd_pcm_open [ %s]", devicename);
        free(record_handle);
        return NULL;
    }

    if (record_set_params(record_handle, record_params) < 0) {
        ERROR("Error set_snd_pcm_params");
        free(record_handle);
        return NULL;
    }
    //snd_pcm_dump(record_handle->handle, record_handle->log);
    return record_handle;
}

static ssize_t read_pcm(record_handle_t *record_handle)
{
    ssize_t r;
    size_t result = 0;
    size_t count = record_handle->chunk_size;
    uint8_t *data = record_handle->data_buf;

    while (count > 0) {
        //录count个frame到data中
        r = snd_pcm_readi(record_handle->handle, data, count);

        if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
            snd_pcm_wait(record_handle->handle, 1000);
        } else if (r == -EPIPE) {
            snd_pcm_prepare(record_handle->handle);
            fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>\n");
        } else if (r == -ESTRPIPE) {
            fprintf(stderr, "<<<<<<<<<<<<<<< Need suspend >>>>>>>>>>>>>>>\n");
        } else if (r < 0) {
            fprintf(stderr, "Error snd_pcm_readi: [%s]", snd_strerror(r));
            return -1;
        }

        if (r > 0) {
            result += r;
            count -= r;
            data += r * record_handle->bits_per_frame / 8;
        }
    }
    return result;
}

uint8_t *mozart_record_get_data(record_handle_t * record_handle)
{
    if (read_pcm(record_handle) != record_handle->chunk_size)
        return NULL;
    return record_handle->data_buf;
}

void mozart_release_record_handle(record_handle_t * record_handle)
{
    snd_pcm_drain(record_handle->handle);
    free(record_handle->data_buf);
    snd_output_close(record_handle->log);
    snd_pcm_close(record_handle->handle);
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值