1+ #include <alsa/asoundlib.h>
2+ #include <stdio.h>
3+ #include <stdlib.h>
4+ #include <time.h>
5+ #include <string.h>
6+ #include <sys/time.h>
7+
8+ // WAV文件头结构
9+ typedef struct {
10+ char chunk_id [4 ]; // "RIFF"
11+ int chunk_size ; // 文件总长度 - 8
12+ char format [4 ]; // "WAVE"
13+
14+ char subchunk1_id [4 ]; // "fmt "
15+ int subchunk1_size ; // 16 (PCM格式)
16+ short audio_format ; // 1 (PCM)
17+ short num_channels ; // 声道数
18+ int sample_rate ; // 采样率
19+ int byte_rate ; // 每秒字节数
20+ short block_align ; // 每个样本的字节数
21+ short bits_per_sample ; // 位深度
22+
23+ char subchunk2_id [4 ]; // "data"
24+ int subchunk2_size ; // 音频数据长度
25+ } WavHeader ;
26+
27+ // 获取当前时间(毫秒级精度)
28+ double get_current_time_ms () {
29+ struct timeval tv ;
30+ gettimeofday (& tv , NULL );
31+ return (tv .tv_sec * 1000.0 ) + (tv .tv_usec / 1000.0 );
32+ }
33+
34+ int main () {
35+ // ALSA设备参数
36+ const char * device = "default" ;
37+ snd_pcm_t * capture_handle ;
38+ snd_pcm_hw_params_t * hw_params ;
39+ int err ;
40+
41+ // 音频参数
42+ unsigned int sample_rate = 44100 ; // 44.1 kHz
43+ int channels = 2 ; // 立体声
44+ snd_pcm_uframes_t frames = 1024 ; // 每周期帧数
45+ int bits_per_sample = 16 ; // 16位采样
46+
47+ // 计算录制时间(5秒)
48+ const int duration_sec = 5 ;
49+ const double duration_ms = duration_sec * 1000.0 ; // 转换为毫秒
50+ const size_t total_frames = sample_rate * duration_sec ;
51+ const size_t buffer_size = frames * channels * (bits_per_sample / 8 );
52+
53+ // 打开PCM设备
54+ if ((err = snd_pcm_open (& capture_handle , device , SND_PCM_STREAM_CAPTURE , 0 )) < 0 ) {
55+ fprintf (stderr , "无法打开设备: %s\n" , snd_strerror (err ));
56+ return 1 ;
57+ }
58+
59+ // 分配硬件参数结构
60+ if ((err = snd_pcm_hw_params_malloc (& hw_params )) < 0 ) {
61+ fprintf (stderr , "无法分配参数: %s\n" , snd_strerror (err ));
62+ return 1 ;
63+ }
64+
65+ // 初始化参数
66+ if ((err = snd_pcm_hw_params_any (capture_handle , hw_params )) < 0 ) {
67+ fprintf (stderr , "无法初始化参数: %s\n" , snd_strerror (err ));
68+ return 1 ;
69+ }
70+
71+ // 设置参数:交错模式
72+ if ((err = snd_pcm_hw_params_set_access (capture_handle , hw_params , SND_PCM_ACCESS_RW_INTERLEAVED )) < 0 ) {
73+ fprintf (stderr , "无法设置访问模式: %s\n" , snd_strerror (err ));
74+ return 1 ;
75+ }
76+
77+ // 设置采样格式
78+ snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE ;
79+ if ((err = snd_pcm_hw_params_set_format (capture_handle , hw_params , format )) < 0 ) {
80+ fprintf (stderr , "无法设置格式: %s\n" , snd_strerror (err ));
81+ return 1 ;
82+ }
83+
84+ // 设置声道数
85+ if ((err = snd_pcm_hw_params_set_channels (capture_handle , hw_params , channels )) < 0 ) {
86+ fprintf (stderr , "无法设置声道数: %s\n" , snd_strerror (err ));
87+ return 1 ;
88+ }
89+
90+ // 设置采样率
91+ unsigned int actual_rate = sample_rate ;
92+ if ((err = snd_pcm_hw_params_set_rate_near (capture_handle , hw_params , & actual_rate , 0 )) < 0 ) {
93+ fprintf (stderr , "无法设置采样率: %s\n" , snd_strerror (err ));
94+ return 1 ;
95+ }
96+ if (actual_rate != sample_rate ) {
97+ fprintf (stderr , "警告:实际采样率 %u Hz (请求 %u Hz)\n" , actual_rate , sample_rate );
98+ }
99+
100+ // 设置周期大小
101+ if ((err = snd_pcm_hw_params_set_period_size_near (capture_handle , hw_params , & frames , 0 )) < 0 ) {
102+ fprintf (stderr , "无法设置周期大小: %s\n" , snd_strerror (err ));
103+ return 1 ;
104+ }
105+
106+ // 应用参数
107+ if ((err = snd_pcm_hw_params (capture_handle , hw_params )) < 0 ) {
108+ fprintf (stderr , "无法应用参数: %s\n" , snd_strerror (err ));
109+ return 1 ;
110+ }
111+
112+ // 释放参数结构
113+ snd_pcm_hw_params_free (hw_params );
114+
115+ // 准备设备
116+ if ((err = snd_pcm_prepare (capture_handle )) < 0 ) {
117+ fprintf (stderr , "无法准备设备: %s\n" , snd_strerror (err ));
118+ return 1 ;
119+ }
120+
121+ // 创建输出文件
122+ FILE * wav_file = fopen ("recording.wav" , "wb" );
123+ if (!wav_file ) {
124+ perror ("无法创建WAV文件" );
125+ return 1 ;
126+ }
127+
128+ // 预留WAV文件头位置
129+ WavHeader header ;
130+ memset (& header , 0 , sizeof (header ));
131+ fwrite (& header , 1 , sizeof (header ), wav_file );
132+
133+ // 分配音频缓冲区
134+ char * buffer = malloc (buffer_size );
135+ if (!buffer ) {
136+ perror ("内存分配失败" );
137+ return 1 ;
138+ }
139+
140+ // 录制音频(改进时间显示逻辑)
141+ size_t frames_recorded = 0 ;
142+ double start_time_ms = get_current_time_ms (); // 录制开始时间
143+ printf ("开始录制...\n" );
144+
145+ while (frames_recorded < total_frames ) {
146+ // 计算已录制时间(毫秒)
147+ double current_time_ms = get_current_time_ms ();
148+ double elapsed_ms = current_time_ms - start_time_ms ;
149+ double remaining_ms = duration_ms - elapsed_ms ;
150+
151+ double remain_time = remaining_ms / 1000.0 ;
152+ if (remain_time < 0.0 ) {
153+ remain_time = 0.0 ;
154+ }
155+
156+ // 显示倒计时(格式:剩余时间 秒.毫秒)
157+ printf ("\r剩余时间: %5.3lf秒" , remain_time );
158+ fflush (stdout );
159+
160+ // 从设备读取数据(保持不变)
161+ snd_pcm_uframes_t frames_to_read = frames ;
162+ if (frames_recorded + frames_to_read > total_frames ) {
163+ frames_to_read = total_frames - frames_recorded ;
164+ }
165+
166+ if ((err = snd_pcm_readi (capture_handle , buffer , frames_to_read )) != frames_to_read ) {
167+ fprintf (stderr , "读取错误: %s\n" , snd_strerror (err ));
168+ break ;
169+ }
170+
171+ // 写入文件(保持不变)
172+ size_t bytes_to_write = frames_to_read * channels * (bits_per_sample / 8 );
173+ fwrite (buffer , 1 , bytes_to_write , wav_file );
174+ frames_recorded += frames_to_read ;
175+ }
176+
177+ printf ("\n录制完成\n" );
178+
179+ // 填充WAV文件头
180+ size_t data_size = frames_recorded * channels * (bits_per_sample / 8 );
181+
182+ memcpy (header .chunk_id , "RIFF" , 4 );
183+ header .chunk_size = 36 + data_size ;
184+ memcpy (header .format , "WAVE" , 4 );
185+
186+ memcpy (header .subchunk1_id , "fmt " , 4 );
187+ header .subchunk1_size = 16 ;
188+ header .audio_format = 1 ; // PCM
189+ header .num_channels = channels ;
190+ header .sample_rate = sample_rate ;
191+ header .byte_rate = sample_rate * channels * (bits_per_sample / 8 );
192+ header .block_align = channels * (bits_per_sample / 8 );
193+ header .bits_per_sample = bits_per_sample ;
194+
195+ memcpy (header .subchunk2_id , "data" , 4 );
196+ header .subchunk2_size = data_size ;
197+
198+ // 重写文件头
199+ fseek (wav_file , 0 , SEEK_SET );
200+ fwrite (& header , 1 , sizeof (header ), wav_file );
201+
202+ // 清理资源
203+ fclose (wav_file );
204+ free (buffer );
205+ snd_pcm_close (capture_handle );
206+
207+ printf ("文件已保存为 recording.wav\n" );
208+ return 0 ;
209+ }
0 commit comments