[toc]

使用C语言在Windows中创建PCM格式的音频文件

PCM文件里面存储的是经过数模转换后存储的二进制文件,需要支持的播放器才能播放。

1.保存do re mi fa so la ti do到PCM文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>

// 定义 M_PI
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#define SAMPLE_RATE 44100
#define NUM_CHANNELS 1
#define BITS_PER_SAMPLE 16
#define DURATION 0.25 // 每个音符的持续时间(秒)

void write_pcm_data(FILE* file, int16_t* data, int data_size) {
fwrite(data, sizeof(int16_t), data_size, file);
}

int main() {
FILE* file = fopen("output.pcm", "wb");
if (!file) {
printf("Failed to create PCM audio file\n");
return 1;
}

int num_samples = SAMPLE_RATE * DURATION;
int data_size = num_samples * NUM_CHANNELS * 8; // 8个音符
int16_t* data = (int16_t*)malloc(data_size * sizeof(int16_t));

if (!data) {
printf("Memory allocation failed\n");
fclose(file);
return 1;
}

// 生成音阶数据
int frequencies[] = { 261, 293, 329, 349, 392, 440, 493, 523 }; // C4, D4, E4, F4, G4, A4, B4, C5
for (int i = 0; i < 8; i++) {
int frequency = frequencies[i];
for (int j = 0; j < num_samples; j++) {
data[i * num_samples + j] = (int16_t)(32767 * sin(2 * M_PI * frequency * j / SAMPLE_RATE));
}
}

write_pcm_data(file, data, num_samples * 8);

free(data);
fclose(file);

printf("PCM audio file created successfully\n");
return 0;
}

2.将PCM文件转为WAV格式后播放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "winmm.lib")

#define BUFFER_SIZE 4096

void playPCM(const char* filename, int sampleRate, int bitsPerSample, int channels) {
// 打开PCM文件
FILE* file = fopen(filename, "rb");
if (!file) {
printf("无法打开文件: %s\n", filename);
return;
}

// 设置WAVE格式
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = channels;
wfx.nSamplesPerSec = sampleRate;
wfx.wBitsPerSample = bitsPerSample;
wfx.nBlockAlign = (channels * bitsPerSample) / 8;
wfx.nAvgBytesPerSec = sampleRate * wfx.nBlockAlign;
wfx.cbSize = 0;

// 打开音频输出设备
HWAVEOUT hWaveOut;
if (waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) {
printf("无法打开音频输出设备\n");
fclose(file);
return;
}

// 创建缓冲区
char buffer[BUFFER_SIZE];
DWORD bytesRead;

// 播放音频
while ((bytesRead = fread(buffer, 1, BUFFER_SIZE, file)) > 0) {
WAVEHDR waveHdr;
waveHdr.lpData = buffer;
waveHdr.dwBufferLength = bytesRead;
waveHdr.dwFlags = 0;
waveHdr.dwLoops = 0;

// 准备并写入音频数据
waveOutPrepareHeader(hWaveOut, &waveHdr, sizeof(WAVEHDR));
waveOutWrite(hWaveOut, &waveHdr, sizeof(WAVEHDR));

// 等待音频播放完成
while (!(waveHdr.dwFlags & WHDR_DONE)) {
Sleep(10);
}

waveOutUnprepareHeader(hWaveOut, &waveHdr, sizeof(WAVEHDR));
}

// 关闭音频输出设备
waveOutClose(hWaveOut);
fclose(file);
}

int main() {
const char* filename = "output.pcm"; // PCM文件名
int sampleRate = 44100; // 采样率
int bitsPerSample = 16; // 位深
int channels = 2; // 通道数

playPCM(filename, sampleRate, bitsPerSample, channels);
return 0;
}