实现音频pcm数据播放(window平台)

    流程概述:初始化音频输出设备->往设备输入音频数据->关闭音频输出设备。

     流程解析:在播放流程中,音频输出设备只需要初始化一次,主要是配置音频参数,比如采样率、位宽、通道数、音频数据类型等;接着连续往此设备输入正确的音频数据,需要注意每次输入的数据长度是否有限制;播放完成后关闭音频输出设备,结束单次的音频播放。

    流程实现:window平台直接使用Winmm.dll库,库中提供现成的音频设备操作api,比如: waveOutOpenwaveOutClose等。本次使用中主要用到以下几个: waveOutGetNumDevs、waveOutOpenwaveOutResetwaveOutClosewaveOutPrepareHeaderwaveOutUnprepareHeaderwaveOutWrite。

    waveOutGetNumDevs获取播放设备个数,如果返回值为0,说明没有可用于播放输出的设备,接下来没必要执行其它与输出设备相关的操作了。
    waveOutOpen:初始化音频输出设备,包括设置采样率、位宽、通道数等参数,如果open成功,则可以获得输出设备对应的操作句柄,用于后续对此设备进行别的操作。
    waveOutReset:重置播放队列,将播放缓存清空,为接下来执行close做准备。
    waveOutClose:关闭音频输出设备,释放设备的控制权。
    waveOutPrepareHeader:将此段音频数据置为可播放状态,为接下来执行write做准备。
    waveOutUnprepareHeader将此段音频数据置为不可播放状态。
    waveOutWrite:将音频数据放入播放队列中。

    1、(初始化设备) waveOutGetNumDevs-> waveOutOpen->
    2、(输入数据)waveOutPrepareHeader->waveOutWrite->(重复此环节)
    3、(关闭设备)waveOutUnprepareHeader->waveOutReset->waveOutClose


    注意事项与建议:
1、 上述提到的api都自带互斥锁,必须防备死锁的出现,否则会导致播放阻塞。
2、 放入播放队列中的缓存不能立即取走或做其它用途,因为播放需要时间,只有在回调中抛出的缓存才是播放完成的。所以必须要有一个合适大小的缓存队列,以保证数据缓存与播放缓存两不误。

3、 执行 waveOutOpen 时建议注册回调函数,回调可以接收到相应的播放消息,比如每次(帧)数据播放完都会有 WOM_DONE消息,此时根据需要是否对已经使用完的资源回收。
4、音频播放速度是固定的(音频参数不变的情况下),为了减少出现因数据不及时(特别是实时的音频数据)出现的播放卡顿现象,建议增加音频数据缓存,比如一次性缓存到32k才放入播放队列等。
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的音频PCM数据相似度分析的C语言源码示例: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define SAMPLE_RATE 44100 // 采样率 #define NUM_SAMPLES 44100 // 采样数 #define WINDOW_SIZE 512 // 窗口大小 #define HOP_SIZE 256 // 跳跃大小 typedef struct { float* data; int size; } AudioBuffer; float calculate_rms(AudioBuffer* buffer) { float sum = 0.0f; for (int i = 0; i < buffer->size; i++) { sum += buffer->data[i] * buffer->data[i]; } return sqrtf(sum / buffer->size); } float calculate_similarity(AudioBuffer* buffer1, AudioBuffer* buffer2) { int num_windows = (NUM_SAMPLES - WINDOW_SIZE) / HOP_SIZE + 1; float* window1 = (float*)malloc(sizeof(float) * WINDOW_SIZE); float* window2 = (float*)malloc(sizeof(float) * WINDOW_SIZE); float* buffer1_rms = (float*)malloc(sizeof(float) * num_windows); float* buffer2_rms = (float*)malloc(sizeof(float) * num_windows); for (int i = 0; i < num_windows; i++) { // 提取窗口 for (int j = 0; j < WINDOW_SIZE; j++) { window1[j] = buffer1->data[i * HOP_SIZE + j]; window2[j] = buffer2->data[i * HOP_SIZE + j]; } // 计算两个窗口的RMS buffer1_rms[i] = calculate_rms(&(AudioBuffer){ window1, WINDOW_SIZE }); buffer2_rms[i] = calculate_rms(&(AudioBuffer){ window2, WINDOW_SIZE }); } // 计算相似度 float sum = 0.0f; for (int i = 0; i < num_windows; i++) { sum += buffer1_rms[i] * buffer2_rms[i]; } float similarity = sum / num_windows; free(window1); free(window2); free(buffer1_rms); free(buffer2_rms); return similarity; } int main() { AudioBuffer buffer1 = { .data = (float*)malloc(sizeof(float) * NUM_SAMPLES), .size = NUM_SAMPLES }; AudioBuffer buffer2 = { .data = (float*)malloc(sizeof(float) * NUM_SAMPLES), .size = NUM_SAMPLES }; // 填充音频数据 for (int i = 0; i < NUM_SAMPLES; i++) { buffer1.data[i] = sinf(2.0f * M_PI * 440.0f * i / SAMPLE_RATE); buffer2.data[i] = sinf(2.0f * M_PI * 440.0f * i / SAMPLE_RATE + M_PI / 2.0f); } float similarity = calculate_similarity(&buffer1, &buffer2); printf("Similarity: %f\n", similarity); free(buffer1.data); free(buffer2.data); return 0; } ``` 该示例代码使用RMS(均方根)和窗口技术来计算两个音频PCM数据的相似度。通过将音频数据分成多个窗口,然后计算每个窗口的RMS,最后计算RMS的平均值来计算相似度。这种方法可以减少时间域中的干扰,并更好地捕获频域中的相似度。该示例仅用于演示目的,实际应用中可能需要更复杂的算法来获得更准确的结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值