【问题标题】:generating an .au file in c在c中生成一个.au文件
【发布时间】: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;
}

有谁知道我做错了什么?

【问题讨论】:

    标签: c audio


    【解决方案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就这一点而言是正确的,但这里有更多的基本问题——

        1. sin() 函数的输出是一个介于 -1.0 .. +1.0 之间的浮点数;然后你就拿这个发言,这将永远是零。

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

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

        类似伪代码(未测试...)

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-01-29
            • 1970-01-01
            • 1970-01-01
            • 2021-12-30
            • 1970-01-01
            相关资源
            最近更新 更多