miniaudio:音频开源库的首选

软件开发中遇到需要播放音频文件时,可以使用操作系统提供的API也可以依赖于第三方库,通常第三方库多因其简单易用的接口而成为首选。miniaudio便是游戏的音频开源库之一。

miniaudio 是一个轻量级的音频播放、采集、播放+采集的库,专注于提供简单易用的 API 和跨平台的音频播放功能。它具有以下特点:

  • 轻量级: miniaudio 是一个小巧的库,header-only,不依赖于其他外部库,易于集成到各种项目中。

  • 跨平台: 支持 Windows、macOS、Linux 等多个主流操作系统,并提供了对于多种平台的兼容性。

  • 简单易用: miniaudio 提供了简洁的 API,无需复杂的配置,即使对音频编程不熟悉的开发者也能够快速上手。

下载和安装

下载链接见(github.com/mackron/min... /tree/0.11.21),

miniaudio作为header-only的开源库,只需将miniaudio.h头文件集成到项目中即可。在使用时,需要在包含头文件前定义宏MINIAUDIO_IMPLEMENTATION,形如:

arduino 复制代码
#define MINIAUDIO_IMPLEMENTATION
#include"miniaudio/miniaudio.h"

使用

miniaudio分为上层(High Low)接口和底层(Low Level)接口两种,高层接口做了封装,使用起来更加方便,对于开发者来讲更像一个黑盒子。=反而底层接口,开发者可以获得操作音频原始数据的机会。

接下来将分别使用上层接口、底层接口来播放本地文件以及录制声音。

ini 复制代码
#define MINIAUDIO_IMPLEMENTATION
#include"miniaudio/miniaudio.h"
//上层接口播放本地文件
void using_high_level_playback()
{
    ma_result result;
    ma_engine engine;
    result = ma_engine_init(NULL, &engine);
    if (result != MA_SUCCESS) {
        return ;
    }


    //"d://22.mp3"本地文件,需替换为自己的文件
    ma_engine_play_sound(&engine, "d://22.mp3", NULL);
    printf("Press Enter to quit...");
    getchar();

    ma_engine_uninit(&engine);
}



//底层接口播放本地文件
void data_callback_playback(ma_device*pDevice, void*pOutput,
        constvoid*pInput, ma_uint32frameCount)
{
    ma_decoder* pDecoder = (ma_decoder*)pDevice->pUserData;
    if (pDecoder == NULL) {
        return;
    }

    ma_decoder_read_pcm_frames(pDecoder, pOutput, frameCount, NULL);
    (void)pInput;
}



void using_low_level_playback()
{
    ma_result result;
    ma_decoder decoder;
    ma_device_config deviceConfig;
    ma_device device;


    result = ma_decoder_init_file("d://22.mp3", NULL, &decoder);
    if (result != MA_SUCCESS) {
        return;
    }


    deviceConfig = ma_device_config_init(ma_device_type_playback);
    deviceConfig.playback.format = decoder.outputFormat;
    deviceConfig.playback.channels = decoder.outputChannels;
    deviceConfig.sampleRate = decoder.outputSampleRate;
    deviceConfig.dataCallback = data_callback_playback;
    deviceConfig.pUserData = &decoder;


    if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) {
        printf("Failed to open playback device.\n");
        ma_decoder_uninit(&decoder);
        return ;
    }


    if (ma_device_start(&device) != MA_SUCCESS) {
        printf("Failed to start playback device.\n");
        ma_device_uninit(&device);
        ma_decoder_uninit(&decoder);
        return ;
    }


    printf("Press Enter to quit...");
    getchar();


    ma_device_uninit(&device);
    ma_decoder_uninit(&decoder);
}




//采集声音
void data_callback_capture(ma_device*pDevice, void*pOutput,
            constvoid*pInput, ma_uint32frameCount)
{
    ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData;
    MA_ASSERT(pEncoder != NULL);
    ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL);
    (void)pOutput;
}


int using_capture()
{
    ma_result result;
    ma_encoder_config encoderConfig;
    ma_encoder encoder;
    ma_device_config deviceConfig;
    ma_device device;



    encoderConfig = ma_encoder_config_init(ma_encoding_format_wav, ma_format_f32, 2, 48000);


    if (ma_encoder_init_file("d://capture.wav", &encoderConfig, &encoder) != MA_SUCCESS) {
        printf("Failed to initialize output file.\n");
        return -1;
    }


    deviceConfig = ma_device_config_init(ma_device_type_duplex);
    deviceConfig.capture.format = encoder.config.format;
    deviceConfig.capture.channels = encoder.config.channels;
    deviceConfig.sampleRate = encoder.config.sampleRate;
    deviceConfig.dataCallback = data_callback_capture;
    deviceConfig.pUserData = &encoder;


    result = ma_device_init(NULL, &deviceConfig, &device);
    if (result != MA_SUCCESS) {
        printf("Failed to initialize capture device.\n");
        return -2;
    }


    result = ma_device_start(&device);
    if (result != MA_SUCCESS) {
        ma_device_uninit(&device);
        printf("Failed to start device.\n");
        return -3;
    }


    printf("Press Enter to stop recording...\n");
    getchar();


    ma_device_uninit(&device);
    ma_encoder_uninit(&encoder);


    return0;
}

对比播放本地文件部分代码,扎心的发现,上层接口使用极其少量的代码实现了和底层接口相同的功能。但是底层接口,使得我们获得了在回调函数data_callback操作pcm数据的可能,当然若没有修改pcm的需求时,可以直接使用上层的接口。

总结

miniaudio作为一个header-only且MIT协议的开源库,极大地方便了在项目中的集成。同时,miniaudio支持播放、采集、采集同时播放的功能,可视为音频开源库的首选。

本文使用 文章同步助手 同步

相关推荐
郝学胜_神的一滴8 小时前
CMake 034:生成器表达式:解耦构建时序、精简分支逻辑的终极利器
c++·cmake
见过夏天1 天前
C++ 基础入门完全指南
c++
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
BadBadBad__AK3 天前
线段树维护区间 k 次方和
c++·数学·算法·stl
卷无止境3 天前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
卷无止境3 天前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
郝学胜_神的一滴4 天前
CMake 30:循环语法全解|foreach_while双循环精讲、迭代技巧与实战避坑指南
c++·cmake
卷无止境6 天前
C++ 的Eigen 库全解析
c++
卷无止境6 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端
郝学胜_神的一滴6 天前
CMake 27:缓存变量的特性、语法、类型与实操全解
c++·cmake