ALSA

ALSA(Advanced Linux Sound Architecture),即高级Linux声音架构。
在内核设备驱动层,ALSA提供了alsa-driver,同时在应用层,ALSA为我们提供了alsa-lib,应用程序只要调-用alsa-lib提供的API(libasound.so),即可以完成对底层音频硬件的控制。内核空间中,alsa-soc其实是对alsa-driver的进一步封装,他针对嵌入式设备提供了一些列增强的功能。

alsa_audio.h
#ifndef ALSA_AUDIO_H
#define ALSA_AUDIO_H

#include <QObject>

#include <alsa/asoundlib.h>

class ALSA_Audio : public QObject
{
    Q_OBJECT
public:
    explicit ALSA_Audio(QObject *parent = nullptr);


    void capture_start();
    void capture_stop();
    /**
     * @brief 读取音频数据
     * @param buffer 音频数据
     * @param buffer_size 音频数据大小
     * @param frames 读取的音频帧数
     * @return 0 成功,-1 失败
     */
    int audio_read(char **buffer, int *buffer_size, unsigned long *frames);

    void playback_start();
    void playback_stop();
    /**
     * @brief audio_write 播放音频
     * @param buffer 音频数据
     * @param frames 播放的音频帧数
     * @return 0 成功,-1 失败
     */
    int audio_write(char *buffer);



private:
    bool m_is_capture_start;
    snd_pcm_t *m_capture_pcm;
    char *m_capture_buffer;
    unsigned long m_capture_buffer_size;
    snd_pcm_uframes_t m_capture_frames;       // 一次读的帧数


    bool m_is_playback_start;
    snd_pcm_t *m_playback_pcm;
    snd_pcm_uframes_t m_playback_frames;       // 一次写的帧数

    /**
     * @brief ALSA_Audio::set_hw_params
     * @param pcm
     * @param hw_params
     * @param rate 采样频率
     * @param format 格式
     * @param channels 通道数
     * @param frames 一次读写的帧数
     * @return
     */
    int set_hw_params(snd_pcm_t *pcm, unsigned int rate, snd_pcm_format_t format, unsigned int channels, snd_pcm_uframes_t frames);



signals:

public slots:
};

#endif // ALSA_AUDIO_H

alsa_audio.cpp
#include "alsa_audio.h"
#include "global.h"

#include <QDebug>

#include <math.h>
#include <inttypes.h>



ALSA_Audio::ALSA_Audio(QObject *parent) : QObject(parent)
{
    m_is_capture_start = false;
    m_is_playback_start = false;
}



int ALSA_Audio::set_hw_params(snd_pcm_t *pcm, unsigned int rate, snd_pcm_format_t format, unsigned int channels, snd_pcm_uframes_t frames)
{
    snd_pcm_uframes_t period_size;          // 一个处理周期需要的帧数
    snd_pcm_uframes_t hw_buffer_size;      // 硬件缓冲区大小
    snd_pcm_hw_params_t *hw_params;
    int ret;
    int dir = 0;



    // 初始化硬件参数结构体
    snd_pcm_hw_params_malloc(&hw_params);
    // 设置默认的硬件参数
    snd_pcm_hw_params_any(pcm, hw_params);

    // 以下为设置所需的硬件参数

    // 设置音频数据记录方式
    CHECK_RETURN(snd_pcm_hw_params_set_access(pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED));
    // 格式。使用16位采样大小,小端模式(SND_PCM_FORMAT_S16_LE)
    CHECK_RETURN(snd_pcm_hw_params_set_format(pcm, hw_params, format));
    // 设置音频通道数
    CHECK_RETURN(snd_pcm_hw_params_set_channels(pcm, hw_params, channels));
    // 采样频率,一次采集为一帧数据
    //CHECK_RETURN(snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir));          // 设置相近的值
    CHECK_RETURN(snd_pcm_hw_params_set_rate(pcm, hw_params, rate, dir));
    // 一个处理周期需要的帧数
    period_size = frames * 5;
    CHECK_RETURN(snd_pcm_hw_params_set_period_size_near(pcm, hw_params, &period_size, &dir)); // 设置相近的值
//    // 硬件缓冲区大小, 单位:帧(frame)
//    hw_buffer_size = period_size * 16;
//    CHECK_RETURN(snd_pcm_hw_params_set_buffer_size_near(pcm, hw_params, &hw_buffer_size));

    // 将参数写入pcm驱动
    CHECK_RETURN(snd_pcm_hw_params(pcm, hw_params));

    snd_pcm_hw_params_free(hw_params);     // 释放不再使用的hw_params空间

    printf("one frames=%ldbytes\n", snd_pcm_frames_to_bytes(pcm, 1));
    unsigned int val;
    snd_pcm_hw_params_get_channels(hw_params, &val);
    printf("channels=%d\n", val);

    if (ret < 0) {
        printf("error: unable to set hw parameters: %s\n", snd_strerror(ret));
        return -1;
    }
    return 0;
}


