在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_OFFSETDATA_SIZEENCODINGSAMPLE_RATECHANNELS 的值。

注意:注意 .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文件的主要内容,如果未能解决你的问题,请参考以下文章

csharp 在C#中生成一个随机数

[转]如何禁止 IIS 在 C:WindowsSystem32LogFilesHTTPERR 中生成日志文件

在.net项目中生成的pdb是啥文件?

在目标c中生成组合的有效方法[重复]

在gcc、g++中生成ASM代码需要啥

如何在字典中生成所有可能的组合