在c中生成.au文件

时间:2011-03-30 08:25:01

标签: c audio

出于病态的好奇心,我一直试图想出一个能在C中产生4秒440 A音符的程序。但是,在VLC中播放输出的文件不会产生任何音乐。

使用维基百科作为本页面上.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;
}

有谁知道我做错了什么?

4 个答案:

答案 0 :(得分:4)

您不希望文件作为文本打开 - 它可能会使用任何书面换行进行有趣的事情。

sound = fopen("output.au", "wb");

您不想写字符 - 使用fwrite而不是fputc

fwrite(".snd", 1, 4, sound);

另请注意.au文件是big-endian - 如果您使用的是x86或x86_64,则您的本机字节顺序是小端,并且您需要在编写之前转换数据。

答案 1 :(得分:1)

问题是fputc,它只输出一个char。例如,您不能使用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);

答案 2 :(得分:0)

关于一次写一个角色的评论是这样的,但这里有更多的基本问题 -

  1. sin()函数的输出是一个范围在-1.0 .. + 1.0之间的浮点数;然后,你要发言,这将始终为零。

  2. 该正弦生成器的每个样本输出需要从float转换为32位整数。

  3. 您需要以不同方式转换样本位置 - 您需要计算角频率(以弧度为单位) 正弦波在样本之间前进。

  4. 伪代码(未经测试......)

    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...
    }
    

答案 3 :(得分:0)

这是一个经过工作和测试的C ++版本。

createTone()构建标题并生成音调数据。 This wikipedia article描述了标头变量DATA_OFFSETDATA_SIZEENCODINGSAMPLE_RATECHANNELS的值。

注意:请注意.au文件是使用BIG ENDIAN生成的,因此在Intel x86 / x64上进行编译时,您需要进行转换,这就是加载htonl的原因。 / p>

此代码使用线性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;
}