生成DTMF拨号音WAV文件时出现损坏无声音问题求助
DTMF WAV文件损坏无声音问题修复
核心错误分析
- WAV头字段赋值错误:代码中多处使用
==比较运算符而非=赋值运算符,导致SubChunk2Size、ChunkSize、ByteRate等关键头字段未正确初始化,这是文件损坏的主要原因。 - tone_length类型错误:将
tone_length定义为int,但atof(argv[2])返回浮点数(如0.5会被截断为0),导致采样数计算完全错误。 - 电话号码校验逻辑失效:用
atof将电话号码字符串转为整数,无法处理*、#、-等字符,且判断条件逻辑混乱,根本无法校验输入合法性。 - 采样数据覆盖问题:内层循环每次从
pData[0]开始写入,覆盖之前数字的采样数据,最终只保留最后一个数字的采样。 - WAV头缺失必要字段:未设置
BlockAlign字段,这是WAV格式要求的必填项。 - 内存泄漏与冗余操作:
pFile = malloc(sizeof(WAVEFILE));完全多余,后续被fopen覆盖,导致内存泄漏。 - 头字段计算错误:
SubChunk2Size和ChunkSize的计算公式错误,导致文件大小标识不正确。 - 三角函数精度不足:使用硬编码的
3.14159而非标准库的M_PI,精度不足;且未使用浮点类型计算相位,可能导致溢出或精度损失。
修复后的代码
#define _USE_MATH_DEFINES #include <stdio.h> #include <stdbool.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <stdint.h> #define BYTES_PER_SAMPLE 2 #define SAMPLE_RATE 44100 #define NUM_CHANNELS 1 #define AMPLITUDE 16382 void show_usage(void); typedef struct { uint8_t ChunkID[4]; uint32_t ChunkSize; // 4 + (8+SubChunk1Size) + (8+SubChunk2Size) uint8_t Format[4]; uint8_t SubChunk1ID[4]; uint32_t SubChunk1Size; uint16_t AudioFormat; uint16_t NumChannels; uint32_t SampleRate; uint32_t ByteRate; //SampleRate * NumChannels * BitsPerSample/8 uint16_t BlockAlign; //NumChannels * BitsPerSample/8 uint16_t BitsPerSample; uint8_t SubChunk2ID[4]; uint32_t SubChunk2Size; //NumSamples * NumChannels * BitsPerSample/8 } WAVEFILE; int main (int argc, const char* argv[]) { FILE *pFile = NULL; int16_t *pData; WAVEFILE myWavHeader; WAVEFILE *pWave = &myWavHeader; int phoneLength = strlen(argv[3]); int freq1, freq2; float tone_length; // 检查参数数量 if (argc != 4) { show_usage(); return -1; } // 校验电话号码每个字符的合法性 for (int i = 0; i < phoneLength; i++) { char c = argv[3][i]; if (!( (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '-' )) { show_usage(); return -1; } } // 转换并校验tone_length tone_length = atof(argv[2]); if (!(tone_length >= 0.1 && tone_length <= 1.0)) { show_usage(); return -1; } // 计算总采样字节数并分配内存 uint32_t total_samples = phoneLength * (uint32_t)(SAMPLE_RATE * tone_length); uint32_t total_bytes = total_samples * BYTES_PER_SAMPLE; pData = malloc(total_bytes); if(pData == NULL) { puts("内存分配失败:无法分配采样数据内存"); return -1; } // 填充WAV头字段 memcpy(myWavHeader.ChunkID, "RIFF", 4); memcpy(myWavHeader.Format, "WAVE", 4); memcpy(myWavHeader.SubChunk1ID, "fmt ", 4); memcpy(myWavHeader.SubChunk2ID, "data", 4); myWavHeader.SubChunk1Size = 16; myWavHeader.AudioFormat = 1; // PCM myWavHeader.NumChannels = NUM_CHANNELS; myWavHeader.SampleRate = SAMPLE_RATE; myWavHeader.BitsPerSample = 16; myWavHeader.BlockAlign = myWavHeader.NumChannels * (myWavHeader.BitsPerSample / 8); myWavHeader.ByteRate = myWavHeader.SampleRate * myWavHeader.BlockAlign; myWavHeader.SubChunk2Size = total_bytes; myWavHeader.ChunkSize = 36 + myWavHeader.SubChunk2Size; // RIFF块大小计算 // 打开文件 pFile = fopen(argv[1], "wb"); if( pFile == NULL) { puts("文件打开失败:无法创建或写入目标文件"); free(pData); return -1; } // 生成DTMF采样数据 int sample_idx = 0; int samples_per_tone = (int)(SAMPLE_RATE * tone_length); for(int i = 0; i < phoneLength; i++) { char c = argv[3][i]; // 设置对应频率,'-'为静音(频率0) switch(c) { case '1': freq1=1209; freq2=697; break; case '2': freq1=1336; freq2=697; break; case '3': freq1=1477; freq2=697; break; case '4': freq1=1209; freq2=770; break; case '5': freq1=1336; freq2=770; break; case '6': freq1=1477; freq2=770; break; case '7': freq1=1209; freq2=852; break; case '8': freq1=1336; freq2=852; break; case '9': freq1=1477; freq2=852; break; case '*': freq1=1209; freq2=941; break; case '0': freq1=1336; freq2=941; break; case '#': freq1=1477; freq2=941; break; case '-': freq1=0; freq2=0; break; default: freq1=0; freq2=0; break; } // 生成当前数字的采样 for(int j = 0; j < samples_per_tone; j++) { double phase1 = 2 * M_PI * freq1 * j / SAMPLE_RATE; double phase2 = 2 * M_PI * freq2 * j / SAMPLE_RATE; int16_t sample = AMPLITUDE * (sin(phase1) + sin(phase2)); pData[sample_idx++] = sample; } } // 写入WAV头 if(fwrite(pWave, sizeof(WAVEFILE), 1, pFile) != 1) { puts("写入文件头失败"); fclose(pFile); free(pData); return -1; } // 写入采样数据 if(fwrite(pData, total_bytes, 1, pFile) != 1) { puts("写入采样数据失败"); fclose(pFile); free(pData); return -1; } // 清理资源 fclose(pFile); free(pData); puts("WAV文件生成成功"); return 0; } void show_usage(void) { printf("\n用法: ./a.exe X Y Z\n\n"); printf(" X 是要创建的WAV文件名\n"); printf(" Y 是每个DTMF音调的持续时间(秒),有效值范围0.1-1.0\n"); printf(" Z 是要生成音调的数字字符串,合法字符为0-9、#、*和-(静音)\n"); printf("\n示例: ./a.exe wavy.wav 0.3 123-3831\n"); }
额外说明
- 修复了所有头字段的赋值和计算错误,确保WAV格式符合标准。
- 改用
float存储tone_length,避免浮点数截断问题。 - 逐个字符校验电话号码输入,确保合法性。
- 调整采样数据写入逻辑,每个数字的采样写入到正确的偏移位置,不再覆盖。
- 添加了
M_PI的定义,提升三角函数计算精度。 - 完善了错误处理流程,内存分配或文件操作失败时及时释放资源并退出。
- 支持
-字符作为静音,生成全0采样。
内容的提问来源于stack exchange,提问作者Devin Jones
相关产品推荐
相关产品推荐

