создание файла .au в c - PullRequest
2 голосов
/ 30 марта 2011

Из болезненного любопытства я пытался придумать программу, которая будет генерировать 4-секундную ноту в 440 А на C. Однако воспроизведение выходного файла в 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 ]

4 голосов
/ 30 марта 2011

Вы не хотите, чтобы файл открывался как текст - он может делать забавные вещи с любыми письменными переводами строки.

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

Вы не хотите писать символов - используйте fwrite, а не fputc

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

Также обратите внимание, что файлы .au имеют формат с прямым порядком байтов - если вы используете x86 или x86_64, ваш собственный порядок байтов имеет младший порядок байтов, и вам необходимо преобразовать данные перед записью.

1 голос
/ 30 марта 2011

Проблема в fputc, он выводит только символ.Например, вы не можете написать MAGIC_NUM сразу, используя fputc.Решением может быть определение ваших собственных функций 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);
0 голосов
/ 30 сентября 2014

Это рабочая и проверенная версия для C ++.

createTone() создает заголовок и генерирует тональные данные. Эта статья в Википедии описывает значения переменных заголовка 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;
}
0 голосов
/ 30 марта 2011

Комментарии по написанию персонажа за один раз дошли до точки, но здесь есть более фундаментальные проблемы -

  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...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...