出于病态的好奇心,我一直试图想出一个能在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;
}
有谁知道我做错了什么?
答案 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)
关于一次写一个角色的评论是这样的,但这里有更多的基本问题 -
sin()
函数的输出是一个范围在-1.0 .. + 1.0之间的浮点数;然后,你要发言,这将始终为零。
该正弦生成器的每个样本输出需要从float转换为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...
}
答案 3 :(得分:0)
这是一个经过工作和测试的C ++版本。
createTone()
构建标题并生成音调数据。 This wikipedia article描述了标头变量DATA_OFFSET
,DATA_SIZE
,ENCODING
,SAMPLE_RATE
和CHANNELS
的值。
注意:请注意.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;
}