在c中生成一个.au文件
Posted
技术标签:
【中文标题】在c中生成一个.au文件【英文标题】:generating an .au file in c 【发布时间】:2011-03-30 08:25:00 【问题描述】:出于病态的好奇心,我一直在尝试编写一个程序,该程序将在 C 中生成 4 秒 440 A 音符。但是,在 VLC 中播放输出的文件不会产生任何音乐。
使用 Wikipedia 作为此页面上 .au 标头的指南:http://en.wikipedia.org/wiki/Au_file_format 我想出了这个:
#include <stdio.h>
#include <math.h>
#define MAGIC_NUM 0x2e736e64
#define DEFAULT_OFFSET 24
#define UNKNOWN_SIZE 0xffffffff
#define BIT_32_PCM 5
#define STEREO 2
#define SAMPLE_RATE 8000
#define DURATION 4
#define MIDDLE_A 440
int main(int argc, char** argv)
FILE* sound;
sound = fopen("output.au", "w");
//write header
fputc(MAGIC_NUM, sound);
fputc(DEFAULT_OFFSET, sound);
fputc(UNKNOWN_SIZE, sound);
fputc(BIT_32_PCM, sound);
fputc(SAMPLE_RATE, sound);
fputc(STEREO, sound);
//write a duration of a constant note
int i;
for(i = 0; i <= DURATION * SAMPLE_RATE; i++)
fputc((int)floor(MIDDLE_A * sin(i)), sound);
fclose(sound);
return 0;
有谁知道我做错了什么?
【问题讨论】:
【参考方案1】:您不希望文件以文本形式打开 - 它可能会与任何书面换行符一起做有趣的事情。
sound = fopen("output.au", "wb");
你不想写字符 - 使用fwrite
而不是fputc
fwrite(".snd", 1, 4, sound);
另请注意,.au 文件是大端 - 如果您使用的是 x86 或 x86_64,则您的原生字节顺序是小端,您需要在写入数据之前对其进行转换。
【讨论】:
【参考方案2】:问题是 fputc,它只输出一个字符。例如,您不能使用 fputc 一次写入 MAGIC_NUM。一个解决方案可能是定义您自己的 fput 函数:
// write a word (2 bytes)
void fputw (unsigned int value, FILE* f)
fputc(value & 0xff, f);
fputc(value >> 8 & 0xff, f);
// write a dword (4 bytes)
void fputdw(unsigned int value, FILE* f)
fputc(value & 0xff, f);
fputc(value >> 8 & 0xff, f);
fputc(value >> 16 & 0xff, f);
fputc(value >> 24 & 0xff, f);
//write header
fputdw(MAGIC_NUM, sound);
fputdw(DEFAULT_OFFSET, sound);
fputdw(UNKNOWN_SIZE, sound);
fputdw(BIT_32_PCM, sound);
fputdw(SAMPLE_RATE, sound);
fputdw(STEREO, sound);
【讨论】:
【参考方案3】:关于一次写一个字符的cmets就这一点而言是正确的,但这里有更多的基本问题——
sin()
函数的输出是一个介于 -1.0 .. +1.0 之间的浮点数;然后你就拿这个发言,这将永远是零。
该正弦发生器的每个样本输出需要从浮点数转换为 32 位整数。
您需要以不同的方式转换样本位置——您需要计算角频率(以弧度为单位) 正弦波在样本之间前进。
类似伪代码(未测试...)
SAMPLE_RATE = 44100.0
FREQ = 440.0
PI = 3.14159
DURATION = 4
AMPLITUDE = 1.0
ANGULAR_FREQ = (2 * PI * FREQ) / SAMPLE_RATE
for (int i = 0; i < (int) DURATION * SAMPLE_RATE; ++i)
// get sample value in range -1.0 .. + 1.0
floatSample = AMPLITUDE * sin(i * ANGULAR_FREQ)
// convert to correctly scaled integer representation
intSample = (int) floor(0x7FFFFFFF * floatSample);
// write to file, send out DAC, whatever...
【讨论】:
【参考方案4】:这是 C++ 的工作和测试版本。
createTone()
构建标题并生成音调数据。 This wikipedia article 描述了标头变量 DATA_OFFSET
、DATA_SIZE
、ENCODING
、SAMPLE_RATE
和 CHANNELS
的值。
注意:注意 .au 文件是使用 BIG ENDIAN 生成的,因此在 Intel x86/x64 上编译时需要进行转换,这就是加载 htonl
的原因。
此代码使用线性 PCM 8 调制,对于其他调制,您需要修改/替换 pcm8()
函数。
#include <iostream>
#include <fstream>
#include <sstream>
#include <math.h>
#include <arpa/inet.h>
const int MAGIC_WORD = htonl(0x2e736e64);
const int DATA_OFFSET = htonl(24);
const int DATA_SIZE = htonl(0xffffffff);
const int ENCODING = htonl(0x00000002);
const int SAMPLE_RATE = 0x00001F40;
const int CHANNELS = htonl(0x00000005);
const double PI = 3.14159265358979323846L;
const int PCM_MAX = 0xffffffff;
int pcm8(double value)
double v = ((value + 1) / 2);
double delta = 1.0 / pow(2, 8);
double ret = round(v / delta);
int r = (int) ret;
return r;
void createTone(int duration, double frec)
std::ofstream file;
file.open("tone.au", std::ios::binary);
// header
const int sampleRate = htonl(SAMPLE_RATE);
file.write((char*) &MAGIC_WORD, sizeof(int));
file.write((char*) &DATA_OFFSET, sizeof(int));
file.write((char*) &DATA_SIZE, sizeof(int));
file.write((char*) &ENCODING, sizeof(int));
file.write((char*) &sampleRate, sizeof(int));
file.write((char*) &CHANNELS, sizeof(int));
// data
double t = 0;
while (t < duration)
int level = htonl(pcm8(sin(2 * PI * frec * t)));
file.write((char*) &level, sizeof(int));
t += 1.0 / ((double) SAMPLE_RATE);
file.close();
int main(int argc, char** argv)
createTone(4, 440.0);
return 0;
【讨论】:
以上是关于在c中生成一个.au文件的主要内容,如果未能解决你的问题,请参考以下文章