void ALSA_Audio::capture_start()
{
    m_capture_frames = 160;     // 此处160为固定值,发送接收均使用此值
    unsigned int rate = 8000;                               // 采样频率
    snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;        // 使用16位采样大小,小端模式
    unsigned int channels = 1;                              // 通道数
    int ret;

    if(m_is_capture_start)
    {
        printf("error: alsa audio capture is started!\n");
        return;
    }

    ret = snd_pcm_open(&m_capture_pcm, "plughw:1,0", SND_PCM_STREAM_CAPTURE, 0);       // 使用plughw:0,0
    if(ret < 0)
    {
        printf("snd_pcm_open error: %s\n", snd_strerror(ret));
        return;
    }

    // 设置硬件参数
    if(set_hw_params(m_capture_pcm, rate, format, channels, m_capture_frames) < 0)
    {
        return;
    }

    // 使用buffer保存一次处理得到的数据
    m_capture_buffer_size = m_capture_frames * static_cast<unsigned long>(snd_pcm_format_width(format) / 8 * static_cast<int>(channels));
    m_capture_buffer_size *= 5;         // * 5 表示使用5倍的缓存空间
    printf("snd_pcm_format_width(format):%d\n", snd_pcm_format_width(format));
    printf("m_capture_buffer_size:%ld\n", m_capture_buffer_size);
    m_capture_buffer = static_cast<char *>(malloc(sizeof(char) * m_capture_buffer_size));
    memset(m_capture_buffer, 0, m_capture_buffer_size);

    // 获取一次处理所需要的时间,单位us
    // 1/rate * frames * 10^6 = period_time, 即:采集一帧所需的时间 * 一次处理所需的帧数 * 10^6 = 一次处理所需的时间(单位us)
    // snd_pcm_hw_params_get_period_time(m_capture_hw_params, &m_period_time, &dir);

    m_is_capture_start = true;
}

void ALSA_Audio::capture_stop()
{
    if(m_is_capture_start == false)
    {
        printf("error: alsa audio capture is not start!");
        return;
    }

    m_is_capture_start = false;

    snd_pcm_drain(m_capture_pcm);
    snd_pcm_close(m_capture_pcm);
    free(m_capture_buffer);
}

int ALSA_Audio::audio_read(char **buffer, int *buffer_size, unsigned long *frames)
{
    int ret;
    if(m_is_capture_start == false)
    {
        printf("error: alsa audio capture is stopped!\n");
        return -1;
    }
    memset(m_capture_buffer, 0, m_capture_buffer_size);
    ret = static_cast<int>(snd_pcm_readi(m_capture_pcm, m_capture_buffer, m_capture_frames));
    printf("strlen(m_capture_buffer)=%ld\n", strlen(m_capture_buffer));
    if (ret == -EPIPE)
    {
       /* EPIPE means overrun */
       printf("overrun occurred\n");
       snd_pcm_prepare(m_capture_pcm);
    }
    else if (ret < 0)
    {
       printf("error from read: %s\n", snd_strerror(ret));
    }
    else if (ret != static_cast<int>(m_capture_frames))
    {
       printf("short read, read %d frames\n", ret);
    }

    if(m_capture_buffer == nullptr)
    {
        printf("error: alsa audio capture_buffer is empty!\n");
        return -1;
    }
    *buffer = m_capture_buffer;
    *buffer_size = static_cast<int>(m_capture_buffer_size / 5);
    *frames = m_capture_frames;

    return 0;
}



void ALSA_Audio::playback_start()
{
    m_playback_frames = 160;     // 此处160为固定值,发送接收均使用此值
    unsigned int rate = 8000;                               // 采样频率
    snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;        // 使用16位采样大小,小端模式
    unsigned int channels = 1;                              // 通道数
    int ret;


    if(m_is_playback_start)
    {
        printf("error: alsa audio playback is started!\n");
        return;
    }

    ret = snd_pcm_open(&m_playback_pcm, "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0);      // 使用plughw:0,0
    if(ret < 0)
    {
        printf("snd_pcm_open error: %s\n", snd_strerror(ret));
        return;
    }

    // 设置硬件参数
    if(set_hw_params(m_playback_pcm, rate, format, channels, m_playback_frames) < 0)
    {
        return;
    }


    m_is_playback_start = true;

}

void ALSA_Audio::playback_stop()
{
    if(m_is_playback_start == false)
    {
        printf("error: alsa audio playback is not start!");
        return;
    }

    m_is_playback_start = false;

    snd_pcm_drain(m_playback_pcm);
    snd_pcm_close(m_playback_pcm);
}


int ALSA_Audio::audio_write(char *buffer)
{
    long ret;
    if(m_is_playback_start == false)
    {
        printf("error: alsa audio playback is stopped!\n");
        return -1;
    }
    else
    {
        ret = snd_pcm_writei(m_playback_pcm, buffer, m_playback_frames);
        if(ret == -EPIPE)
        {
            /* EPIPE means underrun  */
            printf("underrun occurred\n");
            snd_pcm_prepare(m_playback_pcm);
        }
        else if (ret < 0)
        {
           printf("error from write: %s\n", snd_strerror(static_cast<int>(ret)));
        }
        else if (ret != static_cast<long>(m_playback_frames))
        {
           printf("short write, write %ld frames\n", ret);
        }
    }
    return 0;
}








©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容

  • 一.声音参数基本概念: 声音是连续模拟量,计算机将它离散化之后用数字表示,就有了以下几个名词术语。 样本长度(sa...
    cs1001阅读 2,728评论 0 2
  • 一.声音参数基本概念: 声音是连续模拟量,计算机将它离散化之后用数字表示,就有了以下几个名词术语。 样本长度(sa...
    cs1001阅读 5,397评论 0 3
  • 前言 今天跟王总聊天,他狠狠地(形容词)叼了我,他说你为什么不写一些比较牛逼的文章,为什么总是写一些教别人如何安装...
    da4ac7fb9bd5阅读 4,945评论 0 49
  • 简介 高级 Linux 声音体系(Advanced Linux Sound Architecture,ALSA)是...
    Pokerpoke阅读 5,373评论 0 1
  • 参考 http://blog.csdn.net/reille/article/details/5855859htt...
    jkCodic阅读 2,010评论 0 0